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
53pub(crate) enum CommandEncoderStatus {
55 Recording(CommandBufferMutable),
67
68 Locked(CommandBufferMutable),
77
78 Finished(CommandBufferMutable),
86
87 Error,
94}
95
96impl CommandEncoderStatus {
97 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 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 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 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
178pub(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
226pub(crate) struct CommandEncoder {
247 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
255
256 pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
268
269 pub(crate) device: Arc<Device>,
270
271 pub(crate) is_open: bool,
278
279 pub(crate) hal_label: Option<String>,
280}
281
282impl CommandEncoder {
283 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 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 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 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 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 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 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
429 self.device.command_allocator.release_encoder(raw);
430 }
431}
432
433pub(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
444pub struct CommandBufferMutable {
446 pub(crate) encoder: CommandEncoder,
451
452 pub(crate) trackers: Tracker,
454
455 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
497pub struct CommandBuffer {
516 pub(crate) device: Arc<Device>,
517 support_clear_texture: bool,
518 label: String,
520
521 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#[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 pub commands: Vec<C>,
692
693 pub dynamic_offsets: Vec<wgt::DynamicOffset>,
698
699 pub string_data: Vec<u8>,
704
705 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 if offsets.is_empty() {
989 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
992 if current_bind_group.set_and_check_redundant(bind_group_id) {
994 return true;
995 }
996 }
997 } else {
998 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 #[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}