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