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
51pub(crate) enum CommandEncoderStatus {
53    Recording(CommandBufferMutable),
65
66    Locked(CommandBufferMutable),
75
76    Finished(CommandBufferMutable),
84
85    Error,
92}
93
94impl CommandEncoderStatus {
95    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    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    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                    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
176pub(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
224pub(crate) struct CommandEncoder {
245    pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
253
254    pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
266
267    pub(crate) device: Arc<Device>,
268
269    pub(crate) is_open: bool,
276
277    pub(crate) hal_label: Option<String>,
278}
279
280impl CommandEncoder {
281    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    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    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    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    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    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        let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
427        self.device.command_allocator.release_encoder(raw);
428    }
429}
430
431pub(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
441pub struct CommandBufferMutable {
443    pub(crate) encoder: CommandEncoder,
448
449    pub(crate) trackers: Tracker,
451
452    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
492pub struct CommandBuffer {
511    pub(crate) device: Arc<Device>,
512    support_clear_texture: bool,
513    label: String,
515
516    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#[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    pub commands: Vec<C>,
686
687    pub dynamic_offsets: Vec<wgt::DynamicOffset>,
692
693    pub string_data: Vec<u8>,
698
699    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        if offsets.is_empty() {
983            if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
986                if current_bind_group.set_and_check_redundant(bind_group_id) {
988                    return true;
989                }
990            }
991        } else {
992            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    #[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}