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, panic};
35
36pub(crate) use self::clear::clear_texture;
37#[cfg(feature = "serde")]
38pub(crate) use self::encoder_command::serde_object_reference_struct;
39#[cfg(any(feature = "trace", feature = "replay"))]
40#[doc(hidden)]
41pub use self::encoder_command::PointerReferences;
42pub use self::{
43 bundle::*,
44 clear::ClearError,
45 compute::*,
46 compute_command::ArcComputeCommand,
47 draw::*,
48 encoder_command::{ArcCommand, ArcReferences, Command, IdReferences, ReferenceType},
49 query::*,
50 render::*,
51 render_command::ArcRenderCommand,
52 transfer::*,
53};
54pub(crate) use allocator::CommandAllocator;
55
56pub use self::{compute_command::ComputeCommand, render_command::RenderCommand};
58
59pub(crate) use timestamp_writes::ArcPassTimestampWrites;
60pub use timestamp_writes::PassTimestampWrites;
61
62use self::{
63 clear::{clear_buffer, clear_texture_cmd},
64 memory_init::CommandBufferTextureMemoryActions,
65 ray_tracing::build_acceleration_structures,
66 transition_resources::transition_resources,
67};
68
69use crate::binding_model::BindingError;
70use crate::command::encoder::EncodingState;
71use crate::command::transition_resources::TransitionResourcesError;
72use crate::device::queue::TempResource;
73use crate::device::{Device, DeviceError, MissingFeatures};
74use crate::id::Id;
75use crate::lock::{rank, Mutex};
76use crate::snatch::SnatchGuard;
77
78use crate::init_tracker::BufferInitTrackerAction;
79use crate::ray_tracing::{AsAction, BuildAccelerationStructureError};
80use crate::resource::{
81 DestroyedResourceError, Fallible, InvalidResourceError, Labeled, ParentDevice as _, QuerySet,
82};
83use crate::storage::Storage;
84use crate::track::{DeviceTracker, ResourceUsageCompatibilityError, Tracker, UsageScope};
85use crate::{api_log, global::Global, id, resource_log, Label};
86use crate::{hal_label, LabelHelpers};
87
88use wgt::error::{ErrorType, WebGpuError};
89
90use thiserror::Error;
91
92pub type TexelCopyBufferInfo = ffi::TexelCopyBufferInfo;
94pub type TexelCopyTextureInfo = ffi::TexelCopyTextureInfo;
96pub type CopyExternalImageDestInfo = ffi::CopyExternalImageDestInfo;
98
99const PUSH_CONSTANT_CLEAR_ARRAY: &[u32] = &[0_u32; 64];
100
101pub(crate) enum CommandEncoderStatus {
107 Recording(CommandBufferMutable),
119
120 Locked(CommandBufferMutable),
129
130 Consumed,
131
132 Finished(CommandBufferMutable),
143
144 Error(CommandEncoderError),
149
150 Transitioning,
153}
154
155impl CommandEncoderStatus {
156 #[doc(hidden)]
157 fn replay(&mut self, commands: Vec<Command<ArcReferences>>) {
158 let Self::Recording(cmd_buf_data) = self else {
159 panic!("encoder should be in the recording state");
160 };
161 cmd_buf_data.commands.extend(commands);
162 }
163
164 fn push_with<F: FnOnce() -> Result<ArcCommand, E>, E: Clone + Into<CommandEncoderError>>(
180 &mut self,
181 f: F,
182 ) -> Result<(), EncoderStateError> {
183 match self {
184 Self::Recording(cmd_buf_data) => {
185 cmd_buf_data.encoder.api.set(EncodingApi::Wgpu);
186 match f() {
187 Ok(cmd) => cmd_buf_data.commands.push(cmd),
188 Err(err) => {
189 self.invalidate(err);
190 }
191 }
192 Ok(())
193 }
194 Self::Locked(_) => {
195 self.invalidate(EncoderStateError::Locked);
198 Ok(())
199 }
200 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
203 Self::Consumed => Err(EncoderStateError::Ended),
204 Self::Error(_) => Ok(()),
207 Self::Transitioning => unreachable!(),
208 }
209 }
210
211 fn with_buffer<
225 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
226 E: Clone + Into<CommandEncoderError>,
227 >(
228 &mut self,
229 api: EncodingApi,
230 f: F,
231 ) -> Result<(), EncoderStateError> {
232 match self {
233 Self::Recording(inner) => {
234 inner.encoder.api.set(api);
235 RecordingGuard { inner: self }.record(f);
236 Ok(())
237 }
238 Self::Locked(_) => {
239 self.invalidate(EncoderStateError::Locked);
242 Ok(())
243 }
244 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
247 Self::Consumed => Err(EncoderStateError::Ended),
248 Self::Error(_) => Ok(()),
251 Self::Transitioning => unreachable!(),
252 }
253 }
254
255 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
263 &mut self,
264 f: F,
265 ) -> T {
266 match self {
267 Self::Recording(inner) => {
268 inner.encoder.api.set(EncodingApi::Raw);
269 RecordingGuard { inner: self }.record_as_hal_mut(f)
270 }
271 Self::Locked(_) => {
272 self.invalidate(EncoderStateError::Locked);
273 f(None)
274 }
275 Self::Finished(_) => {
276 self.invalidate(EncoderStateError::Ended);
277 f(None)
278 }
279 Self::Consumed => f(None),
280 Self::Error(_) => f(None),
281 Self::Transitioning => unreachable!(),
282 }
283 }
284
285 fn lock_encoder(&mut self) -> Result<(), EncoderStateError> {
291 match mem::replace(self, Self::Transitioning) {
292 Self::Recording(inner) => {
293 *self = Self::Locked(inner);
294 Ok(())
295 }
296 st @ Self::Finished(_) => {
297 *self = st;
301 Err(EncoderStateError::Ended)
302 }
303 Self::Locked(_) => Err(self.invalidate(EncoderStateError::Locked)),
304 st @ Self::Consumed => {
305 *self = st;
306 Err(EncoderStateError::Ended)
307 }
308 st @ Self::Error(_) => {
309 *self = st;
310 Err(EncoderStateError::Invalid)
311 }
312 Self::Transitioning => unreachable!(),
313 }
314 }
315
316 fn unlock_encoder(&mut self) -> Result<(), EncoderStateError> {
326 match mem::replace(self, Self::Transitioning) {
327 Self::Locked(inner) => {
328 *self = Self::Recording(inner);
329 Ok(())
330 }
331 st @ Self::Finished(_) => {
332 *self = st;
333 Err(EncoderStateError::Ended)
334 }
335 Self::Recording(_) => {
336 *self = Self::Error(EncoderStateError::Unlocked.into());
337 Err(EncoderStateError::Unlocked)
338 }
339 st @ Self::Consumed => {
340 *self = st;
341 Err(EncoderStateError::Ended)
342 }
343 st @ Self::Error(_) => {
344 *self = st;
347 Ok(())
348 }
349 Self::Transitioning => unreachable!(),
350 }
351 }
352
353 fn finish(&mut self) -> Self {
354 match mem::replace(self, Self::Consumed) {
357 Self::Recording(inner) => {
358 if inner.encoder.api != EncodingApi::Raw {
361 assert!(!inner.encoder.is_open);
362 }
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
483#[derive(Copy, Clone, Debug, Eq, PartialEq)]
487pub enum EncodingApi {
488 Wgpu,
490
491 Raw,
493
494 Undecided,
496
497 InternalUse,
499}
500
501impl EncodingApi {
502 pub(crate) fn set(&mut self, api: EncodingApi) {
503 match *self {
504 EncodingApi::Undecided => {
505 *self = api;
506 }
507 self_api if self_api != api => {
508 panic!("Mixing the wgpu encoding API with the raw encoding API is not permitted");
509 }
510 _ => {}
511 }
512 }
513}
514
515pub(crate) struct InnerCommandEncoder {
531 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
539
540 pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
552
553 pub(crate) device: Arc<Device>,
554
555 pub(crate) is_open: bool,
562
563 pub(crate) api: EncodingApi,
569
570 pub(crate) label: String,
571}
572
573impl InnerCommandEncoder {
574 fn close_and_swap(&mut self) -> Result<(), DeviceError> {
600 assert!(self.is_open);
601 self.is_open = false;
602
603 let new =
604 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
605 self.list.insert(self.list.len() - 1, new);
606
607 Ok(())
608 }
609
610 pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> {
621 assert!(self.is_open);
622 self.is_open = false;
623
624 let new =
625 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
626 self.list.insert(0, new);
627
628 Ok(())
629 }
630
631 pub(crate) fn close(&mut self) -> Result<(), DeviceError> {
642 assert!(self.is_open);
643 self.is_open = false;
644
645 let cmd_buf =
646 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
647 self.list.push(cmd_buf);
648
649 Ok(())
650 }
651
652 fn close_if_open(&mut self) -> Result<(), DeviceError> {
663 if self.is_open {
664 self.is_open = false;
665 let cmd_buf =
666 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
667 self.list.push(cmd_buf);
668 }
669
670 Ok(())
671 }
672
673 fn open_if_closed(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
679 if !self.is_open {
680 self.is_open = true;
681 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
682 unsafe { self.raw.begin_encoding(hal_label) }
683 .map_err(|e| self.device.handle_hal_error(e))?;
684 }
685
686 Ok(self.raw.as_mut())
687 }
688
689 pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
693 if !self.is_open {
694 self.is_open = true;
695 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
696 unsafe { self.raw.begin_encoding(hal_label) }
697 .map_err(|e| self.device.handle_hal_error(e))?;
698 }
699
700 Ok(self.raw.as_mut())
701 }
702
703 pub(crate) fn open_pass(
712 &mut self,
713 label: Option<&str>,
714 ) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
715 assert!(!self.is_open);
716 self.is_open = true;
717
718 let hal_label = hal_label(label, self.device.instance_flags);
719 unsafe { self.raw.begin_encoding(hal_label) }
720 .map_err(|e| self.device.handle_hal_error(e))?;
721
722 Ok(self.raw.as_mut())
723 }
724}
725
726impl Drop for InnerCommandEncoder {
727 fn drop(&mut self) {
728 if self.is_open {
729 unsafe { self.raw.discard_encoding() };
730 }
731 unsafe {
732 self.raw.reset_all(mem::take(&mut self.list));
733 }
734 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
736 self.device.command_allocator.release_encoder(raw);
737 }
738}
739
740pub(crate) struct BakedCommands {
743 pub(crate) encoder: InnerCommandEncoder,
744 pub(crate) trackers: Tracker,
745 pub(crate) temp_resources: Vec<TempResource>,
746 pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
747 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
748 texture_memory_actions: CommandBufferTextureMemoryActions,
749}
750
751pub struct CommandBufferMutable {
753 pub(crate) encoder: InnerCommandEncoder,
758
759 pub(crate) trackers: Tracker,
761
762 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
769 texture_memory_actions: CommandBufferTextureMemoryActions,
770
771 as_actions: Vec<AsAction>,
772 temp_resources: Vec<TempResource>,
773
774 indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
775
776 pub(crate) commands: Vec<Command<ArcReferences>>,
777
778 #[cfg(feature = "trace")]
781 pub(crate) trace_commands: Option<Vec<Command<PointerReferences>>>,
782}
783
784impl CommandBufferMutable {
785 pub(crate) fn into_baked_commands(self) -> BakedCommands {
786 BakedCommands {
787 encoder: self.encoder,
788 trackers: self.trackers,
789 temp_resources: self.temp_resources,
790 indirect_draw_validation_resources: self.indirect_draw_validation_resources,
791 buffer_memory_init_actions: self.buffer_memory_init_actions,
792 texture_memory_actions: self.texture_memory_actions,
793 }
794 }
795}
796
797pub struct CommandBuffer {
803 pub(crate) device: Arc<Device>,
804 label: String,
806
807 pub(crate) data: Mutex<CommandEncoderStatus>,
809}
810
811impl Drop for CommandBuffer {
812 fn drop(&mut self) {
813 resource_log!("Drop {}", self.error_ident());
814 }
815}
816
817impl CommandEncoder {
818 pub(crate) fn new(
819 encoder: Box<dyn hal::DynCommandEncoder>,
820 device: &Arc<Device>,
821 label: &Label,
822 ) -> Self {
823 CommandEncoder {
824 device: device.clone(),
825 label: label.to_string(),
826 data: Mutex::new(
827 rank::COMMAND_BUFFER_DATA,
828 CommandEncoderStatus::Recording(CommandBufferMutable {
829 encoder: InnerCommandEncoder {
830 raw: ManuallyDrop::new(encoder),
831 list: Vec::new(),
832 device: device.clone(),
833 is_open: false,
834 api: EncodingApi::Undecided,
835 label: label.to_string(),
836 },
837 trackers: Tracker::new(),
838 buffer_memory_init_actions: Default::default(),
839 texture_memory_actions: Default::default(),
840 as_actions: Default::default(),
841 temp_resources: Default::default(),
842 indirect_draw_validation_resources:
843 crate::indirect_validation::DrawResources::new(device.clone()),
844 commands: Vec::new(),
845 #[cfg(feature = "trace")]
846 trace_commands: if device.trace.lock().is_some() {
847 Some(Vec::new())
848 } else {
849 None
850 },
851 }),
852 ),
853 }
854 }
855
856 pub(crate) fn new_invalid(
857 device: &Arc<Device>,
858 label: &Label,
859 err: CommandEncoderError,
860 ) -> Self {
861 CommandEncoder {
862 device: device.clone(),
863 label: label.to_string(),
864 data: Mutex::new(rank::COMMAND_BUFFER_DATA, CommandEncoderStatus::Error(err)),
865 }
866 }
867
868 pub(crate) fn insert_barriers_from_tracker(
869 raw: &mut dyn hal::DynCommandEncoder,
870 base: &mut Tracker,
871 head: &Tracker,
872 snatch_guard: &SnatchGuard,
873 ) {
874 profiling::scope!("insert_barriers");
875
876 base.buffers.set_from_tracker(&head.buffers);
877 base.textures.set_from_tracker(&head.textures);
878
879 Self::drain_barriers(raw, base, snatch_guard);
880 }
881
882 pub(crate) fn insert_barriers_from_scope(
883 raw: &mut dyn hal::DynCommandEncoder,
884 base: &mut Tracker,
885 head: &UsageScope,
886 snatch_guard: &SnatchGuard,
887 ) {
888 profiling::scope!("insert_barriers");
889
890 base.buffers.set_from_usage_scope(&head.buffers);
891 base.textures.set_from_usage_scope(&head.textures);
892
893 Self::drain_barriers(raw, base, snatch_guard);
894 }
895
896 pub(crate) fn drain_barriers(
897 raw: &mut dyn hal::DynCommandEncoder,
898 base: &mut Tracker,
899 snatch_guard: &SnatchGuard,
900 ) {
901 profiling::scope!("drain_barriers");
902
903 let buffer_barriers = base
904 .buffers
905 .drain_transitions(snatch_guard)
906 .collect::<Vec<_>>();
907 let (transitions, textures) = base.textures.drain_transitions(snatch_guard);
908 let texture_barriers = transitions
909 .into_iter()
910 .enumerate()
911 .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))
912 .collect::<Vec<_>>();
913
914 unsafe {
915 raw.transition_buffers(&buffer_barriers);
916 raw.transition_textures(&texture_barriers);
917 }
918 }
919
920 pub(crate) fn insert_barriers_from_device_tracker(
921 raw: &mut dyn hal::DynCommandEncoder,
922 base: &mut DeviceTracker,
923 head: &Tracker,
924 snatch_guard: &SnatchGuard,
925 ) {
926 profiling::scope!("insert_barriers_from_device_tracker");
927
928 let buffer_barriers = base
929 .buffers
930 .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)
931 .collect::<Vec<_>>();
932
933 let texture_barriers = base
934 .textures
935 .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)
936 .collect::<Vec<_>>();
937
938 unsafe {
939 raw.transition_buffers(&buffer_barriers);
940 raw.transition_textures(&texture_barriers);
941 }
942 }
943
944 fn finish(
945 self: &Arc<Self>,
946 desc: &wgt::CommandBufferDescriptor<Label>,
947 ) -> (Arc<CommandBuffer>, Option<CommandEncoderError>) {
948 let mut cmd_enc_status = self.data.lock();
949
950 let res = match cmd_enc_status.finish() {
951 CommandEncoderStatus::Finished(cmd_buf_data) => Ok(cmd_buf_data),
952 CommandEncoderStatus::Error(err) => Err(err),
953 _ => unreachable!(),
954 };
955
956 let res = res.and_then(|mut cmd_buf_data| {
957 self.device.check_is_valid()?;
958 let snatch_guard = self.device.snatchable_lock.read();
959 let mut debug_scope_depth = 0;
960
961 if cmd_buf_data.encoder.api == EncodingApi::Raw {
962 assert!(cmd_buf_data.commands.is_empty());
965 }
966
967 let mut commands = mem::take(&mut cmd_buf_data.commands);
968 #[cfg(not(feature = "trace"))]
969 let command_iter = commands.drain(..);
970 #[cfg(feature = "trace")]
971 let mut trace_commands = None;
972
973 #[cfg(feature = "trace")]
974 let command_iter = {
975 if self.device.trace.lock().is_some() {
976 trace_commands = Some(
977 cmd_buf_data
978 .trace_commands
979 .insert(Vec::with_capacity(commands.len())),
980 );
981 }
982
983 commands.drain(..).inspect(|cmd| {
984 use crate::device::trace::IntoTrace;
985
986 if let Some(ref mut trace) = trace_commands {
987 trace.push(cmd.clone().to_trace());
988 }
989 })
990 };
991
992 for command in command_iter {
993 if matches!(
994 command,
995 ArcCommand::RunRenderPass { .. } | ArcCommand::RunComputePass { .. }
996 ) {
997 let mut state = EncodingState {
1002 device: &self.device,
1003 raw_encoder: &mut cmd_buf_data.encoder,
1004 tracker: &mut cmd_buf_data.trackers,
1005 buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1006 texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1007 as_actions: &mut cmd_buf_data.as_actions,
1008 temp_resources: &mut cmd_buf_data.temp_resources,
1009 indirect_draw_validation_resources: &mut cmd_buf_data
1010 .indirect_draw_validation_resources,
1011 snatch_guard: &snatch_guard,
1012 debug_scope_depth: &mut debug_scope_depth,
1013 };
1014
1015 match command {
1016 ArcCommand::RunRenderPass {
1017 pass,
1018 color_attachments,
1019 depth_stencil_attachment,
1020 timestamp_writes,
1021 occlusion_query_set,
1022 multiview_mask,
1023 } => {
1024 api_log!(
1025 "Begin encoding render pass with '{}' label",
1026 pass.label.as_deref().unwrap_or("")
1027 );
1028 let res = encode_render_pass(
1029 &mut state,
1030 pass,
1031 color_attachments,
1032 depth_stencil_attachment,
1033 timestamp_writes,
1034 occlusion_query_set,
1035 multiview_mask,
1036 );
1037 match res.as_ref() {
1038 Err(err) => api_log!("Finished encoding render pass ({err:?})"),
1039 Ok(_) => api_log!("Finished encoding render pass (success)"),
1040 }
1041 res?;
1042 }
1043 ArcCommand::RunComputePass {
1044 pass,
1045 timestamp_writes,
1046 } => {
1047 api_log!(
1048 "Begin encoding compute pass with '{}' label",
1049 pass.label.as_deref().unwrap_or("")
1050 );
1051 let res = encode_compute_pass(&mut state, pass, timestamp_writes);
1052 match res.as_ref() {
1053 Err(err) => api_log!("Finished encoding compute pass ({err:?})"),
1054 Ok(_) => api_log!("Finished encoding compute pass (success)"),
1055 }
1056 res?;
1057 }
1058 _ => unreachable!(),
1059 }
1060 } else {
1061 let raw_encoder = cmd_buf_data.encoder.open_if_closed()?;
1067 let mut state = EncodingState {
1068 device: &self.device,
1069 raw_encoder,
1070 tracker: &mut cmd_buf_data.trackers,
1071 buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1072 texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1073 as_actions: &mut cmd_buf_data.as_actions,
1074 temp_resources: &mut cmd_buf_data.temp_resources,
1075 indirect_draw_validation_resources: &mut cmd_buf_data
1076 .indirect_draw_validation_resources,
1077 snatch_guard: &snatch_guard,
1078 debug_scope_depth: &mut debug_scope_depth,
1079 };
1080 match command {
1081 ArcCommand::CopyBufferToBuffer {
1082 src,
1083 src_offset,
1084 dst,
1085 dst_offset,
1086 size,
1087 } => {
1088 copy_buffer_to_buffer(
1089 &mut state, &src, src_offset, &dst, dst_offset, size,
1090 )?;
1091 }
1092 ArcCommand::CopyBufferToTexture { src, dst, size } => {
1093 copy_buffer_to_texture(&mut state, &src, &dst, &size)?;
1094 }
1095 ArcCommand::CopyTextureToBuffer { src, dst, size } => {
1096 copy_texture_to_buffer(&mut state, &src, &dst, &size)?;
1097 }
1098 ArcCommand::CopyTextureToTexture { src, dst, size } => {
1099 copy_texture_to_texture(&mut state, &src, &dst, &size)?;
1100 }
1101 ArcCommand::ClearBuffer { dst, offset, size } => {
1102 clear_buffer(&mut state, dst, offset, size)?;
1103 }
1104 ArcCommand::ClearTexture {
1105 dst,
1106 subresource_range,
1107 } => {
1108 clear_texture_cmd(&mut state, dst, &subresource_range)?;
1109 }
1110 ArcCommand::WriteTimestamp {
1111 query_set,
1112 query_index,
1113 } => {
1114 write_timestamp(&mut state, query_set, query_index)?;
1115 }
1116 ArcCommand::ResolveQuerySet {
1117 query_set,
1118 start_query,
1119 query_count,
1120 destination,
1121 destination_offset,
1122 } => {
1123 resolve_query_set(
1124 &mut state,
1125 query_set,
1126 start_query,
1127 query_count,
1128 destination,
1129 destination_offset,
1130 )?;
1131 }
1132 ArcCommand::PushDebugGroup(label) => {
1133 push_debug_group(&mut state, &label)?;
1134 }
1135 ArcCommand::PopDebugGroup => {
1136 pop_debug_group(&mut state)?;
1137 }
1138 ArcCommand::InsertDebugMarker(label) => {
1139 insert_debug_marker(&mut state, &label)?;
1140 }
1141 ArcCommand::BuildAccelerationStructures { blas, tlas } => {
1142 build_acceleration_structures(&mut state, blas, tlas)?;
1143 }
1144 ArcCommand::TransitionResources {
1145 buffer_transitions,
1146 texture_transitions,
1147 } => {
1148 transition_resources(
1149 &mut state,
1150 buffer_transitions,
1151 texture_transitions,
1152 )?;
1153 }
1154 ArcCommand::RunComputePass { .. } | ArcCommand::RunRenderPass { .. } => {
1155 unreachable!()
1156 }
1157 }
1158 }
1159 }
1160
1161 if debug_scope_depth > 0 {
1162 Err(CommandEncoderError::DebugGroupError(
1163 DebugGroupError::MissingPop,
1164 ))?;
1165 }
1166
1167 cmd_buf_data.encoder.close_if_open()?;
1169
1170 Ok(cmd_buf_data)
1174 });
1175
1176 let (data, error) = match res {
1177 Err(e) => {
1178 if e.is_destroyed_error() {
1179 (CommandEncoderStatus::Error(e.clone()), None)
1182 } else {
1183 (CommandEncoderStatus::Error(e.clone()), Some(e))
1184 }
1185 }
1186
1187 Ok(data) => (CommandEncoderStatus::Finished(data), None),
1188 };
1189
1190 let cmd_buf = Arc::new(CommandBuffer {
1191 device: self.device.clone(),
1192 label: desc.label.to_string(),
1193 data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),
1194 });
1195
1196 (cmd_buf, error)
1197 }
1198}
1199
1200impl CommandBuffer {
1201 #[doc(hidden)]
1207 pub fn from_trace(device: &Arc<Device>, commands: Vec<Command<ArcReferences>>) -> Arc<Self> {
1208 let encoder = device.create_command_encoder(&None).unwrap();
1209 let mut cmd_enc_status = encoder.data.lock();
1210 cmd_enc_status.replay(commands);
1211 drop(cmd_enc_status);
1212
1213 let (cmd_buf, error) = encoder.finish(&wgt::CommandBufferDescriptor { label: None });
1214 if let Some(err) = error {
1215 panic!("CommandEncoder::finish failed: {err}");
1216 }
1217
1218 cmd_buf
1219 }
1220
1221 pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {
1222 use CommandEncoderStatus as St;
1223 match mem::replace(
1224 &mut *self.data.lock(),
1225 CommandEncoderStatus::Error(EncoderStateError::Submitted.into()),
1226 ) {
1227 St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
1228 St::Error(err) => Err(err),
1229 St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(),
1230 }
1231 }
1232}
1233
1234crate::impl_resource_type!(CommandBuffer);
1235crate::impl_labeled!(CommandBuffer);
1236crate::impl_parent_device!(CommandBuffer);
1237crate::impl_storage_item!(CommandBuffer);
1238
1239#[doc(hidden)]
1251#[derive(Debug, Clone)]
1252#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1253pub struct BasePass<C, E> {
1254 pub label: Option<String>,
1255
1256 #[cfg_attr(feature = "serde", serde(skip, default = "Option::default"))]
1264 pub error: Option<E>,
1265
1266 pub commands: Vec<C>,
1272
1273 pub dynamic_offsets: Vec<wgt::DynamicOffset>,
1278
1279 pub string_data: Vec<u8>,
1284
1285 pub push_constant_data: Vec<u32>,
1290}
1291
1292impl<C: Clone, E: Clone> BasePass<C, E> {
1293 fn new(label: &Label) -> Self {
1294 Self {
1295 label: label.as_deref().map(str::to_owned),
1296 error: None,
1297 commands: Vec::new(),
1298 dynamic_offsets: Vec::new(),
1299 string_data: Vec::new(),
1300 push_constant_data: Vec::new(),
1301 }
1302 }
1303
1304 fn new_invalid(label: &Label, err: E) -> Self {
1305 Self {
1306 label: label.as_deref().map(str::to_owned),
1307 error: Some(err),
1308 commands: Vec::new(),
1309 dynamic_offsets: Vec::new(),
1310 string_data: Vec::new(),
1311 push_constant_data: Vec::new(),
1312 }
1313 }
1314
1315 fn take(&mut self) -> Result<BasePass<C, Infallible>, E> {
1322 match self.error.as_ref() {
1323 Some(err) => Err(err.clone()),
1324 None => Ok(BasePass {
1325 label: self.label.clone(),
1326 error: None,
1327 commands: mem::take(&mut self.commands),
1328 dynamic_offsets: mem::take(&mut self.dynamic_offsets),
1329 string_data: mem::take(&mut self.string_data),
1330 push_constant_data: mem::take(&mut self.push_constant_data),
1331 }),
1332 }
1333 }
1334}
1335
1336macro_rules! pass_base {
1359 ($pass:expr, $scope:expr $(,)?) => {
1360 match (&$pass.parent, &$pass.base.error) {
1361 (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),
1363 (&Some(_), &Some(_)) => return Ok(()),
1365 (&Some(_), &None) => &mut $pass.base,
1367 }
1368 };
1369}
1370pub(crate) use pass_base;
1371
1372macro_rules! pass_try {
1384 ($base:expr, $scope:expr, $res:expr $(,)?) => {
1385 match $res.map_pass_err($scope) {
1386 Ok(val) => val,
1387 Err(err) => {
1388 $base.error.get_or_insert(err);
1389 return Ok(());
1390 }
1391 }
1392 };
1393}
1394pub(crate) use pass_try;
1395
1396#[derive(Clone, Debug, Error)]
1401#[non_exhaustive]
1402pub enum EncoderStateError {
1403 #[error("Encoder is invalid")]
1408 Invalid,
1409
1410 #[error("Encoding must not have ended")]
1413 Ended,
1414
1415 #[error("Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
1421 Locked,
1422
1423 #[error(
1426 "Encoder is not currently locked. A pass can only be ended while the encoder is locked."
1427 )]
1428 Unlocked,
1429
1430 #[error("This command buffer has already been submitted.")]
1435 Submitted,
1436}
1437
1438impl WebGpuError for EncoderStateError {
1439 fn webgpu_error_type(&self) -> ErrorType {
1440 match self {
1441 EncoderStateError::Invalid
1442 | EncoderStateError::Ended
1443 | EncoderStateError::Locked
1444 | EncoderStateError::Unlocked
1445 | EncoderStateError::Submitted => ErrorType::Validation,
1446 }
1447 }
1448}
1449
1450#[derive(Clone, Debug, Error)]
1451#[non_exhaustive]
1452pub enum CommandEncoderError {
1453 #[error(transparent)]
1454 State(#[from] EncoderStateError),
1455 #[error(transparent)]
1456 Device(#[from] DeviceError),
1457 #[error(transparent)]
1458 InvalidResource(#[from] InvalidResourceError),
1459 #[error(transparent)]
1460 DestroyedResource(#[from] DestroyedResourceError),
1461 #[error(transparent)]
1462 ResourceUsage(#[from] ResourceUsageCompatibilityError),
1463 #[error(transparent)]
1464 DebugGroupError(#[from] DebugGroupError),
1465 #[error(transparent)]
1466 MissingFeatures(#[from] MissingFeatures),
1467 #[error(transparent)]
1468 Transfer(#[from] TransferError),
1469 #[error(transparent)]
1470 Clear(#[from] ClearError),
1471 #[error(transparent)]
1472 Query(#[from] QueryError),
1473 #[error(transparent)]
1474 BuildAccelerationStructure(#[from] BuildAccelerationStructureError),
1475 #[error(transparent)]
1476 TransitionResources(#[from] TransitionResourcesError),
1477 #[error(transparent)]
1478 ComputePass(#[from] ComputePassError),
1479 #[error(transparent)]
1480 RenderPass(#[from] RenderPassError),
1481}
1482
1483impl CommandEncoderError {
1484 fn is_destroyed_error(&self) -> bool {
1485 matches!(
1486 self,
1487 Self::DestroyedResource(_)
1488 | Self::Clear(ClearError::DestroyedResource(_))
1489 | Self::Query(QueryError::DestroyedResource(_))
1490 | Self::ComputePass(ComputePassError {
1491 inner: ComputePassErrorInner::DestroyedResource(_),
1492 ..
1493 })
1494 | Self::RenderPass(RenderPassError {
1495 inner: RenderPassErrorInner::DestroyedResource(_),
1496 ..
1497 })
1498 | Self::RenderPass(RenderPassError {
1499 inner: RenderPassErrorInner::RenderCommand(
1500 RenderCommandError::DestroyedResource(_)
1501 ),
1502 ..
1503 })
1504 | Self::RenderPass(RenderPassError {
1505 inner: RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError(
1506 BindingError::DestroyedResource(_)
1507 )),
1508 ..
1509 })
1510 )
1511 }
1512}
1513
1514impl WebGpuError for CommandEncoderError {
1515 fn webgpu_error_type(&self) -> ErrorType {
1516 let e: &dyn WebGpuError = match self {
1517 Self::Device(e) => e,
1518 Self::InvalidResource(e) => e,
1519 Self::DebugGroupError(e) => e,
1520 Self::MissingFeatures(e) => e,
1521 Self::State(e) => e,
1522 Self::DestroyedResource(e) => e,
1523 Self::Transfer(e) => e,
1524 Self::Clear(e) => e,
1525 Self::Query(e) => e,
1526 Self::BuildAccelerationStructure(e) => e,
1527 Self::TransitionResources(e) => e,
1528 Self::ResourceUsage(e) => e,
1529 Self::ComputePass(e) => e,
1530 Self::RenderPass(e) => e,
1531 };
1532 e.webgpu_error_type()
1533 }
1534}
1535
1536#[derive(Clone, Debug, Error)]
1537#[non_exhaustive]
1538pub enum DebugGroupError {
1539 #[error("Cannot pop debug group, because number of pushed debug groups is zero")]
1540 InvalidPop,
1541 #[error("A debug group was not popped before the encoder was finished")]
1542 MissingPop,
1543}
1544
1545impl WebGpuError for DebugGroupError {
1546 fn webgpu_error_type(&self) -> ErrorType {
1547 match self {
1548 Self::InvalidPop | Self::MissingPop => ErrorType::Validation,
1549 }
1550 }
1551}
1552
1553#[derive(Clone, Debug, Error)]
1554#[non_exhaustive]
1555pub enum TimestampWritesError {
1556 #[error(
1557 "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
1558 )]
1559 IndicesEqual { idx: u32 },
1560 #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
1561 IndicesMissing,
1562}
1563
1564impl WebGpuError for TimestampWritesError {
1565 fn webgpu_error_type(&self) -> ErrorType {
1566 match self {
1567 Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,
1568 }
1569 }
1570}
1571
1572impl Global {
1573 fn resolve_buffer_id(
1574 &self,
1575 buffer_id: Id<id::markers::Buffer>,
1576 ) -> Result<Arc<crate::resource::Buffer>, InvalidResourceError> {
1577 self.hub.buffers.get(buffer_id).get()
1578 }
1579
1580 fn resolve_texture_id(
1581 &self,
1582 texture_id: Id<id::markers::Texture>,
1583 ) -> Result<Arc<crate::resource::Texture>, InvalidResourceError> {
1584 self.hub.textures.get(texture_id).get()
1585 }
1586
1587 fn resolve_query_set(
1588 &self,
1589 query_set_id: Id<id::markers::QuerySet>,
1590 ) -> Result<Arc<QuerySet>, InvalidResourceError> {
1591 self.hub.query_sets.get(query_set_id).get()
1592 }
1593
1594 pub fn command_encoder_finish(
1602 &self,
1603 encoder_id: id::CommandEncoderId,
1604 desc: &wgt::CommandBufferDescriptor<Label>,
1605 id_in: Option<id::CommandBufferId>,
1606 ) -> (id::CommandBufferId, Option<(String, CommandEncoderError)>) {
1607 profiling::scope!("CommandEncoder::finish");
1608
1609 let hub = &self.hub;
1610 let cmd_enc = hub.command_encoders.get(encoder_id);
1611
1612 let (cmd_buf, opt_error) = cmd_enc.finish(desc);
1613 let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(cmd_buf);
1614
1615 (
1616 cmd_buf_id,
1617 opt_error.map(|error| (cmd_enc.label.clone(), error)),
1618 )
1619 }
1620
1621 pub fn command_encoder_push_debug_group(
1622 &self,
1623 encoder_id: id::CommandEncoderId,
1624 label: &str,
1625 ) -> Result<(), EncoderStateError> {
1626 profiling::scope!("CommandEncoder::push_debug_group");
1627 api_log!("CommandEncoder::push_debug_group {label}");
1628
1629 let hub = &self.hub;
1630
1631 let cmd_enc = hub.command_encoders.get(encoder_id);
1632 let mut cmd_buf_data = cmd_enc.data.lock();
1633
1634 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1635 Ok(ArcCommand::PushDebugGroup(label.to_owned()))
1636 })
1637 }
1638
1639 pub fn command_encoder_insert_debug_marker(
1640 &self,
1641 encoder_id: id::CommandEncoderId,
1642 label: &str,
1643 ) -> Result<(), EncoderStateError> {
1644 profiling::scope!("CommandEncoder::insert_debug_marker");
1645 api_log!("CommandEncoder::insert_debug_marker {label}");
1646
1647 let hub = &self.hub;
1648
1649 let cmd_enc = hub.command_encoders.get(encoder_id);
1650 let mut cmd_buf_data = cmd_enc.data.lock();
1651
1652 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1653 Ok(ArcCommand::InsertDebugMarker(label.to_owned()))
1654 })
1655 }
1656
1657 pub fn command_encoder_pop_debug_group(
1658 &self,
1659 encoder_id: id::CommandEncoderId,
1660 ) -> Result<(), EncoderStateError> {
1661 profiling::scope!("CommandEncoder::pop_debug_marker");
1662 api_log!("CommandEncoder::pop_debug_group");
1663
1664 let hub = &self.hub;
1665
1666 let cmd_enc = hub.command_encoders.get(encoder_id);
1667 let mut cmd_buf_data = cmd_enc.data.lock();
1668
1669 cmd_buf_data
1670 .push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::PopDebugGroup) })
1671 }
1672
1673 fn validate_pass_timestamp_writes<E>(
1674 device: &Device,
1675 query_sets: &Storage<Fallible<QuerySet>>,
1676 timestamp_writes: &PassTimestampWrites,
1677 ) -> Result<ArcPassTimestampWrites, E>
1678 where
1679 E: From<TimestampWritesError>
1680 + From<QueryUseError>
1681 + From<DeviceError>
1682 + From<MissingFeatures>
1683 + From<InvalidResourceError>,
1684 {
1685 let &PassTimestampWrites {
1686 query_set,
1687 beginning_of_pass_write_index,
1688 end_of_pass_write_index,
1689 } = timestamp_writes;
1690
1691 device.require_features(wgt::Features::TIMESTAMP_QUERY)?;
1692
1693 let query_set = query_sets.get(query_set).get()?;
1694
1695 query_set.same_device(device)?;
1696
1697 for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
1698 .into_iter()
1699 .flatten()
1700 {
1701 query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
1702 }
1703
1704 if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
1705 if begin == end {
1706 return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());
1707 }
1708 }
1709
1710 if beginning_of_pass_write_index
1711 .or(end_of_pass_write_index)
1712 .is_none()
1713 {
1714 return Err(TimestampWritesError::IndicesMissing.into());
1715 }
1716
1717 Ok(ArcPassTimestampWrites {
1718 query_set,
1719 beginning_of_pass_write_index,
1720 end_of_pass_write_index,
1721 })
1722 }
1723}
1724
1725pub(crate) fn push_debug_group(
1726 state: &mut EncodingState,
1727 label: &str,
1728) -> Result<(), CommandEncoderError> {
1729 *state.debug_scope_depth += 1;
1730
1731 if !state
1732 .device
1733 .instance_flags
1734 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1735 {
1736 unsafe { state.raw_encoder.begin_debug_marker(label) };
1737 }
1738
1739 Ok(())
1740}
1741
1742pub(crate) fn insert_debug_marker(
1743 state: &mut EncodingState,
1744 label: &str,
1745) -> Result<(), CommandEncoderError> {
1746 if !state
1747 .device
1748 .instance_flags
1749 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1750 {
1751 unsafe { state.raw_encoder.insert_debug_marker(label) };
1752 }
1753
1754 Ok(())
1755}
1756
1757pub(crate) fn pop_debug_group(state: &mut EncodingState) -> Result<(), CommandEncoderError> {
1758 if *state.debug_scope_depth == 0 {
1759 return Err(DebugGroupError::InvalidPop.into());
1760 }
1761 *state.debug_scope_depth -= 1;
1762
1763 if !state
1764 .device
1765 .instance_flags
1766 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1767 {
1768 unsafe { state.raw_encoder.end_debug_marker() };
1769 }
1770
1771 Ok(())
1772}
1773
1774fn push_constant_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
1775where
1776 PushFn: FnMut(u32, &[u32]),
1777{
1778 let mut count_words = 0_u32;
1779 let size_words = size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT;
1780 while count_words < size_words {
1781 let count_bytes = count_words * wgt::PUSH_CONSTANT_ALIGNMENT;
1782 let size_to_write_words =
1783 (size_words - count_words).min(PUSH_CONSTANT_CLEAR_ARRAY.len() as u32);
1784
1785 push_fn(
1786 offset + count_bytes,
1787 &PUSH_CONSTANT_CLEAR_ARRAY[0..size_to_write_words as usize],
1788 );
1789
1790 count_words += size_to_write_words;
1791 }
1792}
1793
1794#[derive(Debug, Copy, Clone)]
1795struct StateChange<T> {
1796 last_state: Option<T>,
1797}
1798
1799impl<T: Copy + PartialEq> StateChange<T> {
1800 fn new() -> Self {
1801 Self { last_state: None }
1802 }
1803 fn set_and_check_redundant(&mut self, new_state: T) -> bool {
1804 let already_set = self.last_state == Some(new_state);
1805 self.last_state = Some(new_state);
1806 already_set
1807 }
1808 fn reset(&mut self) {
1809 self.last_state = None;
1810 }
1811}
1812
1813impl<T: Copy + PartialEq> Default for StateChange<T> {
1814 fn default() -> Self {
1815 Self::new()
1816 }
1817}
1818
1819#[derive(Debug)]
1820struct BindGroupStateChange {
1821 last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],
1822}
1823
1824impl BindGroupStateChange {
1825 fn new() -> Self {
1826 Self {
1827 last_states: [StateChange::new(); hal::MAX_BIND_GROUPS],
1828 }
1829 }
1830
1831 fn set_and_check_redundant(
1832 &mut self,
1833 bind_group_id: Option<id::BindGroupId>,
1834 index: u32,
1835 dynamic_offsets: &mut Vec<u32>,
1836 offsets: &[wgt::DynamicOffset],
1837 ) -> bool {
1838 if offsets.is_empty() {
1840 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1843 if current_bind_group.set_and_check_redundant(bind_group_id) {
1845 return true;
1846 }
1847 }
1848 } else {
1849 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1853 current_bind_group.reset();
1854 }
1855 dynamic_offsets.extend_from_slice(offsets);
1856 }
1857 false
1858 }
1859 fn reset(&mut self) {
1860 self.last_states = [StateChange::new(); hal::MAX_BIND_GROUPS];
1861 }
1862}
1863
1864impl Default for BindGroupStateChange {
1865 fn default() -> Self {
1866 Self::new()
1867 }
1868}
1869
1870trait MapPassErr<T> {
1872 fn map_pass_err(self, scope: PassErrorScope) -> T;
1873}
1874
1875impl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>
1876where
1877 E: MapPassErr<F>,
1878{
1879 fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {
1880 self.map_err(|err| err.map_pass_err(scope))
1881 }
1882}
1883
1884impl MapPassErr<PassStateError> for EncoderStateError {
1885 fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {
1886 PassStateError { scope, inner: self }
1887 }
1888}
1889
1890#[derive(Clone, Copy, Debug)]
1891pub enum DrawKind {
1892 Draw,
1893 DrawIndirect,
1894 MultiDrawIndirect,
1895 MultiDrawIndirectCount,
1896}
1897
1898#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1900#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1901pub enum DrawCommandFamily {
1902 Draw,
1903 DrawIndexed,
1904 DrawMeshTasks,
1905}
1906
1907#[derive(Clone, Copy, Debug, Error)]
1917pub enum PassErrorScope {
1918 #[error("In a bundle parameter")]
1921 Bundle,
1922 #[error("In a pass parameter")]
1923 Pass,
1924 #[error("In a set_bind_group command")]
1925 SetBindGroup,
1926 #[error("In a set_pipeline command")]
1927 SetPipelineRender,
1928 #[error("In a set_pipeline command")]
1929 SetPipelineCompute,
1930 #[error("In a set_push_constant command")]
1931 SetPushConstant,
1932 #[error("In a set_vertex_buffer command")]
1933 SetVertexBuffer,
1934 #[error("In a set_index_buffer command")]
1935 SetIndexBuffer,
1936 #[error("In a set_blend_constant command")]
1937 SetBlendConstant,
1938 #[error("In a set_stencil_reference command")]
1939 SetStencilReference,
1940 #[error("In a set_viewport command")]
1941 SetViewport,
1942 #[error("In a set_scissor_rect command")]
1943 SetScissorRect,
1944 #[error("In a draw command, kind: {kind:?}")]
1945 Draw {
1946 kind: DrawKind,
1947 family: DrawCommandFamily,
1948 },
1949 #[error("In a write_timestamp command")]
1950 WriteTimestamp,
1951 #[error("In a begin_occlusion_query command")]
1952 BeginOcclusionQuery,
1953 #[error("In a end_occlusion_query command")]
1954 EndOcclusionQuery,
1955 #[error("In a begin_pipeline_statistics_query command")]
1956 BeginPipelineStatisticsQuery,
1957 #[error("In a end_pipeline_statistics_query command")]
1958 EndPipelineStatisticsQuery,
1959 #[error("In a execute_bundle command")]
1960 ExecuteBundle,
1961 #[error("In a dispatch command, indirect:{indirect}")]
1962 Dispatch { indirect: bool },
1963 #[error("In a push_debug_group command")]
1964 PushDebugGroup,
1965 #[error("In a pop_debug_group command")]
1966 PopDebugGroup,
1967 #[error("In a insert_debug_marker command")]
1968 InsertDebugMarker,
1969}
1970
1971#[derive(Clone, Debug, Error)]
1973#[error("{scope}")]
1974pub struct PassStateError {
1975 pub scope: PassErrorScope,
1976 #[source]
1977 pub(super) inner: EncoderStateError,
1978}
1979
1980impl WebGpuError for PassStateError {
1981 fn webgpu_error_type(&self) -> ErrorType {
1982 let Self { scope: _, inner } = self;
1983 inner.webgpu_error_type()
1984 }
1985}