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}