wgpu_core/command/
mod.rs

1mod allocator;
2mod bind;
3mod bundle;
4mod clear;
5mod compute;
6mod compute_command;
7mod draw;
8mod memory_init;
9mod query;
10mod ray_tracing;
11mod render;
12mod render_command;
13mod timestamp_writes;
14mod transfer;
15
16use std::mem::{self, ManuallyDrop};
17use std::sync::Arc;
18
19pub(crate) use self::clear::clear_texture;
20pub use self::{
21    bundle::*, clear::ClearError, compute::*, compute_command::ComputeCommand, draw::*, query::*,
22    render::*, render_command::RenderCommand, transfer::*,
23};
24pub(crate) use allocator::CommandAllocator;
25
26pub(crate) use timestamp_writes::ArcPassTimestampWrites;
27pub use timestamp_writes::PassTimestampWrites;
28
29use self::memory_init::CommandBufferTextureMemoryActions;
30
31use crate::device::queue::TempResource;
32use crate::device::{Device, DeviceError, MissingFeatures};
33use crate::lock::{rank, Mutex};
34use crate::snatch::SnatchGuard;
35
36use crate::init_tracker::BufferInitTrackerAction;
37use crate::ray_tracing::{BlasAction, TlasAction};
38use crate::resource::{Fallible, InvalidResourceError, Labeled, ParentDevice as _, QuerySet};
39use crate::storage::Storage;
40use crate::track::{DeviceTracker, Tracker, UsageScope};
41use crate::{api_log, global::Global, id, resource_log, Label};
42use crate::{hal_label, LabelHelpers};
43
44use thiserror::Error;
45
46#[cfg(feature = "trace")]
47use crate::device::trace::Command as TraceCommand;
48
49const PUSH_CONSTANT_CLEAR_ARRAY: &[u32] = &[0_u32; 64];
50
51/// The current state of a [`CommandBuffer`].
52pub(crate) enum CommandEncoderStatus {
53    /// Ready to record commands. An encoder's initial state.
54    ///
55    /// Command building methods like [`command_encoder_clear_buffer`] and
56    /// [`compute_pass_end`] require the encoder to be in this
57    /// state.
58    ///
59    /// This corresponds to WebGPU's "open" state.
60    /// See <https://www.w3.org/TR/webgpu/#encoder-state-open>
61    ///
62    /// [`command_encoder_clear_buffer`]: Global::command_encoder_clear_buffer
63    /// [`compute_pass_end`]: Global::compute_pass_end
64    Recording(CommandBufferMutable),
65
66    /// Locked by a render or compute pass.
67    ///
68    /// This state is entered when a render/compute pass is created,
69    /// and exited when the pass is ended.
70    ///
71    /// As long as the command encoder is locked, any command building operation on it will fail
72    /// and put the encoder into the [`Self::Error`] state.
73    /// See <https://www.w3.org/TR/webgpu/#encoder-state-locked>
74    Locked(CommandBufferMutable),
75
76    /// Command recording is complete, and the buffer is ready for submission.
77    ///
78    /// [`Global::command_encoder_finish`] transitions a
79    /// `CommandBuffer` from the `Recording` state into this state.
80    ///
81    /// [`Global::queue_submit`] drops command buffers unless they are
82    /// in this state.
83    Finished(CommandBufferMutable),
84
85    /// An error occurred while recording a compute or render pass.
86    ///
87    /// When a `CommandEncoder` is left in this state, we have also
88    /// returned an error result from the function that encountered
89    /// the problem. Future attempts to use the encoder (for example,
90    /// calls to [`Self::record`]) will also return errors.
91    Error,
92}
93
94impl CommandEncoderStatus {
95    /// Checks that the encoder is in the [`Self::Recording`] state.
96    pub(crate) fn record(&mut self) -> Result<RecordingGuard<'_>, CommandEncoderError> {
97        match self {
98            Self::Recording(_) => Ok(RecordingGuard { inner: self }),
99            Self::Locked(_) => {
100                *self = Self::Error;
101                Err(CommandEncoderError::Locked)
102            }
103            Self::Finished(_) => Err(CommandEncoderError::NotRecording),
104            Self::Error => Err(CommandEncoderError::Invalid),
105        }
106    }
107
108    #[cfg(feature = "trace")]
109    fn get_inner(&mut self) -> Result<&mut CommandBufferMutable, CommandEncoderError> {
110        match self {
111            Self::Locked(inner) | Self::Finished(inner) | Self::Recording(inner) => Ok(inner),
112            Self::Error => Err(CommandEncoderError::Invalid),
113        }
114    }
115
116    /// Locks the encoder by putting it in the [`Self::Locked`] state.
117    ///
118    /// Call [`Self::unlock_encoder`] to put the [`CommandBuffer`] back into the [`Self::Recording`] state.
119    fn lock_encoder(&mut self) -> Result<(), CommandEncoderError> {
120        match mem::replace(self, Self::Error) {
121            Self::Recording(inner) => {
122                *self = Self::Locked(inner);
123                Ok(())
124            }
125            Self::Finished(inner) => {
126                *self = Self::Finished(inner);
127                Err(CommandEncoderError::NotRecording)
128            }
129            Self::Locked(_) => Err(CommandEncoderError::Locked),
130            Self::Error => Err(CommandEncoderError::Invalid),
131        }
132    }
133
134    /// Unlocks the [`CommandBuffer`] and puts it back into the [`Self::Recording`] state.
135    ///
136    /// This function is the unlocking counterpart to [`Self::lock_encoder`].
137    ///
138    /// It is only valid to call this function if the encoder is in the [`Self::Locked`] state.
139    fn unlock_encoder(&mut self) -> Result<RecordingGuard<'_>, CommandEncoderError> {
140        match mem::replace(self, Self::Error) {
141            Self::Locked(inner) => {
142                *self = Self::Recording(inner);
143                Ok(RecordingGuard { inner: self })
144            }
145            Self::Finished(inner) => {
146                *self = Self::Finished(inner);
147                Err(CommandEncoderError::NotRecording)
148            }
149            Self::Recording(_) => Err(CommandEncoderError::Invalid),
150            Self::Error => Err(CommandEncoderError::Invalid),
151        }
152    }
153
154    fn finish(&mut self) -> Result<(), CommandEncoderError> {
155        match mem::replace(self, Self::Error) {
156            Self::Recording(mut inner) => {
157                if let Err(e) = inner.encoder.close_if_open() {
158                    Err(e.into())
159                } else {
160                    *self = Self::Finished(inner);
161                    // Note: if we want to stop tracking the swapchain texture view,
162                    // this is the place to do it.
163                    Ok(())
164                }
165            }
166            Self::Finished(inner) => {
167                *self = Self::Finished(inner);
168                Err(CommandEncoderError::NotRecording)
169            }
170            Self::Locked(_) => Err(CommandEncoderError::Locked),
171            Self::Error => Err(CommandEncoderError::Invalid),
172        }
173    }
174}
175
176/// A guard to enforce error reporting, for a [`CommandBuffer`] in the [`Recording`] state.
177///
178/// An [`RecordingGuard`] holds a mutable reference to a [`CommandEncoderStatus`] that
179/// has been verified to be in the [`Recording`] state. The [`RecordingGuard`] dereferences
180/// mutably to the [`CommandBufferMutable`] that the status holds.
181///
182/// Dropping an [`RecordingGuard`] sets the [`CommandBuffer`]'s state to
183/// [`CommandEncoderStatus::Error`]. If your use of the guard was
184/// successful, call its [`mark_successful`] method to dispose of it.
185///
186/// [`Recording`]: CommandEncoderStatus::Recording
187/// [`mark_successful`]: Self::mark_successful
188pub(crate) struct RecordingGuard<'a> {
189    inner: &'a mut CommandEncoderStatus,
190}
191
192impl<'a> RecordingGuard<'a> {
193    pub(crate) fn mark_successful(self) {
194        mem::forget(self)
195    }
196}
197
198impl<'a> Drop for RecordingGuard<'a> {
199    fn drop(&mut self) {
200        *self.inner = CommandEncoderStatus::Error;
201    }
202}
203
204impl<'a> std::ops::Deref for RecordingGuard<'a> {
205    type Target = CommandBufferMutable;
206
207    fn deref(&self) -> &Self::Target {
208        match &*self.inner {
209            CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
210            _ => unreachable!(),
211        }
212    }
213}
214
215impl<'a> std::ops::DerefMut for RecordingGuard<'a> {
216    fn deref_mut(&mut self) -> &mut Self::Target {
217        match self.inner {
218            CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
219            _ => unreachable!(),
220        }
221    }
222}
223
224/// A raw [`CommandEncoder`][rce], and the raw [`CommandBuffer`][rcb]s built from it.
225///
226/// Each wgpu-core [`CommandBuffer`] owns an instance of this type, which is
227/// where the commands are actually stored.
228///
229/// This holds a `Vec` of raw [`CommandBuffer`][rcb]s, not just one. We are not
230/// always able to record commands in the order in which they must ultimately be
231/// submitted to the queue, but raw command buffers don't permit inserting new
232/// commands into the middle of a recorded stream. However, hal queue submission
233/// accepts a series of command buffers at once, so we can simply break the
234/// stream up into multiple buffers, and then reorder the buffers. See
235/// [`CommandEncoder::close_and_swap`] for a specific example of this.
236///
237/// Note that a [`CommandEncoderId`] actually refers to a [`CommandBuffer`].
238/// Methods that take a command encoder id actually look up the command buffer,
239/// and then use its encoder.
240///
241/// [rce]: hal::Api::CommandEncoder
242/// [rcb]: hal::Api::CommandBuffer
243/// [`CommandEncoderId`]: crate::id::CommandEncoderId
244pub(crate) struct CommandEncoder {
245    /// The underlying `wgpu_hal` [`CommandEncoder`].
246    ///
247    /// Successfully executed command buffers' encoders are saved in a
248    /// [`CommandAllocator`] for recycling.
249    ///
250    /// [`CommandEncoder`]: hal::Api::CommandEncoder
251    /// [`CommandAllocator`]: crate::command::CommandAllocator
252    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
253
254    /// All the raw command buffers for our owning [`CommandBuffer`], in
255    /// submission order.
256    ///
257    /// These command buffers were all constructed with `raw`. The
258    /// [`wgpu_hal::CommandEncoder`] trait forbids these from outliving `raw`,
259    /// and requires that we provide all of these when we call
260    /// [`raw.reset_all()`][CE::ra], so the encoder and its buffers travel
261    /// together.
262    ///
263    /// [CE::ra]: hal::CommandEncoder::reset_all
264    /// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
265    pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
266
267    pub(crate) device: Arc<Device>,
268
269    /// True if `raw` is in the "recording" state.
270    ///
271    /// See the documentation for [`wgpu_hal::CommandEncoder`] for
272    /// details on the states `raw` can be in.
273    ///
274    /// [`wgpu_hal::CommandEncoder`]: hal::CommandEncoder
275    pub(crate) is_open: bool,
276
277    pub(crate) hal_label: Option<String>,
278}
279
280impl CommandEncoder {
281    /// Finish the current command buffer and insert it just before
282    /// the last element in [`self.list`][l].
283    ///
284    /// On return, the underlying hal encoder is closed.
285    ///
286    /// What is this for?
287    ///
288    /// The `wgpu_hal` contract requires that each render or compute pass's
289    /// commands be preceded by calls to [`transition_buffers`] and
290    /// [`transition_textures`], to put the resources the pass operates on in
291    /// the appropriate state. Unfortunately, we don't know which transitions
292    /// are needed until we're done recording the pass itself. Rather than
293    /// iterating over the pass twice, we note the necessary transitions as we
294    /// record its commands, finish the raw command buffer for the actual pass,
295    /// record a new raw command buffer for the transitions, and jam that buffer
296    /// in just before the pass's. This is the function that jams in the
297    /// transitions' command buffer.
298    ///
299    /// # Panics
300    ///
301    /// - If the encoder is not open.
302    ///
303    /// [l]: CommandEncoder::list
304    /// [`transition_buffers`]: hal::CommandEncoder::transition_buffers
305    /// [`transition_textures`]: hal::CommandEncoder::transition_textures
306    fn close_and_swap(&mut self) -> Result<(), DeviceError> {
307        assert!(self.is_open);
308        self.is_open = false;
309
310        let new =
311            unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
312        self.list.insert(self.list.len() - 1, new);
313
314        Ok(())
315    }
316
317    /// Finish the current command buffer and insert it at the beginning
318    /// of [`self.list`][l].
319    ///
320    /// On return, the underlying hal encoder is closed.
321    ///
322    /// # Panics
323    ///
324    /// - If the encoder is not open.
325    ///
326    /// [l]: CommandEncoder::list
327    pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> {
328        assert!(self.is_open);
329        self.is_open = false;
330
331        let new =
332            unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
333        self.list.insert(0, new);
334
335        Ok(())
336    }
337
338    /// Finish the current command buffer, and push it onto
339    /// the end of [`self.list`][l].
340    ///
341    /// On return, the underlying hal encoder is closed.
342    ///
343    /// # Panics
344    ///
345    /// - If the encoder is not open.
346    ///
347    /// [l]: CommandEncoder::list
348    pub(crate) fn close(&mut self) -> Result<(), DeviceError> {
349        assert!(self.is_open);
350        self.is_open = false;
351
352        let cmd_buf =
353            unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
354        self.list.push(cmd_buf);
355
356        Ok(())
357    }
358
359    /// Finish the current command buffer, if any, and add it to the
360    /// end of [`self.list`][l].
361    ///
362    /// If we have opened this command encoder, finish its current
363    /// command buffer, and push it onto the end of [`self.list`][l].
364    /// If this command buffer is closed, do nothing.
365    ///
366    /// On return, the underlying hal encoder is closed.
367    ///
368    /// [l]: CommandEncoder::list
369    fn close_if_open(&mut self) -> Result<(), DeviceError> {
370        if self.is_open {
371            self.is_open = false;
372            let cmd_buf =
373                unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
374            self.list.push(cmd_buf);
375        }
376
377        Ok(())
378    }
379
380    /// Begin recording a new command buffer, if we haven't already.
381    ///
382    /// The underlying hal encoder is put in the "recording" state.
383    pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
384        if !self.is_open {
385            self.is_open = true;
386            let hal_label = self.hal_label.as_deref();
387            unsafe { self.raw.begin_encoding(hal_label) }
388                .map_err(|e| self.device.handle_hal_error(e))?;
389        }
390
391        Ok(self.raw.as_mut())
392    }
393
394    /// Begin recording a new command buffer for a render pass, with
395    /// its own label.
396    ///
397    /// The underlying hal encoder is put in the "recording" state.
398    ///
399    /// # Panics
400    ///
401    /// - If the encoder is already open.
402    pub(crate) fn open_pass(
403        &mut self,
404        label: Option<&str>,
405    ) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
406        assert!(!self.is_open);
407        self.is_open = true;
408
409        let hal_label = hal_label(label, self.device.instance_flags);
410        unsafe { self.raw.begin_encoding(hal_label) }
411            .map_err(|e| self.device.handle_hal_error(e))?;
412
413        Ok(self.raw.as_mut())
414    }
415}
416
417impl Drop for CommandEncoder {
418    fn drop(&mut self) {
419        if self.is_open {
420            unsafe { self.raw.discard_encoding() };
421        }
422        unsafe {
423            self.raw.reset_all(mem::take(&mut self.list));
424        }
425        // SAFETY: We are in the Drop impl and we don't use self.raw anymore after this point.
426        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
427        self.device.command_allocator.release_encoder(raw);
428    }
429}
430
431/// Look at the documentation for [`CommandBufferMutable`] for an explanation of
432/// the fields in this struct. This is the "built" counterpart to that type.
433pub(crate) struct BakedCommands {
434    pub(crate) encoder: CommandEncoder,
435    pub(crate) trackers: Tracker,
436    pub(crate) temp_resources: Vec<TempResource>,
437    buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
438    texture_memory_actions: CommandBufferTextureMemoryActions,
439}
440
441/// The mutable state of a [`CommandBuffer`].
442pub struct CommandBufferMutable {
443    /// The [`wgpu_hal::Api::CommandBuffer`]s we've built so far, and the encoder
444    /// they belong to.
445    ///
446    /// [`wgpu_hal::Api::CommandBuffer`]: hal::Api::CommandBuffer
447    pub(crate) encoder: CommandEncoder,
448
449    /// All the resources that the commands recorded so far have referred to.
450    pub(crate) trackers: Tracker,
451
452    /// The regions of buffers and textures these commands will read and write.
453    ///
454    /// This is used to determine which portions of which
455    /// buffers/textures we actually need to initialize. If we're
456    /// definitely going to write to something before we read from it,
457    /// we don't need to clear its contents.
458    buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
459    texture_memory_actions: CommandBufferTextureMemoryActions,
460
461    pub(crate) pending_query_resets: QueryResetMap,
462
463    blas_actions: Vec<BlasAction>,
464    tlas_actions: Vec<TlasAction>,
465    temp_resources: Vec<TempResource>,
466
467    #[cfg(feature = "trace")]
468    pub(crate) commands: Option<Vec<TraceCommand>>,
469}
470
471impl CommandBufferMutable {
472    pub(crate) fn open_encoder_and_tracker(
473        &mut self,
474    ) -> Result<(&mut dyn hal::DynCommandEncoder, &mut Tracker), DeviceError> {
475        let encoder = self.encoder.open()?;
476        let tracker = &mut self.trackers;
477
478        Ok((encoder, tracker))
479    }
480
481    pub(crate) fn into_baked_commands(self) -> BakedCommands {
482        BakedCommands {
483            encoder: self.encoder,
484            trackers: self.trackers,
485            temp_resources: self.temp_resources,
486            buffer_memory_init_actions: self.buffer_memory_init_actions,
487            texture_memory_actions: self.texture_memory_actions,
488        }
489    }
490}
491
492/// A buffer of commands to be submitted to the GPU for execution.
493///
494/// Whereas the WebGPU API uses two separate types for command buffers and
495/// encoders, this type is a fusion of the two:
496///
497/// - During command recording, this holds a [`CommandEncoder`] accepting this
498///   buffer's commands. In this state, the [`CommandBuffer`] type behaves like
499///   a WebGPU `GPUCommandEncoder`.
500///
501/// - Once command recording is finished by calling
502///   [`Global::command_encoder_finish`], no further recording is allowed. The
503///   internal [`CommandEncoder`] is retained solely as a storage pool for the
504///   raw command buffers. In this state, the value behaves like a WebGPU
505///   `GPUCommandBuffer`.
506///
507/// - Once a command buffer is submitted to the queue, it is removed from the id
508///   registry, and its contents are taken to construct a [`BakedCommands`],
509///   whose contents eventually become the property of the submission queue.
510pub struct CommandBuffer {
511    pub(crate) device: Arc<Device>,
512    support_clear_texture: bool,
513    /// The `label` from the descriptor used to create the resource.
514    label: String,
515
516    /// The mutable state of this command buffer.
517    pub(crate) data: Mutex<CommandEncoderStatus>,
518}
519
520impl Drop for CommandBuffer {
521    fn drop(&mut self) {
522        resource_log!("Drop {}", self.error_ident());
523    }
524}
525
526impl CommandBuffer {
527    pub(crate) fn new(
528        encoder: Box<dyn hal::DynCommandEncoder>,
529        device: &Arc<Device>,
530        label: &Label,
531    ) -> Self {
532        CommandBuffer {
533            device: device.clone(),
534            support_clear_texture: device.features.contains(wgt::Features::CLEAR_TEXTURE),
535            label: label.to_string(),
536            data: Mutex::new(
537                rank::COMMAND_BUFFER_DATA,
538                CommandEncoderStatus::Recording(CommandBufferMutable {
539                    encoder: CommandEncoder {
540                        raw: ManuallyDrop::new(encoder),
541                        list: Vec::new(),
542                        device: device.clone(),
543                        is_open: false,
544                        hal_label: label.to_hal(device.instance_flags).map(str::to_owned),
545                    },
546                    trackers: Tracker::new(),
547                    buffer_memory_init_actions: Default::default(),
548                    texture_memory_actions: Default::default(),
549                    pending_query_resets: QueryResetMap::new(),
550                    blas_actions: Default::default(),
551                    tlas_actions: Default::default(),
552                    temp_resources: Default::default(),
553                    #[cfg(feature = "trace")]
554                    commands: if device.trace.lock().is_some() {
555                        Some(Vec::new())
556                    } else {
557                        None
558                    },
559                }),
560            ),
561        }
562    }
563
564    pub(crate) fn new_invalid(device: &Arc<Device>, label: &Label) -> Self {
565        CommandBuffer {
566            device: device.clone(),
567            support_clear_texture: device.features.contains(wgt::Features::CLEAR_TEXTURE),
568            label: label.to_string(),
569            data: Mutex::new(rank::COMMAND_BUFFER_DATA, CommandEncoderStatus::Error),
570        }
571    }
572
573    pub(crate) fn insert_barriers_from_tracker(
574        raw: &mut dyn hal::DynCommandEncoder,
575        base: &mut Tracker,
576        head: &Tracker,
577        snatch_guard: &SnatchGuard,
578    ) {
579        profiling::scope!("insert_barriers");
580
581        base.buffers.set_from_tracker(&head.buffers);
582        base.textures.set_from_tracker(&head.textures);
583
584        Self::drain_barriers(raw, base, snatch_guard);
585    }
586
587    pub(crate) fn insert_barriers_from_scope(
588        raw: &mut dyn hal::DynCommandEncoder,
589        base: &mut Tracker,
590        head: &UsageScope,
591        snatch_guard: &SnatchGuard,
592    ) {
593        profiling::scope!("insert_barriers");
594
595        base.buffers.set_from_usage_scope(&head.buffers);
596        base.textures.set_from_usage_scope(&head.textures);
597
598        Self::drain_barriers(raw, base, snatch_guard);
599    }
600
601    pub(crate) fn drain_barriers(
602        raw: &mut dyn hal::DynCommandEncoder,
603        base: &mut Tracker,
604        snatch_guard: &SnatchGuard,
605    ) {
606        profiling::scope!("drain_barriers");
607
608        let buffer_barriers = base
609            .buffers
610            .drain_transitions(snatch_guard)
611            .collect::<Vec<_>>();
612        let (transitions, textures) = base.textures.drain_transitions(snatch_guard);
613        let texture_barriers = transitions
614            .into_iter()
615            .enumerate()
616            .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))
617            .collect::<Vec<_>>();
618
619        unsafe {
620            raw.transition_buffers(&buffer_barriers);
621            raw.transition_textures(&texture_barriers);
622        }
623    }
624
625    pub(crate) fn insert_barriers_from_device_tracker(
626        raw: &mut dyn hal::DynCommandEncoder,
627        base: &mut DeviceTracker,
628        head: &Tracker,
629        snatch_guard: &SnatchGuard,
630    ) {
631        profiling::scope!("insert_barriers_from_device_tracker");
632
633        let buffer_barriers = base
634            .buffers
635            .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)
636            .collect::<Vec<_>>();
637
638        let texture_barriers = base
639            .textures
640            .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)
641            .collect::<Vec<_>>();
642
643        unsafe {
644            raw.transition_buffers(&buffer_barriers);
645            raw.transition_textures(&texture_barriers);
646        }
647    }
648}
649
650impl CommandBuffer {
651    pub fn take_finished<'a>(&'a self) -> Result<CommandBufferMutable, InvalidResourceError> {
652        let status = mem::replace(&mut *self.data.lock(), CommandEncoderStatus::Error);
653        match status {
654            CommandEncoderStatus::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
655            CommandEncoderStatus::Recording(_)
656            | CommandEncoderStatus::Locked(_)
657            | CommandEncoderStatus::Error => Err(InvalidResourceError(self.error_ident())),
658        }
659    }
660}
661
662crate::impl_resource_type!(CommandBuffer);
663crate::impl_labeled!(CommandBuffer);
664crate::impl_parent_device!(CommandBuffer);
665crate::impl_storage_item!(CommandBuffer);
666
667/// A stream of commands for a render pass or compute pass.
668///
669/// This also contains side tables referred to by certain commands,
670/// like dynamic offsets for [`SetBindGroup`] or string data for
671/// [`InsertDebugMarker`].
672///
673/// Render passes use `BasePass<RenderCommand>`, whereas compute
674/// passes use `BasePass<ComputeCommand>`.
675///
676/// [`SetBindGroup`]: RenderCommand::SetBindGroup
677/// [`InsertDebugMarker`]: RenderCommand::InsertDebugMarker
678#[doc(hidden)]
679#[derive(Debug, Clone)]
680#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
681pub struct BasePass<C> {
682    pub label: Option<String>,
683
684    /// The stream of commands.
685    pub commands: Vec<C>,
686
687    /// Dynamic offsets consumed by [`SetBindGroup`] commands in `commands`.
688    ///
689    /// Each successive `SetBindGroup` consumes the next
690    /// [`num_dynamic_offsets`] values from this list.
691    pub dynamic_offsets: Vec<wgt::DynamicOffset>,
692
693    /// Strings used by debug instructions.
694    ///
695    /// Each successive [`PushDebugGroup`] or [`InsertDebugMarker`]
696    /// instruction consumes the next `len` bytes from this vector.
697    pub string_data: Vec<u8>,
698
699    /// Data used by `SetPushConstant` instructions.
700    ///
701    /// See the documentation for [`RenderCommand::SetPushConstant`]
702    /// and [`ComputeCommand::SetPushConstant`] for details.
703    pub push_constant_data: Vec<u32>,
704}
705
706impl<C: Clone> BasePass<C> {
707    fn new(label: &Label) -> Self {
708        Self {
709            label: label.as_ref().map(|cow| cow.to_string()),
710            commands: Vec::new(),
711            dynamic_offsets: Vec::new(),
712            string_data: Vec::new(),
713            push_constant_data: Vec::new(),
714        }
715    }
716}
717
718#[derive(Clone, Debug, Error)]
719#[non_exhaustive]
720pub enum CommandEncoderError {
721    #[error("Command encoder is invalid")]
722    Invalid,
723    #[error("Command encoder must be active")]
724    NotRecording,
725    #[error(transparent)]
726    Device(#[from] DeviceError),
727    #[error("Command encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
728    Locked,
729
730    #[error(transparent)]
731    InvalidColorAttachment(#[from] ColorAttachmentError),
732    #[error(transparent)]
733    InvalidAttachment(#[from] AttachmentError),
734    #[error(transparent)]
735    InvalidResource(#[from] InvalidResourceError),
736    #[error(transparent)]
737    MissingFeatures(#[from] MissingFeatures),
738    #[error(
739        "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
740    )]
741    TimestampWriteIndicesEqual { idx: u32 },
742    #[error(transparent)]
743    TimestampWritesInvalid(#[from] QueryUseError),
744    #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
745    TimestampWriteIndicesMissing,
746}
747
748impl Global {
749    pub fn command_encoder_finish(
750        &self,
751        encoder_id: id::CommandEncoderId,
752        _desc: &wgt::CommandBufferDescriptor<Label>,
753    ) -> (id::CommandBufferId, Option<CommandEncoderError>) {
754        profiling::scope!("CommandEncoder::finish");
755
756        let hub = &self.hub;
757
758        let cmd_buf = hub.command_buffers.get(encoder_id.into_command_buffer_id());
759
760        let error = match cmd_buf.data.lock().finish() {
761            Ok(_) => None,
762            Err(e) => Some(e),
763        };
764
765        (encoder_id.into_command_buffer_id(), error)
766    }
767
768    pub fn command_encoder_push_debug_group(
769        &self,
770        encoder_id: id::CommandEncoderId,
771        label: &str,
772    ) -> Result<(), CommandEncoderError> {
773        profiling::scope!("CommandEncoder::push_debug_group");
774        api_log!("CommandEncoder::push_debug_group {label}");
775
776        let hub = &self.hub;
777
778        let cmd_buf = hub.command_buffers.get(encoder_id.into_command_buffer_id());
779        let mut cmd_buf_data = cmd_buf.data.lock();
780        let mut cmd_buf_data_guard = cmd_buf_data.record()?;
781        let cmd_buf_data = &mut *cmd_buf_data_guard;
782
783        #[cfg(feature = "trace")]
784        if let Some(ref mut list) = cmd_buf_data.commands {
785            list.push(TraceCommand::PushDebugGroup(label.to_string()));
786        }
787
788        let cmd_buf_raw = cmd_buf_data.encoder.open()?;
789        if !cmd_buf
790            .device
791            .instance_flags
792            .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
793        {
794            unsafe {
795                cmd_buf_raw.begin_debug_marker(label);
796            }
797        }
798
799        cmd_buf_data_guard.mark_successful();
800        Ok(())
801    }
802
803    pub fn command_encoder_insert_debug_marker(
804        &self,
805        encoder_id: id::CommandEncoderId,
806        label: &str,
807    ) -> Result<(), CommandEncoderError> {
808        profiling::scope!("CommandEncoder::insert_debug_marker");
809        api_log!("CommandEncoder::insert_debug_marker {label}");
810
811        let hub = &self.hub;
812
813        let cmd_buf = hub.command_buffers.get(encoder_id.into_command_buffer_id());
814        let mut cmd_buf_data = cmd_buf.data.lock();
815        let mut cmd_buf_data_guard = cmd_buf_data.record()?;
816        let cmd_buf_data = &mut *cmd_buf_data_guard;
817
818        #[cfg(feature = "trace")]
819        if let Some(ref mut list) = cmd_buf_data.commands {
820            list.push(TraceCommand::InsertDebugMarker(label.to_string()));
821        }
822
823        if !cmd_buf
824            .device
825            .instance_flags
826            .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
827        {
828            let cmd_buf_raw = cmd_buf_data.encoder.open()?;
829            unsafe {
830                cmd_buf_raw.insert_debug_marker(label);
831            }
832        }
833
834        cmd_buf_data_guard.mark_successful();
835        Ok(())
836    }
837
838    pub fn command_encoder_pop_debug_group(
839        &self,
840        encoder_id: id::CommandEncoderId,
841    ) -> Result<(), CommandEncoderError> {
842        profiling::scope!("CommandEncoder::pop_debug_marker");
843        api_log!("CommandEncoder::pop_debug_group");
844
845        let hub = &self.hub;
846
847        let cmd_buf = hub.command_buffers.get(encoder_id.into_command_buffer_id());
848        let mut cmd_buf_data = cmd_buf.data.lock();
849        let mut cmd_buf_data_guard = cmd_buf_data.record()?;
850        let cmd_buf_data = &mut *cmd_buf_data_guard;
851
852        #[cfg(feature = "trace")]
853        if let Some(ref mut list) = cmd_buf_data.commands {
854            list.push(TraceCommand::PopDebugGroup);
855        }
856
857        let cmd_buf_raw = cmd_buf_data.encoder.open()?;
858        if !cmd_buf
859            .device
860            .instance_flags
861            .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
862        {
863            unsafe {
864                cmd_buf_raw.end_debug_marker();
865            }
866        }
867
868        cmd_buf_data_guard.mark_successful();
869        Ok(())
870    }
871
872    fn validate_pass_timestamp_writes(
873        device: &Device,
874        query_sets: &Storage<Fallible<QuerySet>>,
875        timestamp_writes: &PassTimestampWrites,
876    ) -> Result<ArcPassTimestampWrites, CommandEncoderError> {
877        let &PassTimestampWrites {
878            query_set,
879            beginning_of_pass_write_index,
880            end_of_pass_write_index,
881        } = timestamp_writes;
882
883        device.require_features(wgt::Features::TIMESTAMP_QUERY)?;
884
885        let query_set = query_sets.get(query_set).get()?;
886
887        query_set.same_device(device)?;
888
889        for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
890            .into_iter()
891            .flatten()
892        {
893            query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
894        }
895
896        if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
897            if begin == end {
898                return Err(CommandEncoderError::TimestampWriteIndicesEqual { idx: begin });
899            }
900        }
901
902        if beginning_of_pass_write_index
903            .or(end_of_pass_write_index)
904            .is_none()
905        {
906            return Err(CommandEncoderError::TimestampWriteIndicesMissing);
907        }
908
909        Ok(ArcPassTimestampWrites {
910            query_set,
911            beginning_of_pass_write_index,
912            end_of_pass_write_index,
913        })
914    }
915}
916
917fn push_constant_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
918where
919    PushFn: FnMut(u32, &[u32]),
920{
921    let mut count_words = 0_u32;
922    let size_words = size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT;
923    while count_words < size_words {
924        let count_bytes = count_words * wgt::PUSH_CONSTANT_ALIGNMENT;
925        let size_to_write_words =
926            (size_words - count_words).min(PUSH_CONSTANT_CLEAR_ARRAY.len() as u32);
927
928        push_fn(
929            offset + count_bytes,
930            &PUSH_CONSTANT_CLEAR_ARRAY[0..size_to_write_words as usize],
931        );
932
933        count_words += size_to_write_words;
934    }
935}
936
937#[derive(Debug, Copy, Clone)]
938struct StateChange<T> {
939    last_state: Option<T>,
940}
941
942impl<T: Copy + PartialEq> StateChange<T> {
943    fn new() -> Self {
944        Self { last_state: None }
945    }
946    fn set_and_check_redundant(&mut self, new_state: T) -> bool {
947        let already_set = self.last_state == Some(new_state);
948        self.last_state = Some(new_state);
949        already_set
950    }
951    fn reset(&mut self) {
952        self.last_state = None;
953    }
954}
955
956impl<T: Copy + PartialEq> Default for StateChange<T> {
957    fn default() -> Self {
958        Self::new()
959    }
960}
961
962#[derive(Debug)]
963struct BindGroupStateChange {
964    last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],
965}
966
967impl BindGroupStateChange {
968    fn new() -> Self {
969        Self {
970            last_states: [StateChange::new(); hal::MAX_BIND_GROUPS],
971        }
972    }
973
974    fn set_and_check_redundant(
975        &mut self,
976        bind_group_id: Option<id::BindGroupId>,
977        index: u32,
978        dynamic_offsets: &mut Vec<u32>,
979        offsets: &[wgt::DynamicOffset],
980    ) -> bool {
981        // For now never deduplicate bind groups with dynamic offsets.
982        if offsets.is_empty() {
983            // If this get returns None, that means we're well over the limit,
984            // so let the call through to get a proper error
985            if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
986                // Bail out if we're binding the same bind group.
987                if current_bind_group.set_and_check_redundant(bind_group_id) {
988                    return true;
989                }
990            }
991        } else {
992            // We intentionally remove the memory of this bind group if we have dynamic offsets,
993            // such that if you try to bind this bind group later with _no_ dynamic offsets it
994            // tries to bind it again and gives a proper validation error.
995            if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
996                current_bind_group.reset();
997            }
998            dynamic_offsets.extend_from_slice(offsets);
999        }
1000        false
1001    }
1002    fn reset(&mut self) {
1003        self.last_states = [StateChange::new(); hal::MAX_BIND_GROUPS];
1004    }
1005}
1006
1007impl Default for BindGroupStateChange {
1008    fn default() -> Self {
1009        Self::new()
1010    }
1011}
1012
1013trait MapPassErr<T, O> {
1014    fn map_pass_err(self, scope: PassErrorScope) -> Result<T, O>;
1015}
1016
1017#[derive(Clone, Copy, Debug)]
1018pub enum DrawKind {
1019    Draw,
1020    DrawIndirect,
1021    MultiDrawIndirect,
1022    MultiDrawIndirectCount,
1023}
1024
1025#[derive(Clone, Copy, Debug, Error)]
1026pub enum PassErrorScope {
1027    // TODO: Extract out the 2 error variants below so that we can always
1028    // include the ResourceErrorIdent of the pass around all inner errors
1029    #[error("In a bundle parameter")]
1030    Bundle,
1031    #[error("In a pass parameter")]
1032    Pass,
1033    #[error("In a set_bind_group command")]
1034    SetBindGroup,
1035    #[error("In a set_pipeline command")]
1036    SetPipelineRender,
1037    #[error("In a set_pipeline command")]
1038    SetPipelineCompute,
1039    #[error("In a set_push_constant command")]
1040    SetPushConstant,
1041    #[error("In a set_vertex_buffer command")]
1042    SetVertexBuffer,
1043    #[error("In a set_index_buffer command")]
1044    SetIndexBuffer,
1045    #[error("In a set_blend_constant command")]
1046    SetBlendConstant,
1047    #[error("In a set_stencil_reference command")]
1048    SetStencilReference,
1049    #[error("In a set_viewport command")]
1050    SetViewport,
1051    #[error("In a set_scissor_rect command")]
1052    SetScissorRect,
1053    #[error("In a draw command, kind: {kind:?}")]
1054    Draw { kind: DrawKind, indexed: bool },
1055    #[error("In a write_timestamp command")]
1056    WriteTimestamp,
1057    #[error("In a begin_occlusion_query command")]
1058    BeginOcclusionQuery,
1059    #[error("In a end_occlusion_query command")]
1060    EndOcclusionQuery,
1061    #[error("In a begin_pipeline_statistics_query command")]
1062    BeginPipelineStatisticsQuery,
1063    #[error("In a end_pipeline_statistics_query command")]
1064    EndPipelineStatisticsQuery,
1065    #[error("In a execute_bundle command")]
1066    ExecuteBundle,
1067    #[error("In a dispatch command, indirect:{indirect}")]
1068    Dispatch { indirect: bool },
1069    #[error("In a push_debug_group command")]
1070    PushDebugGroup,
1071    #[error("In a pop_debug_group command")]
1072    PopDebugGroup,
1073    #[error("In a insert_debug_marker command")]
1074    InsertDebugMarker,
1075}