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