Skip to main content

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                        device.ordered_buffer_usages,
899                        device.ordered_texture_usages,
900                    ),
901                    buffer_memory_init_actions: Default::default(),
902                    texture_memory_actions: Default::default(),
903                    as_actions: Default::default(),
904                    temp_resources: Default::default(),
905                    indirect_draw_validation_resources:
906                        crate::indirect_validation::DrawResources::new(device.clone()),
907                    commands: Vec::new(),
908                    #[cfg(feature = "trace")]
909                    trace_commands: if device.trace.lock().is_some() {
910                        Some(Vec::new())
911                    } else {
912                        None
913                    },
914                }),
915            ),
916        }
917    }
918
919    pub(crate) fn new_invalid(
920        device: &Arc<Device>,
921        label: &Label,
922        err: CommandEncoderError,
923    ) -> Self {
924        CommandEncoder {
925            device: device.clone(),
926            label: label.to_string(),
927            data: Mutex::new(rank::COMMAND_BUFFER_DATA, make_error_state(err)),
928        }
929    }
930
931    pub(crate) fn insert_barriers_from_tracker(
932        raw: &mut dyn hal::DynCommandEncoder,
933        base: &mut Tracker,
934        head: &Tracker,
935        snatch_guard: &SnatchGuard,
936    ) {
937        profiling::scope!("insert_barriers");
938
939        base.buffers.set_from_tracker(&head.buffers);
940        base.textures.set_from_tracker(&head.textures);
941
942        Self::drain_barriers(raw, base, snatch_guard);
943    }
944
945    pub(crate) fn insert_barriers_from_scope(
946        raw: &mut dyn hal::DynCommandEncoder,
947        base: &mut Tracker,
948        head: &UsageScope,
949        snatch_guard: &SnatchGuard,
950    ) {
951        profiling::scope!("insert_barriers");
952
953        base.buffers.set_from_usage_scope(&head.buffers);
954        base.textures.set_from_usage_scope(&head.textures);
955
956        Self::drain_barriers(raw, base, snatch_guard);
957    }
958
959    pub(crate) fn drain_barriers(
960        raw: &mut dyn hal::DynCommandEncoder,
961        base: &mut Tracker,
962        snatch_guard: &SnatchGuard,
963    ) {
964        profiling::scope!("drain_barriers");
965
966        let buffer_barriers = base
967            .buffers
968            .drain_transitions(snatch_guard)
969            .collect::<Vec<_>>();
970        let (transitions, textures) = base.textures.drain_transitions(snatch_guard);
971        let texture_barriers = transitions
972            .into_iter()
973            .enumerate()
974            .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))
975            .collect::<Vec<_>>();
976
977        unsafe {
978            raw.transition_buffers(&buffer_barriers);
979            raw.transition_textures(&texture_barriers);
980        }
981    }
982
983    pub(crate) fn insert_barriers_from_device_tracker(
984        raw: &mut dyn hal::DynCommandEncoder,
985        base: &mut DeviceTracker,
986        head: &Tracker,
987        snatch_guard: &SnatchGuard,
988    ) {
989        profiling::scope!("insert_barriers_from_device_tracker");
990
991        let buffer_barriers = base
992            .buffers
993            .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)
994            .collect::<Vec<_>>();
995
996        let texture_barriers = base
997            .textures
998            .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)
999            .collect::<Vec<_>>();
1000
1001        unsafe {
1002            raw.transition_buffers(&buffer_barriers);
1003            raw.transition_textures(&texture_barriers);
1004        }
1005    }
1006
1007    fn encode_commands(
1008        device: &Arc<Device>,
1009        cmd_buf_data: &mut CommandBufferMutable,
1010    ) -> Result<(), CommandEncoderError> {
1011        device.check_is_valid()?;
1012        let snatch_guard = device.snatchable_lock.read();
1013        let mut debug_scope_depth = 0;
1014
1015        if cmd_buf_data.encoder.api == EncodingApi::Raw {
1016            // Should have panicked on the first call that switched APIs,
1017            // but lets be sure.
1018            assert!(cmd_buf_data.commands.is_empty());
1019        }
1020
1021        let commands = mem::take(&mut cmd_buf_data.commands);
1022
1023        #[cfg(feature = "trace")]
1024        if device.trace.lock().is_some() {
1025            cmd_buf_data.trace_commands = Some(
1026                commands
1027                    .iter()
1028                    .map(crate::device::trace::IntoTrace::to_trace)
1029                    .collect(),
1030            );
1031        }
1032
1033        for command in commands {
1034            if matches!(
1035                command,
1036                ArcCommand::RunRenderPass { .. } | ArcCommand::RunComputePass { .. }
1037            ) {
1038                // Compute passes and render passes can accept either an
1039                // open or closed encoder. This state object holds an
1040                // `InnerCommandEncoder`. See the documentation of
1041                // [`EncodingState`].
1042                let mut state = EncodingState {
1043                    device,
1044                    raw_encoder: &mut cmd_buf_data.encoder,
1045                    tracker: &mut cmd_buf_data.trackers,
1046                    buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1047                    texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1048                    as_actions: &mut cmd_buf_data.as_actions,
1049                    temp_resources: &mut cmd_buf_data.temp_resources,
1050                    indirect_draw_validation_resources: &mut cmd_buf_data
1051                        .indirect_draw_validation_resources,
1052                    snatch_guard: &snatch_guard,
1053                    debug_scope_depth: &mut debug_scope_depth,
1054                };
1055
1056                match command {
1057                    ArcCommand::RunRenderPass {
1058                        pass,
1059                        color_attachments,
1060                        depth_stencil_attachment,
1061                        timestamp_writes,
1062                        occlusion_query_set,
1063                        multiview_mask,
1064                    } => {
1065                        api_log!(
1066                            "Begin encoding render pass with '{}' label",
1067                            pass.label.as_deref().unwrap_or("")
1068                        );
1069                        let res = render::encode_render_pass(
1070                            &mut state,
1071                            pass,
1072                            color_attachments,
1073                            depth_stencil_attachment,
1074                            timestamp_writes,
1075                            occlusion_query_set,
1076                            multiview_mask,
1077                        );
1078                        match res.as_ref() {
1079                            Err(err) => {
1080                                api_log!("Finished encoding render pass ({err:?})")
1081                            }
1082                            Ok(_) => {
1083                                api_log!("Finished encoding render pass (success)")
1084                            }
1085                        }
1086                        res?;
1087                    }
1088                    ArcCommand::RunComputePass {
1089                        pass,
1090                        timestamp_writes,
1091                    } => {
1092                        api_log!(
1093                            "Begin encoding compute pass with '{}' label",
1094                            pass.label.as_deref().unwrap_or("")
1095                        );
1096                        let res = compute::encode_compute_pass(&mut state, pass, timestamp_writes);
1097                        match res.as_ref() {
1098                            Err(err) => {
1099                                api_log!("Finished encoding compute pass ({err:?})")
1100                            }
1101                            Ok(_) => {
1102                                api_log!("Finished encoding compute pass (success)")
1103                            }
1104                        }
1105                        res?;
1106                    }
1107                    _ => unreachable!(),
1108                }
1109            } else {
1110                // All the other non-pass encoding routines assume the
1111                // encoder is open, so open it if necessary. This state
1112                // object holds an `&mut dyn hal::DynCommandEncoder`. By
1113                // convention, a bare HAL encoder reference in
1114                // [`EncodingState`] must always be an open encoder.
1115                let raw_encoder = cmd_buf_data.encoder.open_if_closed()?;
1116                let mut state = EncodingState {
1117                    device,
1118                    raw_encoder,
1119                    tracker: &mut cmd_buf_data.trackers,
1120                    buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1121                    texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1122                    as_actions: &mut cmd_buf_data.as_actions,
1123                    temp_resources: &mut cmd_buf_data.temp_resources,
1124                    indirect_draw_validation_resources: &mut cmd_buf_data
1125                        .indirect_draw_validation_resources,
1126                    snatch_guard: &snatch_guard,
1127                    debug_scope_depth: &mut debug_scope_depth,
1128                };
1129                match command {
1130                    ArcCommand::CopyBufferToBuffer {
1131                        src,
1132                        src_offset,
1133                        dst,
1134                        dst_offset,
1135                        size,
1136                    } => {
1137                        transfer::copy_buffer_to_buffer(
1138                            &mut state, &src, src_offset, &dst, dst_offset, size,
1139                        )?;
1140                    }
1141                    ArcCommand::CopyBufferToTexture { src, dst, size } => {
1142                        transfer::copy_buffer_to_texture(&mut state, &src, &dst, &size)?;
1143                    }
1144                    ArcCommand::CopyTextureToBuffer { src, dst, size } => {
1145                        transfer::copy_texture_to_buffer(&mut state, &src, &dst, &size)?;
1146                    }
1147                    ArcCommand::CopyTextureToTexture { src, dst, size } => {
1148                        transfer::copy_texture_to_texture(&mut state, &src, &dst, &size)?;
1149                    }
1150                    ArcCommand::ClearBuffer { dst, offset, size } => {
1151                        clear::clear_buffer(&mut state, dst, offset, size)?;
1152                    }
1153                    ArcCommand::ClearTexture {
1154                        dst,
1155                        subresource_range,
1156                    } => {
1157                        clear::clear_texture_cmd(&mut state, dst, &subresource_range)?;
1158                    }
1159                    ArcCommand::WriteTimestamp {
1160                        query_set,
1161                        query_index,
1162                    } => {
1163                        query::write_timestamp(&mut state, query_set, query_index)?;
1164                    }
1165                    ArcCommand::ResolveQuerySet {
1166                        query_set,
1167                        start_query,
1168                        query_count,
1169                        destination,
1170                        destination_offset,
1171                    } => {
1172                        query::resolve_query_set(
1173                            &mut state,
1174                            query_set,
1175                            start_query,
1176                            query_count,
1177                            destination,
1178                            destination_offset,
1179                        )?;
1180                    }
1181                    ArcCommand::PushDebugGroup(label) => {
1182                        push_debug_group(&mut state, &label)?;
1183                    }
1184                    ArcCommand::PopDebugGroup => {
1185                        pop_debug_group(&mut state)?;
1186                    }
1187                    ArcCommand::InsertDebugMarker(label) => {
1188                        insert_debug_marker(&mut state, &label)?;
1189                    }
1190                    ArcCommand::BuildAccelerationStructures { blas, tlas } => {
1191                        ray_tracing::build_acceleration_structures(&mut state, blas, tlas)?;
1192                    }
1193                    ArcCommand::TransitionResources {
1194                        buffer_transitions,
1195                        texture_transitions,
1196                    } => {
1197                        transition_resources::transition_resources(
1198                            &mut state,
1199                            buffer_transitions,
1200                            texture_transitions,
1201                        )?;
1202                    }
1203                    ArcCommand::RunComputePass { .. } | ArcCommand::RunRenderPass { .. } => {
1204                        unreachable!()
1205                    }
1206                }
1207            }
1208        }
1209
1210        if debug_scope_depth > 0 {
1211            Err(CommandEncoderError::DebugGroupError(
1212                DebugGroupError::MissingPop,
1213            ))?;
1214        }
1215
1216        // Close the encoder, unless it was closed already by a render or compute pass.
1217        cmd_buf_data.encoder.close_if_open()?;
1218
1219        // Note: if we want to stop tracking the swapchain texture view,
1220        // this is the place to do it.
1221
1222        Ok(())
1223    }
1224
1225    fn finish(
1226        self: &Arc<Self>,
1227        desc: &wgt::CommandBufferDescriptor<Label>,
1228    ) -> (Arc<CommandBuffer>, Option<CommandEncoderError>) {
1229        let mut cmd_enc_status = self.data.lock();
1230
1231        let res = match cmd_enc_status.finish() {
1232            CommandEncoderStatus::Finished(mut cmd_buf_data) => {
1233                match Self::encode_commands(&self.device, &mut cmd_buf_data) {
1234                    Ok(()) => Ok(cmd_buf_data),
1235                    Err(error) => Err(EncoderErrorState {
1236                        error,
1237                        #[cfg(feature = "trace")]
1238                        trace_commands: mem::take(&mut cmd_buf_data.trace_commands),
1239                    }),
1240                }
1241            }
1242            CommandEncoderStatus::Error(error_state) => Err(error_state),
1243            _ => unreachable!(),
1244        };
1245
1246        let (data, error) = match res {
1247            Err(EncoderErrorState {
1248                error,
1249                #[cfg(feature = "trace")]
1250                trace_commands,
1251            }) => {
1252                // Normally, commands are added to the trace when submitted, but
1253                // since this command buffer won't be submitted, add it to the
1254                // trace now.
1255                #[cfg(feature = "trace")]
1256                if let Some(trace) = self.device.trace.lock().as_mut() {
1257                    use alloc::string::ToString;
1258
1259                    trace.add(crate::device::trace::Action::FailedCommands {
1260                        commands: trace_commands,
1261                        failed_at_submit: None,
1262                        error: error.to_string(),
1263                    });
1264                }
1265
1266                if error.is_destroyed_error() {
1267                    // Errors related to destroyed resources are not reported until the
1268                    // command buffer is submitted.
1269                    (make_error_state(error), None)
1270                } else {
1271                    (make_error_state(error.clone()), Some(error))
1272                }
1273            }
1274
1275            Ok(data) => (CommandEncoderStatus::Finished(data), None),
1276        };
1277
1278        let cmd_buf = Arc::new(CommandBuffer {
1279            device: self.device.clone(),
1280            label: desc.label.to_string(),
1281            data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),
1282        });
1283
1284        (cmd_buf, error)
1285    }
1286}
1287
1288impl CommandBuffer {
1289    /// Replay commands from a trace.
1290    ///
1291    /// This is exposed for the `player` crate only. It is not a public API.
1292    /// It is not guaranteed to apply all of the validation that the original
1293    /// entrypoints provide.
1294    #[doc(hidden)]
1295    pub fn from_trace(device: &Arc<Device>, commands: Vec<Command<ArcReferences>>) -> Arc<Self> {
1296        let encoder = device.create_command_encoder(&None).unwrap();
1297        let mut cmd_enc_status = encoder.data.lock();
1298        cmd_enc_status.replay(commands);
1299        drop(cmd_enc_status);
1300
1301        let (cmd_buf, error) = encoder.finish(&wgt::CommandBufferDescriptor { label: None });
1302        if let Some(err) = error {
1303            panic!("CommandEncoder::finish failed: {err}");
1304        }
1305
1306        cmd_buf
1307    }
1308
1309    pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {
1310        use CommandEncoderStatus as St;
1311        match mem::replace(
1312            &mut *self.data.lock(),
1313            make_error_state(EncoderStateError::Submitted),
1314        ) {
1315            St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
1316            St::Error(EncoderErrorState {
1317                #[cfg(feature = "trace")]
1318                    trace_commands: _,
1319                error,
1320            }) => Err(error),
1321            St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(),
1322        }
1323    }
1324}
1325
1326crate::impl_resource_type!(CommandBuffer);
1327crate::impl_labeled!(CommandBuffer);
1328crate::impl_parent_device!(CommandBuffer);
1329crate::impl_storage_item!(CommandBuffer);
1330
1331/// A stream of commands for a render pass or compute pass.
1332///
1333/// This also contains side tables referred to by certain commands,
1334/// like dynamic offsets for [`SetBindGroup`] or string data for
1335/// [`InsertDebugMarker`].
1336///
1337/// Render passes use `BasePass<RenderCommand>`, whereas compute
1338/// passes use `BasePass<ComputeCommand>`.
1339///
1340/// [`SetBindGroup`]: RenderCommand::SetBindGroup
1341/// [`InsertDebugMarker`]: RenderCommand::InsertDebugMarker
1342#[doc(hidden)]
1343#[derive(Debug, Clone)]
1344#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1345pub struct BasePass<C, E> {
1346    pub label: Option<String>,
1347
1348    /// If the pass is invalid, contains the error that caused the invalidation.
1349    ///
1350    /// If the pass is valid, this is `None`.
1351    ///
1352    /// Passes are serialized into traces. but we don't support doing so for
1353    /// passes containing errors. These serde attributes allow `E` to be
1354    /// `Infallible`.
1355    #[cfg_attr(feature = "serde", serde(skip, default = "Option::default"))]
1356    pub error: Option<E>,
1357
1358    /// The stream of commands.
1359    ///
1360    /// The commands are moved out of this vector when the pass is ended (i.e.
1361    /// at the same time that `parent` is taken out of the
1362    /// `ComputePass`/`RenderPass`).
1363    pub commands: Vec<C>,
1364
1365    /// Dynamic offsets consumed by [`SetBindGroup`] commands in `commands`.
1366    ///
1367    /// Each successive `SetBindGroup` consumes the next
1368    /// [`num_dynamic_offsets`] values from this list.
1369    pub dynamic_offsets: Vec<wgt::DynamicOffset>,
1370
1371    /// Strings used by debug instructions.
1372    ///
1373    /// Each successive [`PushDebugGroup`] or [`InsertDebugMarker`]
1374    /// instruction consumes the next `len` bytes from this vector.
1375    pub string_data: Vec<u8>,
1376
1377    /// Data used by `SetImmediate` instructions.
1378    ///
1379    /// See the documentation for [`RenderCommand::SetImmediate`]
1380    /// and [`ComputeCommand::SetImmediate`] for details.
1381    pub immediates_data: Vec<u32>,
1382}
1383
1384impl<C: Clone, E: Clone> BasePass<C, E> {
1385    fn new(label: &Label) -> Self {
1386        Self {
1387            label: label.as_deref().map(str::to_owned),
1388            error: None,
1389            commands: Vec::new(),
1390            dynamic_offsets: Vec::new(),
1391            string_data: Vec::new(),
1392            immediates_data: Vec::new(),
1393        }
1394    }
1395
1396    fn new_invalid(label: &Label, err: E) -> Self {
1397        Self {
1398            label: label.as_deref().map(str::to_owned),
1399            error: Some(err),
1400            commands: Vec::new(),
1401            dynamic_offsets: Vec::new(),
1402            string_data: Vec::new(),
1403            immediates_data: Vec::new(),
1404        }
1405    }
1406
1407    /// Takes the commands from the pass, or returns an error if the pass is
1408    /// invalid.
1409    ///
1410    /// This is called when the pass is ended, at the same time that the
1411    /// `parent` member of the `ComputePass` or `RenderPass` containing the pass
1412    /// is taken.
1413    fn take(&mut self) -> Result<BasePass<C, Infallible>, E> {
1414        match self.error.as_ref() {
1415            Some(err) => Err(err.clone()),
1416            None => Ok(BasePass {
1417                label: self.label.clone(),
1418                error: None,
1419                commands: mem::take(&mut self.commands),
1420                dynamic_offsets: mem::take(&mut self.dynamic_offsets),
1421                string_data: mem::take(&mut self.string_data),
1422                immediates_data: mem::take(&mut self.immediates_data),
1423            }),
1424        }
1425    }
1426}
1427
1428/// Checks the state of a [`compute::ComputePass`] or [`render::RenderPass`] and
1429/// evaluates to a mutable reference to the [`BasePass`], if the pass is open and
1430/// valid.
1431///
1432/// If the pass is ended or not valid, **returns from the invoking function**,
1433/// like the `?` operator.
1434///
1435/// If the pass is ended (i.e. the application is attempting to record a command
1436/// on a finished pass), returns `Err(EncoderStateError::Ended)` from the
1437/// invoking function, for immediate propagation as a validation error.
1438///
1439/// If the pass is open but invalid (i.e. a previous command encountered an
1440/// error), returns `Ok(())` from the invoking function. The pass should already
1441/// have stored the previous error, which will be transferred to the parent
1442/// encoder when the pass is ended, and then raised as a validation error when
1443/// `finish()` is called for the parent).
1444///
1445/// Although in many cases the functionality of `pass_base!` could be achieved
1446/// by combining a helper method on the passes with the `pass_try!` macro,
1447/// taking the mutable reference to the base pass in a macro avoids borrowing
1448/// conflicts when a reference to some other member of the pass struct is
1449/// needed simultaneously with the base pass reference.
1450macro_rules! pass_base {
1451    ($pass:expr, $scope:expr $(,)?) => {
1452        match (&$pass.parent, &$pass.base.error) {
1453            // Pass is ended
1454            (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),
1455            // Pass is invalid
1456            (&Some(_), &Some(_)) => return Ok(()),
1457            // Pass is open and valid
1458            (&Some(_), &None) => &mut $pass.base,
1459        }
1460    };
1461}
1462pub(crate) use pass_base;
1463
1464/// Handles the error case in an expression of type `Result<T, E>`.
1465///
1466/// This macro operates like the `?` operator (or, in early Rust versions, the
1467/// `try!` macro, hence the name `pass_try`). **When there is an error, the
1468/// macro returns from the invoking function.** However, `Ok(())`, and not the
1469/// error itself, is returned. The error is stored in the pass and will later be
1470/// transferred to the parent encoder when the pass ends, and then raised as a
1471/// validation error when `finish()` is called for the parent.
1472///
1473/// `pass_try!` also calls [`MapPassErr::map_pass_err`] to annotate the error
1474/// with the command being encoded at the time it occurred.
1475macro_rules! pass_try {
1476    ($base:expr, $scope:expr, $res:expr $(,)?) => {
1477        match $res.map_pass_err($scope) {
1478            Ok(val) => val,
1479            Err(err) => {
1480                $base.error.get_or_insert(err);
1481                return Ok(());
1482            }
1483        }
1484    };
1485}
1486pub(crate) use pass_try;
1487
1488/// Errors related to the state of a command or pass encoder.
1489///
1490/// The exact behavior of these errors may change based on the resolution of
1491/// <https://github.com/gpuweb/gpuweb/issues/5207>.
1492#[derive(Clone, Debug, Error)]
1493#[non_exhaustive]
1494pub enum EncoderStateError {
1495    /// Used internally by wgpu functions to indicate the encoder already
1496    /// contained an error. This variant should usually not be seen by users of
1497    /// the API, since an effort should be made to provide the caller with a
1498    /// more specific reason for the encoder being invalid.
1499    #[error("Encoder is invalid")]
1500    Invalid,
1501
1502    /// Returned immediately when an attempt is made to encode a command using
1503    /// an encoder that has already finished.
1504    #[error("Encoding must not have ended")]
1505    Ended,
1506
1507    /// Returned by a subsequent call to `encoder.finish()`, if there was an
1508    /// attempt to open a second pass on the encoder while it was locked for
1509    /// a first pass (i.e. the first pass was still open).
1510    ///
1511    /// Note: only command encoders can be locked (not pass encoders).
1512    #[error("Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
1513    Locked,
1514
1515    /// Returned when attempting to end a pass if the parent encoder is not
1516    /// locked. This can only happen if pass begin/end calls are mismatched.
1517    #[error(
1518        "Encoder is not currently locked. A pass can only be ended while the encoder is locked."
1519    )]
1520    Unlocked,
1521
1522    /// The command buffer has already been submitted.
1523    ///
1524    /// Although command encoders and command buffers are distinct WebGPU
1525    /// objects, we use `CommandEncoderStatus` for both.
1526    #[error("This command buffer has already been submitted.")]
1527    Submitted,
1528}
1529
1530impl WebGpuError for EncoderStateError {
1531    fn webgpu_error_type(&self) -> ErrorType {
1532        match self {
1533            EncoderStateError::Invalid
1534            | EncoderStateError::Ended
1535            | EncoderStateError::Locked
1536            | EncoderStateError::Unlocked
1537            | EncoderStateError::Submitted => ErrorType::Validation,
1538        }
1539    }
1540}
1541
1542#[derive(Clone, Debug, Error)]
1543#[non_exhaustive]
1544pub enum CommandEncoderError {
1545    #[error(transparent)]
1546    State(#[from] EncoderStateError),
1547    #[error(transparent)]
1548    Device(#[from] DeviceError),
1549    #[error(transparent)]
1550    InvalidResource(#[from] InvalidResourceError),
1551    #[error(transparent)]
1552    DestroyedResource(#[from] DestroyedResourceError),
1553    #[error(transparent)]
1554    ResourceUsage(#[from] ResourceUsageCompatibilityError),
1555    #[error(transparent)]
1556    DebugGroupError(#[from] DebugGroupError),
1557    #[error(transparent)]
1558    MissingFeatures(#[from] MissingFeatures),
1559    #[error(transparent)]
1560    Transfer(#[from] TransferError),
1561    #[error(transparent)]
1562    Clear(#[from] ClearError),
1563    #[error(transparent)]
1564    Query(#[from] QueryError),
1565    #[error(transparent)]
1566    BuildAccelerationStructure(#[from] BuildAccelerationStructureError),
1567    #[error(transparent)]
1568    TransitionResources(#[from] TransitionResourcesError),
1569    #[error(transparent)]
1570    ComputePass(#[from] ComputePassError),
1571    #[error(transparent)]
1572    RenderPass(#[from] RenderPassError),
1573}
1574
1575impl CommandEncoderError {
1576    fn is_destroyed_error(&self) -> bool {
1577        matches!(
1578            self,
1579            Self::DestroyedResource(_)
1580                | Self::Clear(ClearError::DestroyedResource(_))
1581                | Self::Query(QueryError::DestroyedResource(_))
1582                | Self::ComputePass(ComputePassError {
1583                    inner: ComputePassErrorInner::DestroyedResource(_),
1584                    ..
1585                })
1586                | Self::RenderPass(RenderPassError {
1587                    inner: RenderPassErrorInner::DestroyedResource(_),
1588                    ..
1589                })
1590                | Self::RenderPass(RenderPassError {
1591                    inner: RenderPassErrorInner::RenderCommand(
1592                        RenderCommandError::DestroyedResource(_)
1593                    ),
1594                    ..
1595                })
1596                | Self::RenderPass(RenderPassError {
1597                    inner: RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError(
1598                        BindingError::DestroyedResource(_)
1599                    )),
1600                    ..
1601                })
1602        )
1603    }
1604}
1605
1606impl WebGpuError for CommandEncoderError {
1607    fn webgpu_error_type(&self) -> ErrorType {
1608        match self {
1609            Self::Device(e) => e.webgpu_error_type(),
1610            Self::InvalidResource(e) => e.webgpu_error_type(),
1611            Self::DebugGroupError(e) => e.webgpu_error_type(),
1612            Self::MissingFeatures(e) => e.webgpu_error_type(),
1613            Self::State(e) => e.webgpu_error_type(),
1614            Self::DestroyedResource(e) => e.webgpu_error_type(),
1615            Self::Transfer(e) => e.webgpu_error_type(),
1616            Self::Clear(e) => e.webgpu_error_type(),
1617            Self::Query(e) => e.webgpu_error_type(),
1618            Self::BuildAccelerationStructure(e) => e.webgpu_error_type(),
1619            Self::TransitionResources(e) => e.webgpu_error_type(),
1620            Self::ResourceUsage(e) => e.webgpu_error_type(),
1621            Self::ComputePass(e) => e.webgpu_error_type(),
1622            Self::RenderPass(e) => e.webgpu_error_type(),
1623        }
1624    }
1625}
1626
1627#[derive(Clone, Debug, Error)]
1628#[non_exhaustive]
1629pub enum DebugGroupError {
1630    #[error("Cannot pop debug group, because number of pushed debug groups is zero")]
1631    InvalidPop,
1632    #[error("A debug group was not popped before the encoder was finished")]
1633    MissingPop,
1634}
1635
1636impl WebGpuError for DebugGroupError {
1637    fn webgpu_error_type(&self) -> ErrorType {
1638        match self {
1639            Self::InvalidPop | Self::MissingPop => ErrorType::Validation,
1640        }
1641    }
1642}
1643
1644#[derive(Clone, Debug, Error)]
1645#[non_exhaustive]
1646pub enum TimestampWritesError {
1647    #[error(
1648        "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
1649    )]
1650    IndicesEqual { idx: u32 },
1651    #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
1652    IndicesMissing,
1653}
1654
1655impl WebGpuError for TimestampWritesError {
1656    fn webgpu_error_type(&self) -> ErrorType {
1657        match self {
1658            Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,
1659        }
1660    }
1661}
1662
1663impl Global {
1664    fn resolve_buffer_id(
1665        &self,
1666        buffer_id: Id<id::markers::Buffer>,
1667    ) -> Result<Arc<crate::resource::Buffer>, InvalidResourceError> {
1668        self.hub.buffers.get(buffer_id).get()
1669    }
1670
1671    fn resolve_texture_id(
1672        &self,
1673        texture_id: Id<id::markers::Texture>,
1674    ) -> Result<Arc<crate::resource::Texture>, InvalidResourceError> {
1675        self.hub.textures.get(texture_id).get()
1676    }
1677
1678    fn resolve_query_set(
1679        &self,
1680        query_set_id: Id<id::markers::QuerySet>,
1681    ) -> Result<Arc<QuerySet>, InvalidResourceError> {
1682        self.hub.query_sets.get(query_set_id).get()
1683    }
1684
1685    /// Finishes a command encoder, creating a command buffer and returning errors that were
1686    /// deferred until now.
1687    ///
1688    /// The returned `String` is the label of the command encoder, supplied so that `wgpu` can
1689    /// include the label when printing deferred errors without having its own copy of the label.
1690    /// This is a kludge and should be replaced if we think of a better solution to propagating
1691    /// labels.
1692    pub fn command_encoder_finish(
1693        &self,
1694        encoder_id: id::CommandEncoderId,
1695        desc: &wgt::CommandBufferDescriptor<Label>,
1696        id_in: Option<id::CommandBufferId>,
1697    ) -> (id::CommandBufferId, Option<(String, CommandEncoderError)>) {
1698        profiling::scope!("CommandEncoder::finish");
1699
1700        let hub = &self.hub;
1701        let cmd_enc = hub.command_encoders.get(encoder_id);
1702
1703        let (cmd_buf, opt_error) = cmd_enc.finish(desc);
1704        let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(cmd_buf);
1705
1706        (
1707            cmd_buf_id,
1708            opt_error.map(|error| (cmd_enc.label.clone(), error)),
1709        )
1710    }
1711
1712    pub fn command_encoder_push_debug_group(
1713        &self,
1714        encoder_id: id::CommandEncoderId,
1715        label: &str,
1716    ) -> Result<(), EncoderStateError> {
1717        profiling::scope!("CommandEncoder::push_debug_group");
1718        api_log!("CommandEncoder::push_debug_group {label}");
1719
1720        let hub = &self.hub;
1721
1722        let cmd_enc = hub.command_encoders.get(encoder_id);
1723        let mut cmd_buf_data = cmd_enc.data.lock();
1724
1725        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1726            Ok(ArcCommand::PushDebugGroup(label.to_owned()))
1727        })
1728    }
1729
1730    pub fn command_encoder_insert_debug_marker(
1731        &self,
1732        encoder_id: id::CommandEncoderId,
1733        label: &str,
1734    ) -> Result<(), EncoderStateError> {
1735        profiling::scope!("CommandEncoder::insert_debug_marker");
1736        api_log!("CommandEncoder::insert_debug_marker {label}");
1737
1738        let hub = &self.hub;
1739
1740        let cmd_enc = hub.command_encoders.get(encoder_id);
1741        let mut cmd_buf_data = cmd_enc.data.lock();
1742
1743        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1744            Ok(ArcCommand::InsertDebugMarker(label.to_owned()))
1745        })
1746    }
1747
1748    pub fn command_encoder_pop_debug_group(
1749        &self,
1750        encoder_id: id::CommandEncoderId,
1751    ) -> Result<(), EncoderStateError> {
1752        profiling::scope!("CommandEncoder::pop_debug_marker");
1753        api_log!("CommandEncoder::pop_debug_group");
1754
1755        let hub = &self.hub;
1756
1757        let cmd_enc = hub.command_encoders.get(encoder_id);
1758        let mut cmd_buf_data = cmd_enc.data.lock();
1759
1760        cmd_buf_data
1761            .push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::PopDebugGroup) })
1762    }
1763
1764    fn validate_pass_timestamp_writes<E>(
1765        device: &Device,
1766        query_sets: &Storage<Fallible<QuerySet>>,
1767        timestamp_writes: &PassTimestampWrites,
1768    ) -> Result<ArcPassTimestampWrites, E>
1769    where
1770        E: From<TimestampWritesError>
1771            + From<QueryUseError>
1772            + From<DeviceError>
1773            + From<MissingFeatures>
1774            + From<InvalidResourceError>,
1775    {
1776        let &PassTimestampWrites {
1777            query_set,
1778            beginning_of_pass_write_index,
1779            end_of_pass_write_index,
1780        } = timestamp_writes;
1781
1782        device.require_features(wgt::Features::TIMESTAMP_QUERY)?;
1783
1784        let query_set = query_sets.get(query_set).get()?;
1785
1786        query_set.same_device(device)?;
1787
1788        for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
1789            .into_iter()
1790            .flatten()
1791        {
1792            query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
1793        }
1794
1795        if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
1796            if begin == end {
1797                return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());
1798            }
1799        }
1800
1801        if beginning_of_pass_write_index
1802            .or(end_of_pass_write_index)
1803            .is_none()
1804        {
1805            return Err(TimestampWritesError::IndicesMissing.into());
1806        }
1807
1808        Ok(ArcPassTimestampWrites {
1809            query_set,
1810            beginning_of_pass_write_index,
1811            end_of_pass_write_index,
1812        })
1813    }
1814}
1815
1816pub(crate) fn push_debug_group(
1817    state: &mut EncodingState,
1818    label: &str,
1819) -> Result<(), CommandEncoderError> {
1820    *state.debug_scope_depth += 1;
1821
1822    if !state
1823        .device
1824        .instance_flags
1825        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1826    {
1827        unsafe { state.raw_encoder.begin_debug_marker(label) };
1828    }
1829
1830    Ok(())
1831}
1832
1833pub(crate) fn insert_debug_marker(
1834    state: &mut EncodingState,
1835    label: &str,
1836) -> Result<(), CommandEncoderError> {
1837    if !state
1838        .device
1839        .instance_flags
1840        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1841    {
1842        unsafe { state.raw_encoder.insert_debug_marker(label) };
1843    }
1844
1845    Ok(())
1846}
1847
1848pub(crate) fn pop_debug_group(state: &mut EncodingState) -> Result<(), CommandEncoderError> {
1849    if *state.debug_scope_depth == 0 {
1850        return Err(DebugGroupError::InvalidPop.into());
1851    }
1852    *state.debug_scope_depth -= 1;
1853
1854    if !state
1855        .device
1856        .instance_flags
1857        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1858    {
1859        unsafe { state.raw_encoder.end_debug_marker() };
1860    }
1861
1862    Ok(())
1863}
1864
1865fn immediates_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
1866where
1867    PushFn: FnMut(u32, &[u32]),
1868{
1869    let mut count_words = 0_u32;
1870    let size_words = size_bytes / wgt::IMMEDIATE_DATA_ALIGNMENT;
1871    while count_words < size_words {
1872        let count_bytes = count_words * wgt::IMMEDIATE_DATA_ALIGNMENT;
1873        let size_to_write_words =
1874            (size_words - count_words).min(IMMEDIATES_CLEAR_ARRAY.len() as u32);
1875
1876        push_fn(
1877            offset + count_bytes,
1878            &IMMEDIATES_CLEAR_ARRAY[0..size_to_write_words as usize],
1879        );
1880
1881        count_words += size_to_write_words;
1882    }
1883}
1884
1885#[derive(Debug, Copy, Clone)]
1886struct StateChange<T> {
1887    last_state: Option<T>,
1888}
1889
1890impl<T: Copy + PartialEq> StateChange<T> {
1891    fn new() -> Self {
1892        Self { last_state: None }
1893    }
1894    fn set_and_check_redundant(&mut self, new_state: T) -> bool {
1895        let already_set = self.last_state == Some(new_state);
1896        self.last_state = Some(new_state);
1897        already_set
1898    }
1899    fn reset(&mut self) {
1900        self.last_state = None;
1901    }
1902}
1903
1904impl<T: Copy + PartialEq> Default for StateChange<T> {
1905    fn default() -> Self {
1906        Self::new()
1907    }
1908}
1909
1910#[derive(Debug)]
1911struct BindGroupStateChange {
1912    last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],
1913}
1914
1915impl BindGroupStateChange {
1916    fn new() -> Self {
1917        Self {
1918            last_states: [StateChange::new(); hal::MAX_BIND_GROUPS],
1919        }
1920    }
1921
1922    fn set_and_check_redundant(
1923        &mut self,
1924        bind_group_id: Option<id::BindGroupId>,
1925        index: u32,
1926        dynamic_offsets: &mut Vec<u32>,
1927        offsets: &[wgt::DynamicOffset],
1928    ) -> bool {
1929        // For now never deduplicate bind groups with dynamic offsets.
1930        if offsets.is_empty() {
1931            // If this get returns None, that means we're well over the limit,
1932            // so let the call through to get a proper error
1933            if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1934                // Bail out if we're binding the same bind group.
1935                if current_bind_group.set_and_check_redundant(bind_group_id) {
1936                    return true;
1937                }
1938            }
1939        } else {
1940            // We intentionally remove the memory of this bind group if we have dynamic offsets,
1941            // such that if you try to bind this bind group later with _no_ dynamic offsets it
1942            // tries to bind it again and gives a proper validation error.
1943            if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1944                current_bind_group.reset();
1945            }
1946            dynamic_offsets.extend_from_slice(offsets);
1947        }
1948        false
1949    }
1950    fn reset(&mut self) {
1951        self.last_states = [StateChange::new(); hal::MAX_BIND_GROUPS];
1952    }
1953}
1954
1955impl Default for BindGroupStateChange {
1956    fn default() -> Self {
1957        Self::new()
1958    }
1959}
1960
1961/// Helper to attach [`PassErrorScope`] to errors.
1962trait MapPassErr<T> {
1963    fn map_pass_err(self, scope: PassErrorScope) -> T;
1964}
1965
1966impl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>
1967where
1968    E: MapPassErr<F>,
1969{
1970    fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {
1971        self.map_err(|err| err.map_pass_err(scope))
1972    }
1973}
1974
1975impl MapPassErr<PassStateError> for EncoderStateError {
1976    fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {
1977        PassStateError { scope, inner: self }
1978    }
1979}
1980
1981#[derive(Clone, Copy, Debug)]
1982pub enum DrawKind {
1983    Draw,
1984    DrawIndirect,
1985    MultiDrawIndirect,
1986    MultiDrawIndirectCount,
1987}
1988
1989/// The type of draw command(indexed or not, or mesh shader)
1990#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1991#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1992pub enum DrawCommandFamily {
1993    Draw,
1994    DrawIndexed,
1995    DrawMeshTasks,
1996}
1997
1998/// A command that can be recorded in a pass or bundle.
1999///
2000/// This is used to provide context for errors during command recording.
2001/// [`MapPassErr`] is used as a helper to attach a `PassErrorScope` to
2002/// an error.
2003///
2004/// The [`PassErrorScope::Bundle`] and [`PassErrorScope::Pass`] variants
2005/// are used when the error occurs during the opening or closing of the
2006/// pass or bundle.
2007#[derive(Clone, Copy, Debug, Error)]
2008pub enum PassErrorScope {
2009    // TODO: Extract out the 2 error variants below so that we can always
2010    // include the ResourceErrorIdent of the pass around all inner errors
2011    #[error("In a bundle parameter")]
2012    Bundle,
2013    #[error("In a pass parameter")]
2014    Pass,
2015    #[error("In a set_bind_group command")]
2016    SetBindGroup,
2017    #[error("In a set_pipeline command")]
2018    SetPipelineRender,
2019    #[error("In a set_pipeline command")]
2020    SetPipelineCompute,
2021    #[error("In a set_immediates command")]
2022    SetImmediate,
2023    #[error("In a set_vertex_buffer command")]
2024    SetVertexBuffer,
2025    #[error("In a set_index_buffer command")]
2026    SetIndexBuffer,
2027    #[error("In a set_blend_constant command")]
2028    SetBlendConstant,
2029    #[error("In a set_stencil_reference command")]
2030    SetStencilReference,
2031    #[error("In a set_viewport command")]
2032    SetViewport,
2033    #[error("In a set_scissor_rect command")]
2034    SetScissorRect,
2035    #[error("In a draw command, kind: {kind:?}")]
2036    Draw {
2037        kind: DrawKind,
2038        family: DrawCommandFamily,
2039    },
2040    #[error("In a write_timestamp command")]
2041    WriteTimestamp,
2042    #[error("In a begin_occlusion_query command")]
2043    BeginOcclusionQuery,
2044    #[error("In a end_occlusion_query command")]
2045    EndOcclusionQuery,
2046    #[error("In a begin_pipeline_statistics_query command")]
2047    BeginPipelineStatisticsQuery,
2048    #[error("In a end_pipeline_statistics_query command")]
2049    EndPipelineStatisticsQuery,
2050    #[error("In a execute_bundle command")]
2051    ExecuteBundle,
2052    #[error("In a dispatch command, indirect:{indirect}")]
2053    Dispatch { indirect: bool },
2054    #[error("In a push_debug_group command")]
2055    PushDebugGroup,
2056    #[error("In a pop_debug_group command")]
2057    PopDebugGroup,
2058    #[error("In a insert_debug_marker command")]
2059    InsertDebugMarker,
2060}
2061
2062/// Variant of `EncoderStateError` that includes the pass scope.
2063#[derive(Clone, Debug, Error)]
2064#[error("{scope}")]
2065pub struct PassStateError {
2066    pub scope: PassErrorScope,
2067    #[source]
2068    pub(super) inner: EncoderStateError,
2069}
2070
2071impl WebGpuError for PassStateError {
2072    fn webgpu_error_type(&self) -> ErrorType {
2073        let Self { scope: _, inner } = self;
2074        inner.webgpu_error_type()
2075    }
2076}