1mod allocator;
12mod bind;
13mod bundle;
14mod clear;
15mod compute;
16mod compute_command;
17mod draw;
18mod encoder;
19mod encoder_command;
20pub mod ffi;
21mod memory_init;
22mod pass;
23mod query;
24mod ray_tracing;
25mod render;
26mod render_command;
27mod timestamp_writes;
28mod transfer;
29mod transition_resources;
30
31use alloc::{borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec::Vec};
32use core::convert::Infallible;
33use core::mem::{self, ManuallyDrop};
34use core::ops;
35
36pub(crate) use self::clear::clear_texture;
37pub use self::{
38 bundle::*,
39 clear::ClearError,
40 compute::*,
41 compute_command::{ArcComputeCommand, ComputeCommand},
42 draw::*,
43 encoder_command::{ArcCommand, Command},
44 query::*,
45 render::*,
46 render_command::{ArcRenderCommand, RenderCommand},
47 transfer::*,
48};
49pub(crate) use allocator::CommandAllocator;
50
51pub(crate) use timestamp_writes::ArcPassTimestampWrites;
52pub use timestamp_writes::PassTimestampWrites;
53
54use self::{
55 clear::{clear_buffer, clear_texture_cmd},
56 memory_init::CommandBufferTextureMemoryActions,
57 ray_tracing::build_acceleration_structures,
58 transition_resources::transition_resources,
59};
60
61use crate::binding_model::BindingError;
62use crate::command::encoder::EncodingState;
63use crate::command::transition_resources::TransitionResourcesError;
64use crate::device::queue::TempResource;
65use crate::device::{Device, DeviceError, MissingFeatures};
66use crate::id::Id;
67use crate::lock::{rank, Mutex};
68use crate::snatch::SnatchGuard;
69
70use crate::init_tracker::BufferInitTrackerAction;
71use crate::ray_tracing::{AsAction, BuildAccelerationStructureError};
72use crate::resource::{
73 DestroyedResourceError, Fallible, InvalidResourceError, Labeled, ParentDevice as _, QuerySet,
74};
75use crate::storage::Storage;
76use crate::track::{DeviceTracker, ResourceUsageCompatibilityError, Tracker, UsageScope};
77use crate::{api_log, global::Global, id, resource_log, Label};
78use crate::{hal_label, LabelHelpers};
79
80use wgt::error::{ErrorType, WebGpuError};
81
82use thiserror::Error;
83
84#[cfg(feature = "trace")]
85type TraceCommand = Command;
86
87pub type TexelCopyBufferInfo = ffi::TexelCopyBufferInfo;
89pub type TexelCopyTextureInfo = ffi::TexelCopyTextureInfo;
91pub type CopyExternalImageDestInfo = ffi::CopyExternalImageDestInfo;
93
94const PUSH_CONSTANT_CLEAR_ARRAY: &[u32] = &[0_u32; 64];
95
96pub(crate) enum CommandEncoderStatus {
102 Recording(CommandBufferMutable),
114
115 Locked(CommandBufferMutable),
124
125 Consumed,
126
127 Finished(CommandBufferMutable),
138
139 Error(CommandEncoderError),
144
145 Transitioning,
148}
149
150impl CommandEncoderStatus {
151 #[cfg(feature = "trace")]
152 fn trace(&mut self) -> Option<&mut Vec<TraceCommand>> {
153 match self {
154 Self::Recording(cmd_buf_data) => cmd_buf_data.trace_commands.as_mut(),
155 _ => None,
156 }
157 }
158
159 fn push_with<F: FnOnce() -> Result<ArcCommand, E>, E: Clone + Into<CommandEncoderError>>(
175 &mut self,
176 f: F,
177 ) -> Result<(), EncoderStateError> {
178 match self {
179 Self::Recording(cmd_buf_data) => {
180 match f() {
181 Ok(cmd) => cmd_buf_data.commands.push(cmd),
182 Err(err) => {
183 self.invalidate(err);
184 }
185 }
186 Ok(())
187 }
188 Self::Locked(_) => {
189 self.invalidate(EncoderStateError::Locked);
192 Ok(())
193 }
194 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
197 Self::Consumed => Err(EncoderStateError::Ended),
198 Self::Error(_) => Ok(()),
201 Self::Transitioning => unreachable!(),
202 }
203 }
204
205 fn with_buffer<
219 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
220 E: Clone + Into<CommandEncoderError>,
221 >(
222 &mut self,
223 f: F,
224 ) -> Result<(), EncoderStateError> {
225 match self {
226 Self::Recording(_) => {
227 RecordingGuard { inner: self }.record(f);
228 Ok(())
229 }
230 Self::Locked(_) => {
231 self.invalidate(EncoderStateError::Locked);
234 Ok(())
235 }
236 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
239 Self::Consumed => Err(EncoderStateError::Ended),
240 Self::Error(_) => Ok(()),
243 Self::Transitioning => unreachable!(),
244 }
245 }
246
247 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
255 &mut self,
256 f: F,
257 ) -> T {
258 match self {
259 Self::Recording(_) => RecordingGuard { inner: self }.record_as_hal_mut(f),
260 Self::Locked(_) => {
261 self.invalidate(EncoderStateError::Locked);
262 f(None)
263 }
264 Self::Finished(_) => {
265 self.invalidate(EncoderStateError::Ended);
266 f(None)
267 }
268 Self::Consumed => f(None),
269 Self::Error(_) => f(None),
270 Self::Transitioning => unreachable!(),
271 }
272 }
273
274 #[cfg(all(feature = "trace", any(feature = "serde", feature = "replay")))]
275 fn get_inner(&mut self) -> &mut CommandBufferMutable {
276 match self {
277 Self::Locked(inner) | Self::Finished(inner) | Self::Recording(inner) => inner,
278 Self::Consumed => unreachable!("command encoder is consumed"),
283 Self::Error(_) => unreachable!("passes in a trace do not store errors"),
284 Self::Transitioning => unreachable!(),
285 }
286 }
287
288 fn lock_encoder(&mut self) -> Result<(), EncoderStateError> {
294 match mem::replace(self, Self::Transitioning) {
295 Self::Recording(inner) => {
296 *self = Self::Locked(inner);
297 Ok(())
298 }
299 st @ Self::Finished(_) => {
300 *self = st;
304 Err(EncoderStateError::Ended)
305 }
306 Self::Locked(_) => Err(self.invalidate(EncoderStateError::Locked)),
307 st @ Self::Consumed => {
308 *self = st;
309 Err(EncoderStateError::Ended)
310 }
311 st @ Self::Error(_) => {
312 *self = st;
313 Err(EncoderStateError::Invalid)
314 }
315 Self::Transitioning => unreachable!(),
316 }
317 }
318
319 fn unlock_encoder(&mut self) -> Result<(), EncoderStateError> {
329 match mem::replace(self, Self::Transitioning) {
330 Self::Locked(inner) => {
331 *self = Self::Recording(inner);
332 Ok(())
333 }
334 st @ Self::Finished(_) => {
335 *self = st;
336 Err(EncoderStateError::Ended)
337 }
338 Self::Recording(_) => {
339 *self = Self::Error(EncoderStateError::Unlocked.into());
340 Err(EncoderStateError::Unlocked)
341 }
342 st @ Self::Consumed => {
343 *self = st;
344 Err(EncoderStateError::Ended)
345 }
346 st @ Self::Error(_) => {
347 *self = st;
350 Ok(())
351 }
352 Self::Transitioning => unreachable!(),
353 }
354 }
355
356 fn finish(&mut self) -> Self {
357 match mem::replace(self, Self::Consumed) {
360 Self::Recording(inner) => {
361 assert!(!inner.encoder.is_open);
363 Self::Finished(inner)
364 }
365 Self::Consumed | Self::Finished(_) => Self::Error(EncoderStateError::Ended.into()),
366 Self::Locked(_) => Self::Error(EncoderStateError::Locked.into()),
367 st @ Self::Error(_) => st,
368 Self::Transitioning => unreachable!(),
369 }
370 }
371
372 fn invalidate<E: Clone + Into<CommandEncoderError>>(&mut self, err: E) -> E {
378 let enc_err = err.clone().into();
379 api_log!("Invalidating command encoder: {enc_err:?}");
380 *self = Self::Error(enc_err);
381 err
382 }
383}
384
385pub(crate) struct RecordingGuard<'a> {
398 inner: &'a mut CommandEncoderStatus,
399}
400
401impl<'a> RecordingGuard<'a> {
402 pub(crate) fn mark_successful(self) {
403 mem::forget(self)
404 }
405
406 fn record<
407 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
408 E: Clone + Into<CommandEncoderError>,
409 >(
410 mut self,
411 f: F,
412 ) {
413 match f(&mut self) {
414 Ok(()) => self.mark_successful(),
415 Err(err) => {
416 self.inner.invalidate(err);
417 }
418 }
419 }
420
421 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
424 mut self,
425 f: F,
426 ) -> T {
427 let res = f(Some(&mut self));
428 self.mark_successful();
429 res
430 }
431}
432
433impl<'a> Drop for RecordingGuard<'a> {
434 fn drop(&mut self) {
435 if matches!(*self.inner, CommandEncoderStatus::Error(_)) {
436 return;
438 }
439 self.inner.invalidate(EncoderStateError::Invalid);
440 }
441}
442
443impl<'a> ops::Deref for RecordingGuard<'a> {
444 type Target = CommandBufferMutable;
445
446 fn deref(&self) -> &Self::Target {
447 match &*self.inner {
448 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
449 _ => unreachable!(),
450 }
451 }
452}
453
454impl<'a> ops::DerefMut for RecordingGuard<'a> {
455 fn deref_mut(&mut self) -> &mut Self::Target {
456 match self.inner {
457 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
458 _ => unreachable!(),
459 }
460 }
461}
462
463pub(crate) struct CommandEncoder {
464 pub(crate) device: Arc<Device>,
465
466 pub(crate) label: String,
467
468 pub(crate) data: Mutex<CommandEncoderStatus>,
470}
471
472crate::impl_resource_type!(CommandEncoder);
473crate::impl_labeled!(CommandEncoder);
474crate::impl_parent_device!(CommandEncoder);
475crate::impl_storage_item!(CommandEncoder);
476
477impl Drop for CommandEncoder {
478 fn drop(&mut self) {
479 resource_log!("Drop {}", self.error_ident());
480 }
481}
482
483pub(crate) struct InnerCommandEncoder {
499 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
507
508 pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
520
521 pub(crate) device: Arc<Device>,
522
523 pub(crate) is_open: bool,
530
531 pub(crate) label: String,
532}
533
534impl InnerCommandEncoder {
535 fn close_and_swap(&mut self) -> Result<(), DeviceError> {
561 assert!(self.is_open);
562 self.is_open = false;
563
564 let new =
565 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
566 self.list.insert(self.list.len() - 1, new);
567
568 Ok(())
569 }
570
571 pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> {
582 assert!(self.is_open);
583 self.is_open = false;
584
585 let new =
586 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
587 self.list.insert(0, new);
588
589 Ok(())
590 }
591
592 pub(crate) fn close(&mut self) -> Result<(), DeviceError> {
603 assert!(self.is_open);
604 self.is_open = false;
605
606 let cmd_buf =
607 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
608 self.list.push(cmd_buf);
609
610 Ok(())
611 }
612
613 fn close_if_open(&mut self) -> Result<(), DeviceError> {
624 if self.is_open {
625 self.is_open = false;
626 let cmd_buf =
627 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
628 self.list.push(cmd_buf);
629 }
630
631 Ok(())
632 }
633
634 fn open_if_closed(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
640 if !self.is_open {
641 self.is_open = true;
642 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
643 unsafe { self.raw.begin_encoding(hal_label) }
644 .map_err(|e| self.device.handle_hal_error(e))?;
645 }
646
647 Ok(self.raw.as_mut())
648 }
649
650 pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
654 if !self.is_open {
655 self.is_open = true;
656 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
657 unsafe { self.raw.begin_encoding(hal_label) }
658 .map_err(|e| self.device.handle_hal_error(e))?;
659 }
660
661 Ok(self.raw.as_mut())
662 }
663
664 pub(crate) fn open_pass(
673 &mut self,
674 label: Option<&str>,
675 ) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
676 assert!(!self.is_open);
677 self.is_open = true;
678
679 let hal_label = hal_label(label, self.device.instance_flags);
680 unsafe { self.raw.begin_encoding(hal_label) }
681 .map_err(|e| self.device.handle_hal_error(e))?;
682
683 Ok(self.raw.as_mut())
684 }
685}
686
687impl Drop for InnerCommandEncoder {
688 fn drop(&mut self) {
689 if self.is_open {
690 unsafe { self.raw.discard_encoding() };
691 }
692 unsafe {
693 self.raw.reset_all(mem::take(&mut self.list));
694 }
695 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
697 self.device.command_allocator.release_encoder(raw);
698 }
699}
700
701pub(crate) struct BakedCommands {
704 pub(crate) encoder: InnerCommandEncoder,
705 pub(crate) trackers: Tracker,
706 pub(crate) temp_resources: Vec<TempResource>,
707 pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
708 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
709 texture_memory_actions: CommandBufferTextureMemoryActions,
710}
711
712pub struct CommandBufferMutable {
714 pub(crate) encoder: InnerCommandEncoder,
719
720 pub(crate) trackers: Tracker,
722
723 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
730 texture_memory_actions: CommandBufferTextureMemoryActions,
731
732 as_actions: Vec<AsAction>,
733 temp_resources: Vec<TempResource>,
734
735 indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
736
737 pub(crate) commands: Vec<ArcCommand>,
738
739 #[cfg(feature = "trace")]
740 pub(crate) trace_commands: Option<Vec<TraceCommand>>,
741}
742
743impl CommandBufferMutable {
744 pub(crate) fn into_baked_commands(self) -> BakedCommands {
745 BakedCommands {
746 encoder: self.encoder,
747 trackers: self.trackers,
748 temp_resources: self.temp_resources,
749 indirect_draw_validation_resources: self.indirect_draw_validation_resources,
750 buffer_memory_init_actions: self.buffer_memory_init_actions,
751 texture_memory_actions: self.texture_memory_actions,
752 }
753 }
754}
755
756pub struct CommandBuffer {
762 pub(crate) device: Arc<Device>,
763 label: String,
765
766 pub(crate) data: Mutex<CommandEncoderStatus>,
768}
769
770impl Drop for CommandBuffer {
771 fn drop(&mut self) {
772 resource_log!("Drop {}", self.error_ident());
773 }
774}
775
776impl CommandEncoder {
777 pub(crate) fn new(
778 encoder: Box<dyn hal::DynCommandEncoder>,
779 device: &Arc<Device>,
780 label: &Label,
781 ) -> Self {
782 CommandEncoder {
783 device: device.clone(),
784 label: label.to_string(),
785 data: Mutex::new(
786 rank::COMMAND_BUFFER_DATA,
787 CommandEncoderStatus::Recording(CommandBufferMutable {
788 encoder: InnerCommandEncoder {
789 raw: ManuallyDrop::new(encoder),
790 list: Vec::new(),
791 device: device.clone(),
792 is_open: false,
793 label: label.to_string(),
794 },
795 trackers: Tracker::new(),
796 buffer_memory_init_actions: Default::default(),
797 texture_memory_actions: Default::default(),
798 as_actions: Default::default(),
799 temp_resources: Default::default(),
800 indirect_draw_validation_resources:
801 crate::indirect_validation::DrawResources::new(device.clone()),
802 commands: Vec::new(),
803 #[cfg(feature = "trace")]
804 trace_commands: if device.trace.lock().is_some() {
805 Some(Vec::new())
806 } else {
807 None
808 },
809 }),
810 ),
811 }
812 }
813
814 pub(crate) fn new_invalid(
815 device: &Arc<Device>,
816 label: &Label,
817 err: CommandEncoderError,
818 ) -> Self {
819 CommandEncoder {
820 device: device.clone(),
821 label: label.to_string(),
822 data: Mutex::new(rank::COMMAND_BUFFER_DATA, CommandEncoderStatus::Error(err)),
823 }
824 }
825
826 pub(crate) fn insert_barriers_from_tracker(
827 raw: &mut dyn hal::DynCommandEncoder,
828 base: &mut Tracker,
829 head: &Tracker,
830 snatch_guard: &SnatchGuard,
831 ) {
832 profiling::scope!("insert_barriers");
833
834 base.buffers.set_from_tracker(&head.buffers);
835 base.textures.set_from_tracker(&head.textures);
836
837 Self::drain_barriers(raw, base, snatch_guard);
838 }
839
840 pub(crate) fn insert_barriers_from_scope(
841 raw: &mut dyn hal::DynCommandEncoder,
842 base: &mut Tracker,
843 head: &UsageScope,
844 snatch_guard: &SnatchGuard,
845 ) {
846 profiling::scope!("insert_barriers");
847
848 base.buffers.set_from_usage_scope(&head.buffers);
849 base.textures.set_from_usage_scope(&head.textures);
850
851 Self::drain_barriers(raw, base, snatch_guard);
852 }
853
854 pub(crate) fn drain_barriers(
855 raw: &mut dyn hal::DynCommandEncoder,
856 base: &mut Tracker,
857 snatch_guard: &SnatchGuard,
858 ) {
859 profiling::scope!("drain_barriers");
860
861 let buffer_barriers = base
862 .buffers
863 .drain_transitions(snatch_guard)
864 .collect::<Vec<_>>();
865 let (transitions, textures) = base.textures.drain_transitions(snatch_guard);
866 let texture_barriers = transitions
867 .into_iter()
868 .enumerate()
869 .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))
870 .collect::<Vec<_>>();
871
872 unsafe {
873 raw.transition_buffers(&buffer_barriers);
874 raw.transition_textures(&texture_barriers);
875 }
876 }
877
878 pub(crate) fn insert_barriers_from_device_tracker(
879 raw: &mut dyn hal::DynCommandEncoder,
880 base: &mut DeviceTracker,
881 head: &Tracker,
882 snatch_guard: &SnatchGuard,
883 ) {
884 profiling::scope!("insert_barriers_from_device_tracker");
885
886 let buffer_barriers = base
887 .buffers
888 .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)
889 .collect::<Vec<_>>();
890
891 let texture_barriers = base
892 .textures
893 .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)
894 .collect::<Vec<_>>();
895
896 unsafe {
897 raw.transition_buffers(&buffer_barriers);
898 raw.transition_textures(&texture_barriers);
899 }
900 }
901
902 fn finish(
903 self: &Arc<Self>,
904 desc: &wgt::CommandBufferDescriptor<Label>,
905 ) -> (Arc<CommandBuffer>, Option<CommandEncoderError>) {
906 let mut cmd_enc_status = self.data.lock();
907
908 let res = match cmd_enc_status.finish() {
909 CommandEncoderStatus::Finished(cmd_buf_data) => Ok(cmd_buf_data),
910 CommandEncoderStatus::Error(err) => Err(err),
911 _ => unreachable!(),
912 };
913
914 let res = res.and_then(|mut cmd_buf_data| {
915 self.device.check_is_valid()?;
916 let snatch_guard = self.device.snatchable_lock.read();
917 let mut debug_scope_depth = 0;
918
919 let mut commands = mem::take(&mut cmd_buf_data.commands);
920 for command in commands.drain(..) {
921 if matches!(
922 command,
923 ArcCommand::RunRenderPass { .. } | ArcCommand::RunComputePass { .. }
924 ) {
925 let mut state = EncodingState {
930 device: &self.device,
931 raw_encoder: &mut cmd_buf_data.encoder,
932 tracker: &mut cmd_buf_data.trackers,
933 buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
934 texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
935 as_actions: &mut cmd_buf_data.as_actions,
936 temp_resources: &mut cmd_buf_data.temp_resources,
937 indirect_draw_validation_resources: &mut cmd_buf_data
938 .indirect_draw_validation_resources,
939 snatch_guard: &snatch_guard,
940 debug_scope_depth: &mut debug_scope_depth,
941 };
942
943 match command {
944 ArcCommand::RunRenderPass {
945 pass,
946 color_attachments,
947 depth_stencil_attachment,
948 timestamp_writes,
949 occlusion_query_set,
950 } => {
951 encode_render_pass(
952 &mut state,
953 pass,
954 color_attachments,
955 depth_stencil_attachment,
956 timestamp_writes,
957 occlusion_query_set,
958 )?;
959 }
960 ArcCommand::RunComputePass {
961 pass,
962 timestamp_writes,
963 } => {
964 encode_compute_pass(&mut state, pass, timestamp_writes)?;
965 }
966 _ => unreachable!(),
967 }
968 } else {
969 let raw_encoder = cmd_buf_data.encoder.open_if_closed()?;
975 let mut state = EncodingState {
976 device: &self.device,
977 raw_encoder,
978 tracker: &mut cmd_buf_data.trackers,
979 buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
980 texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
981 as_actions: &mut cmd_buf_data.as_actions,
982 temp_resources: &mut cmd_buf_data.temp_resources,
983 indirect_draw_validation_resources: &mut cmd_buf_data
984 .indirect_draw_validation_resources,
985 snatch_guard: &snatch_guard,
986 debug_scope_depth: &mut debug_scope_depth,
987 };
988 match command {
989 ArcCommand::CopyBufferToBuffer {
990 src,
991 src_offset,
992 dst,
993 dst_offset,
994 size,
995 } => {
996 copy_buffer_to_buffer(
997 &mut state, &src, src_offset, &dst, dst_offset, size,
998 )?;
999 }
1000 ArcCommand::CopyBufferToTexture { src, dst, size } => {
1001 copy_buffer_to_texture(&mut state, &src, &dst, &size)?;
1002 }
1003 ArcCommand::CopyTextureToBuffer { src, dst, size } => {
1004 copy_texture_to_buffer(&mut state, &src, &dst, &size)?;
1005 }
1006 ArcCommand::CopyTextureToTexture { src, dst, size } => {
1007 copy_texture_to_texture(&mut state, &src, &dst, &size)?;
1008 }
1009 ArcCommand::ClearBuffer { dst, offset, size } => {
1010 clear_buffer(&mut state, dst, offset, size)?;
1011 }
1012 ArcCommand::ClearTexture {
1013 dst,
1014 subresource_range,
1015 } => {
1016 clear_texture_cmd(&mut state, dst, &subresource_range)?;
1017 }
1018 ArcCommand::WriteTimestamp {
1019 query_set,
1020 query_index,
1021 } => {
1022 write_timestamp(&mut state, query_set, query_index)?;
1023 }
1024 ArcCommand::ResolveQuerySet {
1025 query_set,
1026 start_query,
1027 query_count,
1028 destination,
1029 destination_offset,
1030 } => {
1031 resolve_query_set(
1032 &mut state,
1033 query_set,
1034 start_query,
1035 query_count,
1036 destination,
1037 destination_offset,
1038 )?;
1039 }
1040 ArcCommand::PushDebugGroup(label) => {
1041 push_debug_group(&mut state, &label)?;
1042 }
1043 ArcCommand::PopDebugGroup => {
1044 pop_debug_group(&mut state)?;
1045 }
1046 ArcCommand::InsertDebugMarker(label) => {
1047 insert_debug_marker(&mut state, &label)?;
1048 }
1049 ArcCommand::BuildAccelerationStructures { blas, tlas } => {
1050 build_acceleration_structures(&mut state, blas, tlas)?;
1051 }
1052 ArcCommand::TransitionResources {
1053 buffer_transitions,
1054 texture_transitions,
1055 } => {
1056 transition_resources(
1057 &mut state,
1058 buffer_transitions,
1059 texture_transitions,
1060 )?;
1061 }
1062 ArcCommand::RunComputePass { .. } | ArcCommand::RunRenderPass { .. } => {
1063 unreachable!()
1064 }
1065 }
1066 }
1067 }
1068
1069 if debug_scope_depth > 0 {
1070 Err(CommandEncoderError::DebugGroupError(
1071 DebugGroupError::MissingPop,
1072 ))?;
1073 }
1074
1075 cmd_buf_data.encoder.close_if_open()?;
1077
1078 Ok(cmd_buf_data)
1082 });
1083
1084 let (data, error) = match res {
1085 Err(e) => {
1086 if e.is_destroyed_error() {
1087 (CommandEncoderStatus::Error(e.clone()), None)
1090 } else {
1091 (CommandEncoderStatus::Error(e.clone()), Some(e))
1092 }
1093 }
1094
1095 Ok(data) => (CommandEncoderStatus::Finished(data), None),
1096 };
1097
1098 let cmd_buf = Arc::new(CommandBuffer {
1099 device: self.device.clone(),
1100 label: desc.label.to_string(),
1101 data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),
1102 });
1103
1104 (cmd_buf, error)
1105 }
1106}
1107
1108impl CommandBuffer {
1109 pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {
1110 use CommandEncoderStatus as St;
1111 match mem::replace(
1112 &mut *self.data.lock(),
1113 CommandEncoderStatus::Error(EncoderStateError::Submitted.into()),
1114 ) {
1115 St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
1116 St::Error(err) => Err(err),
1117 St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(),
1118 }
1119 }
1120}
1121
1122crate::impl_resource_type!(CommandBuffer);
1123crate::impl_labeled!(CommandBuffer);
1124crate::impl_parent_device!(CommandBuffer);
1125crate::impl_storage_item!(CommandBuffer);
1126
1127#[doc(hidden)]
1139#[derive(Debug, Clone)]
1140#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1141pub struct BasePass<C, E> {
1142 pub label: Option<String>,
1143
1144 #[cfg_attr(feature = "serde", serde(skip, default = "Option::default"))]
1152 pub error: Option<E>,
1153
1154 pub commands: Vec<C>,
1160
1161 pub dynamic_offsets: Vec<wgt::DynamicOffset>,
1166
1167 pub string_data: Vec<u8>,
1172
1173 pub push_constant_data: Vec<u32>,
1178}
1179
1180impl<C: Clone, E: Clone> BasePass<C, E> {
1181 fn new(label: &Label) -> Self {
1182 Self {
1183 label: label.as_deref().map(str::to_owned),
1184 error: None,
1185 commands: Vec::new(),
1186 dynamic_offsets: Vec::new(),
1187 string_data: Vec::new(),
1188 push_constant_data: Vec::new(),
1189 }
1190 }
1191
1192 fn new_invalid(label: &Label, err: E) -> Self {
1193 Self {
1194 label: label.as_deref().map(str::to_owned),
1195 error: Some(err),
1196 commands: Vec::new(),
1197 dynamic_offsets: Vec::new(),
1198 string_data: Vec::new(),
1199 push_constant_data: Vec::new(),
1200 }
1201 }
1202
1203 fn take(&mut self) -> Result<BasePass<C, Infallible>, E> {
1210 match self.error.as_ref() {
1211 Some(err) => Err(err.clone()),
1212 None => Ok(BasePass {
1213 label: self.label.clone(),
1214 error: None,
1215 commands: mem::take(&mut self.commands),
1216 dynamic_offsets: mem::take(&mut self.dynamic_offsets),
1217 string_data: mem::take(&mut self.string_data),
1218 push_constant_data: mem::take(&mut self.push_constant_data),
1219 }),
1220 }
1221 }
1222}
1223
1224macro_rules! pass_base {
1247 ($pass:expr, $scope:expr $(,)?) => {
1248 match (&$pass.parent, &$pass.base.error) {
1249 (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),
1251 (&Some(_), &Some(_)) => return Ok(()),
1253 (&Some(_), &None) => &mut $pass.base,
1255 }
1256 };
1257}
1258pub(crate) use pass_base;
1259
1260macro_rules! pass_try {
1272 ($base:expr, $scope:expr, $res:expr $(,)?) => {
1273 match $res.map_pass_err($scope) {
1274 Ok(val) => val,
1275 Err(err) => {
1276 $base.error.get_or_insert(err);
1277 return Ok(());
1278 }
1279 }
1280 };
1281}
1282pub(crate) use pass_try;
1283
1284#[derive(Clone, Debug, Error)]
1289#[non_exhaustive]
1290pub enum EncoderStateError {
1291 #[error("Encoder is invalid")]
1296 Invalid,
1297
1298 #[error("Encoding must not have ended")]
1301 Ended,
1302
1303 #[error("Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
1309 Locked,
1310
1311 #[error(
1314 "Encoder is not currently locked. A pass can only be ended while the encoder is locked."
1315 )]
1316 Unlocked,
1317
1318 #[error("This command buffer has already been submitted.")]
1323 Submitted,
1324}
1325
1326impl WebGpuError for EncoderStateError {
1327 fn webgpu_error_type(&self) -> ErrorType {
1328 match self {
1329 EncoderStateError::Invalid
1330 | EncoderStateError::Ended
1331 | EncoderStateError::Locked
1332 | EncoderStateError::Unlocked
1333 | EncoderStateError::Submitted => ErrorType::Validation,
1334 }
1335 }
1336}
1337
1338#[derive(Clone, Debug, Error)]
1339#[non_exhaustive]
1340pub enum CommandEncoderError {
1341 #[error(transparent)]
1342 State(#[from] EncoderStateError),
1343 #[error(transparent)]
1344 Device(#[from] DeviceError),
1345 #[error(transparent)]
1346 InvalidResource(#[from] InvalidResourceError),
1347 #[error(transparent)]
1348 DestroyedResource(#[from] DestroyedResourceError),
1349 #[error(transparent)]
1350 ResourceUsage(#[from] ResourceUsageCompatibilityError),
1351 #[error(transparent)]
1352 DebugGroupError(#[from] DebugGroupError),
1353 #[error(transparent)]
1354 MissingFeatures(#[from] MissingFeatures),
1355 #[error(transparent)]
1356 Transfer(#[from] TransferError),
1357 #[error(transparent)]
1358 Clear(#[from] ClearError),
1359 #[error(transparent)]
1360 Query(#[from] QueryError),
1361 #[error(transparent)]
1362 BuildAccelerationStructure(#[from] BuildAccelerationStructureError),
1363 #[error(transparent)]
1364 TransitionResources(#[from] TransitionResourcesError),
1365 #[error(transparent)]
1366 ComputePass(#[from] ComputePassError),
1367 #[error(transparent)]
1368 RenderPass(#[from] RenderPassError),
1369}
1370
1371impl CommandEncoderError {
1372 fn is_destroyed_error(&self) -> bool {
1373 matches!(
1374 self,
1375 Self::DestroyedResource(_)
1376 | Self::Clear(ClearError::DestroyedResource(_))
1377 | Self::Query(QueryError::DestroyedResource(_))
1378 | Self::ComputePass(ComputePassError {
1379 inner: ComputePassErrorInner::DestroyedResource(_),
1380 ..
1381 })
1382 | Self::RenderPass(RenderPassError {
1383 inner: RenderPassErrorInner::DestroyedResource(_),
1384 ..
1385 })
1386 | Self::RenderPass(RenderPassError {
1387 inner: RenderPassErrorInner::RenderCommand(
1388 RenderCommandError::DestroyedResource(_)
1389 ),
1390 ..
1391 })
1392 | Self::RenderPass(RenderPassError {
1393 inner: RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError(
1394 BindingError::DestroyedResource(_)
1395 )),
1396 ..
1397 })
1398 )
1399 }
1400}
1401
1402impl WebGpuError for CommandEncoderError {
1403 fn webgpu_error_type(&self) -> ErrorType {
1404 let e: &dyn WebGpuError = match self {
1405 Self::Device(e) => e,
1406 Self::InvalidResource(e) => e,
1407 Self::DebugGroupError(e) => e,
1408 Self::MissingFeatures(e) => e,
1409 Self::State(e) => e,
1410 Self::DestroyedResource(e) => e,
1411 Self::Transfer(e) => e,
1412 Self::Clear(e) => e,
1413 Self::Query(e) => e,
1414 Self::BuildAccelerationStructure(e) => e,
1415 Self::TransitionResources(e) => e,
1416 Self::ResourceUsage(e) => e,
1417 Self::ComputePass(e) => e,
1418 Self::RenderPass(e) => e,
1419 };
1420 e.webgpu_error_type()
1421 }
1422}
1423
1424#[derive(Clone, Debug, Error)]
1425#[non_exhaustive]
1426pub enum DebugGroupError {
1427 #[error("Cannot pop debug group, because number of pushed debug groups is zero")]
1428 InvalidPop,
1429 #[error("A debug group was not popped before the encoder was finished")]
1430 MissingPop,
1431}
1432
1433impl WebGpuError for DebugGroupError {
1434 fn webgpu_error_type(&self) -> ErrorType {
1435 match self {
1436 Self::InvalidPop | Self::MissingPop => ErrorType::Validation,
1437 }
1438 }
1439}
1440
1441#[derive(Clone, Debug, Error)]
1442#[non_exhaustive]
1443pub enum TimestampWritesError {
1444 #[error(
1445 "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
1446 )]
1447 IndicesEqual { idx: u32 },
1448 #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
1449 IndicesMissing,
1450}
1451
1452impl WebGpuError for TimestampWritesError {
1453 fn webgpu_error_type(&self) -> ErrorType {
1454 match self {
1455 Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,
1456 }
1457 }
1458}
1459
1460impl Global {
1461 fn resolve_buffer_id(
1462 &self,
1463 buffer_id: Id<id::markers::Buffer>,
1464 ) -> Result<Arc<crate::resource::Buffer>, InvalidResourceError> {
1465 self.hub.buffers.get(buffer_id).get()
1466 }
1467
1468 fn resolve_texture_id(
1469 &self,
1470 texture_id: Id<id::markers::Texture>,
1471 ) -> Result<Arc<crate::resource::Texture>, InvalidResourceError> {
1472 self.hub.textures.get(texture_id).get()
1473 }
1474
1475 fn resolve_query_set(
1476 &self,
1477 query_set_id: Id<id::markers::QuerySet>,
1478 ) -> Result<Arc<QuerySet>, InvalidResourceError> {
1479 self.hub.query_sets.get(query_set_id).get()
1480 }
1481
1482 pub fn command_encoder_finish(
1483 &self,
1484 encoder_id: id::CommandEncoderId,
1485 desc: &wgt::CommandBufferDescriptor<Label>,
1486 id_in: Option<id::CommandBufferId>,
1487 ) -> (id::CommandBufferId, Option<CommandEncoderError>) {
1488 profiling::scope!("CommandEncoder::finish");
1489
1490 let hub = &self.hub;
1491
1492 let cmd_enc = hub.command_encoders.get(encoder_id);
1493
1494 let (cmd_buf, error) = cmd_enc.finish(desc);
1495 let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(cmd_buf);
1496
1497 (cmd_buf_id, error)
1498 }
1499
1500 pub fn command_encoder_push_debug_group(
1501 &self,
1502 encoder_id: id::CommandEncoderId,
1503 label: &str,
1504 ) -> Result<(), EncoderStateError> {
1505 profiling::scope!("CommandEncoder::push_debug_group");
1506 api_log!("CommandEncoder::push_debug_group {label}");
1507
1508 let hub = &self.hub;
1509
1510 let cmd_enc = hub.command_encoders.get(encoder_id);
1511 let mut cmd_buf_data = cmd_enc.data.lock();
1512
1513 #[cfg(feature = "trace")]
1514 if let Some(ref mut list) = cmd_buf_data.trace() {
1515 list.push(TraceCommand::PushDebugGroup(label.to_owned()));
1516 }
1517
1518 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1519 Ok(ArcCommand::PushDebugGroup(label.to_owned()))
1520 })
1521 }
1522
1523 pub fn command_encoder_insert_debug_marker(
1524 &self,
1525 encoder_id: id::CommandEncoderId,
1526 label: &str,
1527 ) -> Result<(), EncoderStateError> {
1528 profiling::scope!("CommandEncoder::insert_debug_marker");
1529 api_log!("CommandEncoder::insert_debug_marker {label}");
1530
1531 let hub = &self.hub;
1532
1533 let cmd_enc = hub.command_encoders.get(encoder_id);
1534 let mut cmd_buf_data = cmd_enc.data.lock();
1535
1536 #[cfg(feature = "trace")]
1537 if let Some(ref mut list) = cmd_buf_data.trace() {
1538 list.push(TraceCommand::InsertDebugMarker(label.to_owned()));
1539 }
1540
1541 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1542 Ok(ArcCommand::InsertDebugMarker(label.to_owned()))
1543 })
1544 }
1545
1546 pub fn command_encoder_pop_debug_group(
1547 &self,
1548 encoder_id: id::CommandEncoderId,
1549 ) -> Result<(), EncoderStateError> {
1550 profiling::scope!("CommandEncoder::pop_debug_marker");
1551 api_log!("CommandEncoder::pop_debug_group");
1552
1553 let hub = &self.hub;
1554
1555 let cmd_enc = hub.command_encoders.get(encoder_id);
1556 let mut cmd_buf_data = cmd_enc.data.lock();
1557
1558 #[cfg(feature = "trace")]
1559 if let Some(ref mut list) = cmd_buf_data.trace() {
1560 list.push(TraceCommand::PopDebugGroup);
1561 }
1562
1563 cmd_buf_data
1564 .push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::PopDebugGroup) })
1565 }
1566
1567 fn validate_pass_timestamp_writes<E>(
1568 device: &Device,
1569 query_sets: &Storage<Fallible<QuerySet>>,
1570 timestamp_writes: &PassTimestampWrites,
1571 ) -> Result<ArcPassTimestampWrites, E>
1572 where
1573 E: From<TimestampWritesError>
1574 + From<QueryUseError>
1575 + From<DeviceError>
1576 + From<MissingFeatures>
1577 + From<InvalidResourceError>,
1578 {
1579 let &PassTimestampWrites {
1580 query_set,
1581 beginning_of_pass_write_index,
1582 end_of_pass_write_index,
1583 } = timestamp_writes;
1584
1585 device.require_features(wgt::Features::TIMESTAMP_QUERY)?;
1586
1587 let query_set = query_sets.get(query_set).get()?;
1588
1589 query_set.same_device(device)?;
1590
1591 for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
1592 .into_iter()
1593 .flatten()
1594 {
1595 query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
1596 }
1597
1598 if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
1599 if begin == end {
1600 return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());
1601 }
1602 }
1603
1604 if beginning_of_pass_write_index
1605 .or(end_of_pass_write_index)
1606 .is_none()
1607 {
1608 return Err(TimestampWritesError::IndicesMissing.into());
1609 }
1610
1611 Ok(ArcPassTimestampWrites {
1612 query_set,
1613 beginning_of_pass_write_index,
1614 end_of_pass_write_index,
1615 })
1616 }
1617}
1618
1619pub(crate) fn push_debug_group(
1620 state: &mut EncodingState,
1621 label: &str,
1622) -> Result<(), CommandEncoderError> {
1623 *state.debug_scope_depth += 1;
1624
1625 if !state
1626 .device
1627 .instance_flags
1628 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1629 {
1630 unsafe { state.raw_encoder.begin_debug_marker(label) };
1631 }
1632
1633 Ok(())
1634}
1635
1636pub(crate) fn insert_debug_marker(
1637 state: &mut EncodingState,
1638 label: &str,
1639) -> Result<(), CommandEncoderError> {
1640 if !state
1641 .device
1642 .instance_flags
1643 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1644 {
1645 unsafe { state.raw_encoder.insert_debug_marker(label) };
1646 }
1647
1648 Ok(())
1649}
1650
1651pub(crate) fn pop_debug_group(state: &mut EncodingState) -> Result<(), CommandEncoderError> {
1652 if *state.debug_scope_depth == 0 {
1653 return Err(DebugGroupError::InvalidPop.into());
1654 }
1655 *state.debug_scope_depth -= 1;
1656
1657 if !state
1658 .device
1659 .instance_flags
1660 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1661 {
1662 unsafe { state.raw_encoder.end_debug_marker() };
1663 }
1664
1665 Ok(())
1666}
1667
1668fn push_constant_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
1669where
1670 PushFn: FnMut(u32, &[u32]),
1671{
1672 let mut count_words = 0_u32;
1673 let size_words = size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT;
1674 while count_words < size_words {
1675 let count_bytes = count_words * wgt::PUSH_CONSTANT_ALIGNMENT;
1676 let size_to_write_words =
1677 (size_words - count_words).min(PUSH_CONSTANT_CLEAR_ARRAY.len() as u32);
1678
1679 push_fn(
1680 offset + count_bytes,
1681 &PUSH_CONSTANT_CLEAR_ARRAY[0..size_to_write_words as usize],
1682 );
1683
1684 count_words += size_to_write_words;
1685 }
1686}
1687
1688#[derive(Debug, Copy, Clone)]
1689struct StateChange<T> {
1690 last_state: Option<T>,
1691}
1692
1693impl<T: Copy + PartialEq> StateChange<T> {
1694 fn new() -> Self {
1695 Self { last_state: None }
1696 }
1697 fn set_and_check_redundant(&mut self, new_state: T) -> bool {
1698 let already_set = self.last_state == Some(new_state);
1699 self.last_state = Some(new_state);
1700 already_set
1701 }
1702 fn reset(&mut self) {
1703 self.last_state = None;
1704 }
1705}
1706
1707impl<T: Copy + PartialEq> Default for StateChange<T> {
1708 fn default() -> Self {
1709 Self::new()
1710 }
1711}
1712
1713#[derive(Debug)]
1714struct BindGroupStateChange {
1715 last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],
1716}
1717
1718impl BindGroupStateChange {
1719 fn new() -> Self {
1720 Self {
1721 last_states: [StateChange::new(); hal::MAX_BIND_GROUPS],
1722 }
1723 }
1724
1725 fn set_and_check_redundant(
1726 &mut self,
1727 bind_group_id: Option<id::BindGroupId>,
1728 index: u32,
1729 dynamic_offsets: &mut Vec<u32>,
1730 offsets: &[wgt::DynamicOffset],
1731 ) -> bool {
1732 if offsets.is_empty() {
1734 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1737 if current_bind_group.set_and_check_redundant(bind_group_id) {
1739 return true;
1740 }
1741 }
1742 } else {
1743 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1747 current_bind_group.reset();
1748 }
1749 dynamic_offsets.extend_from_slice(offsets);
1750 }
1751 false
1752 }
1753 fn reset(&mut self) {
1754 self.last_states = [StateChange::new(); hal::MAX_BIND_GROUPS];
1755 }
1756}
1757
1758impl Default for BindGroupStateChange {
1759 fn default() -> Self {
1760 Self::new()
1761 }
1762}
1763
1764trait MapPassErr<T> {
1766 fn map_pass_err(self, scope: PassErrorScope) -> T;
1767}
1768
1769impl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>
1770where
1771 E: MapPassErr<F>,
1772{
1773 fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {
1774 self.map_err(|err| err.map_pass_err(scope))
1775 }
1776}
1777
1778impl MapPassErr<PassStateError> for EncoderStateError {
1779 fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {
1780 PassStateError { scope, inner: self }
1781 }
1782}
1783
1784#[derive(Clone, Copy, Debug)]
1785pub enum DrawKind {
1786 Draw,
1787 DrawIndirect,
1788 MultiDrawIndirect,
1789 MultiDrawIndirectCount,
1790}
1791
1792#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1794#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1795pub enum DrawCommandFamily {
1796 Draw,
1797 DrawIndexed,
1798 DrawMeshTasks,
1799}
1800
1801#[derive(Clone, Copy, Debug, Error)]
1811pub enum PassErrorScope {
1812 #[error("In a bundle parameter")]
1815 Bundle,
1816 #[error("In a pass parameter")]
1817 Pass,
1818 #[error("In a set_bind_group command")]
1819 SetBindGroup,
1820 #[error("In a set_pipeline command")]
1821 SetPipelineRender,
1822 #[error("In a set_pipeline command")]
1823 SetPipelineCompute,
1824 #[error("In a set_push_constant command")]
1825 SetPushConstant,
1826 #[error("In a set_vertex_buffer command")]
1827 SetVertexBuffer,
1828 #[error("In a set_index_buffer command")]
1829 SetIndexBuffer,
1830 #[error("In a set_blend_constant command")]
1831 SetBlendConstant,
1832 #[error("In a set_stencil_reference command")]
1833 SetStencilReference,
1834 #[error("In a set_viewport command")]
1835 SetViewport,
1836 #[error("In a set_scissor_rect command")]
1837 SetScissorRect,
1838 #[error("In a draw command, kind: {kind:?}")]
1839 Draw {
1840 kind: DrawKind,
1841 family: DrawCommandFamily,
1842 },
1843 #[error("In a write_timestamp command")]
1844 WriteTimestamp,
1845 #[error("In a begin_occlusion_query command")]
1846 BeginOcclusionQuery,
1847 #[error("In a end_occlusion_query command")]
1848 EndOcclusionQuery,
1849 #[error("In a begin_pipeline_statistics_query command")]
1850 BeginPipelineStatisticsQuery,
1851 #[error("In a end_pipeline_statistics_query command")]
1852 EndPipelineStatisticsQuery,
1853 #[error("In a execute_bundle command")]
1854 ExecuteBundle,
1855 #[error("In a dispatch command, indirect:{indirect}")]
1856 Dispatch { indirect: bool },
1857 #[error("In a push_debug_group command")]
1858 PushDebugGroup,
1859 #[error("In a pop_debug_group command")]
1860 PopDebugGroup,
1861 #[error("In a insert_debug_marker command")]
1862 InsertDebugMarker,
1863}
1864
1865#[derive(Clone, Debug, Error)]
1867#[error("{scope}")]
1868pub struct PassStateError {
1869 pub scope: PassErrorScope,
1870 #[source]
1871 pub(super) inner: EncoderStateError,
1872}
1873
1874impl WebGpuError for PassStateError {
1875 fn webgpu_error_type(&self) -> ErrorType {
1876 let Self { scope: _, inner } = self;
1877 inner.webgpu_error_type()
1878 }
1879}