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