wgpu_core/command/
mod.rs

1//! # Command Encoding
2//!
3//! TODO: High-level description of command encoding.
4//!
5//! The convention in this module is that functions accepting a [`&mut dyn
6//! hal::DynCommandEncoder`] are low-level helpers and may assume the encoder is
7//! in the open state, ready to encode commands. Encoders that are not open
8//! should be nested within some other container that provides additional
9//! state tracking, like [`InnerCommandEncoder`].
10
11mod allocator;
12mod bind;
13mod bundle;
14mod clear;
15mod compute;
16mod compute_command;
17mod draw;
18mod encoder;
19mod encoder_command;
20pub mod ffi;
21mod memory_init;
22mod pass;
23mod query;
24mod ray_tracing;
25mod render;
26mod render_command;
27mod timestamp_writes;
28mod transfer;
29mod transition_resources;
30
31use alloc::{borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec::Vec};
32use core::convert::Infallible;
33use core::mem::{self, ManuallyDrop};
34use core::{ops, panic};
35
36pub(crate) use self::clear::clear_texture;
37#[cfg(feature = "serde")]
38pub(crate) use self::encoder_command::serde_object_reference_struct;
39#[cfg(any(feature = "trace", feature = "replay"))]
40#[doc(hidden)]
41pub use self::encoder_command::PointerReferences;
42pub use self::{
43    bundle::*,
44    clear::ClearError,
45    compute::*,
46    compute_command::ArcComputeCommand,
47    draw::*,
48    encoder_command::{ArcCommand, ArcReferences, Command, IdReferences, ReferenceType},
49    query::*,
50    render::*,
51    render_command::ArcRenderCommand,
52    transfer::*,
53};
54pub(crate) use allocator::CommandAllocator;
55
56/// cbindgen:ignore
57pub use self::{compute_command::ComputeCommand, render_command::RenderCommand};
58
59pub(crate) use timestamp_writes::ArcPassTimestampWrites;
60pub use timestamp_writes::PassTimestampWrites;
61
62use self::{
63    clear::{clear_buffer, clear_texture_cmd},
64    memory_init::CommandBufferTextureMemoryActions,
65    ray_tracing::build_acceleration_structures,
66    transition_resources::transition_resources,
67};
68
69use crate::binding_model::BindingError;
70use crate::command::encoder::EncodingState;
71use crate::command::transition_resources::TransitionResourcesError;
72use crate::device::queue::TempResource;
73use crate::device::{Device, DeviceError, MissingFeatures};
74use crate::id::Id;
75use crate::lock::{rank, Mutex};
76use crate::snatch::SnatchGuard;
77
78use crate::init_tracker::BufferInitTrackerAction;
79use crate::ray_tracing::{AsAction, BuildAccelerationStructureError};
80use crate::resource::{
81    DestroyedResourceError, Fallible, InvalidResourceError, Labeled, ParentDevice as _, QuerySet,
82};
83use crate::storage::Storage;
84use crate::track::{DeviceTracker, ResourceUsageCompatibilityError, Tracker, UsageScope};
85use crate::{api_log, global::Global, id, resource_log, Label};
86use crate::{hal_label, LabelHelpers};
87
88use wgt::error::{ErrorType, WebGpuError};
89
90use thiserror::Error;
91
92/// cbindgen:ignore
93pub type TexelCopyBufferInfo = ffi::TexelCopyBufferInfo;
94/// cbindgen:ignore
95pub type TexelCopyTextureInfo = ffi::TexelCopyTextureInfo;
96/// cbindgen:ignore
97pub type CopyExternalImageDestInfo = ffi::CopyExternalImageDestInfo;
98
99const PUSH_CONSTANT_CLEAR_ARRAY: &[u32] = &[0_u32; 64];
100
101/// The current state of a command or pass encoder.
102///
103/// In the WebGPU spec, the state of an encoder (open, locked, or ended) is
104/// orthogonal to the validity of the encoder. However, this enum does not
105/// represent the state of an invalid encoder.
106pub(crate) enum CommandEncoderStatus {
107    /// Ready to record commands. An encoder's initial state.
108    ///
109    /// Command building methods like [`command_encoder_clear_buffer`] and
110    /// [`compute_pass_end`] require the encoder to be in this
111    /// state.
112    ///
113    /// This corresponds to WebGPU's "open" state.
114    /// See <https://www.w3.org/TR/webgpu/#encoder-state-open>
115    ///
116    /// [`command_encoder_clear_buffer`]: Global::command_encoder_clear_buffer
117    /// [`compute_pass_end`]: Global::compute_pass_end
118    Recording(CommandBufferMutable),
119
120    /// Locked by a render or compute pass.
121    ///
122    /// This state is entered when a render/compute pass is created,
123    /// and exited when the pass is ended.
124    ///
125    /// As long as the command encoder is locked, any command building operation
126    /// on it will fail and put the encoder into the [`Self::Error`] state. See
127    /// <https://www.w3.org/TR/webgpu/#encoder-state-locked>
128    Locked(CommandBufferMutable),
129
130    Consumed,
131
132    /// Command recording is complete, and the buffer is ready for submission.
133    ///
134    /// [`Global::command_encoder_finish`] transitions a
135    /// `CommandBuffer` from the `Recording` state into this state.
136    ///
137    /// [`Global::queue_submit`] requires that command buffers are
138    /// in this state.
139    ///
140    /// This corresponds to WebGPU's "ended" state.
141    /// See <https://www.w3.org/TR/webgpu/#encoder-state-ended>
142    Finished(CommandBufferMutable),
143
144    /// The command encoder is invalid.
145    ///
146    /// The error that caused the invalidation is stored here, and will
147    /// be raised by `CommandEncoder.finish()`.
148    Error(CommandEncoderError),
149
150    /// Temporary state used internally by methods on `CommandEncoderStatus`.
151    /// Encoder should never be left in this state.
152    Transitioning,
153}
154
155impl CommandEncoderStatus {
156    #[doc(hidden)]
157    fn replay(&mut self, commands: Vec<Command<ArcReferences>>) {
158        let Self::Recording(cmd_buf_data) = self else {
159            panic!("encoder should be in the recording state");
160        };
161        cmd_buf_data.commands.extend(commands);
162    }
163
164    /// Push a command provided by a closure onto the encoder.
165    ///
166    /// If the encoder is in the [`Self::Recording`] state, calls the closure to
167    /// obtain a command, and pushes it onto the encoder. If the closure returns
168    /// an error, stores that error in the encoder for later reporting when
169    /// `finish()` is called. Returns `Ok(())` even if the closure returned an
170    /// error.
171    ///
172    /// If the encoder is not in the [`Self::Recording`] state, the closure will
173    /// not be called and nothing will be recorded. The encoder will be
174    /// invalidated (if it is not already). If the error is a [validation error
175    /// that should be raised immediately][ves], returns it in `Err`, otherwise,
176    /// returns `Ok(())`.
177    ///
178    /// [ves]: https://www.w3.org/TR/webgpu/#abstract-opdef-validate-the-encoder-state
179    fn push_with<F: FnOnce() -> Result<ArcCommand, E>, E: Clone + Into<CommandEncoderError>>(
180        &mut self,
181        f: F,
182    ) -> Result<(), EncoderStateError> {
183        match self {
184            Self::Recording(cmd_buf_data) => {
185                cmd_buf_data.encoder.api.set(EncodingApi::Wgpu);
186                match f() {
187                    Ok(cmd) => cmd_buf_data.commands.push(cmd),
188                    Err(err) => {
189                        self.invalidate(err);
190                    }
191                }
192                Ok(())
193            }
194            Self::Locked(_) => {
195                // Invalidate the encoder and do not record anything, but do not
196                // return an immediate validation error.
197                self.invalidate(EncoderStateError::Locked);
198                Ok(())
199            }
200            // Encoder is ended. Invalidate the encoder, do not record anything,
201            // and return an immediate validation error.
202            Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
203            Self::Consumed => Err(EncoderStateError::Ended),
204            // Encoder is already invalid. Do not record anything, but do not
205            // return an immediate validation error.
206            Self::Error(_) => Ok(()),
207            Self::Transitioning => unreachable!(),
208        }
209    }
210
211    /// Call a closure with the inner command buffer structure.
212    ///
213    /// If the encoder is in the [`Self::Recording`] state, calls the provided
214    /// closure. If the closure returns an error, stores that error in the
215    /// encoder for later reporting when `finish()` is called. Returns `Ok(())`
216    /// even if the closure returned an error.
217    ///
218    /// If the encoder is not in the [`Self::Recording`] state, the closure will
219    /// not be called. The encoder will be invalidated (if it is not already).
220    /// If the error is a [validation error that should be raised
221    /// immediately][ves], returns it in `Err`, otherwise, returns `Ok(())`.
222    ///
223    /// [ves]: https://www.w3.org/TR/webgpu/#abstract-opdef-validate-the-encoder-state
224    fn with_buffer<
225        F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
226        E: Clone + Into<CommandEncoderError>,
227    >(
228        &mut self,
229        api: EncodingApi,
230        f: F,
231    ) -> Result<(), EncoderStateError> {
232        match self {
233            Self::Recording(inner) => {
234                inner.encoder.api.set(api);
235                RecordingGuard { inner: self }.record(f);
236                Ok(())
237            }
238            Self::Locked(_) => {
239                // Invalidate the encoder and do not record anything, but do not
240                // return an immediate validation error.
241                self.invalidate(EncoderStateError::Locked);
242                Ok(())
243            }
244            // Encoder is ended. Invalidate the encoder, do not record anything,
245            // and return an immediate validation error.
246            Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
247            Self::Consumed => Err(EncoderStateError::Ended),
248            // Encoder is already invalid. Do not record anything, but do not
249            // return an immediate validation error.
250            Self::Error(_) => Ok(()),
251            Self::Transitioning => unreachable!(),
252        }
253    }
254
255    /// Special version of record used by `command_encoder_as_hal_mut`. This
256    /// differs from the regular version in two ways:
257    ///
258    /// 1. The recording closure is infallible.
259    /// 2. The recording closure takes `Option<&mut CommandBufferMutable>`, and
260    ///    in the case that the encoder is not in a valid state for recording, the
261    ///    closure is still called, with `None` as its argument.
262    pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
263        &mut self,
264        f: F,
265    ) -> T {
266        match self {
267            Self::Recording(inner) => {
268                inner.encoder.api.set(EncodingApi::Raw);
269                RecordingGuard { inner: self }.record_as_hal_mut(f)
270            }
271            Self::Locked(_) => {
272                self.invalidate(EncoderStateError::Locked);
273                f(None)
274            }
275            Self::Finished(_) => {
276                self.invalidate(EncoderStateError::Ended);
277                f(None)
278            }
279            Self::Consumed => f(None),
280            Self::Error(_) => f(None),
281            Self::Transitioning => unreachable!(),
282        }
283    }
284
285    /// Locks the encoder by putting it in the [`Self::Locked`] state.
286    ///
287    /// Render or compute passes call this on start. At the end of the pass,
288    /// they call [`Self::unlock_encoder`] to put the [`CommandBuffer`] back
289    /// into the [`Self::Recording`] state.
290    fn lock_encoder(&mut self) -> Result<(), EncoderStateError> {
291        match mem::replace(self, Self::Transitioning) {
292            Self::Recording(inner) => {
293                *self = Self::Locked(inner);
294                Ok(())
295            }
296            st @ Self::Finished(_) => {
297                // Attempting to open a pass on a finished encoder raises a
298                // validation error but does not invalidate the encoder. This is
299                // related to https://github.com/gpuweb/gpuweb/issues/5207.
300                *self = st;
301                Err(EncoderStateError::Ended)
302            }
303            Self::Locked(_) => Err(self.invalidate(EncoderStateError::Locked)),
304            st @ Self::Consumed => {
305                *self = st;
306                Err(EncoderStateError::Ended)
307            }
308            st @ Self::Error(_) => {
309                *self = st;
310                Err(EncoderStateError::Invalid)
311            }
312            Self::Transitioning => unreachable!(),
313        }
314    }
315
316    /// Unlocks the encoder and puts it back into the [`Self::Recording`] state.
317    ///
318    /// This function is the unlocking counterpart to [`Self::lock_encoder`]. It
319    /// is only valid to call this function if the encoder is in the
320    /// [`Self::Locked`] state.
321    ///
322    /// If the encoder is in a state other than [`Self::Locked`] and a
323    /// validation error should be raised immediately, returns it in `Err`,
324    /// otherwise, stores the error in the encoder and returns `Ok(())`.
325    fn unlock_encoder(&mut self) -> Result<(), EncoderStateError> {
326        match mem::replace(self, Self::Transitioning) {
327            Self::Locked(inner) => {
328                *self = Self::Recording(inner);
329                Ok(())
330            }
331            st @ Self::Finished(_) => {
332                *self = st;
333                Err(EncoderStateError::Ended)
334            }
335            Self::Recording(_) => {
336                *self = Self::Error(EncoderStateError::Unlocked.into());
337                Err(EncoderStateError::Unlocked)
338            }
339            st @ Self::Consumed => {
340                *self = st;
341                Err(EncoderStateError::Ended)
342            }
343            st @ Self::Error(_) => {
344                // Encoder is already invalid. The error will be reported by
345                // `CommandEncoder.finish`.
346                *self = st;
347                Ok(())
348            }
349            Self::Transitioning => unreachable!(),
350        }
351    }
352
353    fn finish(&mut self) -> Self {
354        // Replace our state with `Consumed`, and return either the inner
355        // state or an error, to be transferred to the command buffer.
356        match mem::replace(self, Self::Consumed) {
357            Self::Recording(inner) => {
358                // Raw encoding leaves the encoder open in `command_encoder_as_hal_mut`.
359                // Otherwise, nothing should have opened it yet.
360                if inner.encoder.api != EncodingApi::Raw {
361                    assert!(!inner.encoder.is_open);
362                }
363                Self::Finished(inner)
364            }
365            Self::Consumed | Self::Finished(_) => Self::Error(EncoderStateError::Ended.into()),
366            Self::Locked(_) => Self::Error(EncoderStateError::Locked.into()),
367            st @ Self::Error(_) => st,
368            Self::Transitioning => unreachable!(),
369        }
370    }
371
372    // Invalidate the command encoder and store the error `err` causing the
373    // invalidation for diagnostic purposes.
374    //
375    // Since we do not track the state of an invalid encoder, it is not
376    // necessary to unlock an encoder that has been invalidated.
377    fn invalidate<E: Clone + Into<CommandEncoderError>>(&mut self, err: E) -> E {
378        let enc_err = err.clone().into();
379        api_log!("Invalidating command encoder: {enc_err:?}");
380        *self = Self::Error(enc_err);
381        err
382    }
383}
384
385/// A guard to enforce error reporting, for a [`CommandBuffer`] in the [`Recording`] state.
386///
387/// An [`RecordingGuard`] holds a mutable reference to a [`CommandEncoderStatus`] that
388/// has been verified to be in the [`Recording`] state. The [`RecordingGuard`] dereferences
389/// mutably to the [`CommandBufferMutable`] that the status holds.
390///
391/// Dropping an [`RecordingGuard`] sets the [`CommandBuffer`]'s state to
392/// [`CommandEncoderStatus::Error`]. If your use of the guard was
393/// successful, call its [`mark_successful`] method to dispose of it.
394///
395/// [`Recording`]: CommandEncoderStatus::Recording
396/// [`mark_successful`]: Self::mark_successful
397pub(crate) struct RecordingGuard<'a> {
398    inner: &'a mut CommandEncoderStatus,
399}
400
401impl<'a> RecordingGuard<'a> {
402    pub(crate) fn mark_successful(self) {
403        mem::forget(self)
404    }
405
406    fn record<
407        F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
408        E: Clone + Into<CommandEncoderError>,
409    >(
410        mut self,
411        f: F,
412    ) {
413        match f(&mut self) {
414            Ok(()) => self.mark_successful(),
415            Err(err) => {
416                self.inner.invalidate(err);
417            }
418        }
419    }
420
421    /// Special version of record used by `command_encoder_as_hal_mut`. This
422    /// version takes an infallible recording closure.
423    pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
424        mut self,
425        f: F,
426    ) -> T {
427        let res = f(Some(&mut self));
428        self.mark_successful();
429        res
430    }
431}
432
433impl<'a> Drop for RecordingGuard<'a> {
434    fn drop(&mut self) {
435        if matches!(*self.inner, CommandEncoderStatus::Error(_)) {
436            // Don't overwrite an error that is already present.
437            return;
438        }
439        self.inner.invalidate(EncoderStateError::Invalid);
440    }
441}
442
443impl<'a> ops::Deref for RecordingGuard<'a> {
444    type Target = CommandBufferMutable;
445
446    fn deref(&self) -> &Self::Target {
447        match &*self.inner {
448            CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
449            _ => unreachable!(),
450        }
451    }
452}
453
454impl<'a> ops::DerefMut for RecordingGuard<'a> {
455    fn deref_mut(&mut self) -> &mut Self::Target {
456        match self.inner {
457            CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
458            _ => unreachable!(),
459        }
460    }
461}
462
463pub(crate) struct CommandEncoder {
464    pub(crate) device: Arc<Device>,
465
466    pub(crate) label: String,
467
468    /// The mutable state of this command encoder.
469    pub(crate) data: Mutex<CommandEncoderStatus>,
470}
471
472crate::impl_resource_type!(CommandEncoder);
473crate::impl_labeled!(CommandEncoder);
474crate::impl_parent_device!(CommandEncoder);
475crate::impl_storage_item!(CommandEncoder);
476
477impl Drop for CommandEncoder {
478    fn drop(&mut self) {
479        resource_log!("Drop {}", self.error_ident());
480    }
481}
482
483/// The encoding API being used with a `CommandEncoder`.
484///
485/// Mixing APIs on the same encoder is not allowed.
486#[derive(Copy, Clone, Debug, Eq, PartialEq)]
487pub enum EncodingApi {
488    // The regular wgpu encoding APIs are being used.
489    Wgpu,
490
491    // The raw hal encoding API is being used.
492    Raw,
493
494    // Neither encoding API has been called yet.
495    Undecided,
496
497    // The encoder is used internally by wgpu.
498    InternalUse,
499}
500
501impl EncodingApi {
502    pub(crate) fn set(&mut self, api: EncodingApi) {
503        match *self {
504            EncodingApi::Undecided => {
505                *self = api;
506            }
507            self_api if self_api != api => {
508                panic!("Mixing the wgpu encoding API with the raw encoding API is not permitted");
509            }
510            _ => {}
511        }
512    }
513}
514
515/// A raw [`CommandEncoder`][rce], and the raw [`CommandBuffer`][rcb]s built from it.
516///
517/// Each wgpu-core [`CommandBuffer`] owns an instance of this type, which is
518/// where the commands are actually stored.
519///
520/// This holds a `Vec` of raw [`CommandBuffer`][rcb]s, not just one. We are not
521/// always able to record commands in the order in which they must ultimately be
522/// submitted to the queue, but raw command buffers don't permit inserting new
523/// commands into the middle of a recorded stream. However, hal queue submission
524/// accepts a series of command buffers at once, so we can simply break the
525/// stream up into multiple buffers, and then reorder the buffers. See
526/// [`InnerCommandEncoder::close_and_swap`] for a specific example of this.
527///
528/// [rce]: hal::Api::CommandEncoder
529/// [rcb]: hal::Api::CommandBuffer
530pub(crate) struct InnerCommandEncoder {
531    /// The underlying `wgpu_hal` [`CommandEncoder`].
532    ///
533    /// Successfully executed command buffers' encoders are saved in a
534    /// [`CommandAllocator`] for recycling.
535    ///
536    /// [`CommandEncoder`]: hal::Api::CommandEncoder
537    /// [`CommandAllocator`]: crate::command::CommandAllocator
538    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
539
540    /// All the raw command buffers for our owning [`CommandBuffer`], in
541    /// submission order.
542    ///
543    /// These command buffers were all constructed with `raw`. The
544    /// [`wgpu_hal::CommandEncoder`] trait forbids these from outliving `raw`,
545    /// and requires that we provide all of these when we call
546    /// [`raw.reset_all()`][CE::ra], so the encoder and its buffers travel
547    /// together.
548    ///
549    /// [CE::ra]: hal::CommandEncoder::reset_all
550    /// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
551    pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
552
553    pub(crate) device: Arc<Device>,
554
555    /// True if `raw` is in the "recording" state.
556    ///
557    /// See the documentation for [`wgpu_hal::CommandEncoder`] for
558    /// details on the states `raw` can be in.
559    ///
560    /// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
561    pub(crate) is_open: bool,
562
563    /// Tracks which API is being used to encode commands.
564    ///
565    /// Mixing the wgpu encoding API with access to the raw hal encoder via
566    /// `as_hal_mut` is not supported. this field tracks which API is being used
567    /// in order to detect and reject invalid usage.
568    pub(crate) api: EncodingApi,
569
570    pub(crate) label: String,
571}
572
573impl InnerCommandEncoder {
574    /// Finish the current command buffer and insert it just before
575    /// the last element in [`self.list`][l].
576    ///
577    /// On return, the underlying hal encoder is closed.
578    ///
579    /// What is this for?
580    ///
581    /// The `wgpu_hal` contract requires that each render or compute pass's
582    /// commands be preceded by calls to [`transition_buffers`] and
583    /// [`transition_textures`], to put the resources the pass operates on in
584    /// the appropriate state. Unfortunately, we don't know which transitions
585    /// are needed until we're done recording the pass itself. Rather than
586    /// iterating over the pass twice, we note the necessary transitions as we
587    /// record its commands, finish the raw command buffer for the actual pass,
588    /// record a new raw command buffer for the transitions, and jam that buffer
589    /// in just before the pass's. This is the function that jams in the
590    /// transitions' command buffer.
591    ///
592    /// # Panics
593    ///
594    /// - If the encoder is not open.
595    ///
596    /// [l]: InnerCommandEncoder::list
597    /// [`transition_buffers`]: hal::CommandEncoder::transition_buffers
598    /// [`transition_textures`]: hal::CommandEncoder::transition_textures
599    fn close_and_swap(&mut self) -> Result<(), DeviceError> {
600        assert!(self.is_open);
601        self.is_open = false;
602
603        let new =
604            unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
605        self.list.insert(self.list.len() - 1, new);
606
607        Ok(())
608    }
609
610    /// Finish the current command buffer and insert it at the beginning
611    /// of [`self.list`][l].
612    ///
613    /// On return, the underlying hal encoder is closed.
614    ///
615    /// # Panics
616    ///
617    /// - If the encoder is not open.
618    ///
619    /// [l]: InnerCommandEncoder::list
620    pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> {
621        assert!(self.is_open);
622        self.is_open = false;
623
624        let new =
625            unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
626        self.list.insert(0, new);
627
628        Ok(())
629    }
630
631    /// Finish the current command buffer, and push it onto
632    /// the end of [`self.list`][l].
633    ///
634    /// On return, the underlying hal encoder is closed.
635    ///
636    /// # Panics
637    ///
638    /// - If the encoder is not open.
639    ///
640    /// [l]: InnerCommandEncoder::list
641    pub(crate) fn close(&mut self) -> Result<(), DeviceError> {
642        assert!(self.is_open);
643        self.is_open = false;
644
645        let cmd_buf =
646            unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
647        self.list.push(cmd_buf);
648
649        Ok(())
650    }
651
652    /// Finish the current command buffer, if any, and add it to the
653    /// end of [`self.list`][l].
654    ///
655    /// If we have opened this command encoder, finish its current
656    /// command buffer, and push it onto the end of [`self.list`][l].
657    /// If this command buffer is closed, do nothing.
658    ///
659    /// On return, the underlying hal encoder is closed.
660    ///
661    /// [l]: InnerCommandEncoder::list
662    fn close_if_open(&mut self) -> Result<(), DeviceError> {
663        if self.is_open {
664            self.is_open = false;
665            let cmd_buf =
666                unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
667            self.list.push(cmd_buf);
668        }
669
670        Ok(())
671    }
672
673    /// If the command encoder is not open, begin recording a new command buffer.
674    ///
675    /// If the command encoder was already open, does nothing.
676    ///
677    /// In both cases, returns a reference to the raw encoder.
678    fn open_if_closed(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
679        if !self.is_open {
680            self.is_open = true;
681            let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
682            unsafe { self.raw.begin_encoding(hal_label) }
683                .map_err(|e| self.device.handle_hal_error(e))?;
684        }
685
686        Ok(self.raw.as_mut())
687    }
688
689    /// Begin recording a new command buffer, if we haven't already.
690    ///
691    /// The underlying hal encoder is put in the "recording" state.
692    pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
693        if !self.is_open {
694            self.is_open = true;
695            let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
696            unsafe { self.raw.begin_encoding(hal_label) }
697                .map_err(|e| self.device.handle_hal_error(e))?;
698        }
699
700        Ok(self.raw.as_mut())
701    }
702
703    /// Begin recording a new command buffer for a render or compute pass, with
704    /// its own label.
705    ///
706    /// The underlying hal encoder is put in the "recording" state.
707    ///
708    /// # Panics
709    ///
710    /// - If the encoder is already open.
711    pub(crate) fn open_pass(
712        &mut self,
713        label: Option<&str>,
714    ) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
715        assert!(!self.is_open);
716        self.is_open = true;
717
718        let hal_label = hal_label(label, self.device.instance_flags);
719        unsafe { self.raw.begin_encoding(hal_label) }
720            .map_err(|e| self.device.handle_hal_error(e))?;
721
722        Ok(self.raw.as_mut())
723    }
724}
725
726impl Drop for InnerCommandEncoder {
727    fn drop(&mut self) {
728        if self.is_open {
729            unsafe { self.raw.discard_encoding() };
730        }
731        unsafe {
732            self.raw.reset_all(mem::take(&mut self.list));
733        }
734        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
735        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
736        self.device.command_allocator.release_encoder(raw);
737    }
738}
739
740/// Look at the documentation for [`CommandBufferMutable`] for an explanation of
741/// the fields in this struct. This is the "built" counterpart to that type.
742pub(crate) struct BakedCommands {
743    pub(crate) encoder: InnerCommandEncoder,
744    pub(crate) trackers: Tracker,
745    pub(crate) temp_resources: Vec<TempResource>,
746    pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
747    buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
748    texture_memory_actions: CommandBufferTextureMemoryActions,
749}
750
751/// The mutable state of a [`CommandBuffer`].
752pub struct CommandBufferMutable {
753    /// The [`wgpu_hal::Api::CommandBuffer`]s we've built so far, and the encoder
754    /// they belong to.
755    ///
756    /// [`wgpu_hal::Api::CommandBuffer`]: hal::Api::CommandBuffer
757    pub(crate) encoder: InnerCommandEncoder,
758
759    /// All the resources that the commands recorded so far have referred to.
760    pub(crate) trackers: Tracker,
761
762    /// The regions of buffers and textures these commands will read and write.
763    ///
764    /// This is used to determine which portions of which
765    /// buffers/textures we actually need to initialize. If we're
766    /// definitely going to write to something before we read from it,
767    /// we don't need to clear its contents.
768    buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
769    texture_memory_actions: CommandBufferTextureMemoryActions,
770
771    as_actions: Vec<AsAction>,
772    temp_resources: Vec<TempResource>,
773
774    indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
775
776    pub(crate) commands: Vec<Command<ArcReferences>>,
777
778    /// If tracing, `command_encoder_finish` replaces the `Arc`s in `commands`
779    /// with integer pointers, and moves them into `trace_commands`.
780    #[cfg(feature = "trace")]
781    pub(crate) trace_commands: Option<Vec<Command<PointerReferences>>>,
782}
783
784impl CommandBufferMutable {
785    pub(crate) fn into_baked_commands(self) -> BakedCommands {
786        BakedCommands {
787            encoder: self.encoder,
788            trackers: self.trackers,
789            temp_resources: self.temp_resources,
790            indirect_draw_validation_resources: self.indirect_draw_validation_resources,
791            buffer_memory_init_actions: self.buffer_memory_init_actions,
792            texture_memory_actions: self.texture_memory_actions,
793        }
794    }
795}
796
797/// A buffer of commands to be submitted to the GPU for execution.
798///
799/// Once a command buffer is submitted to the queue, its contents are taken
800/// to construct a [`BakedCommands`], whose contents eventually become the
801/// property of the submission queue.
802pub struct CommandBuffer {
803    pub(crate) device: Arc<Device>,
804    /// The `label` from the descriptor used to create the resource.
805    label: String,
806
807    /// The mutable state of this command buffer.
808    pub(crate) data: Mutex<CommandEncoderStatus>,
809}
810
811impl Drop for CommandBuffer {
812    fn drop(&mut self) {
813        resource_log!("Drop {}", self.error_ident());
814    }
815}
816
817impl CommandEncoder {
818    pub(crate) fn new(
819        encoder: Box<dyn hal::DynCommandEncoder>,
820        device: &Arc<Device>,
821        label: &Label,
822    ) -> Self {
823        CommandEncoder {
824            device: device.clone(),
825            label: label.to_string(),
826            data: Mutex::new(
827                rank::COMMAND_BUFFER_DATA,
828                CommandEncoderStatus::Recording(CommandBufferMutable {
829                    encoder: InnerCommandEncoder {
830                        raw: ManuallyDrop::new(encoder),
831                        list: Vec::new(),
832                        device: device.clone(),
833                        is_open: false,
834                        api: EncodingApi::Undecided,
835                        label: label.to_string(),
836                    },
837                    trackers: Tracker::new(),
838                    buffer_memory_init_actions: Default::default(),
839                    texture_memory_actions: Default::default(),
840                    as_actions: Default::default(),
841                    temp_resources: Default::default(),
842                    indirect_draw_validation_resources:
843                        crate::indirect_validation::DrawResources::new(device.clone()),
844                    commands: Vec::new(),
845                    #[cfg(feature = "trace")]
846                    trace_commands: if device.trace.lock().is_some() {
847                        Some(Vec::new())
848                    } else {
849                        None
850                    },
851                }),
852            ),
853        }
854    }
855
856    pub(crate) fn new_invalid(
857        device: &Arc<Device>,
858        label: &Label,
859        err: CommandEncoderError,
860    ) -> Self {
861        CommandEncoder {
862            device: device.clone(),
863            label: label.to_string(),
864            data: Mutex::new(rank::COMMAND_BUFFER_DATA, CommandEncoderStatus::Error(err)),
865        }
866    }
867
868    pub(crate) fn insert_barriers_from_tracker(
869        raw: &mut dyn hal::DynCommandEncoder,
870        base: &mut Tracker,
871        head: &Tracker,
872        snatch_guard: &SnatchGuard,
873    ) {
874        profiling::scope!("insert_barriers");
875
876        base.buffers.set_from_tracker(&head.buffers);
877        base.textures.set_from_tracker(&head.textures);
878
879        Self::drain_barriers(raw, base, snatch_guard);
880    }
881
882    pub(crate) fn insert_barriers_from_scope(
883        raw: &mut dyn hal::DynCommandEncoder,
884        base: &mut Tracker,
885        head: &UsageScope,
886        snatch_guard: &SnatchGuard,
887    ) {
888        profiling::scope!("insert_barriers");
889
890        base.buffers.set_from_usage_scope(&head.buffers);
891        base.textures.set_from_usage_scope(&head.textures);
892
893        Self::drain_barriers(raw, base, snatch_guard);
894    }
895
896    pub(crate) fn drain_barriers(
897        raw: &mut dyn hal::DynCommandEncoder,
898        base: &mut Tracker,
899        snatch_guard: &SnatchGuard,
900    ) {
901        profiling::scope!("drain_barriers");
902
903        let buffer_barriers = base
904            .buffers
905            .drain_transitions(snatch_guard)
906            .collect::<Vec<_>>();
907        let (transitions, textures) = base.textures.drain_transitions(snatch_guard);
908        let texture_barriers = transitions
909            .into_iter()
910            .enumerate()
911            .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))
912            .collect::<Vec<_>>();
913
914        unsafe {
915            raw.transition_buffers(&buffer_barriers);
916            raw.transition_textures(&texture_barriers);
917        }
918    }
919
920    pub(crate) fn insert_barriers_from_device_tracker(
921        raw: &mut dyn hal::DynCommandEncoder,
922        base: &mut DeviceTracker,
923        head: &Tracker,
924        snatch_guard: &SnatchGuard,
925    ) {
926        profiling::scope!("insert_barriers_from_device_tracker");
927
928        let buffer_barriers = base
929            .buffers
930            .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)
931            .collect::<Vec<_>>();
932
933        let texture_barriers = base
934            .textures
935            .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)
936            .collect::<Vec<_>>();
937
938        unsafe {
939            raw.transition_buffers(&buffer_barriers);
940            raw.transition_textures(&texture_barriers);
941        }
942    }
943
944    fn finish(
945        self: &Arc<Self>,
946        desc: &wgt::CommandBufferDescriptor<Label>,
947    ) -> (Arc<CommandBuffer>, Option<CommandEncoderError>) {
948        let mut cmd_enc_status = self.data.lock();
949
950        let res = match cmd_enc_status.finish() {
951            CommandEncoderStatus::Finished(cmd_buf_data) => Ok(cmd_buf_data),
952            CommandEncoderStatus::Error(err) => Err(err),
953            _ => unreachable!(),
954        };
955
956        let res = res.and_then(|mut cmd_buf_data| {
957            self.device.check_is_valid()?;
958            let snatch_guard = self.device.snatchable_lock.read();
959            let mut debug_scope_depth = 0;
960
961            if cmd_buf_data.encoder.api == EncodingApi::Raw {
962                // Should have panicked on the first call that switched APIs,
963                // but lets be sure.
964                assert!(cmd_buf_data.commands.is_empty());
965            }
966
967            let mut commands = mem::take(&mut cmd_buf_data.commands);
968            #[cfg(not(feature = "trace"))]
969            let command_iter = commands.drain(..);
970            #[cfg(feature = "trace")]
971            let mut trace_commands = None;
972
973            #[cfg(feature = "trace")]
974            let command_iter = {
975                if self.device.trace.lock().is_some() {
976                    trace_commands = Some(
977                        cmd_buf_data
978                            .trace_commands
979                            .insert(Vec::with_capacity(commands.len())),
980                    );
981                }
982
983                commands.drain(..).inspect(|cmd| {
984                    use crate::device::trace::IntoTrace;
985
986                    if let Some(ref mut trace) = trace_commands {
987                        trace.push(cmd.clone().to_trace());
988                    }
989                })
990            };
991
992            for command in command_iter {
993                if matches!(
994                    command,
995                    ArcCommand::RunRenderPass { .. } | ArcCommand::RunComputePass { .. }
996                ) {
997                    // Compute passes and render passes can accept either an
998                    // open or closed encoder. This state object holds an
999                    // `InnerCommandEncoder`. See the documentation of
1000                    // [`EncodingState`].
1001                    let mut state = EncodingState {
1002                        device: &self.device,
1003                        raw_encoder: &mut cmd_buf_data.encoder,
1004                        tracker: &mut cmd_buf_data.trackers,
1005                        buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1006                        texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1007                        as_actions: &mut cmd_buf_data.as_actions,
1008                        temp_resources: &mut cmd_buf_data.temp_resources,
1009                        indirect_draw_validation_resources: &mut cmd_buf_data
1010                            .indirect_draw_validation_resources,
1011                        snatch_guard: &snatch_guard,
1012                        debug_scope_depth: &mut debug_scope_depth,
1013                    };
1014
1015                    match command {
1016                        ArcCommand::RunRenderPass {
1017                            pass,
1018                            color_attachments,
1019                            depth_stencil_attachment,
1020                            timestamp_writes,
1021                            occlusion_query_set,
1022                            multiview_mask,
1023                        } => {
1024                            api_log!(
1025                                "Begin encoding render pass with '{}' label",
1026                                pass.label.as_deref().unwrap_or("")
1027                            );
1028                            let res = encode_render_pass(
1029                                &mut state,
1030                                pass,
1031                                color_attachments,
1032                                depth_stencil_attachment,
1033                                timestamp_writes,
1034                                occlusion_query_set,
1035                                multiview_mask,
1036                            );
1037                            match res.as_ref() {
1038                                Err(err) => api_log!("Finished encoding render pass ({err:?})"),
1039                                Ok(_) => api_log!("Finished encoding render pass (success)"),
1040                            }
1041                            res?;
1042                        }
1043                        ArcCommand::RunComputePass {
1044                            pass,
1045                            timestamp_writes,
1046                        } => {
1047                            api_log!(
1048                                "Begin encoding compute pass with '{}' label",
1049                                pass.label.as_deref().unwrap_or("")
1050                            );
1051                            let res = encode_compute_pass(&mut state, pass, timestamp_writes);
1052                            match res.as_ref() {
1053                                Err(err) => api_log!("Finished encoding compute pass ({err:?})"),
1054                                Ok(_) => api_log!("Finished encoding compute pass (success)"),
1055                            }
1056                            res?;
1057                        }
1058                        _ => unreachable!(),
1059                    }
1060                } else {
1061                    // All the other non-pass encoding routines assume the
1062                    // encoder is open, so open it if necessary. This state
1063                    // object holds an `&mut dyn hal::DynCommandEncoder`. By
1064                    // convention, a bare HAL encoder reference in
1065                    // [`EncodingState`] must always be an open encoder.
1066                    let raw_encoder = cmd_buf_data.encoder.open_if_closed()?;
1067                    let mut state = EncodingState {
1068                        device: &self.device,
1069                        raw_encoder,
1070                        tracker: &mut cmd_buf_data.trackers,
1071                        buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1072                        texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1073                        as_actions: &mut cmd_buf_data.as_actions,
1074                        temp_resources: &mut cmd_buf_data.temp_resources,
1075                        indirect_draw_validation_resources: &mut cmd_buf_data
1076                            .indirect_draw_validation_resources,
1077                        snatch_guard: &snatch_guard,
1078                        debug_scope_depth: &mut debug_scope_depth,
1079                    };
1080                    match command {
1081                        ArcCommand::CopyBufferToBuffer {
1082                            src,
1083                            src_offset,
1084                            dst,
1085                            dst_offset,
1086                            size,
1087                        } => {
1088                            copy_buffer_to_buffer(
1089                                &mut state, &src, src_offset, &dst, dst_offset, size,
1090                            )?;
1091                        }
1092                        ArcCommand::CopyBufferToTexture { src, dst, size } => {
1093                            copy_buffer_to_texture(&mut state, &src, &dst, &size)?;
1094                        }
1095                        ArcCommand::CopyTextureToBuffer { src, dst, size } => {
1096                            copy_texture_to_buffer(&mut state, &src, &dst, &size)?;
1097                        }
1098                        ArcCommand::CopyTextureToTexture { src, dst, size } => {
1099                            copy_texture_to_texture(&mut state, &src, &dst, &size)?;
1100                        }
1101                        ArcCommand::ClearBuffer { dst, offset, size } => {
1102                            clear_buffer(&mut state, dst, offset, size)?;
1103                        }
1104                        ArcCommand::ClearTexture {
1105                            dst,
1106                            subresource_range,
1107                        } => {
1108                            clear_texture_cmd(&mut state, dst, &subresource_range)?;
1109                        }
1110                        ArcCommand::WriteTimestamp {
1111                            query_set,
1112                            query_index,
1113                        } => {
1114                            write_timestamp(&mut state, query_set, query_index)?;
1115                        }
1116                        ArcCommand::ResolveQuerySet {
1117                            query_set,
1118                            start_query,
1119                            query_count,
1120                            destination,
1121                            destination_offset,
1122                        } => {
1123                            resolve_query_set(
1124                                &mut state,
1125                                query_set,
1126                                start_query,
1127                                query_count,
1128                                destination,
1129                                destination_offset,
1130                            )?;
1131                        }
1132                        ArcCommand::PushDebugGroup(label) => {
1133                            push_debug_group(&mut state, &label)?;
1134                        }
1135                        ArcCommand::PopDebugGroup => {
1136                            pop_debug_group(&mut state)?;
1137                        }
1138                        ArcCommand::InsertDebugMarker(label) => {
1139                            insert_debug_marker(&mut state, &label)?;
1140                        }
1141                        ArcCommand::BuildAccelerationStructures { blas, tlas } => {
1142                            build_acceleration_structures(&mut state, blas, tlas)?;
1143                        }
1144                        ArcCommand::TransitionResources {
1145                            buffer_transitions,
1146                            texture_transitions,
1147                        } => {
1148                            transition_resources(
1149                                &mut state,
1150                                buffer_transitions,
1151                                texture_transitions,
1152                            )?;
1153                        }
1154                        ArcCommand::RunComputePass { .. } | ArcCommand::RunRenderPass { .. } => {
1155                            unreachable!()
1156                        }
1157                    }
1158                }
1159            }
1160
1161            if debug_scope_depth > 0 {
1162                Err(CommandEncoderError::DebugGroupError(
1163                    DebugGroupError::MissingPop,
1164                ))?;
1165            }
1166
1167            // Close the encoder, unless it was closed already by a render or compute pass.
1168            cmd_buf_data.encoder.close_if_open()?;
1169
1170            // Note: if we want to stop tracking the swapchain texture view,
1171            // this is the place to do it.
1172
1173            Ok(cmd_buf_data)
1174        });
1175
1176        let (data, error) = match res {
1177            Err(e) => {
1178                if e.is_destroyed_error() {
1179                    // Errors related to destroyed resources are not reported until the
1180                    // command buffer is submitted.
1181                    (CommandEncoderStatus::Error(e.clone()), None)
1182                } else {
1183                    (CommandEncoderStatus::Error(e.clone()), Some(e))
1184                }
1185            }
1186
1187            Ok(data) => (CommandEncoderStatus::Finished(data), None),
1188        };
1189
1190        let cmd_buf = Arc::new(CommandBuffer {
1191            device: self.device.clone(),
1192            label: desc.label.to_string(),
1193            data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),
1194        });
1195
1196        (cmd_buf, error)
1197    }
1198}
1199
1200impl CommandBuffer {
1201    /// Replay commands from a trace.
1202    ///
1203    /// This is exposed for the `player` crate only. It is not a public API.
1204    /// It is not guaranteed to apply all of the validation that the original
1205    /// entrypoints provide.
1206    #[doc(hidden)]
1207    pub fn from_trace(device: &Arc<Device>, commands: Vec<Command<ArcReferences>>) -> Arc<Self> {
1208        let encoder = device.create_command_encoder(&None).unwrap();
1209        let mut cmd_enc_status = encoder.data.lock();
1210        cmd_enc_status.replay(commands);
1211        drop(cmd_enc_status);
1212
1213        let (cmd_buf, error) = encoder.finish(&wgt::CommandBufferDescriptor { label: None });
1214        if let Some(err) = error {
1215            panic!("CommandEncoder::finish failed: {err}");
1216        }
1217
1218        cmd_buf
1219    }
1220
1221    pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {
1222        use CommandEncoderStatus as St;
1223        match mem::replace(
1224            &mut *self.data.lock(),
1225            CommandEncoderStatus::Error(EncoderStateError::Submitted.into()),
1226        ) {
1227            St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
1228            St::Error(err) => Err(err),
1229            St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(),
1230        }
1231    }
1232}
1233
1234crate::impl_resource_type!(CommandBuffer);
1235crate::impl_labeled!(CommandBuffer);
1236crate::impl_parent_device!(CommandBuffer);
1237crate::impl_storage_item!(CommandBuffer);
1238
1239/// A stream of commands for a render pass or compute pass.
1240///
1241/// This also contains side tables referred to by certain commands,
1242/// like dynamic offsets for [`SetBindGroup`] or string data for
1243/// [`InsertDebugMarker`].
1244///
1245/// Render passes use `BasePass<RenderCommand>`, whereas compute
1246/// passes use `BasePass<ComputeCommand>`.
1247///
1248/// [`SetBindGroup`]: RenderCommand::SetBindGroup
1249/// [`InsertDebugMarker`]: RenderCommand::InsertDebugMarker
1250#[doc(hidden)]
1251#[derive(Debug, Clone)]
1252#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1253pub struct BasePass<C, E> {
1254    pub label: Option<String>,
1255
1256    /// If the pass is invalid, contains the error that caused the invalidation.
1257    ///
1258    /// If the pass is valid, this is `None`.
1259    ///
1260    /// Passes are serialized into traces. but we don't support doing so for
1261    /// passes containing errors. These serde attributes allow `E` to be
1262    /// `Infallible`.
1263    #[cfg_attr(feature = "serde", serde(skip, default = "Option::default"))]
1264    pub error: Option<E>,
1265
1266    /// The stream of commands.
1267    ///
1268    /// The commands are moved out of this vector when the pass is ended (i.e.
1269    /// at the same time that `parent` is taken out of the
1270    /// `ComputePass`/`RenderPass`).
1271    pub commands: Vec<C>,
1272
1273    /// Dynamic offsets consumed by [`SetBindGroup`] commands in `commands`.
1274    ///
1275    /// Each successive `SetBindGroup` consumes the next
1276    /// [`num_dynamic_offsets`] values from this list.
1277    pub dynamic_offsets: Vec<wgt::DynamicOffset>,
1278
1279    /// Strings used by debug instructions.
1280    ///
1281    /// Each successive [`PushDebugGroup`] or [`InsertDebugMarker`]
1282    /// instruction consumes the next `len` bytes from this vector.
1283    pub string_data: Vec<u8>,
1284
1285    /// Data used by `SetPushConstant` instructions.
1286    ///
1287    /// See the documentation for [`RenderCommand::SetPushConstant`]
1288    /// and [`ComputeCommand::SetPushConstant`] for details.
1289    pub push_constant_data: Vec<u32>,
1290}
1291
1292impl<C: Clone, E: Clone> BasePass<C, E> {
1293    fn new(label: &Label) -> Self {
1294        Self {
1295            label: label.as_deref().map(str::to_owned),
1296            error: None,
1297            commands: Vec::new(),
1298            dynamic_offsets: Vec::new(),
1299            string_data: Vec::new(),
1300            push_constant_data: Vec::new(),
1301        }
1302    }
1303
1304    fn new_invalid(label: &Label, err: E) -> Self {
1305        Self {
1306            label: label.as_deref().map(str::to_owned),
1307            error: Some(err),
1308            commands: Vec::new(),
1309            dynamic_offsets: Vec::new(),
1310            string_data: Vec::new(),
1311            push_constant_data: Vec::new(),
1312        }
1313    }
1314
1315    /// Takes the commands from the pass, or returns an error if the pass is
1316    /// invalid.
1317    ///
1318    /// This is called when the pass is ended, at the same time that the
1319    /// `parent` member of the `ComputePass` or `RenderPass` containing the pass
1320    /// is taken.
1321    fn take(&mut self) -> Result<BasePass<C, Infallible>, E> {
1322        match self.error.as_ref() {
1323            Some(err) => Err(err.clone()),
1324            None => Ok(BasePass {
1325                label: self.label.clone(),
1326                error: None,
1327                commands: mem::take(&mut self.commands),
1328                dynamic_offsets: mem::take(&mut self.dynamic_offsets),
1329                string_data: mem::take(&mut self.string_data),
1330                push_constant_data: mem::take(&mut self.push_constant_data),
1331            }),
1332        }
1333    }
1334}
1335
1336/// Checks the state of a [`compute::ComputePass`] or [`render::RenderPass`] and
1337/// evaluates to a mutable reference to the [`BasePass`], if the pass is open and
1338/// valid.
1339///
1340/// If the pass is ended or not valid, **returns from the invoking function**,
1341/// like the `?` operator.
1342///
1343/// If the pass is ended (i.e. the application is attempting to record a command
1344/// on a finished pass), returns `Err(EncoderStateError::Ended)` from the
1345/// invoking function, for immediate propagation as a validation error.
1346///
1347/// If the pass is open but invalid (i.e. a previous command encountered an
1348/// error), returns `Ok(())` from the invoking function. The pass should already
1349/// have stored the previous error, which will be transferred to the parent
1350/// encoder when the pass is ended, and then raised as a validation error when
1351/// `finish()` is called for the parent).
1352///
1353/// Although in many cases the functionality of `pass_base!` could be achieved
1354/// by combining a helper method on the passes with the `pass_try!` macro,
1355/// taking the mutable reference to the base pass in a macro avoids borrowing
1356/// conflicts when a reference to some other member of the pass struct is
1357/// needed simultaneously with the base pass reference.
1358macro_rules! pass_base {
1359    ($pass:expr, $scope:expr $(,)?) => {
1360        match (&$pass.parent, &$pass.base.error) {
1361            // Pass is ended
1362            (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),
1363            // Pass is invalid
1364            (&Some(_), &Some(_)) => return Ok(()),
1365            // Pass is open and valid
1366            (&Some(_), &None) => &mut $pass.base,
1367        }
1368    };
1369}
1370pub(crate) use pass_base;
1371
1372/// Handles the error case in an expression of type `Result<T, E>`.
1373///
1374/// This macro operates like the `?` operator (or, in early Rust versions, the
1375/// `try!` macro, hence the name `pass_try`). **When there is an error, the
1376/// macro returns from the invoking function.** However, `Ok(())`, and not the
1377/// error itself, is returned. The error is stored in the pass and will later be
1378/// transferred to the parent encoder when the pass ends, and then raised as a
1379/// validation error when `finish()` is called for the parent.
1380///
1381/// `pass_try!` also calls [`MapPassErr::map_pass_err`] to annotate the error
1382/// with the command being encoded at the time it occurred.
1383macro_rules! pass_try {
1384    ($base:expr, $scope:expr, $res:expr $(,)?) => {
1385        match $res.map_pass_err($scope) {
1386            Ok(val) => val,
1387            Err(err) => {
1388                $base.error.get_or_insert(err);
1389                return Ok(());
1390            }
1391        }
1392    };
1393}
1394pub(crate) use pass_try;
1395
1396/// Errors related to the state of a command or pass encoder.
1397///
1398/// The exact behavior of these errors may change based on the resolution of
1399/// <https://github.com/gpuweb/gpuweb/issues/5207>.
1400#[derive(Clone, Debug, Error)]
1401#[non_exhaustive]
1402pub enum EncoderStateError {
1403    /// Used internally by wgpu functions to indicate the encoder already
1404    /// contained an error. This variant should usually not be seen by users of
1405    /// the API, since an effort should be made to provide the caller with a
1406    /// more specific reason for the encoder being invalid.
1407    #[error("Encoder is invalid")]
1408    Invalid,
1409
1410    /// Returned immediately when an attempt is made to encode a command using
1411    /// an encoder that has already finished.
1412    #[error("Encoding must not have ended")]
1413    Ended,
1414
1415    /// Returned by a subsequent call to `encoder.finish()`, if there was an
1416    /// attempt to open a second pass on the encoder while it was locked for
1417    /// a first pass (i.e. the first pass was still open).
1418    ///
1419    /// Note: only command encoders can be locked (not pass encoders).
1420    #[error("Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
1421    Locked,
1422
1423    /// Returned when attempting to end a pass if the parent encoder is not
1424    /// locked. This can only happen if pass begin/end calls are mismatched.
1425    #[error(
1426        "Encoder is not currently locked. A pass can only be ended while the encoder is locked."
1427    )]
1428    Unlocked,
1429
1430    /// The command buffer has already been submitted.
1431    ///
1432    /// Although command encoders and command buffers are distinct WebGPU
1433    /// objects, we use `CommandEncoderStatus` for both.
1434    #[error("This command buffer has already been submitted.")]
1435    Submitted,
1436}
1437
1438impl WebGpuError for EncoderStateError {
1439    fn webgpu_error_type(&self) -> ErrorType {
1440        match self {
1441            EncoderStateError::Invalid
1442            | EncoderStateError::Ended
1443            | EncoderStateError::Locked
1444            | EncoderStateError::Unlocked
1445            | EncoderStateError::Submitted => ErrorType::Validation,
1446        }
1447    }
1448}
1449
1450#[derive(Clone, Debug, Error)]
1451#[non_exhaustive]
1452pub enum CommandEncoderError {
1453    #[error(transparent)]
1454    State(#[from] EncoderStateError),
1455    #[error(transparent)]
1456    Device(#[from] DeviceError),
1457    #[error(transparent)]
1458    InvalidResource(#[from] InvalidResourceError),
1459    #[error(transparent)]
1460    DestroyedResource(#[from] DestroyedResourceError),
1461    #[error(transparent)]
1462    ResourceUsage(#[from] ResourceUsageCompatibilityError),
1463    #[error(transparent)]
1464    DebugGroupError(#[from] DebugGroupError),
1465    #[error(transparent)]
1466    MissingFeatures(#[from] MissingFeatures),
1467    #[error(transparent)]
1468    Transfer(#[from] TransferError),
1469    #[error(transparent)]
1470    Clear(#[from] ClearError),
1471    #[error(transparent)]
1472    Query(#[from] QueryError),
1473    #[error(transparent)]
1474    BuildAccelerationStructure(#[from] BuildAccelerationStructureError),
1475    #[error(transparent)]
1476    TransitionResources(#[from] TransitionResourcesError),
1477    #[error(transparent)]
1478    ComputePass(#[from] ComputePassError),
1479    #[error(transparent)]
1480    RenderPass(#[from] RenderPassError),
1481}
1482
1483impl CommandEncoderError {
1484    fn is_destroyed_error(&self) -> bool {
1485        matches!(
1486            self,
1487            Self::DestroyedResource(_)
1488                | Self::Clear(ClearError::DestroyedResource(_))
1489                | Self::Query(QueryError::DestroyedResource(_))
1490                | Self::ComputePass(ComputePassError {
1491                    inner: ComputePassErrorInner::DestroyedResource(_),
1492                    ..
1493                })
1494                | Self::RenderPass(RenderPassError {
1495                    inner: RenderPassErrorInner::DestroyedResource(_),
1496                    ..
1497                })
1498                | Self::RenderPass(RenderPassError {
1499                    inner: RenderPassErrorInner::RenderCommand(
1500                        RenderCommandError::DestroyedResource(_)
1501                    ),
1502                    ..
1503                })
1504                | Self::RenderPass(RenderPassError {
1505                    inner: RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError(
1506                        BindingError::DestroyedResource(_)
1507                    )),
1508                    ..
1509                })
1510        )
1511    }
1512}
1513
1514impl WebGpuError for CommandEncoderError {
1515    fn webgpu_error_type(&self) -> ErrorType {
1516        let e: &dyn WebGpuError = match self {
1517            Self::Device(e) => e,
1518            Self::InvalidResource(e) => e,
1519            Self::DebugGroupError(e) => e,
1520            Self::MissingFeatures(e) => e,
1521            Self::State(e) => e,
1522            Self::DestroyedResource(e) => e,
1523            Self::Transfer(e) => e,
1524            Self::Clear(e) => e,
1525            Self::Query(e) => e,
1526            Self::BuildAccelerationStructure(e) => e,
1527            Self::TransitionResources(e) => e,
1528            Self::ResourceUsage(e) => e,
1529            Self::ComputePass(e) => e,
1530            Self::RenderPass(e) => e,
1531        };
1532        e.webgpu_error_type()
1533    }
1534}
1535
1536#[derive(Clone, Debug, Error)]
1537#[non_exhaustive]
1538pub enum DebugGroupError {
1539    #[error("Cannot pop debug group, because number of pushed debug groups is zero")]
1540    InvalidPop,
1541    #[error("A debug group was not popped before the encoder was finished")]
1542    MissingPop,
1543}
1544
1545impl WebGpuError for DebugGroupError {
1546    fn webgpu_error_type(&self) -> ErrorType {
1547        match self {
1548            Self::InvalidPop | Self::MissingPop => ErrorType::Validation,
1549        }
1550    }
1551}
1552
1553#[derive(Clone, Debug, Error)]
1554#[non_exhaustive]
1555pub enum TimestampWritesError {
1556    #[error(
1557        "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
1558    )]
1559    IndicesEqual { idx: u32 },
1560    #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
1561    IndicesMissing,
1562}
1563
1564impl WebGpuError for TimestampWritesError {
1565    fn webgpu_error_type(&self) -> ErrorType {
1566        match self {
1567            Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,
1568        }
1569    }
1570}
1571
1572impl Global {
1573    fn resolve_buffer_id(
1574        &self,
1575        buffer_id: Id<id::markers::Buffer>,
1576    ) -> Result<Arc<crate::resource::Buffer>, InvalidResourceError> {
1577        self.hub.buffers.get(buffer_id).get()
1578    }
1579
1580    fn resolve_texture_id(
1581        &self,
1582        texture_id: Id<id::markers::Texture>,
1583    ) -> Result<Arc<crate::resource::Texture>, InvalidResourceError> {
1584        self.hub.textures.get(texture_id).get()
1585    }
1586
1587    fn resolve_query_set(
1588        &self,
1589        query_set_id: Id<id::markers::QuerySet>,
1590    ) -> Result<Arc<QuerySet>, InvalidResourceError> {
1591        self.hub.query_sets.get(query_set_id).get()
1592    }
1593
1594    /// Finishes a command encoder, creating a command buffer and returning errors that were
1595    /// deferred until now.
1596    ///
1597    /// The returned `String` is the label of the command encoder, supplied so that `wgpu` can
1598    /// include the label when printing deferred errors without having its own copy of the label.
1599    /// This is a kludge and should be replaced if we think of a better solution to propagating
1600    /// labels.
1601    pub fn command_encoder_finish(
1602        &self,
1603        encoder_id: id::CommandEncoderId,
1604        desc: &wgt::CommandBufferDescriptor<Label>,
1605        id_in: Option<id::CommandBufferId>,
1606    ) -> (id::CommandBufferId, Option<(String, CommandEncoderError)>) {
1607        profiling::scope!("CommandEncoder::finish");
1608
1609        let hub = &self.hub;
1610        let cmd_enc = hub.command_encoders.get(encoder_id);
1611
1612        let (cmd_buf, opt_error) = cmd_enc.finish(desc);
1613        let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(cmd_buf);
1614
1615        (
1616            cmd_buf_id,
1617            opt_error.map(|error| (cmd_enc.label.clone(), error)),
1618        )
1619    }
1620
1621    pub fn command_encoder_push_debug_group(
1622        &self,
1623        encoder_id: id::CommandEncoderId,
1624        label: &str,
1625    ) -> Result<(), EncoderStateError> {
1626        profiling::scope!("CommandEncoder::push_debug_group");
1627        api_log!("CommandEncoder::push_debug_group {label}");
1628
1629        let hub = &self.hub;
1630
1631        let cmd_enc = hub.command_encoders.get(encoder_id);
1632        let mut cmd_buf_data = cmd_enc.data.lock();
1633
1634        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1635            Ok(ArcCommand::PushDebugGroup(label.to_owned()))
1636        })
1637    }
1638
1639    pub fn command_encoder_insert_debug_marker(
1640        &self,
1641        encoder_id: id::CommandEncoderId,
1642        label: &str,
1643    ) -> Result<(), EncoderStateError> {
1644        profiling::scope!("CommandEncoder::insert_debug_marker");
1645        api_log!("CommandEncoder::insert_debug_marker {label}");
1646
1647        let hub = &self.hub;
1648
1649        let cmd_enc = hub.command_encoders.get(encoder_id);
1650        let mut cmd_buf_data = cmd_enc.data.lock();
1651
1652        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1653            Ok(ArcCommand::InsertDebugMarker(label.to_owned()))
1654        })
1655    }
1656
1657    pub fn command_encoder_pop_debug_group(
1658        &self,
1659        encoder_id: id::CommandEncoderId,
1660    ) -> Result<(), EncoderStateError> {
1661        profiling::scope!("CommandEncoder::pop_debug_marker");
1662        api_log!("CommandEncoder::pop_debug_group");
1663
1664        let hub = &self.hub;
1665
1666        let cmd_enc = hub.command_encoders.get(encoder_id);
1667        let mut cmd_buf_data = cmd_enc.data.lock();
1668
1669        cmd_buf_data
1670            .push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::PopDebugGroup) })
1671    }
1672
1673    fn validate_pass_timestamp_writes<E>(
1674        device: &Device,
1675        query_sets: &Storage<Fallible<QuerySet>>,
1676        timestamp_writes: &PassTimestampWrites,
1677    ) -> Result<ArcPassTimestampWrites, E>
1678    where
1679        E: From<TimestampWritesError>
1680            + From<QueryUseError>
1681            + From<DeviceError>
1682            + From<MissingFeatures>
1683            + From<InvalidResourceError>,
1684    {
1685        let &PassTimestampWrites {
1686            query_set,
1687            beginning_of_pass_write_index,
1688            end_of_pass_write_index,
1689        } = timestamp_writes;
1690
1691        device.require_features(wgt::Features::TIMESTAMP_QUERY)?;
1692
1693        let query_set = query_sets.get(query_set).get()?;
1694
1695        query_set.same_device(device)?;
1696
1697        for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
1698            .into_iter()
1699            .flatten()
1700        {
1701            query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
1702        }
1703
1704        if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
1705            if begin == end {
1706                return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());
1707            }
1708        }
1709
1710        if beginning_of_pass_write_index
1711            .or(end_of_pass_write_index)
1712            .is_none()
1713        {
1714            return Err(TimestampWritesError::IndicesMissing.into());
1715        }
1716
1717        Ok(ArcPassTimestampWrites {
1718            query_set,
1719            beginning_of_pass_write_index,
1720            end_of_pass_write_index,
1721        })
1722    }
1723}
1724
1725pub(crate) fn push_debug_group(
1726    state: &mut EncodingState,
1727    label: &str,
1728) -> Result<(), CommandEncoderError> {
1729    *state.debug_scope_depth += 1;
1730
1731    if !state
1732        .device
1733        .instance_flags
1734        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1735    {
1736        unsafe { state.raw_encoder.begin_debug_marker(label) };
1737    }
1738
1739    Ok(())
1740}
1741
1742pub(crate) fn insert_debug_marker(
1743    state: &mut EncodingState,
1744    label: &str,
1745) -> Result<(), CommandEncoderError> {
1746    if !state
1747        .device
1748        .instance_flags
1749        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1750    {
1751        unsafe { state.raw_encoder.insert_debug_marker(label) };
1752    }
1753
1754    Ok(())
1755}
1756
1757pub(crate) fn pop_debug_group(state: &mut EncodingState) -> Result<(), CommandEncoderError> {
1758    if *state.debug_scope_depth == 0 {
1759        return Err(DebugGroupError::InvalidPop.into());
1760    }
1761    *state.debug_scope_depth -= 1;
1762
1763    if !state
1764        .device
1765        .instance_flags
1766        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1767    {
1768        unsafe { state.raw_encoder.end_debug_marker() };
1769    }
1770
1771    Ok(())
1772}
1773
1774fn push_constant_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
1775where
1776    PushFn: FnMut(u32, &[u32]),
1777{
1778    let mut count_words = 0_u32;
1779    let size_words = size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT;
1780    while count_words < size_words {
1781        let count_bytes = count_words * wgt::PUSH_CONSTANT_ALIGNMENT;
1782        let size_to_write_words =
1783            (size_words - count_words).min(PUSH_CONSTANT_CLEAR_ARRAY.len() as u32);
1784
1785        push_fn(
1786            offset + count_bytes,
1787            &PUSH_CONSTANT_CLEAR_ARRAY[0..size_to_write_words as usize],
1788        );
1789
1790        count_words += size_to_write_words;
1791    }
1792}
1793
1794#[derive(Debug, Copy, Clone)]
1795struct StateChange<T> {
1796    last_state: Option<T>,
1797}
1798
1799impl<T: Copy + PartialEq> StateChange<T> {
1800    fn new() -> Self {
1801        Self { last_state: None }
1802    }
1803    fn set_and_check_redundant(&mut self, new_state: T) -> bool {
1804        let already_set = self.last_state == Some(new_state);
1805        self.last_state = Some(new_state);
1806        already_set
1807    }
1808    fn reset(&mut self) {
1809        self.last_state = None;
1810    }
1811}
1812
1813impl<T: Copy + PartialEq> Default for StateChange<T> {
1814    fn default() -> Self {
1815        Self::new()
1816    }
1817}
1818
1819#[derive(Debug)]
1820struct BindGroupStateChange {
1821    last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],
1822}
1823
1824impl BindGroupStateChange {
1825    fn new() -> Self {
1826        Self {
1827            last_states: [StateChange::new(); hal::MAX_BIND_GROUPS],
1828        }
1829    }
1830
1831    fn set_and_check_redundant(
1832        &mut self,
1833        bind_group_id: Option<id::BindGroupId>,
1834        index: u32,
1835        dynamic_offsets: &mut Vec<u32>,
1836        offsets: &[wgt::DynamicOffset],
1837    ) -> bool {
1838        // For now never deduplicate bind groups with dynamic offsets.
1839        if offsets.is_empty() {
1840            // If this get returns None, that means we're well over the limit,
1841            // so let the call through to get a proper error
1842            if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1843                // Bail out if we're binding the same bind group.
1844                if current_bind_group.set_and_check_redundant(bind_group_id) {
1845                    return true;
1846                }
1847            }
1848        } else {
1849            // We intentionally remove the memory of this bind group if we have dynamic offsets,
1850            // such that if you try to bind this bind group later with _no_ dynamic offsets it
1851            // tries to bind it again and gives a proper validation error.
1852            if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1853                current_bind_group.reset();
1854            }
1855            dynamic_offsets.extend_from_slice(offsets);
1856        }
1857        false
1858    }
1859    fn reset(&mut self) {
1860        self.last_states = [StateChange::new(); hal::MAX_BIND_GROUPS];
1861    }
1862}
1863
1864impl Default for BindGroupStateChange {
1865    fn default() -> Self {
1866        Self::new()
1867    }
1868}
1869
1870/// Helper to attach [`PassErrorScope`] to errors.
1871trait MapPassErr<T> {
1872    fn map_pass_err(self, scope: PassErrorScope) -> T;
1873}
1874
1875impl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>
1876where
1877    E: MapPassErr<F>,
1878{
1879    fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {
1880        self.map_err(|err| err.map_pass_err(scope))
1881    }
1882}
1883
1884impl MapPassErr<PassStateError> for EncoderStateError {
1885    fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {
1886        PassStateError { scope, inner: self }
1887    }
1888}
1889
1890#[derive(Clone, Copy, Debug)]
1891pub enum DrawKind {
1892    Draw,
1893    DrawIndirect,
1894    MultiDrawIndirect,
1895    MultiDrawIndirectCount,
1896}
1897
1898/// The type of draw command(indexed or not, or mesh shader)
1899#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1900#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1901pub enum DrawCommandFamily {
1902    Draw,
1903    DrawIndexed,
1904    DrawMeshTasks,
1905}
1906
1907/// A command that can be recorded in a pass or bundle.
1908///
1909/// This is used to provide context for errors during command recording.
1910/// [`MapPassErr`] is used as a helper to attach a `PassErrorScope` to
1911/// an error.
1912///
1913/// The [`PassErrorScope::Bundle`] and [`PassErrorScope::Pass`] variants
1914/// are used when the error occurs during the opening or closing of the
1915/// pass or bundle.
1916#[derive(Clone, Copy, Debug, Error)]
1917pub enum PassErrorScope {
1918    // TODO: Extract out the 2 error variants below so that we can always
1919    // include the ResourceErrorIdent of the pass around all inner errors
1920    #[error("In a bundle parameter")]
1921    Bundle,
1922    #[error("In a pass parameter")]
1923    Pass,
1924    #[error("In a set_bind_group command")]
1925    SetBindGroup,
1926    #[error("In a set_pipeline command")]
1927    SetPipelineRender,
1928    #[error("In a set_pipeline command")]
1929    SetPipelineCompute,
1930    #[error("In a set_push_constant command")]
1931    SetPushConstant,
1932    #[error("In a set_vertex_buffer command")]
1933    SetVertexBuffer,
1934    #[error("In a set_index_buffer command")]
1935    SetIndexBuffer,
1936    #[error("In a set_blend_constant command")]
1937    SetBlendConstant,
1938    #[error("In a set_stencil_reference command")]
1939    SetStencilReference,
1940    #[error("In a set_viewport command")]
1941    SetViewport,
1942    #[error("In a set_scissor_rect command")]
1943    SetScissorRect,
1944    #[error("In a draw command, kind: {kind:?}")]
1945    Draw {
1946        kind: DrawKind,
1947        family: DrawCommandFamily,
1948    },
1949    #[error("In a write_timestamp command")]
1950    WriteTimestamp,
1951    #[error("In a begin_occlusion_query command")]
1952    BeginOcclusionQuery,
1953    #[error("In a end_occlusion_query command")]
1954    EndOcclusionQuery,
1955    #[error("In a begin_pipeline_statistics_query command")]
1956    BeginPipelineStatisticsQuery,
1957    #[error("In a end_pipeline_statistics_query command")]
1958    EndPipelineStatisticsQuery,
1959    #[error("In a execute_bundle command")]
1960    ExecuteBundle,
1961    #[error("In a dispatch command, indirect:{indirect}")]
1962    Dispatch { indirect: bool },
1963    #[error("In a push_debug_group command")]
1964    PushDebugGroup,
1965    #[error("In a pop_debug_group command")]
1966    PopDebugGroup,
1967    #[error("In a insert_debug_marker command")]
1968    InsertDebugMarker,
1969}
1970
1971/// Variant of `EncoderStateError` that includes the pass scope.
1972#[derive(Clone, Debug, Error)]
1973#[error("{scope}")]
1974pub struct PassStateError {
1975    pub scope: PassErrorScope,
1976    #[source]
1977    pub(super) inner: EncoderStateError,
1978}
1979
1980impl WebGpuError for PassStateError {
1981    fn webgpu_error_type(&self) -> ErrorType {
1982        let Self { scope: _, inner } = self;
1983        inner.webgpu_error_type()
1984    }
1985}