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