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