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