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    fn finish(
903        self: &Arc<Self>,
904        desc: &wgt::CommandBufferDescriptor<Label>,
905    ) -> (Arc<CommandBuffer>, Option<CommandEncoderError>) {
906        let mut cmd_enc_status = self.data.lock();
907
908        let res = match cmd_enc_status.finish() {
909            CommandEncoderStatus::Finished(cmd_buf_data) => Ok(cmd_buf_data),
910            CommandEncoderStatus::Error(err) => Err(err),
911            _ => unreachable!(),
912        };
913
914        let res = res.and_then(|mut cmd_buf_data| {
915            self.device.check_is_valid()?;
916            let snatch_guard = self.device.snatchable_lock.read();
917            let mut debug_scope_depth = 0;
918
919            let mut commands = mem::take(&mut cmd_buf_data.commands);
920            for command in commands.drain(..) {
921                if matches!(
922                    command,
923                    ArcCommand::RunRenderPass { .. } | ArcCommand::RunComputePass { .. }
924                ) {
925                    // Compute passes and render passes can accept either an
926                    // open or closed encoder. This state object holds an
927                    // `InnerCommandEncoder`. See the documentation of
928                    // [`EncodingState`].
929                    let mut state = EncodingState {
930                        device: &self.device,
931                        raw_encoder: &mut cmd_buf_data.encoder,
932                        tracker: &mut cmd_buf_data.trackers,
933                        buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
934                        texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
935                        as_actions: &mut cmd_buf_data.as_actions,
936                        temp_resources: &mut cmd_buf_data.temp_resources,
937                        indirect_draw_validation_resources: &mut cmd_buf_data
938                            .indirect_draw_validation_resources,
939                        snatch_guard: &snatch_guard,
940                        debug_scope_depth: &mut debug_scope_depth,
941                    };
942
943                    match command {
944                        ArcCommand::RunRenderPass {
945                            pass,
946                            color_attachments,
947                            depth_stencil_attachment,
948                            timestamp_writes,
949                            occlusion_query_set,
950                        } => {
951                            encode_render_pass(
952                                &mut state,
953                                pass,
954                                color_attachments,
955                                depth_stencil_attachment,
956                                timestamp_writes,
957                                occlusion_query_set,
958                            )?;
959                        }
960                        ArcCommand::RunComputePass {
961                            pass,
962                            timestamp_writes,
963                        } => {
964                            encode_compute_pass(&mut state, pass, timestamp_writes)?;
965                        }
966                        _ => unreachable!(),
967                    }
968                } else {
969                    // All the other non-pass encoding routines assume the
970                    // encoder is open, so open it if necessary. This state
971                    // object holds an `&mut dyn hal::DynCommandEncoder`. By
972                    // convention, a bare HAL encoder reference in
973                    // [`EncodingState`] must always be an open encoder.
974                    let raw_encoder = cmd_buf_data.encoder.open_if_closed()?;
975                    let mut state = EncodingState {
976                        device: &self.device,
977                        raw_encoder,
978                        tracker: &mut cmd_buf_data.trackers,
979                        buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
980                        texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
981                        as_actions: &mut cmd_buf_data.as_actions,
982                        temp_resources: &mut cmd_buf_data.temp_resources,
983                        indirect_draw_validation_resources: &mut cmd_buf_data
984                            .indirect_draw_validation_resources,
985                        snatch_guard: &snatch_guard,
986                        debug_scope_depth: &mut debug_scope_depth,
987                    };
988                    match command {
989                        ArcCommand::CopyBufferToBuffer {
990                            src,
991                            src_offset,
992                            dst,
993                            dst_offset,
994                            size,
995                        } => {
996                            copy_buffer_to_buffer(
997                                &mut state, &src, src_offset, &dst, dst_offset, size,
998                            )?;
999                        }
1000                        ArcCommand::CopyBufferToTexture { src, dst, size } => {
1001                            copy_buffer_to_texture(&mut state, &src, &dst, &size)?;
1002                        }
1003                        ArcCommand::CopyTextureToBuffer { src, dst, size } => {
1004                            copy_texture_to_buffer(&mut state, &src, &dst, &size)?;
1005                        }
1006                        ArcCommand::CopyTextureToTexture { src, dst, size } => {
1007                            copy_texture_to_texture(&mut state, &src, &dst, &size)?;
1008                        }
1009                        ArcCommand::ClearBuffer { dst, offset, size } => {
1010                            clear_buffer(&mut state, dst, offset, size)?;
1011                        }
1012                        ArcCommand::ClearTexture {
1013                            dst,
1014                            subresource_range,
1015                        } => {
1016                            clear_texture_cmd(&mut state, dst, &subresource_range)?;
1017                        }
1018                        ArcCommand::WriteTimestamp {
1019                            query_set,
1020                            query_index,
1021                        } => {
1022                            write_timestamp(&mut state, query_set, query_index)?;
1023                        }
1024                        ArcCommand::ResolveQuerySet {
1025                            query_set,
1026                            start_query,
1027                            query_count,
1028                            destination,
1029                            destination_offset,
1030                        } => {
1031                            resolve_query_set(
1032                                &mut state,
1033                                query_set,
1034                                start_query,
1035                                query_count,
1036                                destination,
1037                                destination_offset,
1038                            )?;
1039                        }
1040                        ArcCommand::PushDebugGroup(label) => {
1041                            push_debug_group(&mut state, &label)?;
1042                        }
1043                        ArcCommand::PopDebugGroup => {
1044                            pop_debug_group(&mut state)?;
1045                        }
1046                        ArcCommand::InsertDebugMarker(label) => {
1047                            insert_debug_marker(&mut state, &label)?;
1048                        }
1049                        ArcCommand::BuildAccelerationStructures { blas, tlas } => {
1050                            build_acceleration_structures(&mut state, blas, tlas)?;
1051                        }
1052                        ArcCommand::TransitionResources {
1053                            buffer_transitions,
1054                            texture_transitions,
1055                        } => {
1056                            transition_resources(
1057                                &mut state,
1058                                buffer_transitions,
1059                                texture_transitions,
1060                            )?;
1061                        }
1062                        ArcCommand::RunComputePass { .. } | ArcCommand::RunRenderPass { .. } => {
1063                            unreachable!()
1064                        }
1065                    }
1066                }
1067            }
1068
1069            if debug_scope_depth > 0 {
1070                Err(CommandEncoderError::DebugGroupError(
1071                    DebugGroupError::MissingPop,
1072                ))?;
1073            }
1074
1075            // Close the encoder, unless it was closed already by a render or compute pass.
1076            cmd_buf_data.encoder.close_if_open()?;
1077
1078            // Note: if we want to stop tracking the swapchain texture view,
1079            // this is the place to do it.
1080
1081            Ok(cmd_buf_data)
1082        });
1083
1084        let (data, error) = match res {
1085            Err(e) => {
1086                if e.is_destroyed_error() {
1087                    // Errors related to destroyed resources are not reported until the
1088                    // command buffer is submitted.
1089                    (CommandEncoderStatus::Error(e.clone()), None)
1090                } else {
1091                    (CommandEncoderStatus::Error(e.clone()), Some(e))
1092                }
1093            }
1094
1095            Ok(data) => (CommandEncoderStatus::Finished(data), None),
1096        };
1097
1098        let cmd_buf = Arc::new(CommandBuffer {
1099            device: self.device.clone(),
1100            label: desc.label.to_string(),
1101            data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),
1102        });
1103
1104        (cmd_buf, error)
1105    }
1106}
1107
1108impl CommandBuffer {
1109    pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {
1110        use CommandEncoderStatus as St;
1111        match mem::replace(
1112            &mut *self.data.lock(),
1113            CommandEncoderStatus::Error(EncoderStateError::Submitted.into()),
1114        ) {
1115            St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
1116            St::Error(err) => Err(err),
1117            St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(),
1118        }
1119    }
1120}
1121
1122crate::impl_resource_type!(CommandBuffer);
1123crate::impl_labeled!(CommandBuffer);
1124crate::impl_parent_device!(CommandBuffer);
1125crate::impl_storage_item!(CommandBuffer);
1126
1127/// A stream of commands for a render pass or compute pass.
1128///
1129/// This also contains side tables referred to by certain commands,
1130/// like dynamic offsets for [`SetBindGroup`] or string data for
1131/// [`InsertDebugMarker`].
1132///
1133/// Render passes use `BasePass<RenderCommand>`, whereas compute
1134/// passes use `BasePass<ComputeCommand>`.
1135///
1136/// [`SetBindGroup`]: RenderCommand::SetBindGroup
1137/// [`InsertDebugMarker`]: RenderCommand::InsertDebugMarker
1138#[doc(hidden)]
1139#[derive(Debug, Clone)]
1140#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1141pub struct BasePass<C, E> {
1142    pub label: Option<String>,
1143
1144    /// If the pass is invalid, contains the error that caused the invalidation.
1145    ///
1146    /// If the pass is valid, this is `None`.
1147    ///
1148    /// Passes are serialized into traces. but we don't support doing so for
1149    /// passes containing errors. These serde attributes allow `E` to be
1150    /// `Infallible`.
1151    #[cfg_attr(feature = "serde", serde(skip, default = "Option::default"))]
1152    pub error: Option<E>,
1153
1154    /// The stream of commands.
1155    ///
1156    /// The commands are moved out of this vector when the pass is ended (i.e.
1157    /// at the same time that `parent` is taken out of the
1158    /// `ComputePass`/`RenderPass`).
1159    pub commands: Vec<C>,
1160
1161    /// Dynamic offsets consumed by [`SetBindGroup`] commands in `commands`.
1162    ///
1163    /// Each successive `SetBindGroup` consumes the next
1164    /// [`num_dynamic_offsets`] values from this list.
1165    pub dynamic_offsets: Vec<wgt::DynamicOffset>,
1166
1167    /// Strings used by debug instructions.
1168    ///
1169    /// Each successive [`PushDebugGroup`] or [`InsertDebugMarker`]
1170    /// instruction consumes the next `len` bytes from this vector.
1171    pub string_data: Vec<u8>,
1172
1173    /// Data used by `SetPushConstant` instructions.
1174    ///
1175    /// See the documentation for [`RenderCommand::SetPushConstant`]
1176    /// and [`ComputeCommand::SetPushConstant`] for details.
1177    pub push_constant_data: Vec<u32>,
1178}
1179
1180impl<C: Clone, E: Clone> BasePass<C, E> {
1181    fn new(label: &Label) -> Self {
1182        Self {
1183            label: label.as_deref().map(str::to_owned),
1184            error: None,
1185            commands: Vec::new(),
1186            dynamic_offsets: Vec::new(),
1187            string_data: Vec::new(),
1188            push_constant_data: Vec::new(),
1189        }
1190    }
1191
1192    fn new_invalid(label: &Label, err: E) -> Self {
1193        Self {
1194            label: label.as_deref().map(str::to_owned),
1195            error: Some(err),
1196            commands: Vec::new(),
1197            dynamic_offsets: Vec::new(),
1198            string_data: Vec::new(),
1199            push_constant_data: Vec::new(),
1200        }
1201    }
1202
1203    /// Takes the commands from the pass, or returns an error if the pass is
1204    /// invalid.
1205    ///
1206    /// This is called when the pass is ended, at the same time that the
1207    /// `parent` member of the `ComputePass` or `RenderPass` containing the pass
1208    /// is taken.
1209    fn take(&mut self) -> Result<BasePass<C, Infallible>, E> {
1210        match self.error.as_ref() {
1211            Some(err) => Err(err.clone()),
1212            None => Ok(BasePass {
1213                label: self.label.clone(),
1214                error: None,
1215                commands: mem::take(&mut self.commands),
1216                dynamic_offsets: mem::take(&mut self.dynamic_offsets),
1217                string_data: mem::take(&mut self.string_data),
1218                push_constant_data: mem::take(&mut self.push_constant_data),
1219            }),
1220        }
1221    }
1222}
1223
1224/// Checks the state of a [`compute::ComputePass`] or [`render::RenderPass`] and
1225/// evaluates to a mutable reference to the [`BasePass`], if the pass is open and
1226/// valid.
1227///
1228/// If the pass is ended or not valid, **returns from the invoking function**,
1229/// like the `?` operator.
1230///
1231/// If the pass is ended (i.e. the application is attempting to record a command
1232/// on a finished pass), returns `Err(EncoderStateError::Ended)` from the
1233/// invoking function, for immediate propagation as a validation error.
1234///
1235/// If the pass is open but invalid (i.e. a previous command encountered an
1236/// error), returns `Ok(())` from the invoking function. The pass should already
1237/// have stored the previous error, which will be transferred to the parent
1238/// encoder when the pass is ended, and then raised as a validation error when
1239/// `finish()` is called for the parent).
1240///
1241/// Although in many cases the functionality of `pass_base!` could be achieved
1242/// by combining a helper method on the passes with the `pass_try!` macro,
1243/// taking the mutable reference to the base pass in a macro avoids borrowing
1244/// conflicts when a reference to some other member of the pass struct is
1245/// needed simultaneously with the base pass reference.
1246macro_rules! pass_base {
1247    ($pass:expr, $scope:expr $(,)?) => {
1248        match (&$pass.parent, &$pass.base.error) {
1249            // Pass is ended
1250            (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),
1251            // Pass is invalid
1252            (&Some(_), &Some(_)) => return Ok(()),
1253            // Pass is open and valid
1254            (&Some(_), &None) => &mut $pass.base,
1255        }
1256    };
1257}
1258pub(crate) use pass_base;
1259
1260/// Handles the error case in an expression of type `Result<T, E>`.
1261///
1262/// This macro operates like the `?` operator (or, in early Rust versions, the
1263/// `try!` macro, hence the name `pass_try`). **When there is an error, the
1264/// macro returns from the invoking function.** However, `Ok(())`, and not the
1265/// error itself, is returned. The error is stored in the pass and will later be
1266/// transferred to the parent encoder when the pass ends, and then raised as a
1267/// validation error when `finish()` is called for the parent.
1268///
1269/// `pass_try!` also calls [`MapPassErr::map_pass_err`] to annotate the error
1270/// with the command being encoded at the time it occurred.
1271macro_rules! pass_try {
1272    ($base:expr, $scope:expr, $res:expr $(,)?) => {
1273        match $res.map_pass_err($scope) {
1274            Ok(val) => val,
1275            Err(err) => {
1276                $base.error.get_or_insert(err);
1277                return Ok(());
1278            }
1279        }
1280    };
1281}
1282pub(crate) use pass_try;
1283
1284/// Errors related to the state of a command or pass encoder.
1285///
1286/// The exact behavior of these errors may change based on the resolution of
1287/// <https://github.com/gpuweb/gpuweb/issues/5207>.
1288#[derive(Clone, Debug, Error)]
1289#[non_exhaustive]
1290pub enum EncoderStateError {
1291    /// Used internally by wgpu functions to indicate the encoder already
1292    /// contained an error. This variant should usually not be seen by users of
1293    /// the API, since an effort should be made to provide the caller with a
1294    /// more specific reason for the encoder being invalid.
1295    #[error("Encoder is invalid")]
1296    Invalid,
1297
1298    /// Returned immediately when an attempt is made to encode a command using
1299    /// an encoder that has already finished.
1300    #[error("Encoding must not have ended")]
1301    Ended,
1302
1303    /// Returned by a subsequent call to `encoder.finish()`, if there was an
1304    /// attempt to open a second pass on the encoder while it was locked for
1305    /// a first pass (i.e. the first pass was still open).
1306    ///
1307    /// Note: only command encoders can be locked (not pass encoders).
1308    #[error("Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
1309    Locked,
1310
1311    /// Returned when attempting to end a pass if the parent encoder is not
1312    /// locked. This can only happen if pass begin/end calls are mismatched.
1313    #[error(
1314        "Encoder is not currently locked. A pass can only be ended while the encoder is locked."
1315    )]
1316    Unlocked,
1317
1318    /// The command buffer has already been submitted.
1319    ///
1320    /// Although command encoders and command buffers are distinct WebGPU
1321    /// objects, we use `CommandEncoderStatus` for both.
1322    #[error("This command buffer has already been submitted.")]
1323    Submitted,
1324}
1325
1326impl WebGpuError for EncoderStateError {
1327    fn webgpu_error_type(&self) -> ErrorType {
1328        match self {
1329            EncoderStateError::Invalid
1330            | EncoderStateError::Ended
1331            | EncoderStateError::Locked
1332            | EncoderStateError::Unlocked
1333            | EncoderStateError::Submitted => ErrorType::Validation,
1334        }
1335    }
1336}
1337
1338#[derive(Clone, Debug, Error)]
1339#[non_exhaustive]
1340pub enum CommandEncoderError {
1341    #[error(transparent)]
1342    State(#[from] EncoderStateError),
1343    #[error(transparent)]
1344    Device(#[from] DeviceError),
1345    #[error(transparent)]
1346    InvalidResource(#[from] InvalidResourceError),
1347    #[error(transparent)]
1348    DestroyedResource(#[from] DestroyedResourceError),
1349    #[error(transparent)]
1350    ResourceUsage(#[from] ResourceUsageCompatibilityError),
1351    #[error(transparent)]
1352    DebugGroupError(#[from] DebugGroupError),
1353    #[error(transparent)]
1354    MissingFeatures(#[from] MissingFeatures),
1355    #[error(transparent)]
1356    Transfer(#[from] TransferError),
1357    #[error(transparent)]
1358    Clear(#[from] ClearError),
1359    #[error(transparent)]
1360    Query(#[from] QueryError),
1361    #[error(transparent)]
1362    BuildAccelerationStructure(#[from] BuildAccelerationStructureError),
1363    #[error(transparent)]
1364    TransitionResources(#[from] TransitionResourcesError),
1365    #[error(transparent)]
1366    ComputePass(#[from] ComputePassError),
1367    #[error(transparent)]
1368    RenderPass(#[from] RenderPassError),
1369}
1370
1371impl CommandEncoderError {
1372    fn is_destroyed_error(&self) -> bool {
1373        matches!(
1374            self,
1375            Self::DestroyedResource(_)
1376                | Self::Clear(ClearError::DestroyedResource(_))
1377                | Self::Query(QueryError::DestroyedResource(_))
1378                | Self::ComputePass(ComputePassError {
1379                    inner: ComputePassErrorInner::DestroyedResource(_),
1380                    ..
1381                })
1382                | Self::RenderPass(RenderPassError {
1383                    inner: RenderPassErrorInner::DestroyedResource(_),
1384                    ..
1385                })
1386                | Self::RenderPass(RenderPassError {
1387                    inner: RenderPassErrorInner::RenderCommand(
1388                        RenderCommandError::DestroyedResource(_)
1389                    ),
1390                    ..
1391                })
1392                | Self::RenderPass(RenderPassError {
1393                    inner: RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError(
1394                        BindingError::DestroyedResource(_)
1395                    )),
1396                    ..
1397                })
1398        )
1399    }
1400}
1401
1402impl WebGpuError for CommandEncoderError {
1403    fn webgpu_error_type(&self) -> ErrorType {
1404        let e: &dyn WebGpuError = match self {
1405            Self::Device(e) => e,
1406            Self::InvalidResource(e) => e,
1407            Self::DebugGroupError(e) => e,
1408            Self::MissingFeatures(e) => e,
1409            Self::State(e) => e,
1410            Self::DestroyedResource(e) => e,
1411            Self::Transfer(e) => e,
1412            Self::Clear(e) => e,
1413            Self::Query(e) => e,
1414            Self::BuildAccelerationStructure(e) => e,
1415            Self::TransitionResources(e) => e,
1416            Self::ResourceUsage(e) => e,
1417            Self::ComputePass(e) => e,
1418            Self::RenderPass(e) => e,
1419        };
1420        e.webgpu_error_type()
1421    }
1422}
1423
1424#[derive(Clone, Debug, Error)]
1425#[non_exhaustive]
1426pub enum DebugGroupError {
1427    #[error("Cannot pop debug group, because number of pushed debug groups is zero")]
1428    InvalidPop,
1429    #[error("A debug group was not popped before the encoder was finished")]
1430    MissingPop,
1431}
1432
1433impl WebGpuError for DebugGroupError {
1434    fn webgpu_error_type(&self) -> ErrorType {
1435        match self {
1436            Self::InvalidPop | Self::MissingPop => ErrorType::Validation,
1437        }
1438    }
1439}
1440
1441#[derive(Clone, Debug, Error)]
1442#[non_exhaustive]
1443pub enum TimestampWritesError {
1444    #[error(
1445        "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
1446    )]
1447    IndicesEqual { idx: u32 },
1448    #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
1449    IndicesMissing,
1450}
1451
1452impl WebGpuError for TimestampWritesError {
1453    fn webgpu_error_type(&self) -> ErrorType {
1454        match self {
1455            Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,
1456        }
1457    }
1458}
1459
1460impl Global {
1461    fn resolve_buffer_id(
1462        &self,
1463        buffer_id: Id<id::markers::Buffer>,
1464    ) -> Result<Arc<crate::resource::Buffer>, InvalidResourceError> {
1465        self.hub.buffers.get(buffer_id).get()
1466    }
1467
1468    fn resolve_texture_id(
1469        &self,
1470        texture_id: Id<id::markers::Texture>,
1471    ) -> Result<Arc<crate::resource::Texture>, InvalidResourceError> {
1472        self.hub.textures.get(texture_id).get()
1473    }
1474
1475    fn resolve_query_set(
1476        &self,
1477        query_set_id: Id<id::markers::QuerySet>,
1478    ) -> Result<Arc<QuerySet>, InvalidResourceError> {
1479        self.hub.query_sets.get(query_set_id).get()
1480    }
1481
1482    pub fn command_encoder_finish(
1483        &self,
1484        encoder_id: id::CommandEncoderId,
1485        desc: &wgt::CommandBufferDescriptor<Label>,
1486        id_in: Option<id::CommandBufferId>,
1487    ) -> (id::CommandBufferId, Option<CommandEncoderError>) {
1488        profiling::scope!("CommandEncoder::finish");
1489
1490        let hub = &self.hub;
1491
1492        let cmd_enc = hub.command_encoders.get(encoder_id);
1493
1494        let (cmd_buf, error) = cmd_enc.finish(desc);
1495        let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(cmd_buf);
1496
1497        (cmd_buf_id, error)
1498    }
1499
1500    pub fn command_encoder_push_debug_group(
1501        &self,
1502        encoder_id: id::CommandEncoderId,
1503        label: &str,
1504    ) -> Result<(), EncoderStateError> {
1505        profiling::scope!("CommandEncoder::push_debug_group");
1506        api_log!("CommandEncoder::push_debug_group {label}");
1507
1508        let hub = &self.hub;
1509
1510        let cmd_enc = hub.command_encoders.get(encoder_id);
1511        let mut cmd_buf_data = cmd_enc.data.lock();
1512
1513        #[cfg(feature = "trace")]
1514        if let Some(ref mut list) = cmd_buf_data.trace() {
1515            list.push(TraceCommand::PushDebugGroup(label.to_owned()));
1516        }
1517
1518        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1519            Ok(ArcCommand::PushDebugGroup(label.to_owned()))
1520        })
1521    }
1522
1523    pub fn command_encoder_insert_debug_marker(
1524        &self,
1525        encoder_id: id::CommandEncoderId,
1526        label: &str,
1527    ) -> Result<(), EncoderStateError> {
1528        profiling::scope!("CommandEncoder::insert_debug_marker");
1529        api_log!("CommandEncoder::insert_debug_marker {label}");
1530
1531        let hub = &self.hub;
1532
1533        let cmd_enc = hub.command_encoders.get(encoder_id);
1534        let mut cmd_buf_data = cmd_enc.data.lock();
1535
1536        #[cfg(feature = "trace")]
1537        if let Some(ref mut list) = cmd_buf_data.trace() {
1538            list.push(TraceCommand::InsertDebugMarker(label.to_owned()));
1539        }
1540
1541        cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1542            Ok(ArcCommand::InsertDebugMarker(label.to_owned()))
1543        })
1544    }
1545
1546    pub fn command_encoder_pop_debug_group(
1547        &self,
1548        encoder_id: id::CommandEncoderId,
1549    ) -> Result<(), EncoderStateError> {
1550        profiling::scope!("CommandEncoder::pop_debug_marker");
1551        api_log!("CommandEncoder::pop_debug_group");
1552
1553        let hub = &self.hub;
1554
1555        let cmd_enc = hub.command_encoders.get(encoder_id);
1556        let mut cmd_buf_data = cmd_enc.data.lock();
1557
1558        #[cfg(feature = "trace")]
1559        if let Some(ref mut list) = cmd_buf_data.trace() {
1560            list.push(TraceCommand::PopDebugGroup);
1561        }
1562
1563        cmd_buf_data
1564            .push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::PopDebugGroup) })
1565    }
1566
1567    fn validate_pass_timestamp_writes<E>(
1568        device: &Device,
1569        query_sets: &Storage<Fallible<QuerySet>>,
1570        timestamp_writes: &PassTimestampWrites,
1571    ) -> Result<ArcPassTimestampWrites, E>
1572    where
1573        E: From<TimestampWritesError>
1574            + From<QueryUseError>
1575            + From<DeviceError>
1576            + From<MissingFeatures>
1577            + From<InvalidResourceError>,
1578    {
1579        let &PassTimestampWrites {
1580            query_set,
1581            beginning_of_pass_write_index,
1582            end_of_pass_write_index,
1583        } = timestamp_writes;
1584
1585        device.require_features(wgt::Features::TIMESTAMP_QUERY)?;
1586
1587        let query_set = query_sets.get(query_set).get()?;
1588
1589        query_set.same_device(device)?;
1590
1591        for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
1592            .into_iter()
1593            .flatten()
1594        {
1595            query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
1596        }
1597
1598        if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
1599            if begin == end {
1600                return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());
1601            }
1602        }
1603
1604        if beginning_of_pass_write_index
1605            .or(end_of_pass_write_index)
1606            .is_none()
1607        {
1608            return Err(TimestampWritesError::IndicesMissing.into());
1609        }
1610
1611        Ok(ArcPassTimestampWrites {
1612            query_set,
1613            beginning_of_pass_write_index,
1614            end_of_pass_write_index,
1615        })
1616    }
1617}
1618
1619pub(crate) fn push_debug_group(
1620    state: &mut EncodingState,
1621    label: &str,
1622) -> Result<(), CommandEncoderError> {
1623    *state.debug_scope_depth += 1;
1624
1625    if !state
1626        .device
1627        .instance_flags
1628        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1629    {
1630        unsafe { state.raw_encoder.begin_debug_marker(label) };
1631    }
1632
1633    Ok(())
1634}
1635
1636pub(crate) fn insert_debug_marker(
1637    state: &mut EncodingState,
1638    label: &str,
1639) -> Result<(), CommandEncoderError> {
1640    if !state
1641        .device
1642        .instance_flags
1643        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1644    {
1645        unsafe { state.raw_encoder.insert_debug_marker(label) };
1646    }
1647
1648    Ok(())
1649}
1650
1651pub(crate) fn pop_debug_group(state: &mut EncodingState) -> Result<(), CommandEncoderError> {
1652    if *state.debug_scope_depth == 0 {
1653        return Err(DebugGroupError::InvalidPop.into());
1654    }
1655    *state.debug_scope_depth -= 1;
1656
1657    if !state
1658        .device
1659        .instance_flags
1660        .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1661    {
1662        unsafe { state.raw_encoder.end_debug_marker() };
1663    }
1664
1665    Ok(())
1666}
1667
1668fn push_constant_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
1669where
1670    PushFn: FnMut(u32, &[u32]),
1671{
1672    let mut count_words = 0_u32;
1673    let size_words = size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT;
1674    while count_words < size_words {
1675        let count_bytes = count_words * wgt::PUSH_CONSTANT_ALIGNMENT;
1676        let size_to_write_words =
1677            (size_words - count_words).min(PUSH_CONSTANT_CLEAR_ARRAY.len() as u32);
1678
1679        push_fn(
1680            offset + count_bytes,
1681            &PUSH_CONSTANT_CLEAR_ARRAY[0..size_to_write_words as usize],
1682        );
1683
1684        count_words += size_to_write_words;
1685    }
1686}
1687
1688#[derive(Debug, Copy, Clone)]
1689struct StateChange<T> {
1690    last_state: Option<T>,
1691}
1692
1693impl<T: Copy + PartialEq> StateChange<T> {
1694    fn new() -> Self {
1695        Self { last_state: None }
1696    }
1697    fn set_and_check_redundant(&mut self, new_state: T) -> bool {
1698        let already_set = self.last_state == Some(new_state);
1699        self.last_state = Some(new_state);
1700        already_set
1701    }
1702    fn reset(&mut self) {
1703        self.last_state = None;
1704    }
1705}
1706
1707impl<T: Copy + PartialEq> Default for StateChange<T> {
1708    fn default() -> Self {
1709        Self::new()
1710    }
1711}
1712
1713#[derive(Debug)]
1714struct BindGroupStateChange {
1715    last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],
1716}
1717
1718impl BindGroupStateChange {
1719    fn new() -> Self {
1720        Self {
1721            last_states: [StateChange::new(); hal::MAX_BIND_GROUPS],
1722        }
1723    }
1724
1725    fn set_and_check_redundant(
1726        &mut self,
1727        bind_group_id: Option<id::BindGroupId>,
1728        index: u32,
1729        dynamic_offsets: &mut Vec<u32>,
1730        offsets: &[wgt::DynamicOffset],
1731    ) -> bool {
1732        // For now never deduplicate bind groups with dynamic offsets.
1733        if offsets.is_empty() {
1734            // If this get returns None, that means we're well over the limit,
1735            // so let the call through to get a proper error
1736            if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1737                // Bail out if we're binding the same bind group.
1738                if current_bind_group.set_and_check_redundant(bind_group_id) {
1739                    return true;
1740                }
1741            }
1742        } else {
1743            // We intentionally remove the memory of this bind group if we have dynamic offsets,
1744            // such that if you try to bind this bind group later with _no_ dynamic offsets it
1745            // tries to bind it again and gives a proper validation error.
1746            if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1747                current_bind_group.reset();
1748            }
1749            dynamic_offsets.extend_from_slice(offsets);
1750        }
1751        false
1752    }
1753    fn reset(&mut self) {
1754        self.last_states = [StateChange::new(); hal::MAX_BIND_GROUPS];
1755    }
1756}
1757
1758impl Default for BindGroupStateChange {
1759    fn default() -> Self {
1760        Self::new()
1761    }
1762}
1763
1764/// Helper to attach [`PassErrorScope`] to errors.
1765trait MapPassErr<T> {
1766    fn map_pass_err(self, scope: PassErrorScope) -> T;
1767}
1768
1769impl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>
1770where
1771    E: MapPassErr<F>,
1772{
1773    fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {
1774        self.map_err(|err| err.map_pass_err(scope))
1775    }
1776}
1777
1778impl MapPassErr<PassStateError> for EncoderStateError {
1779    fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {
1780        PassStateError { scope, inner: self }
1781    }
1782}
1783
1784#[derive(Clone, Copy, Debug)]
1785pub enum DrawKind {
1786    Draw,
1787    DrawIndirect,
1788    MultiDrawIndirect,
1789    MultiDrawIndirectCount,
1790}
1791
1792/// The type of draw command(indexed or not, or mesh shader)
1793#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1794#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1795pub enum DrawCommandFamily {
1796    Draw,
1797    DrawIndexed,
1798    DrawMeshTasks,
1799}
1800
1801/// A command that can be recorded in a pass or bundle.
1802///
1803/// This is used to provide context for errors during command recording.
1804/// [`MapPassErr`] is used as a helper to attach a `PassErrorScope` to
1805/// an error.
1806///
1807/// The [`PassErrorScope::Bundle`] and [`PassErrorScope::Pass`] variants
1808/// are used when the error occurs during the opening or closing of the
1809/// pass or bundle.
1810#[derive(Clone, Copy, Debug, Error)]
1811pub enum PassErrorScope {
1812    // TODO: Extract out the 2 error variants below so that we can always
1813    // include the ResourceErrorIdent of the pass around all inner errors
1814    #[error("In a bundle parameter")]
1815    Bundle,
1816    #[error("In a pass parameter")]
1817    Pass,
1818    #[error("In a set_bind_group command")]
1819    SetBindGroup,
1820    #[error("In a set_pipeline command")]
1821    SetPipelineRender,
1822    #[error("In a set_pipeline command")]
1823    SetPipelineCompute,
1824    #[error("In a set_push_constant command")]
1825    SetPushConstant,
1826    #[error("In a set_vertex_buffer command")]
1827    SetVertexBuffer,
1828    #[error("In a set_index_buffer command")]
1829    SetIndexBuffer,
1830    #[error("In a set_blend_constant command")]
1831    SetBlendConstant,
1832    #[error("In a set_stencil_reference command")]
1833    SetStencilReference,
1834    #[error("In a set_viewport command")]
1835    SetViewport,
1836    #[error("In a set_scissor_rect command")]
1837    SetScissorRect,
1838    #[error("In a draw command, kind: {kind:?}")]
1839    Draw {
1840        kind: DrawKind,
1841        family: DrawCommandFamily,
1842    },
1843    #[error("In a write_timestamp command")]
1844    WriteTimestamp,
1845    #[error("In a begin_occlusion_query command")]
1846    BeginOcclusionQuery,
1847    #[error("In a end_occlusion_query command")]
1848    EndOcclusionQuery,
1849    #[error("In a begin_pipeline_statistics_query command")]
1850    BeginPipelineStatisticsQuery,
1851    #[error("In a end_pipeline_statistics_query command")]
1852    EndPipelineStatisticsQuery,
1853    #[error("In a execute_bundle command")]
1854    ExecuteBundle,
1855    #[error("In a dispatch command, indirect:{indirect}")]
1856    Dispatch { indirect: bool },
1857    #[error("In a push_debug_group command")]
1858    PushDebugGroup,
1859    #[error("In a pop_debug_group command")]
1860    PopDebugGroup,
1861    #[error("In a insert_debug_marker command")]
1862    InsertDebugMarker,
1863}
1864
1865/// Variant of `EncoderStateError` that includes the pass scope.
1866#[derive(Clone, Debug, Error)]
1867#[error("{scope}")]
1868pub struct PassStateError {
1869    pub scope: PassErrorScope,
1870    #[source]
1871    pub(super) inner: EncoderStateError,
1872}
1873
1874impl WebGpuError for PassStateError {
1875    fn webgpu_error_type(&self) -> ErrorType {
1876        let Self { scope: _, inner } = self;
1877        inner.webgpu_error_type()
1878    }
1879}