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