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