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) => Self::Finished(inner),
361 Self::Consumed | Self::Finished(_) => Self::Error(EncoderStateError::Ended.into()),
362 Self::Locked(_) => Self::Error(EncoderStateError::Locked.into()),
363 st @ Self::Error(_) => st,
364 Self::Transitioning => unreachable!(),
365 }
366 }
367
368 fn invalidate<E: Clone + Into<CommandEncoderError>>(&mut self, err: E) -> E {
374 let enc_err = err.clone().into();
375 api_log!("Invalidating command encoder: {enc_err:?}");
376 *self = Self::Error(enc_err);
377 err
378 }
379}
380
381pub(crate) struct RecordingGuard<'a> {
394 inner: &'a mut CommandEncoderStatus,
395}
396
397impl<'a> RecordingGuard<'a> {
398 pub(crate) fn mark_successful(self) {
399 mem::forget(self)
400 }
401
402 fn record<
403 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
404 E: Clone + Into<CommandEncoderError>,
405 >(
406 mut self,
407 f: F,
408 ) {
409 match f(&mut self) {
410 Ok(()) => self.mark_successful(),
411 Err(err) => {
412 self.inner.invalidate(err);
413 }
414 }
415 }
416
417 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
420 mut self,
421 f: F,
422 ) -> T {
423 let res = f(Some(&mut self));
424 self.mark_successful();
425 res
426 }
427}
428
429impl<'a> Drop for RecordingGuard<'a> {
430 fn drop(&mut self) {
431 if matches!(*self.inner, CommandEncoderStatus::Error(_)) {
432 return;
434 }
435 self.inner.invalidate(EncoderStateError::Invalid);
436 }
437}
438
439impl<'a> ops::Deref for RecordingGuard<'a> {
440 type Target = CommandBufferMutable;
441
442 fn deref(&self) -> &Self::Target {
443 match &*self.inner {
444 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
445 _ => unreachable!(),
446 }
447 }
448}
449
450impl<'a> ops::DerefMut for RecordingGuard<'a> {
451 fn deref_mut(&mut self) -> &mut Self::Target {
452 match self.inner {
453 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
454 _ => unreachable!(),
455 }
456 }
457}
458
459pub(crate) struct CommandEncoder {
460 pub(crate) device: Arc<Device>,
461
462 pub(crate) label: String,
463
464 pub(crate) data: Mutex<CommandEncoderStatus>,
466}
467
468crate::impl_resource_type!(CommandEncoder);
469crate::impl_labeled!(CommandEncoder);
470crate::impl_parent_device!(CommandEncoder);
471crate::impl_storage_item!(CommandEncoder);
472
473impl Drop for CommandEncoder {
474 fn drop(&mut self) {
475 resource_log!("Drop {}", self.error_ident());
476 }
477}
478
479pub(crate) struct InnerCommandEncoder {
495 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
503
504 pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
516
517 pub(crate) device: Arc<Device>,
518
519 pub(crate) is_open: bool,
526
527 pub(crate) label: String,
528}
529
530impl InnerCommandEncoder {
531 fn close_and_swap(&mut self) -> Result<(), DeviceError> {
557 assert!(self.is_open);
558 self.is_open = false;
559
560 let new =
561 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
562 self.list.insert(self.list.len() - 1, new);
563
564 Ok(())
565 }
566
567 pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> {
578 assert!(self.is_open);
579 self.is_open = false;
580
581 let new =
582 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
583 self.list.insert(0, new);
584
585 Ok(())
586 }
587
588 pub(crate) fn close(&mut self) -> Result<(), DeviceError> {
599 assert!(self.is_open);
600 self.is_open = false;
601
602 let cmd_buf =
603 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
604 self.list.push(cmd_buf);
605
606 Ok(())
607 }
608
609 fn close_if_open(&mut self) -> Result<(), DeviceError> {
620 if self.is_open {
621 self.is_open = false;
622 let cmd_buf =
623 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
624 self.list.push(cmd_buf);
625 }
626
627 Ok(())
628 }
629
630 fn open_if_closed(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
636 if !self.is_open {
637 self.is_open = true;
638 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
639 unsafe { self.raw.begin_encoding(hal_label) }
640 .map_err(|e| self.device.handle_hal_error(e))?;
641 }
642
643 Ok(self.raw.as_mut())
644 }
645
646 pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
650 if !self.is_open {
651 self.is_open = true;
652 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
653 unsafe { self.raw.begin_encoding(hal_label) }
654 .map_err(|e| self.device.handle_hal_error(e))?;
655 }
656
657 Ok(self.raw.as_mut())
658 }
659
660 pub(crate) fn open_pass(
669 &mut self,
670 label: Option<&str>,
671 ) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
672 assert!(!self.is_open);
673 self.is_open = true;
674
675 let hal_label = hal_label(label, self.device.instance_flags);
676 unsafe { self.raw.begin_encoding(hal_label) }
677 .map_err(|e| self.device.handle_hal_error(e))?;
678
679 Ok(self.raw.as_mut())
680 }
681}
682
683impl Drop for InnerCommandEncoder {
684 fn drop(&mut self) {
685 if self.is_open {
686 unsafe { self.raw.discard_encoding() };
687 }
688 unsafe {
689 self.raw.reset_all(mem::take(&mut self.list));
690 }
691 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
693 self.device.command_allocator.release_encoder(raw);
694 }
695}
696
697pub(crate) struct BakedCommands {
700 pub(crate) encoder: InnerCommandEncoder,
701 pub(crate) trackers: Tracker,
702 pub(crate) temp_resources: Vec<TempResource>,
703 pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
704 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
705 texture_memory_actions: CommandBufferTextureMemoryActions,
706}
707
708pub struct CommandBufferMutable {
710 pub(crate) encoder: InnerCommandEncoder,
715
716 pub(crate) trackers: Tracker,
718
719 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
726 texture_memory_actions: CommandBufferTextureMemoryActions,
727
728 as_actions: Vec<AsAction>,
729 temp_resources: Vec<TempResource>,
730
731 indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
732
733 pub(crate) commands: Vec<ArcCommand>,
734
735 #[cfg(feature = "trace")]
736 pub(crate) trace_commands: Option<Vec<TraceCommand>>,
737}
738
739impl CommandBufferMutable {
740 pub(crate) fn into_baked_commands(self) -> BakedCommands {
741 BakedCommands {
742 encoder: self.encoder,
743 trackers: self.trackers,
744 temp_resources: self.temp_resources,
745 indirect_draw_validation_resources: self.indirect_draw_validation_resources,
746 buffer_memory_init_actions: self.buffer_memory_init_actions,
747 texture_memory_actions: self.texture_memory_actions,
748 }
749 }
750}
751
752pub struct CommandBuffer {
758 pub(crate) device: Arc<Device>,
759 label: String,
761
762 pub(crate) data: Mutex<CommandEncoderStatus>,
764}
765
766impl Drop for CommandBuffer {
767 fn drop(&mut self) {
768 resource_log!("Drop {}", self.error_ident());
769 }
770}
771
772impl CommandEncoder {
773 pub(crate) fn new(
774 encoder: Box<dyn hal::DynCommandEncoder>,
775 device: &Arc<Device>,
776 label: &Label,
777 ) -> Self {
778 CommandEncoder {
779 device: device.clone(),
780 label: label.to_string(),
781 data: Mutex::new(
782 rank::COMMAND_BUFFER_DATA,
783 CommandEncoderStatus::Recording(CommandBufferMutable {
784 encoder: InnerCommandEncoder {
785 raw: ManuallyDrop::new(encoder),
786 list: Vec::new(),
787 device: device.clone(),
788 is_open: false,
789 label: label.to_string(),
790 },
791 trackers: Tracker::new(),
792 buffer_memory_init_actions: Default::default(),
793 texture_memory_actions: Default::default(),
794 as_actions: Default::default(),
795 temp_resources: Default::default(),
796 indirect_draw_validation_resources:
797 crate::indirect_validation::DrawResources::new(device.clone()),
798 commands: Vec::new(),
799 #[cfg(feature = "trace")]
800 trace_commands: if device.trace.lock().is_some() {
801 Some(Vec::new())
802 } else {
803 None
804 },
805 }),
806 ),
807 }
808 }
809
810 pub(crate) fn new_invalid(
811 device: &Arc<Device>,
812 label: &Label,
813 err: CommandEncoderError,
814 ) -> Self {
815 CommandEncoder {
816 device: device.clone(),
817 label: label.to_string(),
818 data: Mutex::new(rank::COMMAND_BUFFER_DATA, CommandEncoderStatus::Error(err)),
819 }
820 }
821
822 pub(crate) fn insert_barriers_from_tracker(
823 raw: &mut dyn hal::DynCommandEncoder,
824 base: &mut Tracker,
825 head: &Tracker,
826 snatch_guard: &SnatchGuard,
827 ) {
828 profiling::scope!("insert_barriers");
829
830 base.buffers.set_from_tracker(&head.buffers);
831 base.textures.set_from_tracker(&head.textures);
832
833 Self::drain_barriers(raw, base, snatch_guard);
834 }
835
836 pub(crate) fn insert_barriers_from_scope(
837 raw: &mut dyn hal::DynCommandEncoder,
838 base: &mut Tracker,
839 head: &UsageScope,
840 snatch_guard: &SnatchGuard,
841 ) {
842 profiling::scope!("insert_barriers");
843
844 base.buffers.set_from_usage_scope(&head.buffers);
845 base.textures.set_from_usage_scope(&head.textures);
846
847 Self::drain_barriers(raw, base, snatch_guard);
848 }
849
850 pub(crate) fn drain_barriers(
851 raw: &mut dyn hal::DynCommandEncoder,
852 base: &mut Tracker,
853 snatch_guard: &SnatchGuard,
854 ) {
855 profiling::scope!("drain_barriers");
856
857 let buffer_barriers = base
858 .buffers
859 .drain_transitions(snatch_guard)
860 .collect::<Vec<_>>();
861 let (transitions, textures) = base.textures.drain_transitions(snatch_guard);
862 let texture_barriers = transitions
863 .into_iter()
864 .enumerate()
865 .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))
866 .collect::<Vec<_>>();
867
868 unsafe {
869 raw.transition_buffers(&buffer_barriers);
870 raw.transition_textures(&texture_barriers);
871 }
872 }
873
874 pub(crate) fn insert_barriers_from_device_tracker(
875 raw: &mut dyn hal::DynCommandEncoder,
876 base: &mut DeviceTracker,
877 head: &Tracker,
878 snatch_guard: &SnatchGuard,
879 ) {
880 profiling::scope!("insert_barriers_from_device_tracker");
881
882 let buffer_barriers = base
883 .buffers
884 .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)
885 .collect::<Vec<_>>();
886
887 let texture_barriers = base
888 .textures
889 .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)
890 .collect::<Vec<_>>();
891
892 unsafe {
893 raw.transition_buffers(&buffer_barriers);
894 raw.transition_textures(&texture_barriers);
895 }
896 }
897}
898
899impl CommandBuffer {
900 pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {
901 use CommandEncoderStatus as St;
902 match mem::replace(
903 &mut *self.data.lock(),
904 CommandEncoderStatus::Error(EncoderStateError::Submitted.into()),
905 ) {
906 St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
907 St::Error(err) => Err(err),
908 St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(),
909 }
910 }
911}
912
913crate::impl_resource_type!(CommandBuffer);
914crate::impl_labeled!(CommandBuffer);
915crate::impl_parent_device!(CommandBuffer);
916crate::impl_storage_item!(CommandBuffer);
917
918#[doc(hidden)]
930#[derive(Debug, Clone)]
931#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
932pub struct BasePass<C, E> {
933 pub label: Option<String>,
934
935 #[cfg_attr(feature = "serde", serde(skip, default = "Option::default"))]
943 pub error: Option<E>,
944
945 pub commands: Vec<C>,
951
952 pub dynamic_offsets: Vec<wgt::DynamicOffset>,
957
958 pub string_data: Vec<u8>,
963
964 pub push_constant_data: Vec<u32>,
969}
970
971impl<C: Clone, E: Clone> BasePass<C, E> {
972 fn new(label: &Label) -> Self {
973 Self {
974 label: label.as_deref().map(str::to_owned),
975 error: None,
976 commands: Vec::new(),
977 dynamic_offsets: Vec::new(),
978 string_data: Vec::new(),
979 push_constant_data: Vec::new(),
980 }
981 }
982
983 fn new_invalid(label: &Label, err: E) -> Self {
984 Self {
985 label: label.as_deref().map(str::to_owned),
986 error: Some(err),
987 commands: Vec::new(),
988 dynamic_offsets: Vec::new(),
989 string_data: Vec::new(),
990 push_constant_data: Vec::new(),
991 }
992 }
993
994 fn take(&mut self) -> Result<BasePass<C, Infallible>, E> {
1001 match self.error.as_ref() {
1002 Some(err) => Err(err.clone()),
1003 None => Ok(BasePass {
1004 label: self.label.clone(),
1005 error: None,
1006 commands: mem::take(&mut self.commands),
1007 dynamic_offsets: mem::take(&mut self.dynamic_offsets),
1008 string_data: mem::take(&mut self.string_data),
1009 push_constant_data: mem::take(&mut self.push_constant_data),
1010 }),
1011 }
1012 }
1013}
1014
1015macro_rules! pass_base {
1038 ($pass:expr, $scope:expr $(,)?) => {
1039 match (&$pass.parent, &$pass.base.error) {
1040 (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),
1042 (&Some(_), &Some(_)) => return Ok(()),
1044 (&Some(_), &None) => &mut $pass.base,
1046 }
1047 };
1048}
1049pub(crate) use pass_base;
1050
1051macro_rules! pass_try {
1063 ($base:expr, $scope:expr, $res:expr $(,)?) => {
1064 match $res.map_pass_err($scope) {
1065 Ok(val) => val,
1066 Err(err) => {
1067 $base.error.get_or_insert(err);
1068 return Ok(());
1069 }
1070 }
1071 };
1072}
1073pub(crate) use pass_try;
1074
1075#[derive(Clone, Debug, Error)]
1080#[non_exhaustive]
1081pub enum EncoderStateError {
1082 #[error("Encoder is invalid")]
1087 Invalid,
1088
1089 #[error("Encoding must not have ended")]
1092 Ended,
1093
1094 #[error("Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
1100 Locked,
1101
1102 #[error(
1105 "Encoder is not currently locked. A pass can only be ended while the encoder is locked."
1106 )]
1107 Unlocked,
1108
1109 #[error("This command buffer has already been submitted.")]
1114 Submitted,
1115}
1116
1117impl WebGpuError for EncoderStateError {
1118 fn webgpu_error_type(&self) -> ErrorType {
1119 match self {
1120 EncoderStateError::Invalid
1121 | EncoderStateError::Ended
1122 | EncoderStateError::Locked
1123 | EncoderStateError::Unlocked
1124 | EncoderStateError::Submitted => ErrorType::Validation,
1125 }
1126 }
1127}
1128
1129#[derive(Clone, Debug, Error)]
1130#[non_exhaustive]
1131pub enum CommandEncoderError {
1132 #[error(transparent)]
1133 State(#[from] EncoderStateError),
1134 #[error(transparent)]
1135 Device(#[from] DeviceError),
1136 #[error(transparent)]
1137 InvalidResource(#[from] InvalidResourceError),
1138 #[error(transparent)]
1139 DestroyedResource(#[from] DestroyedResourceError),
1140 #[error(transparent)]
1141 ResourceUsage(#[from] ResourceUsageCompatibilityError),
1142 #[error(transparent)]
1143 DebugGroupError(#[from] DebugGroupError),
1144 #[error(transparent)]
1145 MissingFeatures(#[from] MissingFeatures),
1146 #[error(transparent)]
1147 Transfer(#[from] TransferError),
1148 #[error(transparent)]
1149 Clear(#[from] ClearError),
1150 #[error(transparent)]
1151 Query(#[from] QueryError),
1152 #[error(transparent)]
1153 BuildAccelerationStructure(#[from] BuildAccelerationStructureError),
1154 #[error(transparent)]
1155 TransitionResources(#[from] TransitionResourcesError),
1156 #[error(transparent)]
1157 ComputePass(#[from] ComputePassError),
1158 #[error(transparent)]
1159 RenderPass(#[from] RenderPassError),
1160}
1161
1162impl CommandEncoderError {
1163 fn is_destroyed_error(&self) -> bool {
1164 matches!(
1165 self,
1166 Self::DestroyedResource(_)
1167 | Self::Clear(ClearError::DestroyedResource(_))
1168 | Self::Query(QueryError::DestroyedResource(_))
1169 | Self::ComputePass(ComputePassError {
1170 inner: ComputePassErrorInner::DestroyedResource(_),
1171 ..
1172 })
1173 | Self::RenderPass(RenderPassError {
1174 inner: RenderPassErrorInner::DestroyedResource(_),
1175 ..
1176 })
1177 | Self::RenderPass(RenderPassError {
1178 inner: RenderPassErrorInner::RenderCommand(
1179 RenderCommandError::DestroyedResource(_)
1180 ),
1181 ..
1182 })
1183 | Self::RenderPass(RenderPassError {
1184 inner: RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError(
1185 BindingError::DestroyedResource(_)
1186 )),
1187 ..
1188 })
1189 )
1190 }
1191}
1192
1193impl WebGpuError for CommandEncoderError {
1194 fn webgpu_error_type(&self) -> ErrorType {
1195 let e: &dyn WebGpuError = match self {
1196 Self::Device(e) => e,
1197 Self::InvalidResource(e) => e,
1198 Self::DebugGroupError(e) => e,
1199 Self::MissingFeatures(e) => e,
1200 Self::State(e) => e,
1201 Self::DestroyedResource(e) => e,
1202 Self::Transfer(e) => e,
1203 Self::Clear(e) => e,
1204 Self::Query(e) => e,
1205 Self::BuildAccelerationStructure(e) => e,
1206 Self::TransitionResources(e) => e,
1207 Self::ResourceUsage(e) => e,
1208 Self::ComputePass(e) => e,
1209 Self::RenderPass(e) => e,
1210 };
1211 e.webgpu_error_type()
1212 }
1213}
1214
1215#[derive(Clone, Debug, Error)]
1216#[non_exhaustive]
1217pub enum DebugGroupError {
1218 #[error("Cannot pop debug group, because number of pushed debug groups is zero")]
1219 InvalidPop,
1220 #[error("A debug group was not popped before the encoder was finished")]
1221 MissingPop,
1222}
1223
1224impl WebGpuError for DebugGroupError {
1225 fn webgpu_error_type(&self) -> ErrorType {
1226 match self {
1227 Self::InvalidPop | Self::MissingPop => ErrorType::Validation,
1228 }
1229 }
1230}
1231
1232#[derive(Clone, Debug, Error)]
1233#[non_exhaustive]
1234pub enum TimestampWritesError {
1235 #[error(
1236 "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
1237 )]
1238 IndicesEqual { idx: u32 },
1239 #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
1240 IndicesMissing,
1241}
1242
1243impl WebGpuError for TimestampWritesError {
1244 fn webgpu_error_type(&self) -> ErrorType {
1245 match self {
1246 Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,
1247 }
1248 }
1249}
1250
1251impl Global {
1252 fn resolve_buffer_id(
1253 &self,
1254 buffer_id: Id<id::markers::Buffer>,
1255 ) -> Result<Arc<crate::resource::Buffer>, InvalidResourceError> {
1256 self.hub.buffers.get(buffer_id).get()
1257 }
1258
1259 fn resolve_texture_id(
1260 &self,
1261 texture_id: Id<id::markers::Texture>,
1262 ) -> Result<Arc<crate::resource::Texture>, InvalidResourceError> {
1263 self.hub.textures.get(texture_id).get()
1264 }
1265
1266 fn resolve_query_set(
1267 &self,
1268 query_set_id: Id<id::markers::QuerySet>,
1269 ) -> Result<Arc<QuerySet>, InvalidResourceError> {
1270 self.hub.query_sets.get(query_set_id).get()
1271 }
1272
1273 pub fn command_encoder_finish(
1274 &self,
1275 encoder_id: id::CommandEncoderId,
1276 desc: &wgt::CommandBufferDescriptor<Label>,
1277 id_in: Option<id::CommandBufferId>,
1278 ) -> (id::CommandBufferId, Option<CommandEncoderError>) {
1279 profiling::scope!("CommandEncoder::finish");
1280
1281 let hub = &self.hub;
1282
1283 let cmd_enc = hub.command_encoders.get(encoder_id);
1284 let mut cmd_enc_status = cmd_enc.data.lock();
1285
1286 let res = match cmd_enc_status.finish() {
1287 CommandEncoderStatus::Finished(cmd_buf_data) => Ok(cmd_buf_data),
1288 CommandEncoderStatus::Error(err) => Err(err),
1289 _ => unreachable!(),
1290 };
1291
1292 let res = res.and_then(|mut cmd_buf_data| {
1293 cmd_enc.device.check_is_valid()?;
1294 let snatch_guard = cmd_enc.device.snatchable_lock.read();
1295 let mut debug_scope_depth = 0;
1296
1297 let mut commands = mem::take(&mut cmd_buf_data.commands);
1298 for command in commands.drain(..) {
1299 if matches!(
1300 command,
1301 ArcCommand::RunRenderPass { .. } | ArcCommand::RunComputePass { .. }
1302 ) {
1303 let mut state = EncodingState {
1308 device: &cmd_enc.device,
1309 raw_encoder: &mut cmd_buf_data.encoder,
1310 tracker: &mut cmd_buf_data.trackers,
1311 buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1312 texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1313 as_actions: &mut cmd_buf_data.as_actions,
1314 temp_resources: &mut cmd_buf_data.temp_resources,
1315 indirect_draw_validation_resources: &mut cmd_buf_data
1316 .indirect_draw_validation_resources,
1317 snatch_guard: &snatch_guard,
1318 debug_scope_depth: &mut debug_scope_depth,
1319 };
1320
1321 match command {
1322 ArcCommand::RunRenderPass {
1323 pass,
1324 color_attachments,
1325 depth_stencil_attachment,
1326 timestamp_writes,
1327 occlusion_query_set,
1328 } => {
1329 encode_render_pass(
1330 &mut state,
1331 pass,
1332 color_attachments,
1333 depth_stencil_attachment,
1334 timestamp_writes,
1335 occlusion_query_set,
1336 )?;
1337 }
1338 ArcCommand::RunComputePass {
1339 pass,
1340 timestamp_writes,
1341 } => {
1342 encode_compute_pass(&mut state, pass, timestamp_writes)?;
1343 }
1344 _ => unreachable!(),
1345 }
1346 } else {
1347 let raw_encoder = cmd_buf_data.encoder.open_if_closed()?;
1353 let mut state = EncodingState {
1354 device: &cmd_enc.device,
1355 raw_encoder,
1356 tracker: &mut cmd_buf_data.trackers,
1357 buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1358 texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1359 as_actions: &mut cmd_buf_data.as_actions,
1360 temp_resources: &mut cmd_buf_data.temp_resources,
1361 indirect_draw_validation_resources: &mut cmd_buf_data
1362 .indirect_draw_validation_resources,
1363 snatch_guard: &snatch_guard,
1364 debug_scope_depth: &mut debug_scope_depth,
1365 };
1366 match command {
1367 ArcCommand::CopyBufferToBuffer {
1368 src,
1369 src_offset,
1370 dst,
1371 dst_offset,
1372 size,
1373 } => {
1374 copy_buffer_to_buffer(
1375 &mut state, &src, src_offset, &dst, dst_offset, size,
1376 )?;
1377 }
1378 ArcCommand::CopyBufferToTexture { src, dst, size } => {
1379 copy_buffer_to_texture(&mut state, &src, &dst, &size)?;
1380 }
1381 ArcCommand::CopyTextureToBuffer { src, dst, size } => {
1382 copy_texture_to_buffer(&mut state, &src, &dst, &size)?;
1383 }
1384 ArcCommand::CopyTextureToTexture { src, dst, size } => {
1385 copy_texture_to_texture(&mut state, &src, &dst, &size)?;
1386 }
1387 ArcCommand::ClearBuffer { dst, offset, size } => {
1388 clear_buffer(&mut state, dst, offset, size)?;
1389 }
1390 ArcCommand::ClearTexture {
1391 dst,
1392 subresource_range,
1393 } => {
1394 clear_texture_cmd(&mut state, dst, &subresource_range)?;
1395 }
1396 ArcCommand::WriteTimestamp {
1397 query_set,
1398 query_index,
1399 } => {
1400 write_timestamp(&mut state, query_set, query_index)?;
1401 }
1402 ArcCommand::ResolveQuerySet {
1403 query_set,
1404 start_query,
1405 query_count,
1406 destination,
1407 destination_offset,
1408 } => {
1409 resolve_query_set(
1410 &mut state,
1411 query_set,
1412 start_query,
1413 query_count,
1414 destination,
1415 destination_offset,
1416 )?;
1417 }
1418 ArcCommand::PushDebugGroup(label) => {
1419 push_debug_group(&mut state, &label)?;
1420 }
1421 ArcCommand::PopDebugGroup => {
1422 pop_debug_group(&mut state)?;
1423 }
1424 ArcCommand::InsertDebugMarker(label) => {
1425 insert_debug_marker(&mut state, &label)?;
1426 }
1427 ArcCommand::BuildAccelerationStructures { blas, tlas } => {
1428 build_acceleration_structures(&mut state, blas, tlas)?;
1429 }
1430 ArcCommand::TransitionResources {
1431 buffer_transitions,
1432 texture_transitions,
1433 } => {
1434 transition_resources(
1435 &mut state,
1436 buffer_transitions,
1437 texture_transitions,
1438 )?;
1439 }
1440 ArcCommand::RunComputePass { .. } | ArcCommand::RunRenderPass { .. } => {
1441 unreachable!()
1442 }
1443 }
1444 }
1445 }
1446
1447 if debug_scope_depth > 0 {
1448 Err(CommandEncoderError::DebugGroupError(
1449 DebugGroupError::MissingPop,
1450 ))?;
1451 }
1452
1453 cmd_buf_data.encoder.close_if_open()?;
1455
1456 Ok(cmd_buf_data)
1460 });
1461
1462 let (data, error) = match res {
1463 Err(e) => {
1464 if e.is_destroyed_error() {
1465 (CommandEncoderStatus::Error(e.clone()), None)
1468 } else {
1469 (CommandEncoderStatus::Error(e.clone()), Some(e))
1470 }
1471 }
1472
1473 Ok(data) => (CommandEncoderStatus::Finished(data), None),
1474 };
1475
1476 let cmd_buf = CommandBuffer {
1477 device: cmd_enc.device.clone(),
1478 label: desc.label.to_string(),
1479 data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),
1480 };
1481
1482 let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(Arc::new(cmd_buf));
1483
1484 (cmd_buf_id, error)
1485 }
1486
1487 pub fn command_encoder_push_debug_group(
1488 &self,
1489 encoder_id: id::CommandEncoderId,
1490 label: &str,
1491 ) -> Result<(), EncoderStateError> {
1492 profiling::scope!("CommandEncoder::push_debug_group");
1493 api_log!("CommandEncoder::push_debug_group {label}");
1494
1495 let hub = &self.hub;
1496
1497 let cmd_enc = hub.command_encoders.get(encoder_id);
1498 let mut cmd_buf_data = cmd_enc.data.lock();
1499
1500 #[cfg(feature = "trace")]
1501 if let Some(ref mut list) = cmd_buf_data.trace() {
1502 list.push(TraceCommand::PushDebugGroup(label.to_owned()));
1503 }
1504
1505 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1506 Ok(ArcCommand::PushDebugGroup(label.to_owned()))
1507 })
1508 }
1509
1510 pub fn command_encoder_insert_debug_marker(
1511 &self,
1512 encoder_id: id::CommandEncoderId,
1513 label: &str,
1514 ) -> Result<(), EncoderStateError> {
1515 profiling::scope!("CommandEncoder::insert_debug_marker");
1516 api_log!("CommandEncoder::insert_debug_marker {label}");
1517
1518 let hub = &self.hub;
1519
1520 let cmd_enc = hub.command_encoders.get(encoder_id);
1521 let mut cmd_buf_data = cmd_enc.data.lock();
1522
1523 #[cfg(feature = "trace")]
1524 if let Some(ref mut list) = cmd_buf_data.trace() {
1525 list.push(TraceCommand::InsertDebugMarker(label.to_owned()));
1526 }
1527
1528 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1529 Ok(ArcCommand::InsertDebugMarker(label.to_owned()))
1530 })
1531 }
1532
1533 pub fn command_encoder_pop_debug_group(
1534 &self,
1535 encoder_id: id::CommandEncoderId,
1536 ) -> Result<(), EncoderStateError> {
1537 profiling::scope!("CommandEncoder::pop_debug_marker");
1538 api_log!("CommandEncoder::pop_debug_group");
1539
1540 let hub = &self.hub;
1541
1542 let cmd_enc = hub.command_encoders.get(encoder_id);
1543 let mut cmd_buf_data = cmd_enc.data.lock();
1544
1545 #[cfg(feature = "trace")]
1546 if let Some(ref mut list) = cmd_buf_data.trace() {
1547 list.push(TraceCommand::PopDebugGroup);
1548 }
1549
1550 cmd_buf_data
1551 .push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::PopDebugGroup) })
1552 }
1553
1554 fn validate_pass_timestamp_writes<E>(
1555 device: &Device,
1556 query_sets: &Storage<Fallible<QuerySet>>,
1557 timestamp_writes: &PassTimestampWrites,
1558 ) -> Result<ArcPassTimestampWrites, E>
1559 where
1560 E: From<TimestampWritesError>
1561 + From<QueryUseError>
1562 + From<DeviceError>
1563 + From<MissingFeatures>
1564 + From<InvalidResourceError>,
1565 {
1566 let &PassTimestampWrites {
1567 query_set,
1568 beginning_of_pass_write_index,
1569 end_of_pass_write_index,
1570 } = timestamp_writes;
1571
1572 device.require_features(wgt::Features::TIMESTAMP_QUERY)?;
1573
1574 let query_set = query_sets.get(query_set).get()?;
1575
1576 query_set.same_device(device)?;
1577
1578 for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
1579 .into_iter()
1580 .flatten()
1581 {
1582 query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
1583 }
1584
1585 if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
1586 if begin == end {
1587 return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());
1588 }
1589 }
1590
1591 if beginning_of_pass_write_index
1592 .or(end_of_pass_write_index)
1593 .is_none()
1594 {
1595 return Err(TimestampWritesError::IndicesMissing.into());
1596 }
1597
1598 Ok(ArcPassTimestampWrites {
1599 query_set,
1600 beginning_of_pass_write_index,
1601 end_of_pass_write_index,
1602 })
1603 }
1604}
1605
1606pub(crate) fn push_debug_group(
1607 state: &mut EncodingState,
1608 label: &str,
1609) -> Result<(), CommandEncoderError> {
1610 *state.debug_scope_depth += 1;
1611
1612 if !state
1613 .device
1614 .instance_flags
1615 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1616 {
1617 unsafe { state.raw_encoder.begin_debug_marker(label) };
1618 }
1619
1620 Ok(())
1621}
1622
1623pub(crate) fn insert_debug_marker(
1624 state: &mut EncodingState,
1625 label: &str,
1626) -> Result<(), CommandEncoderError> {
1627 if !state
1628 .device
1629 .instance_flags
1630 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1631 {
1632 unsafe { state.raw_encoder.insert_debug_marker(label) };
1633 }
1634
1635 Ok(())
1636}
1637
1638pub(crate) fn pop_debug_group(state: &mut EncodingState) -> Result<(), CommandEncoderError> {
1639 if *state.debug_scope_depth == 0 {
1640 return Err(DebugGroupError::InvalidPop.into());
1641 }
1642 *state.debug_scope_depth -= 1;
1643
1644 if !state
1645 .device
1646 .instance_flags
1647 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1648 {
1649 unsafe { state.raw_encoder.end_debug_marker() };
1650 }
1651
1652 Ok(())
1653}
1654
1655fn push_constant_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
1656where
1657 PushFn: FnMut(u32, &[u32]),
1658{
1659 let mut count_words = 0_u32;
1660 let size_words = size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT;
1661 while count_words < size_words {
1662 let count_bytes = count_words * wgt::PUSH_CONSTANT_ALIGNMENT;
1663 let size_to_write_words =
1664 (size_words - count_words).min(PUSH_CONSTANT_CLEAR_ARRAY.len() as u32);
1665
1666 push_fn(
1667 offset + count_bytes,
1668 &PUSH_CONSTANT_CLEAR_ARRAY[0..size_to_write_words as usize],
1669 );
1670
1671 count_words += size_to_write_words;
1672 }
1673}
1674
1675#[derive(Debug, Copy, Clone)]
1676struct StateChange<T> {
1677 last_state: Option<T>,
1678}
1679
1680impl<T: Copy + PartialEq> StateChange<T> {
1681 fn new() -> Self {
1682 Self { last_state: None }
1683 }
1684 fn set_and_check_redundant(&mut self, new_state: T) -> bool {
1685 let already_set = self.last_state == Some(new_state);
1686 self.last_state = Some(new_state);
1687 already_set
1688 }
1689 fn reset(&mut self) {
1690 self.last_state = None;
1691 }
1692}
1693
1694impl<T: Copy + PartialEq> Default for StateChange<T> {
1695 fn default() -> Self {
1696 Self::new()
1697 }
1698}
1699
1700#[derive(Debug)]
1701struct BindGroupStateChange {
1702 last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],
1703}
1704
1705impl BindGroupStateChange {
1706 fn new() -> Self {
1707 Self {
1708 last_states: [StateChange::new(); hal::MAX_BIND_GROUPS],
1709 }
1710 }
1711
1712 fn set_and_check_redundant(
1713 &mut self,
1714 bind_group_id: Option<id::BindGroupId>,
1715 index: u32,
1716 dynamic_offsets: &mut Vec<u32>,
1717 offsets: &[wgt::DynamicOffset],
1718 ) -> bool {
1719 if offsets.is_empty() {
1721 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1724 if current_bind_group.set_and_check_redundant(bind_group_id) {
1726 return true;
1727 }
1728 }
1729 } else {
1730 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1734 current_bind_group.reset();
1735 }
1736 dynamic_offsets.extend_from_slice(offsets);
1737 }
1738 false
1739 }
1740 fn reset(&mut self) {
1741 self.last_states = [StateChange::new(); hal::MAX_BIND_GROUPS];
1742 }
1743}
1744
1745impl Default for BindGroupStateChange {
1746 fn default() -> Self {
1747 Self::new()
1748 }
1749}
1750
1751trait MapPassErr<T> {
1753 fn map_pass_err(self, scope: PassErrorScope) -> T;
1754}
1755
1756impl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>
1757where
1758 E: MapPassErr<F>,
1759{
1760 fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {
1761 self.map_err(|err| err.map_pass_err(scope))
1762 }
1763}
1764
1765impl MapPassErr<PassStateError> for EncoderStateError {
1766 fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {
1767 PassStateError { scope, inner: self }
1768 }
1769}
1770
1771#[derive(Clone, Copy, Debug)]
1772pub enum DrawKind {
1773 Draw,
1774 DrawIndirect,
1775 MultiDrawIndirect,
1776 MultiDrawIndirectCount,
1777}
1778
1779#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1781#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1782pub enum DrawCommandFamily {
1783 Draw,
1784 DrawIndexed,
1785 DrawMeshTasks,
1786}
1787
1788#[derive(Clone, Copy, Debug, Error)]
1798pub enum PassErrorScope {
1799 #[error("In a bundle parameter")]
1802 Bundle,
1803 #[error("In a pass parameter")]
1804 Pass,
1805 #[error("In a set_bind_group command")]
1806 SetBindGroup,
1807 #[error("In a set_pipeline command")]
1808 SetPipelineRender,
1809 #[error("In a set_pipeline command")]
1810 SetPipelineCompute,
1811 #[error("In a set_push_constant command")]
1812 SetPushConstant,
1813 #[error("In a set_vertex_buffer command")]
1814 SetVertexBuffer,
1815 #[error("In a set_index_buffer command")]
1816 SetIndexBuffer,
1817 #[error("In a set_blend_constant command")]
1818 SetBlendConstant,
1819 #[error("In a set_stencil_reference command")]
1820 SetStencilReference,
1821 #[error("In a set_viewport command")]
1822 SetViewport,
1823 #[error("In a set_scissor_rect command")]
1824 SetScissorRect,
1825 #[error("In a draw command, kind: {kind:?}")]
1826 Draw {
1827 kind: DrawKind,
1828 family: DrawCommandFamily,
1829 },
1830 #[error("In a write_timestamp command")]
1831 WriteTimestamp,
1832 #[error("In a begin_occlusion_query command")]
1833 BeginOcclusionQuery,
1834 #[error("In a end_occlusion_query command")]
1835 EndOcclusionQuery,
1836 #[error("In a begin_pipeline_statistics_query command")]
1837 BeginPipelineStatisticsQuery,
1838 #[error("In a end_pipeline_statistics_query command")]
1839 EndPipelineStatisticsQuery,
1840 #[error("In a execute_bundle command")]
1841 ExecuteBundle,
1842 #[error("In a dispatch command, indirect:{indirect}")]
1843 Dispatch { indirect: bool },
1844 #[error("In a push_debug_group command")]
1845 PushDebugGroup,
1846 #[error("In a pop_debug_group command")]
1847 PopDebugGroup,
1848 #[error("In a insert_debug_marker command")]
1849 InsertDebugMarker,
1850}
1851
1852#[derive(Clone, Debug, Error)]
1854#[error("{scope}")]
1855pub struct PassStateError {
1856 pub scope: PassErrorScope,
1857 #[source]
1858 pub(super) inner: EncoderStateError,
1859}
1860
1861impl WebGpuError for PassStateError {
1862 fn webgpu_error_type(&self) -> ErrorType {
1863 let Self { scope: _, inner } = self;
1864 inner.webgpu_error_type()
1865 }
1866}