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 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 AttachmentError, AttachmentErrorLocation, ColorAttachmentError, ColorAttachments, LoadOp,
61 PassChannel, RenderBasePass, RenderPass, RenderPassColorAttachment,
62 RenderPassDepthStencilAttachment, RenderPassDescriptor, RenderPassError,
63 RenderPassErrorInner, ResolvedPassChannel, ResolvedRenderPassDepthStencilAttachment,
64 ResolvedRenderPassDescriptor, 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_dst_stride_of_indirect_args, get_src_stride_of_indirect_args, VertexState},
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 use timestamp_writes::PassTimestampWrites;
87
88use crate::binding_model::{BindGroup, BindingError};
89use crate::device::queue::TempResource;
90use crate::device::{Device, DeviceError, MissingFeatures};
91use crate::lock::{rank, Mutex};
92use crate::snatch::SnatchGuard;
93
94use crate::init_tracker::BufferInitTrackerAction;
95use crate::ray_tracing::{AsAction, BuildAccelerationStructureError};
96use crate::resource::{
97 DestroyedResourceError, InvalidOrDestroyedResourceError, InvalidResourceError, Labeled,
98 ParentDevice as _, QuerySet,
99};
100use crate::track::{DeviceTracker, ResourceUsageCompatibilityError, Tracker, UsageScope};
101use crate::{api_log, global::Global, id, resource_log, Label};
102use crate::{hal_label, LabelHelpers};
103
104use wgt::error::{ErrorType, WebGpuError};
105
106use thiserror::Error;
107
108pub type TexelCopyBufferInfo = ffi::TexelCopyBufferInfo;
110pub type TexelCopyTextureInfo = ffi::TexelCopyTextureInfo;
112pub type CopyExternalImageDestInfo = ffi::CopyExternalImageDestInfo;
114
115pub(crate) struct EncoderErrorState {
116 error: CommandEncoderError,
117
118 #[cfg(feature = "trace")]
119 trace_commands: Option<Vec<Command<PointerReferences>>>,
120}
121
122fn make_error_state<E: Into<CommandEncoderError>>(error: E) -> CommandEncoderStatus {
132 CommandEncoderStatus::Error(EncoderErrorState {
133 error: error.into(),
134
135 #[cfg(feature = "trace")]
136 trace_commands: None,
137 })
138}
139
140pub(crate) enum CommandEncoderStatus {
146 Recording(CommandBufferMutable),
155
156 Locked(CommandBufferMutable),
165
166 Consumed,
167
168 Finished(CommandBufferMutable),
181
182 Error(EncoderErrorState),
187
188 Transitioning,
191}
192
193impl CommandEncoderStatus {
194 #[doc(hidden)]
195 fn replay(&mut self, commands: Vec<Command<ArcReferences>>) {
196 let Self::Recording(cmd_buf_data) = self else {
197 panic!("encoder should be in the recording state");
198 };
199 cmd_buf_data.commands.extend(commands);
200 }
201
202 fn push_with<F: FnOnce() -> Result<ArcCommand, E>, E: Clone + Into<CommandEncoderError>>(
218 &mut self,
219 f: F,
220 ) -> Result<(), EncoderStateError> {
221 match self {
222 Self::Recording(cmd_buf_data) => {
223 cmd_buf_data.encoder.api.set(EncodingApi::Wgpu);
224 match f() {
225 Ok(cmd) => cmd_buf_data.commands.push(cmd),
226 Err(err) => {
227 self.invalidate(err);
228 }
229 }
230 Ok(())
231 }
232 Self::Locked(_) => {
233 self.invalidate(EncoderStateError::Locked);
236 Ok(())
237 }
238 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
241 Self::Consumed => Err(EncoderStateError::Ended),
242 Self::Error(_) => Ok(()),
245 Self::Transitioning => unreachable!(),
246 }
247 }
248
249 fn with_buffer<
263 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
264 E: Clone + Into<CommandEncoderError>,
265 >(
266 &mut self,
267 api: EncodingApi,
268 f: F,
269 ) -> Result<(), EncoderStateError> {
270 match self {
271 Self::Recording(inner) => {
272 inner.encoder.api.set(api);
273 RecordingGuard { inner: self }.record(f);
274 Ok(())
275 }
276 Self::Locked(_) => {
277 self.invalidate(EncoderStateError::Locked);
280 Ok(())
281 }
282 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
285 Self::Consumed => Err(EncoderStateError::Ended),
286 Self::Error(_) => Ok(()),
289 Self::Transitioning => unreachable!(),
290 }
291 }
292
293 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
301 &mut self,
302 f: F,
303 ) -> T {
304 match self {
305 Self::Recording(inner) => {
306 inner.encoder.api.set(EncodingApi::Raw);
307 RecordingGuard { inner: self }.record_as_hal_mut(f)
308 }
309 Self::Locked(_) => {
310 self.invalidate(EncoderStateError::Locked);
311 f(None)
312 }
313 Self::Finished(_) => {
314 self.invalidate(EncoderStateError::Ended);
315 f(None)
316 }
317 Self::Consumed => f(None),
318 Self::Error(_) => f(None),
319 Self::Transitioning => unreachable!(),
320 }
321 }
322
323 fn lock_encoder(&mut self) -> Result<(), EncoderStateError> {
329 match mem::replace(self, Self::Transitioning) {
330 Self::Recording(inner) => {
331 *self = Self::Locked(inner);
332 Ok(())
333 }
334 st @ Self::Finished(_) => {
335 *self = st;
339 Err(EncoderStateError::Ended)
340 }
341 Self::Locked(_) => Err(self.invalidate(EncoderStateError::Locked)),
342 st @ Self::Consumed => {
343 *self = st;
344 Err(EncoderStateError::Ended)
345 }
346 st @ Self::Error(_) => {
347 *self = st;
348 Err(EncoderStateError::Invalid)
349 }
350 Self::Transitioning => unreachable!(),
351 }
352 }
353
354 fn unlock_encoder(&mut self) -> Result<(), EncoderStateError> {
364 match mem::replace(self, Self::Transitioning) {
365 Self::Locked(inner) => {
366 *self = Self::Recording(inner);
367 Ok(())
368 }
369 st @ Self::Finished(_) => {
370 *self = st;
371 Err(EncoderStateError::Ended)
372 }
373 Self::Recording(_) => {
374 *self = make_error_state(EncoderStateError::Unlocked);
375 Err(EncoderStateError::Unlocked)
376 }
377 st @ Self::Consumed => {
378 *self = st;
379 Err(EncoderStateError::Ended)
380 }
381 st @ Self::Error(_) => {
382 *self = st;
385 Ok(())
386 }
387 Self::Transitioning => unreachable!(),
388 }
389 }
390
391 fn finish(&mut self) -> Self {
392 match mem::replace(self, Self::Consumed) {
395 Self::Recording(inner) => {
396 if inner.encoder.api != EncodingApi::Raw {
399 assert!(!inner.encoder.is_open);
400 }
401 Self::Finished(inner)
402 }
403 Self::Consumed | Self::Finished(_) => make_error_state(EncoderStateError::Ended),
404 Self::Locked(_) => make_error_state(EncoderStateError::Locked),
405 st @ Self::Error(_) => st,
406 Self::Transitioning => unreachable!(),
407 }
408 }
409
410 fn invalidate<E: Clone + Into<CommandEncoderError>>(&mut self, err: E) -> E {
418 #[cfg(feature = "trace")]
419 let trace_commands = match self {
420 Self::Recording(cmd_buf_data) => Some(
421 mem::take(&mut cmd_buf_data.commands)
422 .into_iter()
423 .map(crate::device::trace::IntoTrace::into_trace)
424 .collect(),
425 ),
426 _ => None,
427 };
428
429 let enc_err = err.clone().into();
430 api_log!("Invalidating command encoder: {enc_err:?}");
431 *self = Self::Error(EncoderErrorState {
432 error: enc_err,
433 #[cfg(feature = "trace")]
434 trace_commands,
435 });
436 err
437 }
438}
439
440pub(crate) struct RecordingGuard<'a> {
453 inner: &'a mut CommandEncoderStatus,
454}
455
456impl<'a> RecordingGuard<'a> {
457 pub(crate) fn mark_successful(self) {
458 mem::forget(self)
459 }
460
461 fn record<
462 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
463 E: Clone + Into<CommandEncoderError>,
464 >(
465 mut self,
466 f: F,
467 ) {
468 match f(&mut self) {
469 Ok(()) => self.mark_successful(),
470 Err(err) => {
471 self.inner.invalidate(err);
472 }
473 }
474 }
475
476 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
479 mut self,
480 f: F,
481 ) -> T {
482 let res = f(Some(&mut self));
483 self.mark_successful();
484 res
485 }
486}
487
488impl<'a> Drop for RecordingGuard<'a> {
489 fn drop(&mut self) {
490 if matches!(*self.inner, CommandEncoderStatus::Error(_)) {
491 return;
493 }
494 self.inner.invalidate(EncoderStateError::Invalid);
495 }
496}
497
498impl<'a> ops::Deref for RecordingGuard<'a> {
499 type Target = CommandBufferMutable;
500
501 fn deref(&self) -> &Self::Target {
502 match &*self.inner {
503 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
504 _ => unreachable!(),
505 }
506 }
507}
508
509impl<'a> ops::DerefMut for RecordingGuard<'a> {
510 fn deref_mut(&mut self) -> &mut Self::Target {
511 match self.inner {
512 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
513 _ => unreachable!(),
514 }
515 }
516}
517
518pub struct CommandEncoder {
519 pub(crate) device: Arc<Device>,
520
521 pub(crate) label: String,
522
523 pub(crate) data: Mutex<CommandEncoderStatus>,
525}
526
527crate::impl_resource_type!(CommandEncoder);
528crate::impl_labeled!(CommandEncoder);
529crate::impl_parent_device!(CommandEncoder);
530crate::impl_storage_item!(CommandEncoder);
531
532impl Drop for CommandEncoder {
533 #[allow(trivial_casts)]
534 fn drop(&mut self) {
535 profiling::scope!("CommandEncoder::drop");
536 api_log!("CommandEncoder::drop {:?}", self as *const _);
537 resource_log!("Drop {}", self.error_ident());
538 }
539}
540
541#[derive(Copy, Clone, Debug, Eq, PartialEq)]
545pub enum EncodingApi {
546 Wgpu,
548
549 Raw,
551
552 Undecided,
554
555 InternalUse,
557}
558
559impl EncodingApi {
560 pub(crate) fn set(&mut self, api: EncodingApi) {
561 match *self {
562 EncodingApi::Undecided => {
563 *self = api;
564 }
565 self_api if self_api != api => {
566 panic!("Mixing the wgpu encoding API with the raw encoding API is not permitted");
567 }
568 _ => {}
569 }
570 }
571}
572
573pub(crate) struct InnerCommandEncoder {
589 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
597
598 pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
610
611 pub(crate) device: Arc<Device>,
612
613 pub(crate) is_open: bool,
620
621 pub(crate) api: EncodingApi,
627
628 pub(crate) label: String,
629}
630
631impl InnerCommandEncoder {
632 fn close_and_swap(&mut self) -> Result<(), DeviceError> {
664 self.close_and_insert_at(self.list.len() - 1)
665 }
666
667 pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> {
684 self.close_and_insert_at(0)
685 }
686
687 pub(crate) fn close_and_insert_at(&mut self, index: usize) -> Result<(), DeviceError> {
704 assert!(self.is_open);
705 self.is_open = false;
706
707 let cmd_buf =
708 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
709 self.list.insert(index, cmd_buf);
710
711 Ok(())
712 }
713
714 pub(crate) fn close(&mut self) -> Result<(), DeviceError> {
725 assert!(self.is_open);
726 self.is_open = false;
727
728 let cmd_buf =
729 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
730 self.list.push(cmd_buf);
731
732 Ok(())
733 }
734
735 fn close_if_open(&mut self) -> Result<(), DeviceError> {
746 if self.is_open {
747 self.is_open = false;
748 let cmd_buf =
749 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
750 self.list.push(cmd_buf);
751 }
752
753 Ok(())
754 }
755
756 fn open_if_closed(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
762 if !self.is_open {
763 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
764 unsafe { self.raw.begin_encoding(hal_label) }
765 .map_err(|e| self.device.handle_hal_error(e))?;
766 self.is_open = true;
767 }
768
769 Ok(self.raw.as_mut())
770 }
771
772 pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
776 if !self.is_open {
777 let hal_label = hal_label(Some(self.label.as_str()), 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
783 Ok(self.raw.as_mut())
784 }
785
786 pub(crate) fn open_pass(
795 &mut self,
796 label: Option<&str>,
797 ) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
798 assert!(!self.is_open);
799
800 let hal_label = hal_label(label, self.device.instance_flags);
801 unsafe { self.raw.begin_encoding(hal_label) }
802 .map_err(|e| self.device.handle_hal_error(e))?;
803 self.is_open = true;
804
805 Ok(self.raw.as_mut())
806 }
807}
808
809impl Drop for InnerCommandEncoder {
810 fn drop(&mut self) {
811 if self.is_open {
812 unsafe { self.raw.discard_encoding() };
813 }
814 unsafe {
815 self.raw.reset_all(mem::take(&mut self.list));
816 }
817 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
819 self.device.command_allocator.release_encoder(raw);
820 }
821}
822
823pub(crate) struct BakedCommands {
826 pub(crate) encoder: InnerCommandEncoder,
827 pub(crate) trackers: Tracker,
828 pub(crate) temp_resources: Vec<TempResource>,
829 pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
830 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
831 texture_memory_actions: CommandBufferTextureMemoryActions,
832 pub(crate) query_set_writes: query::QuerySetWrites,
833 pub(crate) deferred_query_set_resolves: Vec<query::DeferredQuerySetResolve>,
834}
835
836pub struct CommandBufferMutable {
838 pub(crate) encoder: InnerCommandEncoder,
843
844 pub(crate) trackers: Tracker,
846
847 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
854 texture_memory_actions: CommandBufferTextureMemoryActions,
855
856 as_actions: Vec<AsAction>,
857 temp_resources: Vec<TempResource>,
858
859 indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
860
861 pub(crate) commands: Vec<Command<ArcReferences>>,
862
863 #[cfg(feature = "trace")]
866 pub(crate) trace_commands: Option<Vec<Command<PointerReferences>>>,
867
868 pub(crate) query_set_writes: query::QuerySetWrites,
870 pub(crate) deferred_query_set_resolves: Vec<query::DeferredQuerySetResolve>,
872}
873
874impl CommandBufferMutable {
875 pub(crate) fn into_baked_commands(self) -> BakedCommands {
876 BakedCommands {
877 encoder: self.encoder,
878 trackers: self.trackers,
879 temp_resources: self.temp_resources,
880 indirect_draw_validation_resources: self.indirect_draw_validation_resources,
881 buffer_memory_init_actions: self.buffer_memory_init_actions,
882 texture_memory_actions: self.texture_memory_actions,
883 query_set_writes: self.query_set_writes,
884 deferred_query_set_resolves: self.deferred_query_set_resolves,
885 }
886 }
887}
888
889pub struct CommandBuffer {
895 pub(crate) device: Arc<Device>,
896 label: String,
898
899 pub(crate) data: Mutex<CommandEncoderStatus>,
901}
902
903impl Drop for CommandBuffer {
904 #[allow(trivial_casts)]
905 fn drop(&mut self) {
906 profiling::scope!("CommandBuffer::drop");
907 api_log!("CommandBuffer::drop {:?}", self as *const _);
908 resource_log!("Drop {}", self.error_ident());
909 }
910}
911
912impl CommandEncoder {
913 pub(crate) fn new(
914 encoder: Box<dyn hal::DynCommandEncoder>,
915 device: &Arc<Device>,
916 label: &Label,
917 ) -> Self {
918 CommandEncoder {
919 device: device.clone(),
920 label: label.to_string(),
921 data: Mutex::new(
922 rank::COMMAND_BUFFER_DATA,
923 CommandEncoderStatus::Recording(CommandBufferMutable {
924 encoder: InnerCommandEncoder {
925 raw: ManuallyDrop::new(encoder),
926 list: Vec::new(),
927 device: device.clone(),
928 is_open: false,
929 api: EncodingApi::Undecided,
930 label: label.to_string(),
931 },
932 trackers: Tracker::new(
933 device.ordered_buffer_usages,
934 device.ordered_texture_usages,
935 ),
936 buffer_memory_init_actions: Default::default(),
937 texture_memory_actions: Default::default(),
938 as_actions: Default::default(),
939 temp_resources: Default::default(),
940 indirect_draw_validation_resources:
941 crate::indirect_validation::DrawResources::new(device.clone()),
942 commands: Vec::new(),
943 query_set_writes: Default::default(),
944 deferred_query_set_resolves: Default::default(),
945 #[cfg(feature = "trace")]
946 trace_commands: if device.trace.lock().is_some() {
947 Some(Vec::new())
948 } else {
949 None
950 },
951 }),
952 ),
953 }
954 }
955
956 pub(crate) fn new_invalid(
957 device: &Arc<Device>,
958 label: &Label,
959 err: CommandEncoderError,
960 ) -> Arc<Self> {
961 Arc::new(CommandEncoder {
962 device: device.clone(),
963 label: label.to_string(),
964 data: Mutex::new(rank::COMMAND_BUFFER_DATA, make_error_state(err)),
965 })
966 }
967
968 pub(crate) fn validate_pass_timestamp_writes<E>(
969 device: &Device,
970 timestamp_writes: &PassTimestampWrites<Arc<QuerySet>>,
971 ) -> Result<PassTimestampWrites, E>
972 where
973 E: From<TimestampWritesError>
974 + From<QueryUseError>
975 + From<DeviceError>
976 + From<MissingFeatures>
977 + From<InvalidResourceError>,
978 {
979 let &PassTimestampWrites {
980 ref query_set,
981 beginning_of_pass_write_index,
982 end_of_pass_write_index,
983 } = timestamp_writes;
984
985 device.require_features(wgt::Features::TIMESTAMP_QUERY)?;
986
987 query_set.check_is_valid()?;
988 query_set.same_device(device)?;
989
990 for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
991 .into_iter()
992 .flatten()
993 {
994 query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
995 }
996
997 if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
998 if begin == end {
999 return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());
1000 }
1001 }
1002
1003 if beginning_of_pass_write_index
1004 .or(end_of_pass_write_index)
1005 .is_none()
1006 {
1007 return Err(TimestampWritesError::IndicesMissing.into());
1008 }
1009
1010 Ok(PassTimestampWrites {
1011 query_set: query_set.clone(),
1012 beginning_of_pass_write_index,
1013 end_of_pass_write_index,
1014 })
1015 }
1016
1017 pub(crate) fn insert_barriers_from_tracker(
1018 raw: &mut dyn hal::DynCommandEncoder,
1019 base: &mut Tracker,
1020 head: &Tracker,
1021 snatch_guard: &SnatchGuard,
1022 ) {
1023 profiling::scope!("insert_barriers");
1024
1025 base.buffers.set_from_tracker(&head.buffers);
1026 base.textures.set_from_tracker(&head.textures);
1027
1028 Self::drain_barriers(raw, base, snatch_guard);
1029 }
1030
1031 pub(crate) fn insert_barriers_from_scope(
1032 raw: &mut dyn hal::DynCommandEncoder,
1033 base: &mut Tracker,
1034 head: &UsageScope,
1035 snatch_guard: &SnatchGuard,
1036 ) {
1037 profiling::scope!("insert_barriers");
1038
1039 base.buffers.set_from_usage_scope(&head.buffers);
1040 base.textures.set_from_usage_scope(&head.textures);
1041
1042 Self::drain_barriers(raw, base, snatch_guard);
1043 }
1044
1045 pub(crate) fn drain_barriers(
1046 raw: &mut dyn hal::DynCommandEncoder,
1047 base: &mut Tracker,
1048 snatch_guard: &SnatchGuard,
1049 ) {
1050 profiling::scope!("drain_barriers");
1051
1052 let buffer_barriers = base
1053 .buffers
1054 .drain_transitions(snatch_guard)
1055 .collect::<Vec<_>>();
1056 let (transitions, textures) = base.textures.drain_transitions(snatch_guard);
1057 let texture_barriers = transitions
1058 .into_iter()
1059 .enumerate()
1060 .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))
1061 .collect::<Vec<_>>();
1062
1063 unsafe {
1064 raw.transition_buffers(&buffer_barriers);
1065 raw.transition_textures(&texture_barriers);
1066 }
1067 }
1068
1069 pub(crate) fn insert_barriers_from_device_tracker(
1070 raw: &mut dyn hal::DynCommandEncoder,
1071 base: &mut DeviceTracker,
1072 head: &Tracker,
1073 snatch_guard: &SnatchGuard,
1074 ) {
1075 profiling::scope!("insert_barriers_from_device_tracker");
1076
1077 let buffer_barriers = base
1078 .buffers
1079 .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)
1080 .collect::<Vec<_>>();
1081
1082 let texture_barriers = base
1083 .textures
1084 .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)
1085 .collect::<Vec<_>>();
1086
1087 unsafe {
1088 raw.transition_buffers(&buffer_barriers);
1089 raw.transition_textures(&texture_barriers);
1090 }
1091 }
1092
1093 fn encode_commands(
1094 device: &Arc<Device>,
1095 cmd_buf_data: &mut CommandBufferMutable,
1096 ) -> Result<(), CommandEncoderError> {
1097 device.check_is_valid()?;
1098 let snatch_guard = device.snatchable_lock.read();
1099 let mut debug_scope_depth = 0;
1100
1101 if cmd_buf_data.encoder.api == EncodingApi::Raw {
1102 assert!(cmd_buf_data.commands.is_empty());
1105 }
1106
1107 let commands = mem::take(&mut cmd_buf_data.commands);
1108
1109 #[cfg(feature = "trace")]
1110 if device.trace.lock().is_some() {
1111 cmd_buf_data.trace_commands = Some(
1112 commands
1113 .iter()
1114 .map(crate::device::trace::IntoTrace::to_trace)
1115 .collect(),
1116 );
1117 }
1118
1119 for command in commands {
1120 if matches!(
1121 command,
1122 ArcCommand::RunRenderPass { .. }
1123 | ArcCommand::RunComputePass { .. }
1124 | ArcCommand::ResolveQuerySet { .. }
1125 ) {
1126 let mut state = EncodingState {
1132 device,
1133 raw_encoder: &mut cmd_buf_data.encoder,
1134 tracker: &mut cmd_buf_data.trackers,
1135 buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1136 texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1137 as_actions: &mut cmd_buf_data.as_actions,
1138 temp_resources: &mut cmd_buf_data.temp_resources,
1139 indirect_draw_validation_resources: &mut cmd_buf_data
1140 .indirect_draw_validation_resources,
1141 snatch_guard: &snatch_guard,
1142 debug_scope_depth: &mut debug_scope_depth,
1143 query_set_writes: &mut cmd_buf_data.query_set_writes,
1144 deferred_query_set_resolves: &mut cmd_buf_data.deferred_query_set_resolves,
1145 };
1146
1147 match command {
1148 ArcCommand::RunRenderPass {
1149 pass,
1150 color_attachments,
1151 depth_stencil_attachment,
1152 timestamp_writes,
1153 occlusion_query_set,
1154 multiview_mask,
1155 } => {
1156 api_log!(
1157 "Begin encoding render pass with '{}' label",
1158 pass.label.as_deref().unwrap_or("")
1159 );
1160 let res = render::encode_render_pass(
1161 &mut state,
1162 pass,
1163 color_attachments,
1164 depth_stencil_attachment,
1165 timestamp_writes,
1166 occlusion_query_set,
1167 multiview_mask,
1168 );
1169 match res.as_ref() {
1170 Err(err) => {
1171 api_log!("Finished encoding render pass ({err:?})")
1172 }
1173 Ok(_) => {
1174 api_log!("Finished encoding render pass (success)")
1175 }
1176 }
1177 res?;
1178 }
1179 ArcCommand::RunComputePass {
1180 pass,
1181 timestamp_writes,
1182 } => {
1183 api_log!(
1184 "Begin encoding compute pass with '{}' label",
1185 pass.label.as_deref().unwrap_or("")
1186 );
1187 let res = compute::encode_compute_pass(&mut state, pass, timestamp_writes);
1188 match res.as_ref() {
1189 Err(err) => {
1190 api_log!("Finished encoding compute pass ({err:?})")
1191 }
1192 Ok(_) => {
1193 api_log!("Finished encoding compute pass (success)")
1194 }
1195 }
1196 res?;
1197 }
1198 ArcCommand::ResolveQuerySet {
1199 query_set,
1200 start_query,
1201 query_count,
1202 destination,
1203 destination_offset,
1204 } => {
1205 query::resolve_query_set(
1206 &mut state,
1207 query_set,
1208 start_query,
1209 query_count,
1210 destination,
1211 destination_offset,
1212 )?;
1213 }
1214 _ => unreachable!(),
1215 }
1216 } else {
1217 let raw_encoder = cmd_buf_data.encoder.open_if_closed()?;
1223 let mut state = EncodingState {
1224 device,
1225 raw_encoder,
1226 tracker: &mut cmd_buf_data.trackers,
1227 buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1228 texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1229 as_actions: &mut cmd_buf_data.as_actions,
1230 temp_resources: &mut cmd_buf_data.temp_resources,
1231 indirect_draw_validation_resources: &mut cmd_buf_data
1232 .indirect_draw_validation_resources,
1233 snatch_guard: &snatch_guard,
1234 debug_scope_depth: &mut debug_scope_depth,
1235 query_set_writes: &mut cmd_buf_data.query_set_writes,
1236 deferred_query_set_resolves: &mut cmd_buf_data.deferred_query_set_resolves,
1237 };
1238 match command {
1239 ArcCommand::CopyBufferToBuffer {
1240 src,
1241 src_offset,
1242 dst,
1243 dst_offset,
1244 size,
1245 } => {
1246 transfer::copy_buffer_to_buffer(
1247 &mut state, &src, src_offset, &dst, dst_offset, size,
1248 )?;
1249 }
1250 ArcCommand::CopyBufferToTexture { src, dst, size } => {
1251 transfer::copy_buffer_to_texture(&mut state, &src, &dst, &size)?;
1252 }
1253 ArcCommand::CopyTextureToBuffer { src, dst, size } => {
1254 transfer::copy_texture_to_buffer(&mut state, &src, &dst, &size)?;
1255 }
1256 ArcCommand::CopyTextureToTexture { src, dst, size } => {
1257 transfer::copy_texture_to_texture(&mut state, &src, &dst, &size)?;
1258 }
1259 ArcCommand::ClearBuffer { dst, offset, size } => {
1260 clear::clear_buffer(&mut state, dst, offset, size)?;
1261 }
1262 ArcCommand::ClearTexture {
1263 dst,
1264 subresource_range,
1265 } => {
1266 clear::clear_texture_cmd(&mut state, dst, &subresource_range)?;
1267 }
1268 ArcCommand::WriteTimestamp {
1269 query_set,
1270 query_index,
1271 } => {
1272 query::write_timestamp(&mut state, query_set, query_index)?;
1273 }
1274 ArcCommand::PushDebugGroup(label) => {
1275 push_debug_group(&mut state, &label)?;
1276 }
1277 ArcCommand::PopDebugGroup => {
1278 pop_debug_group(&mut state)?;
1279 }
1280 ArcCommand::InsertDebugMarker(label) => {
1281 insert_debug_marker(&mut state, &label)?;
1282 }
1283 ArcCommand::BuildAccelerationStructures { blas, tlas } => {
1284 ray_tracing::build_acceleration_structures(&mut state, blas, tlas)?;
1285 }
1286 ArcCommand::TransitionResources {
1287 buffer_transitions,
1288 texture_transitions,
1289 } => {
1290 transition_resources::transition_resources(
1291 &mut state,
1292 buffer_transitions,
1293 texture_transitions,
1294 )?;
1295 }
1296 ArcCommand::RunComputePass { .. }
1297 | ArcCommand::RunRenderPass { .. }
1298 | ArcCommand::ResolveQuerySet { .. } => {
1299 unreachable!()
1300 }
1301 }
1302 }
1303 }
1304
1305 if debug_scope_depth > 0 {
1306 Err(CommandEncoderError::DebugGroupError(
1307 DebugGroupError::MissingPop,
1308 ))?;
1309 }
1310
1311 cmd_buf_data.encoder.close_if_open()?;
1313
1314 Ok(())
1318 }
1319
1320 pub fn finish(
1328 self: &Arc<Self>,
1329 desc: &wgt::CommandBufferDescriptor<Label>,
1330 ) -> (Arc<CommandBuffer>, Option<(String, CommandEncoderError)>) {
1331 profiling::scope!("CommandEncoder::finish");
1332
1333 let status = self.data.lock().finish();
1334
1335 let res = match status {
1336 CommandEncoderStatus::Finished(mut cmd_buf_data) => {
1337 match Self::encode_commands(&self.device, &mut cmd_buf_data) {
1338 Ok(()) => Ok(cmd_buf_data),
1339 Err(error) => Err(EncoderErrorState {
1340 error,
1341 #[cfg(feature = "trace")]
1342 trace_commands: mem::take(&mut cmd_buf_data.trace_commands),
1343 }),
1344 }
1345 }
1346 CommandEncoderStatus::Error(error_state) => Err(error_state),
1347 _ => unreachable!(),
1348 };
1349
1350 let (data, error) = match res {
1351 Err(EncoderErrorState {
1352 error,
1353 #[cfg(feature = "trace")]
1354 trace_commands,
1355 }) => {
1356 #[cfg(feature = "trace")]
1360 if let Some(trace) = self.device.trace.lock().as_mut() {
1361 use alloc::string::ToString;
1362
1363 trace.add(crate::device::trace::Action::FailedCommands {
1364 commands: trace_commands,
1365 failed_at_submit: None,
1366 error: error.to_string(),
1367 });
1368 }
1369
1370 if error.is_destroyed_error() {
1371 (make_error_state(error), None)
1374 } else {
1375 (make_error_state(error.clone()), Some(error))
1376 }
1377 }
1378
1379 Ok(data) => (CommandEncoderStatus::Finished(data), None),
1380 };
1381
1382 let cmd_buf = Arc::new(CommandBuffer {
1383 device: self.device.clone(),
1384 label: desc.label.to_string(),
1385 data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),
1386 });
1387
1388 (cmd_buf, error.map(|e| (self.label.clone(), e)))
1389 }
1390}
1391
1392impl CommandBuffer {
1393 #[doc(hidden)]
1399 pub fn from_trace(device: &Arc<Device>, commands: Vec<Command<ArcReferences>>) -> Arc<Self> {
1400 let (encoder, _error) =
1401 device.create_command_encoder(&wgt::CommandEncoderDescriptor { label: None });
1402 let mut cmd_enc_status = encoder.data.lock();
1403 cmd_enc_status.replay(commands);
1404 drop(cmd_enc_status);
1405
1406 let (cmd_buf, error) = encoder.finish(&wgt::CommandBufferDescriptor { label: None });
1407 if let Some((_, err)) = error {
1408 panic!("CommandEncoder::finish failed: {err}");
1409 }
1410
1411 cmd_buf
1412 }
1413
1414 pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {
1415 use CommandEncoderStatus as St;
1416 match mem::replace(
1417 &mut *self.data.lock(),
1418 make_error_state(EncoderStateError::Submitted),
1419 ) {
1420 St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
1421 St::Error(EncoderErrorState {
1422 #[cfg(feature = "trace")]
1423 trace_commands: _,
1424 error,
1425 }) => Err(error),
1426 St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(),
1427 }
1428 }
1429}
1430
1431crate::impl_resource_type!(CommandBuffer);
1432crate::impl_labeled!(CommandBuffer);
1433crate::impl_parent_device!(CommandBuffer);
1434crate::impl_storage_item!(CommandBuffer);
1435
1436#[doc(hidden)]
1448#[derive(Debug, Clone)]
1449#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1450pub struct BasePass<C, E> {
1451 pub label: Option<String>,
1452
1453 #[cfg_attr(feature = "serde", serde(skip, default = "Option::default"))]
1461 pub error: Option<E>,
1462
1463 pub commands: Vec<C>,
1469
1470 pub dynamic_offsets: Vec<wgt::DynamicOffset>,
1475
1476 pub string_data: Vec<u8>,
1481}
1482
1483impl<C: Clone, E: Clone> BasePass<C, E> {
1484 fn new(label: &Label) -> Self {
1485 Self {
1486 label: label.as_deref().map(str::to_owned),
1487 error: None,
1488 commands: Vec::new(),
1489 dynamic_offsets: Vec::new(),
1490 string_data: Vec::new(),
1491 }
1492 }
1493
1494 fn new_invalid(label: &Label, err: E) -> Self {
1495 Self {
1496 label: label.as_deref().map(str::to_owned),
1497 error: Some(err),
1498 commands: Vec::new(),
1499 dynamic_offsets: Vec::new(),
1500 string_data: Vec::new(),
1501 }
1502 }
1503
1504 fn take(&mut self) -> Result<BasePass<C, Infallible>, E> {
1511 match self.error.as_ref() {
1512 Some(err) => Err(err.clone()),
1513 None => Ok(BasePass {
1514 label: self.label.clone(),
1515 error: None,
1516 commands: mem::take(&mut self.commands),
1517 dynamic_offsets: mem::take(&mut self.dynamic_offsets),
1518 string_data: mem::take(&mut self.string_data),
1519 }),
1520 }
1521 }
1522}
1523
1524macro_rules! pass_base {
1547 ($pass:expr, $scope:expr $(,)?) => {
1548 match (&$pass.parent, &$pass.base.error) {
1549 (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),
1551 (&Some(_), &Some(_)) => return Ok(()),
1553 (&Some(_), &None) => &mut $pass.base,
1555 }
1556 };
1557}
1558pub(crate) use pass_base;
1559
1560macro_rules! pass_try {
1572 ($base:expr, $scope:expr, $res:expr $(,)?) => {
1573 match $res.map_pass_err($scope) {
1574 Ok(val) => val,
1575 Err(err) => {
1576 $base.error.get_or_insert(err);
1577 return Ok(());
1578 }
1579 }
1580 };
1581}
1582pub(crate) use pass_try;
1583
1584#[derive(Clone, Debug, Error)]
1589#[non_exhaustive]
1590pub enum EncoderStateError {
1591 #[error("Encoder is invalid")]
1596 Invalid,
1597
1598 #[error("Encoding must not have ended")]
1601 Ended,
1602
1603 #[error("Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
1609 Locked,
1610
1611 #[error(
1614 "Encoder is not currently locked. A pass can only be ended while the encoder is locked."
1615 )]
1616 Unlocked,
1617
1618 #[error("This command buffer has already been submitted.")]
1623 Submitted,
1624}
1625
1626impl WebGpuError for EncoderStateError {
1627 fn webgpu_error_type(&self) -> ErrorType {
1628 match self {
1629 EncoderStateError::Invalid
1630 | EncoderStateError::Ended
1631 | EncoderStateError::Locked
1632 | EncoderStateError::Unlocked
1633 | EncoderStateError::Submitted => ErrorType::Validation,
1634 }
1635 }
1636}
1637
1638#[derive(Clone, Debug, Error)]
1639#[non_exhaustive]
1640pub enum CommandEncoderError {
1641 #[error(transparent)]
1642 State(#[from] EncoderStateError),
1643 #[error(transparent)]
1644 Device(#[from] DeviceError),
1645 #[error(transparent)]
1646 InvalidResource(#[from] InvalidResourceError),
1647 #[error(transparent)]
1648 DestroyedResource(#[from] DestroyedResourceError),
1649 #[error(transparent)]
1650 ResourceUsage(#[from] ResourceUsageCompatibilityError),
1651 #[error(transparent)]
1652 DebugGroupError(#[from] DebugGroupError),
1653 #[error(transparent)]
1654 MissingFeatures(#[from] MissingFeatures),
1655 #[error(transparent)]
1656 Transfer(#[from] TransferError),
1657 #[error(transparent)]
1658 Clear(#[from] ClearError),
1659 #[error(transparent)]
1660 Query(#[from] QueryError),
1661 #[error(transparent)]
1662 BuildAccelerationStructure(#[from] BuildAccelerationStructureError),
1663 #[error(transparent)]
1664 TransitionResources(#[from] TransitionResourcesError),
1665 #[error(transparent)]
1666 ComputePass(#[from] ComputePassError),
1667 #[error(transparent)]
1668 RenderPass(#[from] RenderPassError),
1669}
1670
1671impl From<InvalidOrDestroyedResourceError> for CommandEncoderError {
1672 fn from(err: InvalidOrDestroyedResourceError) -> Self {
1673 match err {
1674 InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
1675 InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
1676 }
1677 }
1678}
1679
1680impl CommandEncoderError {
1681 fn is_destroyed_error(&self) -> bool {
1682 matches!(
1683 self,
1684 Self::DestroyedResource(_)
1685 | Self::Clear(ClearError::DestroyedResource(_))
1686 | Self::Query(QueryError::DestroyedResource(_))
1687 | Self::ComputePass(ComputePassError {
1688 inner: ComputePassErrorInner::DestroyedResource(_),
1689 ..
1690 })
1691 ) || if let Self::RenderPass(pass_error) = self {
1692 matches!(
1693 pass_error.inner.as_ref(),
1694 RenderPassErrorInner::DestroyedResource(_)
1695 | RenderPassErrorInner::RenderCommand(RenderCommandError::DestroyedResource(_))
1696 | RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError(
1697 BindingError::DestroyedResource(_),
1698 ))
1699 )
1700 } else {
1701 false
1702 }
1703 }
1704}
1705
1706impl WebGpuError for CommandEncoderError {
1707 fn webgpu_error_type(&self) -> ErrorType {
1708 match self {
1709 Self::Device(e) => e.webgpu_error_type(),
1710 Self::InvalidResource(e) => e.webgpu_error_type(),
1711 Self::DebugGroupError(e) => e.webgpu_error_type(),
1712 Self::MissingFeatures(e) => e.webgpu_error_type(),
1713 Self::State(e) => e.webgpu_error_type(),
1714 Self::DestroyedResource(e) => e.webgpu_error_type(),
1715 Self::Transfer(e) => e.webgpu_error_type(),
1716 Self::Clear(e) => e.webgpu_error_type(),
1717 Self::Query(e) => e.webgpu_error_type(),
1718 Self::BuildAccelerationStructure(e) => e.webgpu_error_type(),
1719 Self::TransitionResources(e) => e.webgpu_error_type(),
1720 Self::ResourceUsage(e) => e.webgpu_error_type(),
1721 Self::ComputePass(e) => e.webgpu_error_type(),
1722 Self::RenderPass(e) => e.webgpu_error_type(),
1723 }
1724 }
1725}
1726
1727#[derive(Clone, Debug, Error)]
1728#[non_exhaustive]
1729pub enum DebugGroupError {
1730 #[error("Cannot pop debug group, because number of pushed debug groups is zero")]
1731 InvalidPop,
1732 #[error("A debug group was not popped before the encoder was finished")]
1733 MissingPop,
1734}
1735
1736impl WebGpuError for DebugGroupError {
1737 fn webgpu_error_type(&self) -> ErrorType {
1738 match self {
1739 Self::InvalidPop | Self::MissingPop => ErrorType::Validation,
1740 }
1741 }
1742}
1743
1744#[derive(Clone, Debug, Error)]
1745#[non_exhaustive]
1746pub enum TimestampWritesError {
1747 #[error(
1748 "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
1749 )]
1750 IndicesEqual { idx: u32 },
1751 #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
1752 IndicesMissing,
1753}
1754
1755impl WebGpuError for TimestampWritesError {
1756 fn webgpu_error_type(&self) -> ErrorType {
1757 match self {
1758 Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,
1759 }
1760 }
1761}
1762
1763impl CommandEncoder {
1764 pub fn push_debug_group(self: &Arc<Self>, label: &str) -> Result<(), EncoderStateError> {
1765 profiling::scope!("CommandEncoder::push_debug_group");
1766 api_log!("CommandEncoder::push_debug_group {label}");
1767
1768 let mut cmd_buf_data = self.data.lock();
1769
1770 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1771 Ok(ArcCommand::PushDebugGroup(label.to_owned()))
1772 })
1773 }
1774
1775 pub fn insert_debug_marker(self: &Arc<Self>, label: &str) -> Result<(), EncoderStateError> {
1776 profiling::scope!("CommandEncoder::insert_debug_marker");
1777 api_log!("CommandEncoder::insert_debug_marker {label}");
1778
1779 let mut cmd_buf_data = self.data.lock();
1780
1781 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1782 Ok(ArcCommand::InsertDebugMarker(label.to_owned()))
1783 })
1784 }
1785
1786 pub fn pop_debug_group(self: &Arc<Self>) -> Result<(), EncoderStateError> {
1787 profiling::scope!("CommandEncoder::pop_debug_marker");
1788 api_log!("CommandEncoder::pop_debug_group");
1789
1790 let mut cmd_buf_data = self.data.lock();
1791
1792 cmd_buf_data
1793 .push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::PopDebugGroup) })
1794 }
1795}
1796
1797impl Global {
1798 pub fn command_encoder_finish(
1806 &self,
1807 encoder_id: id::CommandEncoderId,
1808 desc: &wgt::CommandBufferDescriptor<Label>,
1809 id_in: Option<id::CommandBufferId>,
1810 ) -> (id::CommandBufferId, Option<(String, CommandEncoderError)>) {
1811 let hub = &self.hub;
1812 let cmd_enc = hub.command_encoders.get(encoder_id);
1813
1814 let (cmd_buf, opt_error) = cmd_enc.finish(desc);
1815 let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(cmd_buf);
1816
1817 (cmd_buf_id, opt_error)
1818 }
1819
1820 pub fn command_encoder_push_debug_group(
1821 &self,
1822 encoder_id: id::CommandEncoderId,
1823 label: &str,
1824 ) -> Result<(), EncoderStateError> {
1825 let hub = &self.hub;
1826
1827 let cmd_enc = hub.command_encoders.get(encoder_id);
1828 cmd_enc.push_debug_group(label)
1829 }
1830
1831 pub fn command_encoder_insert_debug_marker(
1832 &self,
1833 encoder_id: id::CommandEncoderId,
1834 label: &str,
1835 ) -> Result<(), EncoderStateError> {
1836 let hub = &self.hub;
1837
1838 let cmd_enc = hub.command_encoders.get(encoder_id);
1839 cmd_enc.insert_debug_marker(label)
1840 }
1841
1842 pub fn command_encoder_pop_debug_group(
1843 &self,
1844 encoder_id: id::CommandEncoderId,
1845 ) -> Result<(), EncoderStateError> {
1846 let hub = &self.hub;
1847
1848 let cmd_enc = hub.command_encoders.get(encoder_id);
1849 cmd_enc.pop_debug_group()
1850 }
1851}
1852
1853pub(crate) fn push_debug_group(
1854 state: &mut EncodingState,
1855 label: &str,
1856) -> Result<(), CommandEncoderError> {
1857 *state.debug_scope_depth += 1;
1858
1859 if !state
1860 .device
1861 .instance_flags
1862 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1863 {
1864 unsafe { state.raw_encoder.begin_debug_marker(label) };
1865 }
1866
1867 Ok(())
1868}
1869
1870pub(crate) fn insert_debug_marker(
1871 state: &mut EncodingState,
1872 label: &str,
1873) -> Result<(), CommandEncoderError> {
1874 if !state
1875 .device
1876 .instance_flags
1877 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1878 {
1879 unsafe { state.raw_encoder.insert_debug_marker(label) };
1880 }
1881
1882 Ok(())
1883}
1884
1885pub(crate) fn pop_debug_group(state: &mut EncodingState) -> Result<(), CommandEncoderError> {
1886 if *state.debug_scope_depth == 0 {
1887 return Err(DebugGroupError::InvalidPop.into());
1888 }
1889 *state.debug_scope_depth -= 1;
1890
1891 if !state
1892 .device
1893 .instance_flags
1894 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1895 {
1896 unsafe { state.raw_encoder.end_debug_marker() };
1897 }
1898
1899 Ok(())
1900}
1901
1902#[derive(Debug, Copy, Clone)]
1903struct StateChange<T> {
1904 last_state: Option<T>,
1905}
1906
1907impl<T: Clone + PartialEq_> StateChange<T> {
1908 const fn new() -> Self {
1909 Self { last_state: None }
1910 }
1911
1912 fn set_and_check_redundant(&mut self, new_state: &T) -> bool {
1913 let already_set = self.last_state.as_ref().is_some_and(|s| s.eq(new_state));
1914 if !already_set {
1915 self.last_state = Some(new_state.clone());
1916 }
1917 already_set
1918 }
1919
1920 fn reset(&mut self) {
1921 self.last_state = None;
1922 }
1923}
1924
1925impl<T: Clone + PartialEq_> Default for StateChange<T> {
1926 fn default() -> Self {
1927 Self::new()
1928 }
1929}
1930
1931trait PartialEq_ {
1932 fn eq(&self, other: &Self) -> bool;
1933}
1934
1935impl<T: id::Marker> PartialEq_ for id::Id<T> {
1936 fn eq(&self, other: &Self) -> bool {
1937 self == other
1938 }
1939}
1940
1941impl<T> PartialEq_ for Arc<T> {
1942 fn eq(&self, other: &Self) -> bool {
1943 Arc::ptr_eq(self, other)
1944 }
1945}
1946
1947impl<T: PartialEq_> PartialEq_ for Option<T> {
1948 fn eq(&self, other: &Self) -> bool {
1949 match (self, other) {
1950 (Some(a), Some(b)) => a.eq(b),
1951 (None, None) => true,
1952 _ => false,
1953 }
1954 }
1955}
1956
1957#[derive(Debug)]
1958struct BindGroupStateChange {
1959 last_states: [StateChange<Option<Arc<BindGroup>>>; hal::MAX_BIND_GROUPS],
1960}
1961
1962impl BindGroupStateChange {
1963 fn new() -> Self {
1964 Self {
1965 last_states: [const { StateChange::new() }; hal::MAX_BIND_GROUPS],
1966 }
1967 }
1968
1969 fn set_and_check_redundant(
1970 &mut self,
1971 bind_group: &Option<Arc<BindGroup>>,
1972 index: u32,
1973 dynamic_offsets: &mut Vec<u32>,
1974 offsets: &[wgt::DynamicOffset],
1975 ) -> bool {
1976 if offsets.is_empty() {
1978 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1981 if current_bind_group.set_and_check_redundant(bind_group) {
1983 return true;
1984 }
1985 }
1986 } else {
1987 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1991 current_bind_group.reset();
1992 }
1993 dynamic_offsets.extend_from_slice(offsets);
1994 }
1995 false
1996 }
1997 fn reset(&mut self) {
1998 self.last_states = [const { StateChange::new() }; hal::MAX_BIND_GROUPS];
1999 }
2000}
2001
2002impl Default for BindGroupStateChange {
2003 fn default() -> Self {
2004 Self::new()
2005 }
2006}
2007
2008trait MapPassErr<T> {
2010 fn map_pass_err(self, scope: PassErrorScope) -> T;
2011}
2012
2013impl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>
2014where
2015 E: MapPassErr<F>,
2016{
2017 fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {
2018 self.map_err(|err| err.map_pass_err(scope))
2019 }
2020}
2021
2022impl MapPassErr<PassStateError> for EncoderStateError {
2023 fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {
2024 PassStateError { scope, inner: self }
2025 }
2026}
2027
2028#[derive(Clone, Copy, Debug)]
2029pub enum DrawKind {
2030 Draw,
2031 DrawIndirect,
2032 MultiDrawIndirect,
2033 MultiDrawIndirectCount,
2034}
2035
2036#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2038#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2039pub enum DrawCommandFamily {
2040 Draw,
2041 DrawIndexed,
2042 DrawMeshTasks,
2043}
2044
2045#[derive(Clone, Copy, Debug, Error)]
2055pub enum PassErrorScope {
2056 #[error("In a bundle parameter")]
2059 Bundle,
2060 #[error("In a pass parameter")]
2061 Pass,
2062 #[error("In a set_bind_group command")]
2063 SetBindGroup,
2064 #[error("In a set_pipeline command")]
2065 SetPipelineRender,
2066 #[error("In a set_pipeline command")]
2067 SetPipelineCompute,
2068 #[error("In a set_immediates command")]
2069 SetImmediate,
2070 #[error("In a set_vertex_buffer command")]
2071 SetVertexBuffer,
2072 #[error("In a set_index_buffer command")]
2073 SetIndexBuffer,
2074 #[error("In a set_blend_constant command")]
2075 SetBlendConstant,
2076 #[error("In a set_stencil_reference command")]
2077 SetStencilReference,
2078 #[error("In a set_viewport command")]
2079 SetViewport,
2080 #[error("In a set_scissor_rect command")]
2081 SetScissorRect,
2082 #[error("In a draw command, kind: {kind:?}")]
2083 Draw {
2084 kind: DrawKind,
2085 family: DrawCommandFamily,
2086 },
2087 #[error("In a write_timestamp command")]
2088 WriteTimestamp,
2089 #[error("In a begin_occlusion_query command")]
2090 BeginOcclusionQuery,
2091 #[error("In a end_occlusion_query command")]
2092 EndOcclusionQuery,
2093 #[error("In a begin_pipeline_statistics_query command")]
2094 BeginPipelineStatisticsQuery,
2095 #[error("In a end_pipeline_statistics_query command")]
2096 EndPipelineStatisticsQuery,
2097 #[error("In a transition_resources command")]
2098 TransitionResources,
2099 #[error("In a execute_bundle command")]
2100 ExecuteBundle,
2101 #[error("In a dispatch command, indirect:{indirect}")]
2102 Dispatch { indirect: bool },
2103 #[error("In a push_debug_group command")]
2104 PushDebugGroup,
2105 #[error("In a pop_debug_group command")]
2106 PopDebugGroup,
2107 #[error("In a insert_debug_marker command")]
2108 InsertDebugMarker,
2109}
2110
2111#[derive(Clone, Debug, Error)]
2113#[error("{scope}")]
2114pub struct PassStateError {
2115 pub scope: PassErrorScope,
2116 #[source]
2117 pub(super) inner: EncoderStateError,
2118}
2119
2120impl WebGpuError for PassStateError {
2121 fn webgpu_error_type(&self) -> ErrorType {
2122 let Self { scope: _, inner } = self;
2123 inner.webgpu_error_type()
2124 }
2125}