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