wgpu_core/command/
mod.rs

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