wgpu_core/command/
mod.rs

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