Skip to main content

wgpu_core/command/
mod.rs

1//! # Command Encoding
2//!
3//! TODO: High-level description of command encoding.
4//!
5//! The convention in this module is that functions accepting a [`&mut dyn
6//! hal::DynCommandEncoder`] are low-level helpers and may assume the encoder is
7//! in the open state, ready to encode commands. Encoders that are not open
8//! should be nested within some other container that provides additional
9//! state tracking, like [`InnerCommandEncoder`].
10
11mod allocator;
12mod bind;
13mod bundle;
14mod clear;
15mod compute;
16mod compute_command;
17mod draw;
18mod encoder;
19mod encoder_command;
20pub mod ffi;
21mod memory_init;
22mod pass;
23mod query;
24mod ray_tracing;
25mod render;
26mod render_command;
27mod timestamp_writes;
28mod transfer;
29mod transition_resources;
30
31use alloc::{borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec::Vec};
32use core::convert::Infallible;
33use core::mem::{self, ManuallyDrop};
34use core::{ops, panic};
35
36#[cfg(feature = "serde")]
37pub(crate) use self::encoder_command::serde_object_reference_struct;
38#[cfg(any(feature = "trace", feature = "replay"))]
39#[doc(hidden)]
40pub use self::encoder_command::PointerReferences;
41// This module previously did `pub use *` for some of the submodules. When that
42// was removed, every type that was previously public via `use *` was listed
43// here. Some types (in particular `CopySide`) may be exported unnecessarily.
44pub use self::{
45    bundle::{
46        bundle_ffi, CreateRenderBundleError, ExecutionError, RenderBundle, RenderBundleDescriptor,
47        RenderBundleEncoder, RenderBundleEncoderDescriptor, RenderBundleError,
48        RenderBundleErrorInner,
49    },
50    clear::ClearError,
51    compute::{
52        ComputeBasePass, ComputePass, ComputePassDescriptor, ComputePassError,
53        ComputePassErrorInner, DispatchError,
54    },
55    compute_command::ArcComputeCommand,
56    draw::{DrawError, Rect, RenderCommandError},
57    encoder_command::{ArcCommand, ArcReferences, Command, IdReferences, ReferenceType},
58    query::{QueryError, QueryUseError, ResolveError, SimplifiedQueryType},
59    render::{
60        ArcRenderPassColorAttachment, AttachmentError, AttachmentErrorLocation,
61        ColorAttachmentError, ColorAttachments, LoadOp, PassChannel, RenderBasePass, RenderPass,
62        RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor,
63        RenderPassError, RenderPassErrorInner, ResolvedPassChannel,
64        ResolvedRenderPassDepthStencilAttachment, StoreOp,
65    },
66    render_command::ArcRenderCommand,
67    transfer::{CopySide, TransferError},
68    transition_resources::TransitionResourcesError,
69};
70pub(crate) use self::{
71    clear::clear_texture,
72    encoder::EncodingState,
73    memory_init::CommandBufferTextureMemoryActions,
74    render::{get_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, Fallible, InvalidOrDestroyedResourceError, InvalidResourceError,
100    Labeled, 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(crate) struct CommandEncoder {
524    pub(crate) device: Arc<Device>,
525
526    pub(crate) label: String,
527
528    /// The mutable state of this command encoder.
529    pub(crate) data: Mutex<CommandEncoderStatus>,
530}
531
532crate::impl_resource_type!(CommandEncoder);
533crate::impl_labeled!(CommandEncoder);
534crate::impl_parent_device!(CommandEncoder);
535crate::impl_storage_item!(CommandEncoder);
536
537impl Drop for CommandEncoder {
538    fn drop(&mut self) {
539        resource_log!("Drop {}", self.error_ident());
540    }
541}
542
543/// The encoding API being used with a `CommandEncoder`.
544///
545/// Mixing APIs on the same encoder is not allowed.
546#[derive(Copy, Clone, Debug, Eq, PartialEq)]
547pub enum EncodingApi {
548    // The regular wgpu encoding APIs are being used.
549    Wgpu,
550
551    // The raw hal encoding API is being used.
552    Raw,
553
554    // Neither encoding API has been called yet.
555    Undecided,
556
557    // The encoder is used internally by wgpu.
558    InternalUse,
559}
560
561impl EncodingApi {
562    pub(crate) fn set(&mut self, api: EncodingApi) {
563        match *self {
564            EncodingApi::Undecided => {
565                *self = api;
566            }
567            self_api if self_api != api => {
568                panic!("Mixing the wgpu encoding API with the raw encoding API is not permitted");
569            }
570            _ => {}
571        }
572    }
573}
574
575/// A raw [`CommandEncoder`][rce], and the raw [`CommandBuffer`][rcb]s built from it.
576///
577/// Each wgpu-core [`CommandBuffer`] owns an instance of this type, which is
578/// where the commands are actually stored.
579///
580/// This holds a `Vec` of raw [`CommandBuffer`][rcb]s, not just one. We are not
581/// always able to record commands in the order in which they must ultimately be
582/// submitted to the queue, but raw command buffers don't permit inserting new
583/// commands into the middle of a recorded stream. However, hal queue submission
584/// accepts a series of command buffers at once, so we can simply break the
585/// stream up into multiple buffers, and then reorder the buffers. See
586/// [`InnerCommandEncoder::close_and_swap`] for a specific example of this.
587///
588/// [rce]: hal::Api::CommandEncoder
589/// [rcb]: hal::Api::CommandBuffer
590pub(crate) struct InnerCommandEncoder {
591    /// The underlying `wgpu_hal` [`CommandEncoder`].
592    ///
593    /// Successfully executed command buffers' encoders are saved in a
594    /// [`CommandAllocator`] for recycling.
595    ///
596    /// [`CommandEncoder`]: hal::Api::CommandEncoder
597    /// [`CommandAllocator`]: crate::command::CommandAllocator
598    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
599
600    /// All the raw command buffers for our owning [`CommandBuffer`], in
601    /// submission order.
602    ///
603    /// These command buffers were all constructed with `raw`. The
604    /// [`wgpu_hal::CommandEncoder`] trait forbids these from outliving `raw`,
605    /// and requires that we provide all of these when we call
606    /// [`raw.reset_all()`][CE::ra], so the encoder and its buffers travel
607    /// together.
608    ///
609    /// [CE::ra]: hal::CommandEncoder::reset_all
610    /// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
611    pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
612
613    pub(crate) device: Arc<Device>,
614
615    /// True if `raw` is in the "recording" state.
616    ///
617    /// See the documentation for [`wgpu_hal::CommandEncoder`] for
618    /// details on the states `raw` can be in.
619    ///
620    /// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
621    pub(crate) is_open: bool,
622
623    /// Tracks which API is being used to encode commands.
624    ///
625    /// Mixing the wgpu encoding API with access to the raw hal encoder via
626    /// `as_hal_mut` is not supported. this field tracks which API is being used
627    /// in order to detect and reject invalid usage.
628    pub(crate) api: EncodingApi,
629
630    pub(crate) label: String,
631}
632
633impl InnerCommandEncoder {
634    /// Finish the current command buffer and insert it just before
635    /// the last element in [`self.list`][l].
636    ///
637    /// On return, the underlying hal encoder is closed.
638    ///
639    /// What is this for?
640    ///
641    /// The `wgpu_hal` contract requires that each render or compute pass's
642    /// commands be preceded by calls to [`transition_buffers`] and
643    /// [`transition_textures`], to put the resources the pass operates on in
644    /// the appropriate state. Unfortunately, we don't know which transitions
645    /// are needed until we're done recording the pass itself. Rather than
646    /// iterating over the pass twice, we note the necessary transitions as we
647    /// record its commands, finish the raw command buffer for the actual pass,
648    /// record a new raw command buffer for the transitions, and jam that buffer
649    /// in just before the pass's. This is the function that jams in the
650    /// transitions' command buffer.
651    ///
652    /// # Panics
653    ///
654    /// - If the encoder is not open.
655    ///
656    /// # 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<Fallible<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        let query_set = query_set.clone().get()?;
987
988        query_set.same_device(device)?;
989
990        for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
991            .into_iter()
992            .flatten()
993        {
994            query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
995        }
996
997        if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
998            if begin == end {
999                return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());
1000            }
1001        }
1002
1003        if beginning_of_pass_write_index
1004            .or(end_of_pass_write_index)
1005            .is_none()
1006        {
1007            return Err(TimestampWritesError::IndicesMissing.into());
1008        }
1009
1010        Ok(ArcPassTimestampWrites {
1011            query_set,
1012            beginning_of_pass_write_index,
1013            end_of_pass_write_index,
1014        })
1015    }
1016
1017    pub(crate) fn insert_barriers_from_tracker(
1018        raw: &mut dyn hal::DynCommandEncoder,
1019        base: &mut Tracker,
1020        head: &Tracker,
1021        snatch_guard: &SnatchGuard,
1022    ) {
1023        profiling::scope!("insert_barriers");
1024
1025        base.buffers.set_from_tracker(&head.buffers);
1026        base.textures.set_from_tracker(&head.textures);
1027
1028        Self::drain_barriers(raw, base, snatch_guard);
1029    }
1030
1031    pub(crate) fn insert_barriers_from_scope(
1032        raw: &mut dyn hal::DynCommandEncoder,
1033        base: &mut Tracker,
1034        head: &UsageScope,
1035        snatch_guard: &SnatchGuard,
1036    ) {
1037        profiling::scope!("insert_barriers");
1038
1039        base.buffers.set_from_usage_scope(&head.buffers);
1040        base.textures.set_from_usage_scope(&head.textures);
1041
1042        Self::drain_barriers(raw, base, snatch_guard);
1043    }
1044
1045    pub(crate) fn drain_barriers(
1046        raw: &mut dyn hal::DynCommandEncoder,
1047        base: &mut Tracker,
1048        snatch_guard: &SnatchGuard,
1049    ) {
1050        profiling::scope!("drain_barriers");
1051
1052        let buffer_barriers = base
1053            .buffers
1054            .drain_transitions(snatch_guard)
1055            .collect::<Vec<_>>();
1056        let (transitions, textures) = base.textures.drain_transitions(snatch_guard);
1057        let texture_barriers = transitions
1058            .into_iter()
1059            .enumerate()
1060            .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))
1061            .collect::<Vec<_>>();
1062
1063        unsafe {
1064            raw.transition_buffers(&buffer_barriers);
1065            raw.transition_textures(&texture_barriers);
1066        }
1067    }
1068
1069    pub(crate) fn insert_barriers_from_device_tracker(
1070        raw: &mut dyn hal::DynCommandEncoder,
1071        base: &mut DeviceTracker,
1072        head: &Tracker,
1073        snatch_guard: &SnatchGuard,
1074    ) {
1075        profiling::scope!("insert_barriers_from_device_tracker");
1076
1077        let buffer_barriers = base
1078            .buffers
1079            .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)
1080            .collect::<Vec<_>>();
1081
1082        let texture_barriers = base
1083            .textures
1084            .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)
1085            .collect::<Vec<_>>();
1086
1087        unsafe {
1088            raw.transition_buffers(&buffer_barriers);
1089            raw.transition_textures(&texture_barriers);
1090        }
1091    }
1092
1093    fn encode_commands(
1094        device: &Arc<Device>,
1095        cmd_buf_data: &mut CommandBufferMutable,
1096    ) -> Result<(), CommandEncoderError> {
1097        device.check_is_valid()?;
1098        let snatch_guard = device.snatchable_lock.read();
1099        let mut debug_scope_depth = 0;
1100
1101        if cmd_buf_data.encoder.api == EncodingApi::Raw {
1102            // Should have panicked on the first call that switched APIs,
1103            // but lets be sure.
1104            assert!(cmd_buf_data.commands.is_empty());
1105        }
1106
1107        let commands = mem::take(&mut cmd_buf_data.commands);
1108
1109        #[cfg(feature = "trace")]
1110        if device.trace.lock().is_some() {
1111            cmd_buf_data.trace_commands = Some(
1112                commands
1113                    .iter()
1114                    .map(crate::device::trace::IntoTrace::to_trace)
1115                    .collect(),
1116            );
1117        }
1118
1119        for command in commands {
1120            if matches!(
1121                command,
1122                ArcCommand::RunRenderPass { .. }
1123                    | ArcCommand::RunComputePass { .. }
1124                    | ArcCommand::ResolveQuerySet { .. }
1125            ) {
1126                // Compute passes and render passes can accept either an
1127                // open or closed encoder. Resolving query sets needs to
1128                // potentially close and open the encoder. This state
1129                // object holds an `InnerCommandEncoder`. See the
1130                // documentation of [`EncodingState`].
1131                let mut state = EncodingState {
1132                    device,
1133                    raw_encoder: &mut cmd_buf_data.encoder,
1134                    tracker: &mut cmd_buf_data.trackers,
1135                    buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1136                    texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1137                    as_actions: &mut cmd_buf_data.as_actions,
1138                    temp_resources: &mut cmd_buf_data.temp_resources,
1139                    indirect_draw_validation_resources: &mut cmd_buf_data
1140                        .indirect_draw_validation_resources,
1141                    snatch_guard: &snatch_guard,
1142                    debug_scope_depth: &mut debug_scope_depth,
1143                    query_set_writes: &mut cmd_buf_data.query_set_writes,
1144                    deferred_query_set_resolves: &mut cmd_buf_data.deferred_query_set_resolves,
1145                };
1146
1147                match command {
1148                    ArcCommand::RunRenderPass {
1149                        pass,
1150                        color_attachments,
1151                        depth_stencil_attachment,
1152                        timestamp_writes,
1153                        occlusion_query_set,
1154                        multiview_mask,
1155                    } => {
1156                        api_log!(
1157                            "Begin encoding render pass with '{}' label",
1158                            pass.label.as_deref().unwrap_or("")
1159                        );
1160                        let res = render::encode_render_pass(
1161                            &mut state,
1162                            pass,
1163                            color_attachments,
1164                            depth_stencil_attachment,
1165                            timestamp_writes,
1166                            occlusion_query_set,
1167                            multiview_mask,
1168                        );
1169                        match res.as_ref() {
1170                            Err(err) => {
1171                                api_log!("Finished encoding render pass ({err:?})")
1172                            }
1173                            Ok(_) => {
1174                                api_log!("Finished encoding render pass (success)")
1175                            }
1176                        }
1177                        res?;
1178                    }
1179                    ArcCommand::RunComputePass {
1180                        pass,
1181                        timestamp_writes,
1182                    } => {
1183                        api_log!(
1184                            "Begin encoding compute pass with '{}' label",
1185                            pass.label.as_deref().unwrap_or("")
1186                        );
1187                        let res = compute::encode_compute_pass(&mut state, pass, timestamp_writes);
1188                        match res.as_ref() {
1189                            Err(err) => {
1190                                api_log!("Finished encoding compute pass ({err:?})")
1191                            }
1192                            Ok(_) => {
1193                                api_log!("Finished encoding compute pass (success)")
1194                            }
1195                        }
1196                        res?;
1197                    }
1198                    ArcCommand::ResolveQuerySet {
1199                        query_set,
1200                        start_query,
1201                        query_count,
1202                        destination,
1203                        destination_offset,
1204                    } => {
1205                        query::resolve_query_set(
1206                            &mut state,
1207                            query_set,
1208                            start_query,
1209                            query_count,
1210                            destination,
1211                            destination_offset,
1212                        )?;
1213                    }
1214                    _ => unreachable!(),
1215                }
1216            } else {
1217                // All the other non-pass encoding routines assume the
1218                // encoder is open, so open it if necessary. This state
1219                // object holds an `&mut dyn hal::DynCommandEncoder`. By
1220                // convention, a bare HAL encoder reference in
1221                // [`EncodingState`] must always be an open encoder.
1222                let raw_encoder = cmd_buf_data.encoder.open_if_closed()?;
1223                let mut state = EncodingState {
1224                    device,
1225                    raw_encoder,
1226                    tracker: &mut cmd_buf_data.trackers,
1227                    buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1228                    texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1229                    as_actions: &mut cmd_buf_data.as_actions,
1230                    temp_resources: &mut cmd_buf_data.temp_resources,
1231                    indirect_draw_validation_resources: &mut cmd_buf_data
1232                        .indirect_draw_validation_resources,
1233                    snatch_guard: &snatch_guard,
1234                    debug_scope_depth: &mut debug_scope_depth,
1235                    query_set_writes: &mut cmd_buf_data.query_set_writes,
1236                    deferred_query_set_resolves: &mut cmd_buf_data.deferred_query_set_resolves,
1237                };
1238                match command {
1239                    ArcCommand::CopyBufferToBuffer {
1240                        src,
1241                        src_offset,
1242                        dst,
1243                        dst_offset,
1244                        size,
1245                    } => {
1246                        transfer::copy_buffer_to_buffer(
1247                            &mut state, &src, src_offset, &dst, dst_offset, size,
1248                        )?;
1249                    }
1250                    ArcCommand::CopyBufferToTexture { src, dst, size } => {
1251                        transfer::copy_buffer_to_texture(&mut state, &src, &dst, &size)?;
1252                    }
1253                    ArcCommand::CopyTextureToBuffer { src, dst, size } => {
1254                        transfer::copy_texture_to_buffer(&mut state, &src, &dst, &size)?;
1255                    }
1256                    ArcCommand::CopyTextureToTexture { src, dst, size } => {
1257                        transfer::copy_texture_to_texture(&mut state, &src, &dst, &size)?;
1258                    }
1259                    ArcCommand::ClearBuffer { dst, offset, size } => {
1260                        clear::clear_buffer(&mut state, dst, offset, size)?;
1261                    }
1262                    ArcCommand::ClearTexture {
1263                        dst,
1264                        subresource_range,
1265                    } => {
1266                        clear::clear_texture_cmd(&mut state, dst, &subresource_range)?;
1267                    }
1268                    ArcCommand::WriteTimestamp {
1269                        query_set,
1270                        query_index,
1271                    } => {
1272                        query::write_timestamp(&mut state, query_set, query_index)?;
1273                    }
1274                    ArcCommand::PushDebugGroup(label) => {
1275                        push_debug_group(&mut state, &label)?;
1276                    }
1277                    ArcCommand::PopDebugGroup => {
1278                        pop_debug_group(&mut state)?;
1279                    }
1280                    ArcCommand::InsertDebugMarker(label) => {
1281                        insert_debug_marker(&mut state, &label)?;
1282                    }
1283                    ArcCommand::BuildAccelerationStructures { blas, tlas } => {
1284                        ray_tracing::build_acceleration_structures(&mut state, blas, tlas)?;
1285                    }
1286                    ArcCommand::TransitionResources {
1287                        buffer_transitions,
1288                        texture_transitions,
1289                    } => {
1290                        transition_resources::transition_resources(
1291                            &mut state,
1292                            buffer_transitions,
1293                            texture_transitions,
1294                        )?;
1295                    }
1296                    ArcCommand::RunComputePass { .. }
1297                    | ArcCommand::RunRenderPass { .. }
1298                    | ArcCommand::ResolveQuerySet { .. } => {
1299                        unreachable!()
1300                    }
1301                }
1302            }
1303        }
1304
1305        if debug_scope_depth > 0 {
1306            Err(CommandEncoderError::DebugGroupError(
1307                DebugGroupError::MissingPop,
1308            ))?;
1309        }
1310
1311        // Close the encoder, unless it was closed already by a render or compute pass.
1312        cmd_buf_data.encoder.close_if_open()?;
1313
1314        // Note: if we want to stop tracking the swapchain texture view,
1315        // this is the place to do it.
1316
1317        Ok(())
1318    }
1319
1320    fn finish(
1321        self: &Arc<Self>,
1322        desc: &wgt::CommandBufferDescriptor<Label>,
1323    ) -> (Arc<CommandBuffer>, Option<CommandEncoderError>) {
1324        let status = self.data.lock().finish();
1325
1326        let res = match status {
1327            CommandEncoderStatus::Finished(mut cmd_buf_data) => {
1328                match Self::encode_commands(&self.device, &mut cmd_buf_data) {
1329                    Ok(()) => Ok(cmd_buf_data),
1330                    Err(error) => Err(EncoderErrorState {
1331                        error,
1332                        #[cfg(feature = "trace")]
1333                        trace_commands: mem::take(&mut cmd_buf_data.trace_commands),
1334                    }),
1335                }
1336            }
1337            CommandEncoderStatus::Error(error_state) => Err(error_state),
1338            _ => unreachable!(),
1339        };
1340
1341        let (data, error) = match res {
1342            Err(EncoderErrorState {
1343                error,
1344                #[cfg(feature = "trace")]
1345                trace_commands,
1346            }) => {
1347                // Normally, commands are added to the trace when submitted, but
1348                // since this command buffer won't be submitted, add it to the
1349                // trace now.
1350                #[cfg(feature = "trace")]
1351                if let Some(trace) = self.device.trace.lock().as_mut() {
1352                    use alloc::string::ToString;
1353
1354                    trace.add(crate::device::trace::Action::FailedCommands {
1355                        commands: trace_commands,
1356                        failed_at_submit: None,
1357                        error: error.to_string(),
1358                    });
1359                }
1360
1361                if error.is_destroyed_error() {
1362                    // Errors related to destroyed resources are not reported until the
1363                    // command buffer is submitted.
1364                    (make_error_state(error), None)
1365                } else {
1366                    (make_error_state(error.clone()), Some(error))
1367                }
1368            }
1369
1370            Ok(data) => (CommandEncoderStatus::Finished(data), None),
1371        };
1372
1373        let cmd_buf = Arc::new(CommandBuffer {
1374            device: self.device.clone(),
1375            label: desc.label.to_string(),
1376            data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),
1377        });
1378
1379        (cmd_buf, error)
1380    }
1381}
1382
1383impl CommandBuffer {
1384    /// Replay commands from a trace.
1385    ///
1386    /// This is exposed for the `player` crate only. It is not a public API.
1387    /// It is not guaranteed to apply all of the validation that the original
1388    /// entrypoints provide.
1389    #[doc(hidden)]
1390    pub fn from_trace(device: &Arc<Device>, commands: Vec<Command<ArcReferences>>) -> Arc<Self> {
1391        let encoder = device.create_command_encoder(&None).unwrap();
1392        let mut cmd_enc_status = encoder.data.lock();
1393        cmd_enc_status.replay(commands);
1394        drop(cmd_enc_status);
1395
1396        let (cmd_buf, error) = encoder.finish(&wgt::CommandBufferDescriptor { label: None });
1397        if let Some(err) = error {
1398            panic!("CommandEncoder::finish failed: {err}");
1399        }
1400
1401        cmd_buf
1402    }
1403
1404    pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {
1405        use CommandEncoderStatus as St;
1406        match mem::replace(
1407            &mut *self.data.lock(),
1408            make_error_state(EncoderStateError::Submitted),
1409        ) {
1410            St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
1411            St::Error(EncoderErrorState {
1412                #[cfg(feature = "trace")]
1413                    trace_commands: _,
1414                error,
1415            }) => Err(error),
1416            St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(),
1417        }
1418    }
1419}
1420
1421crate::impl_resource_type!(CommandBuffer);
1422crate::impl_labeled!(CommandBuffer);
1423crate::impl_parent_device!(CommandBuffer);
1424crate::impl_storage_item!(CommandBuffer);
1425
1426/// A stream of commands for a render pass or compute pass.
1427///
1428/// This also contains side tables referred to by certain commands,
1429/// like dynamic offsets for [`SetBindGroup`] or string data for
1430/// [`InsertDebugMarker`].
1431///
1432/// Render passes use `BasePass<RenderCommand>`, whereas compute
1433/// passes use `BasePass<ComputeCommand>`.
1434///
1435/// [`SetBindGroup`]: RenderCommand::SetBindGroup
1436/// [`InsertDebugMarker`]: RenderCommand::InsertDebugMarker
1437#[doc(hidden)]
1438#[derive(Debug, Clone)]
1439#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1440pub struct BasePass<C, E> {
1441    pub label: Option<String>,
1442
1443    /// If the pass is invalid, contains the error that caused the invalidation.
1444    ///
1445    /// If the pass is valid, this is `None`.
1446    ///
1447    /// Passes are serialized into traces. but we don't support doing so for
1448    /// passes containing errors. These serde attributes allow `E` to be
1449    /// `Infallible`.
1450    #[cfg_attr(feature = "serde", serde(skip, default = "Option::default"))]
1451    pub error: Option<E>,
1452
1453    /// The stream of commands.
1454    ///
1455    /// The commands are moved out of this vector when the pass is ended (i.e.
1456    /// at the same time that `parent` is taken out of the
1457    /// `ComputePass`/`RenderPass`).
1458    pub commands: Vec<C>,
1459
1460    /// Dynamic offsets consumed by [`SetBindGroup`] commands in `commands`.
1461    ///
1462    /// Each successive `SetBindGroup` consumes the next
1463    /// [`num_dynamic_offsets`] values from this list.
1464    pub dynamic_offsets: Vec<wgt::DynamicOffset>,
1465
1466    /// Strings used by debug instructions.
1467    ///
1468    /// Each successive [`PushDebugGroup`] or [`InsertDebugMarker`]
1469    /// instruction consumes the next `len` bytes from this vector.
1470    pub string_data: Vec<u8>,
1471
1472    /// Data used by `SetImmediate` instructions.
1473    ///
1474    /// See the documentation for [`RenderCommand::SetImmediate`]
1475    /// and [`ComputeCommand::SetImmediate`] for details.
1476    pub immediates_data: Vec<u32>,
1477}
1478
1479impl<C: Clone, E: Clone> BasePass<C, E> {
1480    fn new(label: &Label) -> Self {
1481        Self {
1482            label: label.as_deref().map(str::to_owned),
1483            error: None,
1484            commands: Vec::new(),
1485            dynamic_offsets: Vec::new(),
1486            string_data: Vec::new(),
1487            immediates_data: Vec::new(),
1488        }
1489    }
1490
1491    fn new_invalid(label: &Label, err: E) -> Self {
1492        Self {
1493            label: label.as_deref().map(str::to_owned),
1494            error: Some(err),
1495            commands: Vec::new(),
1496            dynamic_offsets: Vec::new(),
1497            string_data: Vec::new(),
1498            immediates_data: Vec::new(),
1499        }
1500    }
1501
1502    /// Takes the commands from the pass, or returns an error if the pass is
1503    /// invalid.
1504    ///
1505    /// This is called when the pass is ended, at the same time that the
1506    /// `parent` member of the `ComputePass` or `RenderPass` containing the pass
1507    /// is taken.
1508    fn take(&mut self) -> Result<BasePass<C, Infallible>, E> {
1509        match self.error.as_ref() {
1510            Some(err) => Err(err.clone()),
1511            None => Ok(BasePass {
1512                label: self.label.clone(),
1513                error: None,
1514                commands: mem::take(&mut self.commands),
1515                dynamic_offsets: mem::take(&mut self.dynamic_offsets),
1516                string_data: mem::take(&mut self.string_data),
1517                immediates_data: mem::take(&mut self.immediates_data),
1518            }),
1519        }
1520    }
1521}
1522
1523/// Checks the state of a [`compute::ComputePass`] or [`render::RenderPass`] and
1524/// evaluates to a mutable reference to the [`BasePass`], if the pass is open and
1525/// valid.
1526///
1527/// If the pass is ended or not valid, **returns from the invoking function**,
1528/// like the `?` operator.
1529///
1530/// If the pass is ended (i.e. the application is attempting to record a command
1531/// on a finished pass), returns `Err(EncoderStateError::Ended)` from the
1532/// invoking function, for immediate propagation as a validation error.
1533///
1534/// If the pass is open but invalid (i.e. a previous command encountered an
1535/// error), returns `Ok(())` from the invoking function. The pass should already
1536/// have stored the previous error, which will be transferred to the parent
1537/// encoder when the pass is ended, and then raised as a validation error when
1538/// `finish()` is called for the parent).
1539///
1540/// Although in many cases the functionality of `pass_base!` could be achieved
1541/// by combining a helper method on the passes with the `pass_try!` macro,
1542/// taking the mutable reference to the base pass in a macro avoids borrowing
1543/// conflicts when a reference to some other member of the pass struct is
1544/// needed simultaneously with the base pass reference.
1545macro_rules! pass_base {
1546    ($pass:expr, $scope:expr $(,)?) => {
1547        match (&$pass.parent, &$pass.base.error) {
1548            // Pass is ended
1549            (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),
1550            // Pass is invalid
1551            (&Some(_), &Some(_)) => return Ok(()),
1552            // Pass is open and valid
1553            (&Some(_), &None) => &mut $pass.base,
1554        }
1555    };
1556}
1557pub(crate) use pass_base;
1558
1559/// Handles the error case in an expression of type `Result<T, E>`.
1560///
1561/// This macro operates like the `?` operator (or, in early Rust versions, the
1562/// `try!` macro, hence the name `pass_try`). **When there is an error, the
1563/// macro returns from the invoking function.** However, `Ok(())`, and not the
1564/// error itself, is returned. The error is stored in the pass and will later be
1565/// transferred to the parent encoder when the pass ends, and then raised as a
1566/// validation error when `finish()` is called for the parent.
1567///
1568/// `pass_try!` also calls [`MapPassErr::map_pass_err`] to annotate the error
1569/// with the command being encoded at the time it occurred.
1570macro_rules! pass_try {
1571    ($base:expr, $scope:expr, $res:expr $(,)?) => {
1572        match $res.map_pass_err($scope) {
1573            Ok(val) => val,
1574            Err(err) => {
1575                $base.error.get_or_insert(err);
1576                return Ok(());
1577            }
1578        }
1579    };
1580}
1581pub(crate) use pass_try;
1582
1583/// Errors related to the state of a command or pass encoder.
1584///
1585/// The exact behavior of these errors may change based on the resolution of
1586/// <https://github.com/gpuweb/gpuweb/issues/5207>.
1587#[derive(Clone, Debug, Error)]
1588#[non_exhaustive]
1589pub enum EncoderStateError {
1590    /// Used internally by wgpu functions to indicate the encoder already
1591    /// contained an error. This variant should usually not be seen by users of
1592    /// the API, since an effort should be made to provide the caller with a
1593    /// more specific reason for the encoder being invalid.
1594    #[error("Encoder is invalid")]
1595    Invalid,
1596
1597    /// Returned immediately when an attempt is made to encode a command using
1598    /// an encoder that has already finished.
1599    #[error("Encoding must not have ended")]
1600    Ended,
1601
1602    /// Returned by a subsequent call to `encoder.finish()`, if there was an
1603    /// attempt to open a second pass on the encoder while it was locked for
1604    /// a first pass (i.e. the first pass was still open).
1605    ///
1606    /// Note: only command encoders can be locked (not pass encoders).
1607    #[error("Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
1608    Locked,
1609
1610    /// Returned when attempting to end a pass if the parent encoder is not
1611    /// locked. This can only happen if pass begin/end calls are mismatched.
1612    #[error(
1613        "Encoder is not currently locked. A pass can only be ended while the encoder is locked."
1614    )]
1615    Unlocked,
1616
1617    /// The command buffer has already been submitted.
1618    ///
1619    /// Although command encoders and command buffers are distinct WebGPU
1620    /// objects, we use `CommandEncoderStatus` for both.
1621    #[error("This command buffer has already been submitted.")]
1622    Submitted,
1623}
1624
1625impl WebGpuError for EncoderStateError {
1626    fn webgpu_error_type(&self) -> ErrorType {
1627        match self {
1628            EncoderStateError::Invalid
1629            | EncoderStateError::Ended
1630            | EncoderStateError::Locked
1631            | EncoderStateError::Unlocked
1632            | EncoderStateError::Submitted => ErrorType::Validation,
1633        }
1634    }
1635}
1636
1637#[derive(Clone, Debug, Error)]
1638#[non_exhaustive]
1639pub enum CommandEncoderError {
1640    #[error(transparent)]
1641    State(#[from] EncoderStateError),
1642    #[error(transparent)]
1643    Device(#[from] DeviceError),
1644    #[error(transparent)]
1645    InvalidResource(#[from] InvalidResourceError),
1646    #[error(transparent)]
1647    DestroyedResource(#[from] DestroyedResourceError),
1648    #[error(transparent)]
1649    ResourceUsage(#[from] ResourceUsageCompatibilityError),
1650    #[error(transparent)]
1651    DebugGroupError(#[from] DebugGroupError),
1652    #[error(transparent)]
1653    MissingFeatures(#[from] MissingFeatures),
1654    #[error(transparent)]
1655    Transfer(#[from] TransferError),
1656    #[error(transparent)]
1657    Clear(#[from] ClearError),
1658    #[error(transparent)]
1659    Query(#[from] QueryError),
1660    #[error(transparent)]
1661    BuildAccelerationStructure(#[from] BuildAccelerationStructureError),
1662    #[error(transparent)]
1663    TransitionResources(#[from] TransitionResourcesError),
1664    #[error(transparent)]
1665    ComputePass(#[from] ComputePassError),
1666    #[error(transparent)]
1667    RenderPass(#[from] RenderPassError),
1668}
1669
1670impl From<InvalidOrDestroyedResourceError> for CommandEncoderError {
1671    fn from(err: InvalidOrDestroyedResourceError) -> Self {
1672        match err {
1673            InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
1674            InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
1675        }
1676    }
1677}
1678
1679impl CommandEncoderError {
1680    fn is_destroyed_error(&self) -> bool {
1681        matches!(
1682            self,
1683            Self::DestroyedResource(_)
1684                | Self::Clear(ClearError::DestroyedResource(_))
1685                | Self::Query(QueryError::DestroyedResource(_))
1686                | Self::ComputePass(ComputePassError {
1687                    inner: ComputePassErrorInner::DestroyedResource(_),
1688                    ..
1689                })
1690                | Self::RenderPass(RenderPassError {
1691                    inner: RenderPassErrorInner::DestroyedResource(_),
1692                    ..
1693                })
1694                | Self::RenderPass(RenderPassError {
1695                    inner: RenderPassErrorInner::RenderCommand(
1696                        RenderCommandError::DestroyedResource(_)
1697                    ),
1698                    ..
1699                })
1700                | Self::RenderPass(RenderPassError {
1701                    inner: RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError(
1702                        BindingError::DestroyedResource(_)
1703                    )),
1704                    ..
1705                })
1706        )
1707    }
1708}
1709
1710impl WebGpuError for CommandEncoderError {
1711    fn webgpu_error_type(&self) -> ErrorType {
1712        match self {
1713            Self::Device(e) => e.webgpu_error_type(),
1714            Self::InvalidResource(e) => e.webgpu_error_type(),
1715            Self::DebugGroupError(e) => e.webgpu_error_type(),
1716            Self::MissingFeatures(e) => e.webgpu_error_type(),
1717            Self::State(e) => e.webgpu_error_type(),
1718            Self::DestroyedResource(e) => e.webgpu_error_type(),
1719            Self::Transfer(e) => e.webgpu_error_type(),
1720            Self::Clear(e) => e.webgpu_error_type(),
1721            Self::Query(e) => e.webgpu_error_type(),
1722            Self::BuildAccelerationStructure(e) => e.webgpu_error_type(),
1723            Self::TransitionResources(e) => e.webgpu_error_type(),
1724            Self::ResourceUsage(e) => e.webgpu_error_type(),
1725            Self::ComputePass(e) => e.webgpu_error_type(),
1726            Self::RenderPass(e) => e.webgpu_error_type(),
1727        }
1728    }
1729}
1730
1731#[derive(Clone, Debug, Error)]
1732#[non_exhaustive]
1733pub enum DebugGroupError {
1734    #[error("Cannot pop debug group, because number of pushed debug groups is zero")]
1735    InvalidPop,
1736    #[error("A debug group was not popped before the encoder was finished")]
1737    MissingPop,
1738}
1739
1740impl WebGpuError for DebugGroupError {
1741    fn webgpu_error_type(&self) -> ErrorType {
1742        match self {
1743            Self::InvalidPop | Self::MissingPop => ErrorType::Validation,
1744        }
1745    }
1746}
1747
1748#[derive(Clone, Debug, Error)]
1749#[non_exhaustive]
1750pub enum TimestampWritesError {
1751    #[error(
1752        "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
1753    )]
1754    IndicesEqual { idx: u32 },
1755    #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
1756    IndicesMissing,
1757}
1758
1759impl WebGpuError for TimestampWritesError {
1760    fn webgpu_error_type(&self) -> ErrorType {
1761        match self {
1762            Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,
1763        }
1764    }
1765}
1766
1767impl Global {
1768    fn resolve_buffer_id(
1769        &self,
1770        buffer_id: Id<id::markers::Buffer>,
1771    ) -> Result<Arc<crate::resource::Buffer>, InvalidResourceError> {
1772        self.hub.buffers.get(buffer_id).get()
1773    }
1774
1775    fn resolve_texture_id(
1776        &self,
1777        texture_id: Id<id::markers::Texture>,
1778    ) -> Arc<crate::resource::Texture> {
1779        self.hub.textures.get(texture_id)
1780    }
1781
1782    fn resolve_query_set(
1783        &self,
1784        query_set_id: Id<id::markers::QuerySet>,
1785    ) -> Result<Arc<QuerySet>, InvalidResourceError> {
1786        self.hub.query_sets.get(query_set_id).get()
1787    }
1788
1789    /// Finishes a command encoder, creating a command buffer and returning errors that were
1790    /// deferred until now.
1791    ///
1792    /// The returned `String` is the label of the command encoder, supplied so that `wgpu` can
1793    /// include the label when printing deferred errors without having its own copy of the label.
1794    /// This is a kludge and should be replaced if we think of a better solution to propagating
1795    /// labels.
1796    pub fn command_encoder_finish(
1797        &self,
1798        encoder_id: id::CommandEncoderId,
1799        desc: &wgt::CommandBufferDescriptor<Label>,
1800        id_in: Option<id::CommandBufferId>,
1801    ) -> (id::CommandBufferId, Option<(String, CommandEncoderError)>) {
1802        profiling::scope!("CommandEncoder::finish");
1803
1804        let hub = &self.hub;
1805        let cmd_enc = hub.command_encoders.get(encoder_id);
1806
1807        let (cmd_buf, opt_error) = cmd_enc.finish(desc);
1808        let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(cmd_buf);
1809
1810        (
1811            cmd_buf_id,
1812            opt_error.map(|error| (cmd_enc.label.clone(), error)),
1813        )
1814    }
1815
1816    pub fn command_encoder_push_debug_group(
1817        &self,
1818        encoder_id: id::CommandEncoderId,
1819        label: &str,
1820    ) -> Result<(), EncoderStateError> {
1821        profiling::scope!("CommandEncoder::push_debug_group");
1822        api_log!("CommandEncoder::push_debug_group {label}");
1823
1824        let hub = &self.hub;
1825
1826        let cmd_enc = hub.command_encoders.get(encoder_id);
1827        let mut cmd_buf_data = cmd_enc.data.lock();
1828
1829        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1830            Ok(ArcCommand::PushDebugGroup(label.to_owned()))
1831        })
1832    }
1833
1834    pub fn command_encoder_insert_debug_marker(
1835        &self,
1836        encoder_id: id::CommandEncoderId,
1837        label: &str,
1838    ) -> Result<(), EncoderStateError> {
1839        profiling::scope!("CommandEncoder::insert_debug_marker");
1840        api_log!("CommandEncoder::insert_debug_marker {label}");
1841
1842        let hub = &self.hub;
1843
1844        let cmd_enc = hub.command_encoders.get(encoder_id);
1845        let mut cmd_buf_data = cmd_enc.data.lock();
1846
1847        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1848            Ok(ArcCommand::InsertDebugMarker(label.to_owned()))
1849        })
1850    }
1851
1852    pub fn command_encoder_pop_debug_group(
1853        &self,
1854        encoder_id: id::CommandEncoderId,
1855    ) -> Result<(), EncoderStateError> {
1856        profiling::scope!("CommandEncoder::pop_debug_marker");
1857        api_log!("CommandEncoder::pop_debug_group");
1858
1859        let hub = &self.hub;
1860
1861        let cmd_enc = hub.command_encoders.get(encoder_id);
1862        let mut cmd_buf_data = cmd_enc.data.lock();
1863
1864        cmd_buf_data
1865            .push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::PopDebugGroup) })
1866    }
1867}
1868
1869pub(crate) fn push_debug_group(
1870    state: &mut EncodingState,
1871    label: &str,
1872) -> Result<(), CommandEncoderError> {
1873    *state.debug_scope_depth += 1;
1874
1875    if !state
1876        .device
1877        .instance_flags
1878        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1879    {
1880        unsafe { state.raw_encoder.begin_debug_marker(label) };
1881    }
1882
1883    Ok(())
1884}
1885
1886pub(crate) fn insert_debug_marker(
1887    state: &mut EncodingState,
1888    label: &str,
1889) -> Result<(), CommandEncoderError> {
1890    if !state
1891        .device
1892        .instance_flags
1893        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1894    {
1895        unsafe { state.raw_encoder.insert_debug_marker(label) };
1896    }
1897
1898    Ok(())
1899}
1900
1901pub(crate) fn pop_debug_group(state: &mut EncodingState) -> Result<(), CommandEncoderError> {
1902    if *state.debug_scope_depth == 0 {
1903        return Err(DebugGroupError::InvalidPop.into());
1904    }
1905    *state.debug_scope_depth -= 1;
1906
1907    if !state
1908        .device
1909        .instance_flags
1910        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1911    {
1912        unsafe { state.raw_encoder.end_debug_marker() };
1913    }
1914
1915    Ok(())
1916}
1917
1918fn immediates_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
1919where
1920    PushFn: FnMut(u32, &[u32]),
1921{
1922    let mut count_words = 0_u32;
1923    let size_words = size_bytes / wgt::IMMEDIATE_DATA_ALIGNMENT;
1924    while count_words < size_words {
1925        let count_bytes = count_words * wgt::IMMEDIATE_DATA_ALIGNMENT;
1926        let size_to_write_words =
1927            (size_words - count_words).min(IMMEDIATES_CLEAR_ARRAY.len() as u32);
1928
1929        push_fn(
1930            offset + count_bytes,
1931            &IMMEDIATES_CLEAR_ARRAY[0..size_to_write_words as usize],
1932        );
1933
1934        count_words += size_to_write_words;
1935    }
1936}
1937
1938#[derive(Debug, Copy, Clone)]
1939struct StateChange<T> {
1940    last_state: Option<T>,
1941}
1942
1943impl<T: Copy + PartialEq> StateChange<T> {
1944    fn new() -> Self {
1945        Self { last_state: None }
1946    }
1947    fn set_and_check_redundant(&mut self, new_state: T) -> bool {
1948        let already_set = self.last_state == Some(new_state);
1949        self.last_state = Some(new_state);
1950        already_set
1951    }
1952    fn reset(&mut self) {
1953        self.last_state = None;
1954    }
1955}
1956
1957impl<T: Copy + PartialEq> Default for StateChange<T> {
1958    fn default() -> Self {
1959        Self::new()
1960    }
1961}
1962
1963#[derive(Debug)]
1964struct BindGroupStateChange {
1965    last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],
1966}
1967
1968impl BindGroupStateChange {
1969    fn new() -> Self {
1970        Self {
1971            last_states: [StateChange::new(); hal::MAX_BIND_GROUPS],
1972        }
1973    }
1974
1975    fn set_and_check_redundant(
1976        &mut self,
1977        bind_group_id: Option<id::BindGroupId>,
1978        index: u32,
1979        dynamic_offsets: &mut Vec<u32>,
1980        offsets: &[wgt::DynamicOffset],
1981    ) -> bool {
1982        // For now never deduplicate bind groups with dynamic offsets.
1983        if offsets.is_empty() {
1984            // If this get returns None, that means we're well over the limit,
1985            // so let the call through to get a proper error
1986            if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1987                // Bail out if we're binding the same bind group.
1988                if current_bind_group.set_and_check_redundant(bind_group_id) {
1989                    return true;
1990                }
1991            }
1992        } else {
1993            // We intentionally remove the memory of this bind group if we have dynamic offsets,
1994            // such that if you try to bind this bind group later with _no_ dynamic offsets it
1995            // tries to bind it again and gives a proper validation error.
1996            if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1997                current_bind_group.reset();
1998            }
1999            dynamic_offsets.extend_from_slice(offsets);
2000        }
2001        false
2002    }
2003    fn reset(&mut self) {
2004        self.last_states = [StateChange::new(); hal::MAX_BIND_GROUPS];
2005    }
2006}
2007
2008impl Default for BindGroupStateChange {
2009    fn default() -> Self {
2010        Self::new()
2011    }
2012}
2013
2014/// Helper to attach [`PassErrorScope`] to errors.
2015trait MapPassErr<T> {
2016    fn map_pass_err(self, scope: PassErrorScope) -> T;
2017}
2018
2019impl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>
2020where
2021    E: MapPassErr<F>,
2022{
2023    fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {
2024        self.map_err(|err| err.map_pass_err(scope))
2025    }
2026}
2027
2028impl MapPassErr<PassStateError> for EncoderStateError {
2029    fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {
2030        PassStateError { scope, inner: self }
2031    }
2032}
2033
2034#[derive(Clone, Copy, Debug)]
2035pub enum DrawKind {
2036    Draw,
2037    DrawIndirect,
2038    MultiDrawIndirect,
2039    MultiDrawIndirectCount,
2040}
2041
2042/// The type of draw command(indexed or not, or mesh shader)
2043#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2044#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2045pub enum DrawCommandFamily {
2046    Draw,
2047    DrawIndexed,
2048    DrawMeshTasks,
2049}
2050
2051/// A command that can be recorded in a pass or bundle.
2052///
2053/// This is used to provide context for errors during command recording.
2054/// [`MapPassErr`] is used as a helper to attach a `PassErrorScope` to
2055/// an error.
2056///
2057/// The [`PassErrorScope::Bundle`] and [`PassErrorScope::Pass`] variants
2058/// are used when the error occurs during the opening or closing of the
2059/// pass or bundle.
2060#[derive(Clone, Copy, Debug, Error)]
2061pub enum PassErrorScope {
2062    // TODO: Extract out the 2 error variants below so that we can always
2063    // include the ResourceErrorIdent of the pass around all inner errors
2064    #[error("In a bundle parameter")]
2065    Bundle,
2066    #[error("In a pass parameter")]
2067    Pass,
2068    #[error("In a set_bind_group command")]
2069    SetBindGroup,
2070    #[error("In a set_pipeline command")]
2071    SetPipelineRender,
2072    #[error("In a set_pipeline command")]
2073    SetPipelineCompute,
2074    #[error("In a set_immediates command")]
2075    SetImmediate,
2076    #[error("In a set_vertex_buffer command")]
2077    SetVertexBuffer,
2078    #[error("In a set_index_buffer command")]
2079    SetIndexBuffer,
2080    #[error("In a set_blend_constant command")]
2081    SetBlendConstant,
2082    #[error("In a set_stencil_reference command")]
2083    SetStencilReference,
2084    #[error("In a set_viewport command")]
2085    SetViewport,
2086    #[error("In a set_scissor_rect command")]
2087    SetScissorRect,
2088    #[error("In a draw command, kind: {kind:?}")]
2089    Draw {
2090        kind: DrawKind,
2091        family: DrawCommandFamily,
2092    },
2093    #[error("In a write_timestamp command")]
2094    WriteTimestamp,
2095    #[error("In a begin_occlusion_query command")]
2096    BeginOcclusionQuery,
2097    #[error("In a end_occlusion_query command")]
2098    EndOcclusionQuery,
2099    #[error("In a begin_pipeline_statistics_query command")]
2100    BeginPipelineStatisticsQuery,
2101    #[error("In a end_pipeline_statistics_query command")]
2102    EndPipelineStatisticsQuery,
2103    #[error("In a transition_resources command")]
2104    TransitionResources,
2105    #[error("In a execute_bundle command")]
2106    ExecuteBundle,
2107    #[error("In a dispatch command, indirect:{indirect}")]
2108    Dispatch { indirect: bool },
2109    #[error("In a push_debug_group command")]
2110    PushDebugGroup,
2111    #[error("In a pop_debug_group command")]
2112    PopDebugGroup,
2113    #[error("In a insert_debug_marker command")]
2114    InsertDebugMarker,
2115}
2116
2117/// Variant of `EncoderStateError` that includes the pass scope.
2118#[derive(Clone, Debug, Error)]
2119#[error("{scope}")]
2120pub struct PassStateError {
2121    pub scope: PassErrorScope,
2122    #[source]
2123    pub(super) inner: EncoderStateError,
2124}
2125
2126impl WebGpuError for PassStateError {
2127    fn webgpu_error_type(&self) -> ErrorType {
2128        let Self { scope: _, inner } = self;
2129        inner.webgpu_error_type()
2130    }
2131}