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
36#[cfg(feature = "serde")]
37pub(crate) use self::encoder_command::serde_object_reference_struct;
38#[cfg(any(feature = "trace", feature = "replay"))]
39#[doc(hidden)]
40pub use self::encoder_command::PointerReferences;
41pub use self::{
45 bundle::{
46 bundle_ffi, CreateRenderBundleError, ExecutionError, RenderBundle, RenderBundleDescriptor,
47 RenderBundleEncoder, RenderBundleEncoderDescriptor, RenderBundleError,
48 RenderBundleErrorInner,
49 },
50 clear::ClearError,
51 compute::{
52 ComputeBasePass, ComputePass, ComputePassDescriptor, ComputePassError,
53 ComputePassErrorInner, DispatchError,
54 },
55 compute_command::ArcComputeCommand,
56 draw::{DrawError, Rect, RenderCommandError},
57 encoder_command::{ArcCommand, ArcReferences, Command, IdReferences, ReferenceType},
58 query::{QueryError, QueryUseError, ResolveError, SimplifiedQueryType},
59 render::{
60 ArcRenderPassColorAttachment, AttachmentError, AttachmentErrorLocation,
61 ColorAttachmentError, ColorAttachments, LoadOp, PassChannel, RenderBasePass, RenderPass,
62 RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor,
63 RenderPassError, RenderPassErrorInner, ResolvedPassChannel,
64 ResolvedRenderPassDepthStencilAttachment, StoreOp,
65 },
66 render_command::ArcRenderCommand,
67 transfer::{CopySide, TransferError},
68 transition_resources::TransitionResourcesError,
69};
70pub(crate) use self::{
71 clear::clear_texture,
72 encoder::EncodingState,
73 memory_init::CommandBufferTextureMemoryActions,
74 render::{get_stride_of_indirect_args, VertexLimits},
75 transfer::{
76 extract_texture_selector, validate_linear_texture_data, validate_texture_buffer_copy,
77 validate_texture_copy_dst_format, validate_texture_copy_range,
78 },
79};
80
81pub(crate) use allocator::CommandAllocator;
82
83pub use self::{compute_command::ComputeCommand, render_command::RenderCommand};
85
86pub(crate) use timestamp_writes::ArcPassTimestampWrites;
87pub use timestamp_writes::PassTimestampWrites;
88
89use crate::binding_model::BindingError;
90use crate::device::queue::TempResource;
91use crate::device::{Device, DeviceError, MissingFeatures};
92use crate::id::Id;
93use crate::lock::{rank, Mutex};
94use crate::snatch::SnatchGuard;
95
96use crate::init_tracker::BufferInitTrackerAction;
97use crate::ray_tracing::{AsAction, BuildAccelerationStructureError};
98use crate::resource::{
99 DestroyedResourceError, Fallible, InvalidResourceError, Labeled, ParentDevice as _, QuerySet,
100};
101use crate::storage::Storage;
102use crate::track::{DeviceTracker, ResourceUsageCompatibilityError, Tracker, UsageScope};
103use crate::{api_log, global::Global, id, resource_log, Label};
104use crate::{hal_label, LabelHelpers};
105
106use wgt::error::{ErrorType, WebGpuError};
107
108use thiserror::Error;
109
110pub type TexelCopyBufferInfo = ffi::TexelCopyBufferInfo;
112pub type TexelCopyTextureInfo = ffi::TexelCopyTextureInfo;
114pub type CopyExternalImageDestInfo = ffi::CopyExternalImageDestInfo;
116
117const IMMEDIATES_CLEAR_ARRAY: &[u32] = &[0_u32; 64];
118
119pub(crate) struct EncoderErrorState {
120 error: CommandEncoderError,
121
122 #[cfg(feature = "trace")]
123 trace_commands: Option<Vec<Command<PointerReferences>>>,
124}
125
126fn make_error_state<E: Into<CommandEncoderError>>(error: E) -> CommandEncoderStatus {
136 CommandEncoderStatus::Error(EncoderErrorState {
137 error: error.into(),
138
139 #[cfg(feature = "trace")]
140 trace_commands: None,
141 })
142}
143
144pub(crate) enum CommandEncoderStatus {
150 Recording(CommandBufferMutable),
162
163 Locked(CommandBufferMutable),
172
173 Consumed,
174
175 Finished(CommandBufferMutable),
186
187 Error(EncoderErrorState),
192
193 Transitioning,
196}
197
198impl CommandEncoderStatus {
199 #[doc(hidden)]
200 fn replay(&mut self, commands: Vec<Command<ArcReferences>>) {
201 let Self::Recording(cmd_buf_data) = self else {
202 panic!("encoder should be in the recording state");
203 };
204 cmd_buf_data.commands.extend(commands);
205 }
206
207 fn push_with<F: FnOnce() -> Result<ArcCommand, E>, E: Clone + Into<CommandEncoderError>>(
223 &mut self,
224 f: F,
225 ) -> Result<(), EncoderStateError> {
226 match self {
227 Self::Recording(cmd_buf_data) => {
228 cmd_buf_data.encoder.api.set(EncodingApi::Wgpu);
229 match f() {
230 Ok(cmd) => cmd_buf_data.commands.push(cmd),
231 Err(err) => {
232 self.invalidate(err);
233 }
234 }
235 Ok(())
236 }
237 Self::Locked(_) => {
238 self.invalidate(EncoderStateError::Locked);
241 Ok(())
242 }
243 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
246 Self::Consumed => Err(EncoderStateError::Ended),
247 Self::Error(_) => Ok(()),
250 Self::Transitioning => unreachable!(),
251 }
252 }
253
254 fn with_buffer<
268 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
269 E: Clone + Into<CommandEncoderError>,
270 >(
271 &mut self,
272 api: EncodingApi,
273 f: F,
274 ) -> Result<(), EncoderStateError> {
275 match self {
276 Self::Recording(inner) => {
277 inner.encoder.api.set(api);
278 RecordingGuard { inner: self }.record(f);
279 Ok(())
280 }
281 Self::Locked(_) => {
282 self.invalidate(EncoderStateError::Locked);
285 Ok(())
286 }
287 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
290 Self::Consumed => Err(EncoderStateError::Ended),
291 Self::Error(_) => Ok(()),
294 Self::Transitioning => unreachable!(),
295 }
296 }
297
298 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
306 &mut self,
307 f: F,
308 ) -> T {
309 match self {
310 Self::Recording(inner) => {
311 inner.encoder.api.set(EncodingApi::Raw);
312 RecordingGuard { inner: self }.record_as_hal_mut(f)
313 }
314 Self::Locked(_) => {
315 self.invalidate(EncoderStateError::Locked);
316 f(None)
317 }
318 Self::Finished(_) => {
319 self.invalidate(EncoderStateError::Ended);
320 f(None)
321 }
322 Self::Consumed => f(None),
323 Self::Error(_) => f(None),
324 Self::Transitioning => unreachable!(),
325 }
326 }
327
328 fn lock_encoder(&mut self) -> Result<(), EncoderStateError> {
334 match mem::replace(self, Self::Transitioning) {
335 Self::Recording(inner) => {
336 *self = Self::Locked(inner);
337 Ok(())
338 }
339 st @ Self::Finished(_) => {
340 *self = st;
344 Err(EncoderStateError::Ended)
345 }
346 Self::Locked(_) => Err(self.invalidate(EncoderStateError::Locked)),
347 st @ Self::Consumed => {
348 *self = st;
349 Err(EncoderStateError::Ended)
350 }
351 st @ Self::Error(_) => {
352 *self = st;
353 Err(EncoderStateError::Invalid)
354 }
355 Self::Transitioning => unreachable!(),
356 }
357 }
358
359 fn unlock_encoder(&mut self) -> Result<(), EncoderStateError> {
369 match mem::replace(self, Self::Transitioning) {
370 Self::Locked(inner) => {
371 *self = Self::Recording(inner);
372 Ok(())
373 }
374 st @ Self::Finished(_) => {
375 *self = st;
376 Err(EncoderStateError::Ended)
377 }
378 Self::Recording(_) => {
379 *self = make_error_state(EncoderStateError::Unlocked);
380 Err(EncoderStateError::Unlocked)
381 }
382 st @ Self::Consumed => {
383 *self = st;
384 Err(EncoderStateError::Ended)
385 }
386 st @ Self::Error(_) => {
387 *self = st;
390 Ok(())
391 }
392 Self::Transitioning => unreachable!(),
393 }
394 }
395
396 fn finish(&mut self) -> Self {
397 match mem::replace(self, Self::Consumed) {
400 Self::Recording(inner) => {
401 if inner.encoder.api != EncodingApi::Raw {
404 assert!(!inner.encoder.is_open);
405 }
406 Self::Finished(inner)
407 }
408 Self::Consumed | Self::Finished(_) => make_error_state(EncoderStateError::Ended),
409 Self::Locked(_) => make_error_state(EncoderStateError::Locked),
410 st @ Self::Error(_) => st,
411 Self::Transitioning => unreachable!(),
412 }
413 }
414
415 fn invalidate<E: Clone + Into<CommandEncoderError>>(&mut self, err: E) -> E {
423 #[cfg(feature = "trace")]
424 let trace_commands = match self {
425 Self::Recording(cmd_buf_data) => Some(
426 mem::take(&mut cmd_buf_data.commands)
427 .into_iter()
428 .map(crate::device::trace::IntoTrace::into_trace)
429 .collect(),
430 ),
431 _ => None,
432 };
433
434 let enc_err = err.clone().into();
435 api_log!("Invalidating command encoder: {enc_err:?}");
436 *self = Self::Error(EncoderErrorState {
437 error: enc_err,
438 #[cfg(feature = "trace")]
439 trace_commands,
440 });
441 err
442 }
443}
444
445pub(crate) struct RecordingGuard<'a> {
458 inner: &'a mut CommandEncoderStatus,
459}
460
461impl<'a> RecordingGuard<'a> {
462 pub(crate) fn mark_successful(self) {
463 mem::forget(self)
464 }
465
466 fn record<
467 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
468 E: Clone + Into<CommandEncoderError>,
469 >(
470 mut self,
471 f: F,
472 ) {
473 match f(&mut self) {
474 Ok(()) => self.mark_successful(),
475 Err(err) => {
476 self.inner.invalidate(err);
477 }
478 }
479 }
480
481 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
484 mut self,
485 f: F,
486 ) -> T {
487 let res = f(Some(&mut self));
488 self.mark_successful();
489 res
490 }
491}
492
493impl<'a> Drop for RecordingGuard<'a> {
494 fn drop(&mut self) {
495 if matches!(*self.inner, CommandEncoderStatus::Error(_)) {
496 return;
498 }
499 self.inner.invalidate(EncoderStateError::Invalid);
500 }
501}
502
503impl<'a> ops::Deref for RecordingGuard<'a> {
504 type Target = CommandBufferMutable;
505
506 fn deref(&self) -> &Self::Target {
507 match &*self.inner {
508 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
509 _ => unreachable!(),
510 }
511 }
512}
513
514impl<'a> ops::DerefMut for RecordingGuard<'a> {
515 fn deref_mut(&mut self) -> &mut Self::Target {
516 match self.inner {
517 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
518 _ => unreachable!(),
519 }
520 }
521}
522
523pub(crate) struct CommandEncoder {
524 pub(crate) device: Arc<Device>,
525
526 pub(crate) label: String,
527
528 pub(crate) data: Mutex<CommandEncoderStatus>,
530}
531
532crate::impl_resource_type!(CommandEncoder);
533crate::impl_labeled!(CommandEncoder);
534crate::impl_parent_device!(CommandEncoder);
535crate::impl_storage_item!(CommandEncoder);
536
537impl Drop for CommandEncoder {
538 fn drop(&mut self) {
539 resource_log!("Drop {}", self.error_ident());
540 }
541}
542
543#[derive(Copy, Clone, Debug, Eq, PartialEq)]
547pub enum EncodingApi {
548 Wgpu,
550
551 Raw,
553
554 Undecided,
556
557 InternalUse,
559}
560
561impl EncodingApi {
562 pub(crate) fn set(&mut self, api: EncodingApi) {
563 match *self {
564 EncodingApi::Undecided => {
565 *self = api;
566 }
567 self_api if self_api != api => {
568 panic!("Mixing the wgpu encoding API with the raw encoding API is not permitted");
569 }
570 _ => {}
571 }
572 }
573}
574
575pub(crate) struct InnerCommandEncoder {
591 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
599
600 pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
612
613 pub(crate) device: Arc<Device>,
614
615 pub(crate) is_open: bool,
622
623 pub(crate) api: EncodingApi,
629
630 pub(crate) label: String,
631}
632
633impl InnerCommandEncoder {
634 fn close_and_swap(&mut self) -> Result<(), DeviceError> {
660 assert!(self.is_open);
661 self.is_open = false;
662
663 let new =
664 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
665 self.list.insert(self.list.len() - 1, new);
666
667 Ok(())
668 }
669
670 pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> {
681 assert!(self.is_open);
682 self.is_open = false;
683
684 let new =
685 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
686 self.list.insert(0, new);
687
688 Ok(())
689 }
690
691 pub(crate) fn close(&mut self) -> Result<(), DeviceError> {
702 assert!(self.is_open);
703 self.is_open = false;
704
705 let cmd_buf =
706 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
707 self.list.push(cmd_buf);
708
709 Ok(())
710 }
711
712 fn close_if_open(&mut self) -> Result<(), DeviceError> {
723 if self.is_open {
724 self.is_open = false;
725 let cmd_buf =
726 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
727 self.list.push(cmd_buf);
728 }
729
730 Ok(())
731 }
732
733 fn open_if_closed(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
739 if !self.is_open {
740 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
741 unsafe { self.raw.begin_encoding(hal_label) }
742 .map_err(|e| self.device.handle_hal_error(e))?;
743 self.is_open = true;
744 }
745
746 Ok(self.raw.as_mut())
747 }
748
749 pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
753 if !self.is_open {
754 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
755 unsafe { self.raw.begin_encoding(hal_label) }
756 .map_err(|e| self.device.handle_hal_error(e))?;
757 self.is_open = true;
758 }
759
760 Ok(self.raw.as_mut())
761 }
762
763 pub(crate) fn open_pass(
772 &mut self,
773 label: Option<&str>,
774 ) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
775 assert!(!self.is_open);
776
777 let hal_label = hal_label(label, self.device.instance_flags);
778 unsafe { self.raw.begin_encoding(hal_label) }
779 .map_err(|e| self.device.handle_hal_error(e))?;
780 self.is_open = true;
781
782 Ok(self.raw.as_mut())
783 }
784}
785
786impl Drop for InnerCommandEncoder {
787 fn drop(&mut self) {
788 if self.is_open {
789 unsafe { self.raw.discard_encoding() };
790 }
791 unsafe {
792 self.raw.reset_all(mem::take(&mut self.list));
793 }
794 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
796 self.device.command_allocator.release_encoder(raw);
797 }
798}
799
800pub(crate) struct BakedCommands {
803 pub(crate) encoder: InnerCommandEncoder,
804 pub(crate) trackers: Tracker,
805 pub(crate) temp_resources: Vec<TempResource>,
806 pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
807 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
808 texture_memory_actions: CommandBufferTextureMemoryActions,
809}
810
811pub struct CommandBufferMutable {
813 pub(crate) encoder: InnerCommandEncoder,
818
819 pub(crate) trackers: Tracker,
821
822 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
829 texture_memory_actions: CommandBufferTextureMemoryActions,
830
831 as_actions: Vec<AsAction>,
832 temp_resources: Vec<TempResource>,
833
834 indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
835
836 pub(crate) commands: Vec<Command<ArcReferences>>,
837
838 #[cfg(feature = "trace")]
841 pub(crate) trace_commands: Option<Vec<Command<PointerReferences>>>,
842}
843
844impl CommandBufferMutable {
845 pub(crate) fn into_baked_commands(self) -> BakedCommands {
846 BakedCommands {
847 encoder: self.encoder,
848 trackers: self.trackers,
849 temp_resources: self.temp_resources,
850 indirect_draw_validation_resources: self.indirect_draw_validation_resources,
851 buffer_memory_init_actions: self.buffer_memory_init_actions,
852 texture_memory_actions: self.texture_memory_actions,
853 }
854 }
855}
856
857pub struct CommandBuffer {
863 pub(crate) device: Arc<Device>,
864 label: String,
866
867 pub(crate) data: Mutex<CommandEncoderStatus>,
869}
870
871impl Drop for CommandBuffer {
872 fn drop(&mut self) {
873 resource_log!("Drop {}", self.error_ident());
874 }
875}
876
877impl CommandEncoder {
878 pub(crate) fn new(
879 encoder: Box<dyn hal::DynCommandEncoder>,
880 device: &Arc<Device>,
881 label: &Label,
882 ) -> Self {
883 CommandEncoder {
884 device: device.clone(),
885 label: label.to_string(),
886 data: Mutex::new(
887 rank::COMMAND_BUFFER_DATA,
888 CommandEncoderStatus::Recording(CommandBufferMutable {
889 encoder: InnerCommandEncoder {
890 raw: ManuallyDrop::new(encoder),
891 list: Vec::new(),
892 device: device.clone(),
893 is_open: false,
894 api: EncodingApi::Undecided,
895 label: label.to_string(),
896 },
897 trackers: Tracker::new(
898 device.ordered_buffer_usages,
899 device.ordered_texture_usages,
900 ),
901 buffer_memory_init_actions: Default::default(),
902 texture_memory_actions: Default::default(),
903 as_actions: Default::default(),
904 temp_resources: Default::default(),
905 indirect_draw_validation_resources:
906 crate::indirect_validation::DrawResources::new(device.clone()),
907 commands: Vec::new(),
908 #[cfg(feature = "trace")]
909 trace_commands: if device.trace.lock().is_some() {
910 Some(Vec::new())
911 } else {
912 None
913 },
914 }),
915 ),
916 }
917 }
918
919 pub(crate) fn new_invalid(
920 device: &Arc<Device>,
921 label: &Label,
922 err: CommandEncoderError,
923 ) -> Self {
924 CommandEncoder {
925 device: device.clone(),
926 label: label.to_string(),
927 data: Mutex::new(rank::COMMAND_BUFFER_DATA, make_error_state(err)),
928 }
929 }
930
931 pub(crate) fn insert_barriers_from_tracker(
932 raw: &mut dyn hal::DynCommandEncoder,
933 base: &mut Tracker,
934 head: &Tracker,
935 snatch_guard: &SnatchGuard,
936 ) {
937 profiling::scope!("insert_barriers");
938
939 base.buffers.set_from_tracker(&head.buffers);
940 base.textures.set_from_tracker(&head.textures);
941
942 Self::drain_barriers(raw, base, snatch_guard);
943 }
944
945 pub(crate) fn insert_barriers_from_scope(
946 raw: &mut dyn hal::DynCommandEncoder,
947 base: &mut Tracker,
948 head: &UsageScope,
949 snatch_guard: &SnatchGuard,
950 ) {
951 profiling::scope!("insert_barriers");
952
953 base.buffers.set_from_usage_scope(&head.buffers);
954 base.textures.set_from_usage_scope(&head.textures);
955
956 Self::drain_barriers(raw, base, snatch_guard);
957 }
958
959 pub(crate) fn drain_barriers(
960 raw: &mut dyn hal::DynCommandEncoder,
961 base: &mut Tracker,
962 snatch_guard: &SnatchGuard,
963 ) {
964 profiling::scope!("drain_barriers");
965
966 let buffer_barriers = base
967 .buffers
968 .drain_transitions(snatch_guard)
969 .collect::<Vec<_>>();
970 let (transitions, textures) = base.textures.drain_transitions(snatch_guard);
971 let texture_barriers = transitions
972 .into_iter()
973 .enumerate()
974 .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))
975 .collect::<Vec<_>>();
976
977 unsafe {
978 raw.transition_buffers(&buffer_barriers);
979 raw.transition_textures(&texture_barriers);
980 }
981 }
982
983 pub(crate) fn insert_barriers_from_device_tracker(
984 raw: &mut dyn hal::DynCommandEncoder,
985 base: &mut DeviceTracker,
986 head: &Tracker,
987 snatch_guard: &SnatchGuard,
988 ) {
989 profiling::scope!("insert_barriers_from_device_tracker");
990
991 let buffer_barriers = base
992 .buffers
993 .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)
994 .collect::<Vec<_>>();
995
996 let texture_barriers = base
997 .textures
998 .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)
999 .collect::<Vec<_>>();
1000
1001 unsafe {
1002 raw.transition_buffers(&buffer_barriers);
1003 raw.transition_textures(&texture_barriers);
1004 }
1005 }
1006
1007 fn encode_commands(
1008 device: &Arc<Device>,
1009 cmd_buf_data: &mut CommandBufferMutable,
1010 ) -> Result<(), CommandEncoderError> {
1011 device.check_is_valid()?;
1012 let snatch_guard = device.snatchable_lock.read();
1013 let mut debug_scope_depth = 0;
1014
1015 if cmd_buf_data.encoder.api == EncodingApi::Raw {
1016 assert!(cmd_buf_data.commands.is_empty());
1019 }
1020
1021 let commands = mem::take(&mut cmd_buf_data.commands);
1022
1023 #[cfg(feature = "trace")]
1024 if device.trace.lock().is_some() {
1025 cmd_buf_data.trace_commands = Some(
1026 commands
1027 .iter()
1028 .map(crate::device::trace::IntoTrace::to_trace)
1029 .collect(),
1030 );
1031 }
1032
1033 for command in commands {
1034 if matches!(
1035 command,
1036 ArcCommand::RunRenderPass { .. } | ArcCommand::RunComputePass { .. }
1037 ) {
1038 let mut state = EncodingState {
1043 device,
1044 raw_encoder: &mut cmd_buf_data.encoder,
1045 tracker: &mut cmd_buf_data.trackers,
1046 buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1047 texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1048 as_actions: &mut cmd_buf_data.as_actions,
1049 temp_resources: &mut cmd_buf_data.temp_resources,
1050 indirect_draw_validation_resources: &mut cmd_buf_data
1051 .indirect_draw_validation_resources,
1052 snatch_guard: &snatch_guard,
1053 debug_scope_depth: &mut debug_scope_depth,
1054 };
1055
1056 match command {
1057 ArcCommand::RunRenderPass {
1058 pass,
1059 color_attachments,
1060 depth_stencil_attachment,
1061 timestamp_writes,
1062 occlusion_query_set,
1063 multiview_mask,
1064 } => {
1065 api_log!(
1066 "Begin encoding render pass with '{}' label",
1067 pass.label.as_deref().unwrap_or("")
1068 );
1069 let res = render::encode_render_pass(
1070 &mut state,
1071 pass,
1072 color_attachments,
1073 depth_stencil_attachment,
1074 timestamp_writes,
1075 occlusion_query_set,
1076 multiview_mask,
1077 );
1078 match res.as_ref() {
1079 Err(err) => {
1080 api_log!("Finished encoding render pass ({err:?})")
1081 }
1082 Ok(_) => {
1083 api_log!("Finished encoding render pass (success)")
1084 }
1085 }
1086 res?;
1087 }
1088 ArcCommand::RunComputePass {
1089 pass,
1090 timestamp_writes,
1091 } => {
1092 api_log!(
1093 "Begin encoding compute pass with '{}' label",
1094 pass.label.as_deref().unwrap_or("")
1095 );
1096 let res = compute::encode_compute_pass(&mut state, pass, timestamp_writes);
1097 match res.as_ref() {
1098 Err(err) => {
1099 api_log!("Finished encoding compute pass ({err:?})")
1100 }
1101 Ok(_) => {
1102 api_log!("Finished encoding compute pass (success)")
1103 }
1104 }
1105 res?;
1106 }
1107 _ => unreachable!(),
1108 }
1109 } else {
1110 let raw_encoder = cmd_buf_data.encoder.open_if_closed()?;
1116 let mut state = EncodingState {
1117 device,
1118 raw_encoder,
1119 tracker: &mut cmd_buf_data.trackers,
1120 buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1121 texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1122 as_actions: &mut cmd_buf_data.as_actions,
1123 temp_resources: &mut cmd_buf_data.temp_resources,
1124 indirect_draw_validation_resources: &mut cmd_buf_data
1125 .indirect_draw_validation_resources,
1126 snatch_guard: &snatch_guard,
1127 debug_scope_depth: &mut debug_scope_depth,
1128 };
1129 match command {
1130 ArcCommand::CopyBufferToBuffer {
1131 src,
1132 src_offset,
1133 dst,
1134 dst_offset,
1135 size,
1136 } => {
1137 transfer::copy_buffer_to_buffer(
1138 &mut state, &src, src_offset, &dst, dst_offset, size,
1139 )?;
1140 }
1141 ArcCommand::CopyBufferToTexture { src, dst, size } => {
1142 transfer::copy_buffer_to_texture(&mut state, &src, &dst, &size)?;
1143 }
1144 ArcCommand::CopyTextureToBuffer { src, dst, size } => {
1145 transfer::copy_texture_to_buffer(&mut state, &src, &dst, &size)?;
1146 }
1147 ArcCommand::CopyTextureToTexture { src, dst, size } => {
1148 transfer::copy_texture_to_texture(&mut state, &src, &dst, &size)?;
1149 }
1150 ArcCommand::ClearBuffer { dst, offset, size } => {
1151 clear::clear_buffer(&mut state, dst, offset, size)?;
1152 }
1153 ArcCommand::ClearTexture {
1154 dst,
1155 subresource_range,
1156 } => {
1157 clear::clear_texture_cmd(&mut state, dst, &subresource_range)?;
1158 }
1159 ArcCommand::WriteTimestamp {
1160 query_set,
1161 query_index,
1162 } => {
1163 query::write_timestamp(&mut state, query_set, query_index)?;
1164 }
1165 ArcCommand::ResolveQuerySet {
1166 query_set,
1167 start_query,
1168 query_count,
1169 destination,
1170 destination_offset,
1171 } => {
1172 query::resolve_query_set(
1173 &mut state,
1174 query_set,
1175 start_query,
1176 query_count,
1177 destination,
1178 destination_offset,
1179 )?;
1180 }
1181 ArcCommand::PushDebugGroup(label) => {
1182 push_debug_group(&mut state, &label)?;
1183 }
1184 ArcCommand::PopDebugGroup => {
1185 pop_debug_group(&mut state)?;
1186 }
1187 ArcCommand::InsertDebugMarker(label) => {
1188 insert_debug_marker(&mut state, &label)?;
1189 }
1190 ArcCommand::BuildAccelerationStructures { blas, tlas } => {
1191 ray_tracing::build_acceleration_structures(&mut state, blas, tlas)?;
1192 }
1193 ArcCommand::TransitionResources {
1194 buffer_transitions,
1195 texture_transitions,
1196 } => {
1197 transition_resources::transition_resources(
1198 &mut state,
1199 buffer_transitions,
1200 texture_transitions,
1201 )?;
1202 }
1203 ArcCommand::RunComputePass { .. } | ArcCommand::RunRenderPass { .. } => {
1204 unreachable!()
1205 }
1206 }
1207 }
1208 }
1209
1210 if debug_scope_depth > 0 {
1211 Err(CommandEncoderError::DebugGroupError(
1212 DebugGroupError::MissingPop,
1213 ))?;
1214 }
1215
1216 cmd_buf_data.encoder.close_if_open()?;
1218
1219 Ok(())
1223 }
1224
1225 fn finish(
1226 self: &Arc<Self>,
1227 desc: &wgt::CommandBufferDescriptor<Label>,
1228 ) -> (Arc<CommandBuffer>, Option<CommandEncoderError>) {
1229 let mut cmd_enc_status = self.data.lock();
1230
1231 let res = match cmd_enc_status.finish() {
1232 CommandEncoderStatus::Finished(mut cmd_buf_data) => {
1233 match Self::encode_commands(&self.device, &mut cmd_buf_data) {
1234 Ok(()) => Ok(cmd_buf_data),
1235 Err(error) => Err(EncoderErrorState {
1236 error,
1237 #[cfg(feature = "trace")]
1238 trace_commands: mem::take(&mut cmd_buf_data.trace_commands),
1239 }),
1240 }
1241 }
1242 CommandEncoderStatus::Error(error_state) => Err(error_state),
1243 _ => unreachable!(),
1244 };
1245
1246 let (data, error) = match res {
1247 Err(EncoderErrorState {
1248 error,
1249 #[cfg(feature = "trace")]
1250 trace_commands,
1251 }) => {
1252 #[cfg(feature = "trace")]
1256 if let Some(trace) = self.device.trace.lock().as_mut() {
1257 use alloc::string::ToString;
1258
1259 trace.add(crate::device::trace::Action::FailedCommands {
1260 commands: trace_commands,
1261 failed_at_submit: None,
1262 error: error.to_string(),
1263 });
1264 }
1265
1266 if error.is_destroyed_error() {
1267 (make_error_state(error), None)
1270 } else {
1271 (make_error_state(error.clone()), Some(error))
1272 }
1273 }
1274
1275 Ok(data) => (CommandEncoderStatus::Finished(data), None),
1276 };
1277
1278 let cmd_buf = Arc::new(CommandBuffer {
1279 device: self.device.clone(),
1280 label: desc.label.to_string(),
1281 data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),
1282 });
1283
1284 (cmd_buf, error)
1285 }
1286}
1287
1288impl CommandBuffer {
1289 #[doc(hidden)]
1295 pub fn from_trace(device: &Arc<Device>, commands: Vec<Command<ArcReferences>>) -> Arc<Self> {
1296 let encoder = device.create_command_encoder(&None).unwrap();
1297 let mut cmd_enc_status = encoder.data.lock();
1298 cmd_enc_status.replay(commands);
1299 drop(cmd_enc_status);
1300
1301 let (cmd_buf, error) = encoder.finish(&wgt::CommandBufferDescriptor { label: None });
1302 if let Some(err) = error {
1303 panic!("CommandEncoder::finish failed: {err}");
1304 }
1305
1306 cmd_buf
1307 }
1308
1309 pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {
1310 use CommandEncoderStatus as St;
1311 match mem::replace(
1312 &mut *self.data.lock(),
1313 make_error_state(EncoderStateError::Submitted),
1314 ) {
1315 St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
1316 St::Error(EncoderErrorState {
1317 #[cfg(feature = "trace")]
1318 trace_commands: _,
1319 error,
1320 }) => Err(error),
1321 St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(),
1322 }
1323 }
1324}
1325
1326crate::impl_resource_type!(CommandBuffer);
1327crate::impl_labeled!(CommandBuffer);
1328crate::impl_parent_device!(CommandBuffer);
1329crate::impl_storage_item!(CommandBuffer);
1330
1331#[doc(hidden)]
1343#[derive(Debug, Clone)]
1344#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1345pub struct BasePass<C, E> {
1346 pub label: Option<String>,
1347
1348 #[cfg_attr(feature = "serde", serde(skip, default = "Option::default"))]
1356 pub error: Option<E>,
1357
1358 pub commands: Vec<C>,
1364
1365 pub dynamic_offsets: Vec<wgt::DynamicOffset>,
1370
1371 pub string_data: Vec<u8>,
1376
1377 pub immediates_data: Vec<u32>,
1382}
1383
1384impl<C: Clone, E: Clone> BasePass<C, E> {
1385 fn new(label: &Label) -> Self {
1386 Self {
1387 label: label.as_deref().map(str::to_owned),
1388 error: None,
1389 commands: Vec::new(),
1390 dynamic_offsets: Vec::new(),
1391 string_data: Vec::new(),
1392 immediates_data: Vec::new(),
1393 }
1394 }
1395
1396 fn new_invalid(label: &Label, err: E) -> Self {
1397 Self {
1398 label: label.as_deref().map(str::to_owned),
1399 error: Some(err),
1400 commands: Vec::new(),
1401 dynamic_offsets: Vec::new(),
1402 string_data: Vec::new(),
1403 immediates_data: Vec::new(),
1404 }
1405 }
1406
1407 fn take(&mut self) -> Result<BasePass<C, Infallible>, E> {
1414 match self.error.as_ref() {
1415 Some(err) => Err(err.clone()),
1416 None => Ok(BasePass {
1417 label: self.label.clone(),
1418 error: None,
1419 commands: mem::take(&mut self.commands),
1420 dynamic_offsets: mem::take(&mut self.dynamic_offsets),
1421 string_data: mem::take(&mut self.string_data),
1422 immediates_data: mem::take(&mut self.immediates_data),
1423 }),
1424 }
1425 }
1426}
1427
1428macro_rules! pass_base {
1451 ($pass:expr, $scope:expr $(,)?) => {
1452 match (&$pass.parent, &$pass.base.error) {
1453 (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),
1455 (&Some(_), &Some(_)) => return Ok(()),
1457 (&Some(_), &None) => &mut $pass.base,
1459 }
1460 };
1461}
1462pub(crate) use pass_base;
1463
1464macro_rules! pass_try {
1476 ($base:expr, $scope:expr, $res:expr $(,)?) => {
1477 match $res.map_pass_err($scope) {
1478 Ok(val) => val,
1479 Err(err) => {
1480 $base.error.get_or_insert(err);
1481 return Ok(());
1482 }
1483 }
1484 };
1485}
1486pub(crate) use pass_try;
1487
1488#[derive(Clone, Debug, Error)]
1493#[non_exhaustive]
1494pub enum EncoderStateError {
1495 #[error("Encoder is invalid")]
1500 Invalid,
1501
1502 #[error("Encoding must not have ended")]
1505 Ended,
1506
1507 #[error("Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
1513 Locked,
1514
1515 #[error(
1518 "Encoder is not currently locked. A pass can only be ended while the encoder is locked."
1519 )]
1520 Unlocked,
1521
1522 #[error("This command buffer has already been submitted.")]
1527 Submitted,
1528}
1529
1530impl WebGpuError for EncoderStateError {
1531 fn webgpu_error_type(&self) -> ErrorType {
1532 match self {
1533 EncoderStateError::Invalid
1534 | EncoderStateError::Ended
1535 | EncoderStateError::Locked
1536 | EncoderStateError::Unlocked
1537 | EncoderStateError::Submitted => ErrorType::Validation,
1538 }
1539 }
1540}
1541
1542#[derive(Clone, Debug, Error)]
1543#[non_exhaustive]
1544pub enum CommandEncoderError {
1545 #[error(transparent)]
1546 State(#[from] EncoderStateError),
1547 #[error(transparent)]
1548 Device(#[from] DeviceError),
1549 #[error(transparent)]
1550 InvalidResource(#[from] InvalidResourceError),
1551 #[error(transparent)]
1552 DestroyedResource(#[from] DestroyedResourceError),
1553 #[error(transparent)]
1554 ResourceUsage(#[from] ResourceUsageCompatibilityError),
1555 #[error(transparent)]
1556 DebugGroupError(#[from] DebugGroupError),
1557 #[error(transparent)]
1558 MissingFeatures(#[from] MissingFeatures),
1559 #[error(transparent)]
1560 Transfer(#[from] TransferError),
1561 #[error(transparent)]
1562 Clear(#[from] ClearError),
1563 #[error(transparent)]
1564 Query(#[from] QueryError),
1565 #[error(transparent)]
1566 BuildAccelerationStructure(#[from] BuildAccelerationStructureError),
1567 #[error(transparent)]
1568 TransitionResources(#[from] TransitionResourcesError),
1569 #[error(transparent)]
1570 ComputePass(#[from] ComputePassError),
1571 #[error(transparent)]
1572 RenderPass(#[from] RenderPassError),
1573}
1574
1575impl CommandEncoderError {
1576 fn is_destroyed_error(&self) -> bool {
1577 matches!(
1578 self,
1579 Self::DestroyedResource(_)
1580 | Self::Clear(ClearError::DestroyedResource(_))
1581 | Self::Query(QueryError::DestroyedResource(_))
1582 | Self::ComputePass(ComputePassError {
1583 inner: ComputePassErrorInner::DestroyedResource(_),
1584 ..
1585 })
1586 | Self::RenderPass(RenderPassError {
1587 inner: RenderPassErrorInner::DestroyedResource(_),
1588 ..
1589 })
1590 | Self::RenderPass(RenderPassError {
1591 inner: RenderPassErrorInner::RenderCommand(
1592 RenderCommandError::DestroyedResource(_)
1593 ),
1594 ..
1595 })
1596 | Self::RenderPass(RenderPassError {
1597 inner: RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError(
1598 BindingError::DestroyedResource(_)
1599 )),
1600 ..
1601 })
1602 )
1603 }
1604}
1605
1606impl WebGpuError for CommandEncoderError {
1607 fn webgpu_error_type(&self) -> ErrorType {
1608 match self {
1609 Self::Device(e) => e.webgpu_error_type(),
1610 Self::InvalidResource(e) => e.webgpu_error_type(),
1611 Self::DebugGroupError(e) => e.webgpu_error_type(),
1612 Self::MissingFeatures(e) => e.webgpu_error_type(),
1613 Self::State(e) => e.webgpu_error_type(),
1614 Self::DestroyedResource(e) => e.webgpu_error_type(),
1615 Self::Transfer(e) => e.webgpu_error_type(),
1616 Self::Clear(e) => e.webgpu_error_type(),
1617 Self::Query(e) => e.webgpu_error_type(),
1618 Self::BuildAccelerationStructure(e) => e.webgpu_error_type(),
1619 Self::TransitionResources(e) => e.webgpu_error_type(),
1620 Self::ResourceUsage(e) => e.webgpu_error_type(),
1621 Self::ComputePass(e) => e.webgpu_error_type(),
1622 Self::RenderPass(e) => e.webgpu_error_type(),
1623 }
1624 }
1625}
1626
1627#[derive(Clone, Debug, Error)]
1628#[non_exhaustive]
1629pub enum DebugGroupError {
1630 #[error("Cannot pop debug group, because number of pushed debug groups is zero")]
1631 InvalidPop,
1632 #[error("A debug group was not popped before the encoder was finished")]
1633 MissingPop,
1634}
1635
1636impl WebGpuError for DebugGroupError {
1637 fn webgpu_error_type(&self) -> ErrorType {
1638 match self {
1639 Self::InvalidPop | Self::MissingPop => ErrorType::Validation,
1640 }
1641 }
1642}
1643
1644#[derive(Clone, Debug, Error)]
1645#[non_exhaustive]
1646pub enum TimestampWritesError {
1647 #[error(
1648 "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
1649 )]
1650 IndicesEqual { idx: u32 },
1651 #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
1652 IndicesMissing,
1653}
1654
1655impl WebGpuError for TimestampWritesError {
1656 fn webgpu_error_type(&self) -> ErrorType {
1657 match self {
1658 Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,
1659 }
1660 }
1661}
1662
1663impl Global {
1664 fn resolve_buffer_id(
1665 &self,
1666 buffer_id: Id<id::markers::Buffer>,
1667 ) -> Result<Arc<crate::resource::Buffer>, InvalidResourceError> {
1668 self.hub.buffers.get(buffer_id).get()
1669 }
1670
1671 fn resolve_texture_id(
1672 &self,
1673 texture_id: Id<id::markers::Texture>,
1674 ) -> Result<Arc<crate::resource::Texture>, InvalidResourceError> {
1675 self.hub.textures.get(texture_id).get()
1676 }
1677
1678 fn resolve_query_set(
1679 &self,
1680 query_set_id: Id<id::markers::QuerySet>,
1681 ) -> Result<Arc<QuerySet>, InvalidResourceError> {
1682 self.hub.query_sets.get(query_set_id).get()
1683 }
1684
1685 pub fn command_encoder_finish(
1693 &self,
1694 encoder_id: id::CommandEncoderId,
1695 desc: &wgt::CommandBufferDescriptor<Label>,
1696 id_in: Option<id::CommandBufferId>,
1697 ) -> (id::CommandBufferId, Option<(String, CommandEncoderError)>) {
1698 profiling::scope!("CommandEncoder::finish");
1699
1700 let hub = &self.hub;
1701 let cmd_enc = hub.command_encoders.get(encoder_id);
1702
1703 let (cmd_buf, opt_error) = cmd_enc.finish(desc);
1704 let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(cmd_buf);
1705
1706 (
1707 cmd_buf_id,
1708 opt_error.map(|error| (cmd_enc.label.clone(), error)),
1709 )
1710 }
1711
1712 pub fn command_encoder_push_debug_group(
1713 &self,
1714 encoder_id: id::CommandEncoderId,
1715 label: &str,
1716 ) -> Result<(), EncoderStateError> {
1717 profiling::scope!("CommandEncoder::push_debug_group");
1718 api_log!("CommandEncoder::push_debug_group {label}");
1719
1720 let hub = &self.hub;
1721
1722 let cmd_enc = hub.command_encoders.get(encoder_id);
1723 let mut cmd_buf_data = cmd_enc.data.lock();
1724
1725 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1726 Ok(ArcCommand::PushDebugGroup(label.to_owned()))
1727 })
1728 }
1729
1730 pub fn command_encoder_insert_debug_marker(
1731 &self,
1732 encoder_id: id::CommandEncoderId,
1733 label: &str,
1734 ) -> Result<(), EncoderStateError> {
1735 profiling::scope!("CommandEncoder::insert_debug_marker");
1736 api_log!("CommandEncoder::insert_debug_marker {label}");
1737
1738 let hub = &self.hub;
1739
1740 let cmd_enc = hub.command_encoders.get(encoder_id);
1741 let mut cmd_buf_data = cmd_enc.data.lock();
1742
1743 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1744 Ok(ArcCommand::InsertDebugMarker(label.to_owned()))
1745 })
1746 }
1747
1748 pub fn command_encoder_pop_debug_group(
1749 &self,
1750 encoder_id: id::CommandEncoderId,
1751 ) -> Result<(), EncoderStateError> {
1752 profiling::scope!("CommandEncoder::pop_debug_marker");
1753 api_log!("CommandEncoder::pop_debug_group");
1754
1755 let hub = &self.hub;
1756
1757 let cmd_enc = hub.command_encoders.get(encoder_id);
1758 let mut cmd_buf_data = cmd_enc.data.lock();
1759
1760 cmd_buf_data
1761 .push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::PopDebugGroup) })
1762 }
1763
1764 fn validate_pass_timestamp_writes<E>(
1765 device: &Device,
1766 query_sets: &Storage<Fallible<QuerySet>>,
1767 timestamp_writes: &PassTimestampWrites,
1768 ) -> Result<ArcPassTimestampWrites, E>
1769 where
1770 E: From<TimestampWritesError>
1771 + From<QueryUseError>
1772 + From<DeviceError>
1773 + From<MissingFeatures>
1774 + From<InvalidResourceError>,
1775 {
1776 let &PassTimestampWrites {
1777 query_set,
1778 beginning_of_pass_write_index,
1779 end_of_pass_write_index,
1780 } = timestamp_writes;
1781
1782 device.require_features(wgt::Features::TIMESTAMP_QUERY)?;
1783
1784 let query_set = query_sets.get(query_set).get()?;
1785
1786 query_set.same_device(device)?;
1787
1788 for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
1789 .into_iter()
1790 .flatten()
1791 {
1792 query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
1793 }
1794
1795 if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
1796 if begin == end {
1797 return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());
1798 }
1799 }
1800
1801 if beginning_of_pass_write_index
1802 .or(end_of_pass_write_index)
1803 .is_none()
1804 {
1805 return Err(TimestampWritesError::IndicesMissing.into());
1806 }
1807
1808 Ok(ArcPassTimestampWrites {
1809 query_set,
1810 beginning_of_pass_write_index,
1811 end_of_pass_write_index,
1812 })
1813 }
1814}
1815
1816pub(crate) fn push_debug_group(
1817 state: &mut EncodingState,
1818 label: &str,
1819) -> Result<(), CommandEncoderError> {
1820 *state.debug_scope_depth += 1;
1821
1822 if !state
1823 .device
1824 .instance_flags
1825 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1826 {
1827 unsafe { state.raw_encoder.begin_debug_marker(label) };
1828 }
1829
1830 Ok(())
1831}
1832
1833pub(crate) fn insert_debug_marker(
1834 state: &mut EncodingState,
1835 label: &str,
1836) -> Result<(), CommandEncoderError> {
1837 if !state
1838 .device
1839 .instance_flags
1840 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1841 {
1842 unsafe { state.raw_encoder.insert_debug_marker(label) };
1843 }
1844
1845 Ok(())
1846}
1847
1848pub(crate) fn pop_debug_group(state: &mut EncodingState) -> Result<(), CommandEncoderError> {
1849 if *state.debug_scope_depth == 0 {
1850 return Err(DebugGroupError::InvalidPop.into());
1851 }
1852 *state.debug_scope_depth -= 1;
1853
1854 if !state
1855 .device
1856 .instance_flags
1857 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1858 {
1859 unsafe { state.raw_encoder.end_debug_marker() };
1860 }
1861
1862 Ok(())
1863}
1864
1865fn immediates_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
1866where
1867 PushFn: FnMut(u32, &[u32]),
1868{
1869 let mut count_words = 0_u32;
1870 let size_words = size_bytes / wgt::IMMEDIATE_DATA_ALIGNMENT;
1871 while count_words < size_words {
1872 let count_bytes = count_words * wgt::IMMEDIATE_DATA_ALIGNMENT;
1873 let size_to_write_words =
1874 (size_words - count_words).min(IMMEDIATES_CLEAR_ARRAY.len() as u32);
1875
1876 push_fn(
1877 offset + count_bytes,
1878 &IMMEDIATES_CLEAR_ARRAY[0..size_to_write_words as usize],
1879 );
1880
1881 count_words += size_to_write_words;
1882 }
1883}
1884
1885#[derive(Debug, Copy, Clone)]
1886struct StateChange<T> {
1887 last_state: Option<T>,
1888}
1889
1890impl<T: Copy + PartialEq> StateChange<T> {
1891 fn new() -> Self {
1892 Self { last_state: None }
1893 }
1894 fn set_and_check_redundant(&mut self, new_state: T) -> bool {
1895 let already_set = self.last_state == Some(new_state);
1896 self.last_state = Some(new_state);
1897 already_set
1898 }
1899 fn reset(&mut self) {
1900 self.last_state = None;
1901 }
1902}
1903
1904impl<T: Copy + PartialEq> Default for StateChange<T> {
1905 fn default() -> Self {
1906 Self::new()
1907 }
1908}
1909
1910#[derive(Debug)]
1911struct BindGroupStateChange {
1912 last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],
1913}
1914
1915impl BindGroupStateChange {
1916 fn new() -> Self {
1917 Self {
1918 last_states: [StateChange::new(); hal::MAX_BIND_GROUPS],
1919 }
1920 }
1921
1922 fn set_and_check_redundant(
1923 &mut self,
1924 bind_group_id: Option<id::BindGroupId>,
1925 index: u32,
1926 dynamic_offsets: &mut Vec<u32>,
1927 offsets: &[wgt::DynamicOffset],
1928 ) -> bool {
1929 if offsets.is_empty() {
1931 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1934 if current_bind_group.set_and_check_redundant(bind_group_id) {
1936 return true;
1937 }
1938 }
1939 } else {
1940 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1944 current_bind_group.reset();
1945 }
1946 dynamic_offsets.extend_from_slice(offsets);
1947 }
1948 false
1949 }
1950 fn reset(&mut self) {
1951 self.last_states = [StateChange::new(); hal::MAX_BIND_GROUPS];
1952 }
1953}
1954
1955impl Default for BindGroupStateChange {
1956 fn default() -> Self {
1957 Self::new()
1958 }
1959}
1960
1961trait MapPassErr<T> {
1963 fn map_pass_err(self, scope: PassErrorScope) -> T;
1964}
1965
1966impl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>
1967where
1968 E: MapPassErr<F>,
1969{
1970 fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {
1971 self.map_err(|err| err.map_pass_err(scope))
1972 }
1973}
1974
1975impl MapPassErr<PassStateError> for EncoderStateError {
1976 fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {
1977 PassStateError { scope, inner: self }
1978 }
1979}
1980
1981#[derive(Clone, Copy, Debug)]
1982pub enum DrawKind {
1983 Draw,
1984 DrawIndirect,
1985 MultiDrawIndirect,
1986 MultiDrawIndirectCount,
1987}
1988
1989#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1991#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1992pub enum DrawCommandFamily {
1993 Draw,
1994 DrawIndexed,
1995 DrawMeshTasks,
1996}
1997
1998#[derive(Clone, Copy, Debug, Error)]
2008pub enum PassErrorScope {
2009 #[error("In a bundle parameter")]
2012 Bundle,
2013 #[error("In a pass parameter")]
2014 Pass,
2015 #[error("In a set_bind_group command")]
2016 SetBindGroup,
2017 #[error("In a set_pipeline command")]
2018 SetPipelineRender,
2019 #[error("In a set_pipeline command")]
2020 SetPipelineCompute,
2021 #[error("In a set_immediates command")]
2022 SetImmediate,
2023 #[error("In a set_vertex_buffer command")]
2024 SetVertexBuffer,
2025 #[error("In a set_index_buffer command")]
2026 SetIndexBuffer,
2027 #[error("In a set_blend_constant command")]
2028 SetBlendConstant,
2029 #[error("In a set_stencil_reference command")]
2030 SetStencilReference,
2031 #[error("In a set_viewport command")]
2032 SetViewport,
2033 #[error("In a set_scissor_rect command")]
2034 SetScissorRect,
2035 #[error("In a draw command, kind: {kind:?}")]
2036 Draw {
2037 kind: DrawKind,
2038 family: DrawCommandFamily,
2039 },
2040 #[error("In a write_timestamp command")]
2041 WriteTimestamp,
2042 #[error("In a begin_occlusion_query command")]
2043 BeginOcclusionQuery,
2044 #[error("In a end_occlusion_query command")]
2045 EndOcclusionQuery,
2046 #[error("In a begin_pipeline_statistics_query command")]
2047 BeginPipelineStatisticsQuery,
2048 #[error("In a end_pipeline_statistics_query command")]
2049 EndPipelineStatisticsQuery,
2050 #[error("In a execute_bundle command")]
2051 ExecuteBundle,
2052 #[error("In a dispatch command, indirect:{indirect}")]
2053 Dispatch { indirect: bool },
2054 #[error("In a push_debug_group command")]
2055 PushDebugGroup,
2056 #[error("In a pop_debug_group command")]
2057 PopDebugGroup,
2058 #[error("In a insert_debug_marker command")]
2059 InsertDebugMarker,
2060}
2061
2062#[derive(Clone, Debug, Error)]
2064#[error("{scope}")]
2065pub struct PassStateError {
2066 pub scope: PassErrorScope,
2067 #[source]
2068 pub(super) inner: EncoderStateError,
2069}
2070
2071impl WebGpuError for PassStateError {
2072 fn webgpu_error_type(&self) -> ErrorType {
2073 let Self { scope: _, inner } = self;
2074 inner.webgpu_error_type()
2075 }
2076}