1mod allocator;
12mod bind;
13mod bundle;
14mod clear;
15mod compute;
16mod compute_command;
17mod draw;
18mod encoder;
19mod encoder_command;
20pub mod ffi;
21mod memory_init;
22mod pass;
23mod query;
24mod ray_tracing;
25mod render;
26mod render_command;
27mod timestamp_writes;
28mod transfer;
29mod transition_resources;
30
31use alloc::{borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec::Vec};
32use core::convert::Infallible;
33use core::mem::{self, ManuallyDrop};
34use core::{ops, panic};
35
36#[cfg(feature = "serde")]
37pub(crate) use self::encoder_command::serde_object_reference_struct;
38#[cfg(any(feature = "trace", feature = "replay"))]
39#[doc(hidden)]
40pub use self::encoder_command::PointerReferences;
41pub use self::{
45 bundle::{
46 bundle_ffi, CreateRenderBundleError, ExecutionError, RenderBundle, RenderBundleDescriptor,
47 RenderBundleEncoder, RenderBundleEncoderDescriptor, RenderBundleError,
48 RenderBundleErrorInner,
49 },
50 clear::ClearError,
51 compute::{
52 ComputeBasePass, ComputePass, ComputePassDescriptor, ComputePassError,
53 ComputePassErrorInner, DispatchError,
54 },
55 compute_command::ArcComputeCommand,
56 draw::{DrawError, Rect, RenderCommandError},
57 encoder_command::{ArcCommand, ArcReferences, Command, IdReferences, ReferenceType},
58 query::{QueryError, QueryUseError, ResolveError, SimplifiedQueryType},
59 render::{
60 ArcRenderPassColorAttachment, AttachmentError, AttachmentErrorLocation,
61 ColorAttachmentError, ColorAttachments, LoadOp, PassChannel, RenderBasePass, RenderPass,
62 RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor,
63 RenderPassError, RenderPassErrorInner, ResolvedPassChannel,
64 ResolvedRenderPassDepthStencilAttachment, StoreOp,
65 },
66 render_command::ArcRenderCommand,
67 transfer::{CopySide, TransferError},
68 transition_resources::TransitionResourcesError,
69};
70pub(crate) use self::{
71 clear::clear_texture,
72 encoder::EncodingState,
73 memory_init::CommandBufferTextureMemoryActions,
74 render::{get_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::id::Id;
93use crate::lock::{rank, Mutex};
94use crate::snatch::SnatchGuard;
95
96use crate::init_tracker::BufferInitTrackerAction;
97use crate::ray_tracing::{AsAction, BuildAccelerationStructureError};
98use crate::resource::{
99 DestroyedResourceError, InvalidOrDestroyedResourceError, InvalidResourceError, Labeled,
100 ParentDevice as _, QuerySet,
101};
102use crate::track::{DeviceTracker, ResourceUsageCompatibilityError, Tracker, UsageScope};
103use crate::{api_log, global::Global, id, resource_log, Label};
104use crate::{hal_label, LabelHelpers};
105
106use wgt::error::{ErrorType, WebGpuError};
107
108use thiserror::Error;
109
110pub type TexelCopyBufferInfo = ffi::TexelCopyBufferInfo;
112pub type TexelCopyTextureInfo = ffi::TexelCopyTextureInfo;
114pub type CopyExternalImageDestInfo = ffi::CopyExternalImageDestInfo;
116
117const IMMEDIATES_CLEAR_ARRAY: &[u32] = &[0_u32; 64];
118
119pub(crate) struct EncoderErrorState {
120 error: CommandEncoderError,
121
122 #[cfg(feature = "trace")]
123 trace_commands: Option<Vec<Command<PointerReferences>>>,
124}
125
126fn make_error_state<E: Into<CommandEncoderError>>(error: E) -> CommandEncoderStatus {
136 CommandEncoderStatus::Error(EncoderErrorState {
137 error: error.into(),
138
139 #[cfg(feature = "trace")]
140 trace_commands: None,
141 })
142}
143
144pub(crate) enum CommandEncoderStatus {
150 Recording(CommandBufferMutable),
162
163 Locked(CommandBufferMutable),
172
173 Consumed,
174
175 Finished(CommandBufferMutable),
186
187 Error(EncoderErrorState),
192
193 Transitioning,
196}
197
198impl CommandEncoderStatus {
199 #[doc(hidden)]
200 fn replay(&mut self, commands: Vec<Command<ArcReferences>>) {
201 let Self::Recording(cmd_buf_data) = self else {
202 panic!("encoder should be in the recording state");
203 };
204 cmd_buf_data.commands.extend(commands);
205 }
206
207 fn push_with<F: FnOnce() -> Result<ArcCommand, E>, E: Clone + Into<CommandEncoderError>>(
223 &mut self,
224 f: F,
225 ) -> Result<(), EncoderStateError> {
226 match self {
227 Self::Recording(cmd_buf_data) => {
228 cmd_buf_data.encoder.api.set(EncodingApi::Wgpu);
229 match f() {
230 Ok(cmd) => cmd_buf_data.commands.push(cmd),
231 Err(err) => {
232 self.invalidate(err);
233 }
234 }
235 Ok(())
236 }
237 Self::Locked(_) => {
238 self.invalidate(EncoderStateError::Locked);
241 Ok(())
242 }
243 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
246 Self::Consumed => Err(EncoderStateError::Ended),
247 Self::Error(_) => Ok(()),
250 Self::Transitioning => unreachable!(),
251 }
252 }
253
254 fn with_buffer<
268 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
269 E: Clone + Into<CommandEncoderError>,
270 >(
271 &mut self,
272 api: EncodingApi,
273 f: F,
274 ) -> Result<(), EncoderStateError> {
275 match self {
276 Self::Recording(inner) => {
277 inner.encoder.api.set(api);
278 RecordingGuard { inner: self }.record(f);
279 Ok(())
280 }
281 Self::Locked(_) => {
282 self.invalidate(EncoderStateError::Locked);
285 Ok(())
286 }
287 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
290 Self::Consumed => Err(EncoderStateError::Ended),
291 Self::Error(_) => Ok(()),
294 Self::Transitioning => unreachable!(),
295 }
296 }
297
298 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
306 &mut self,
307 f: F,
308 ) -> T {
309 match self {
310 Self::Recording(inner) => {
311 inner.encoder.api.set(EncodingApi::Raw);
312 RecordingGuard { inner: self }.record_as_hal_mut(f)
313 }
314 Self::Locked(_) => {
315 self.invalidate(EncoderStateError::Locked);
316 f(None)
317 }
318 Self::Finished(_) => {
319 self.invalidate(EncoderStateError::Ended);
320 f(None)
321 }
322 Self::Consumed => f(None),
323 Self::Error(_) => f(None),
324 Self::Transitioning => unreachable!(),
325 }
326 }
327
328 fn lock_encoder(&mut self) -> Result<(), EncoderStateError> {
334 match mem::replace(self, Self::Transitioning) {
335 Self::Recording(inner) => {
336 *self = Self::Locked(inner);
337 Ok(())
338 }
339 st @ Self::Finished(_) => {
340 *self = st;
344 Err(EncoderStateError::Ended)
345 }
346 Self::Locked(_) => Err(self.invalidate(EncoderStateError::Locked)),
347 st @ Self::Consumed => {
348 *self = st;
349 Err(EncoderStateError::Ended)
350 }
351 st @ Self::Error(_) => {
352 *self = st;
353 Err(EncoderStateError::Invalid)
354 }
355 Self::Transitioning => unreachable!(),
356 }
357 }
358
359 fn unlock_encoder(&mut self) -> Result<(), EncoderStateError> {
369 match mem::replace(self, Self::Transitioning) {
370 Self::Locked(inner) => {
371 *self = Self::Recording(inner);
372 Ok(())
373 }
374 st @ Self::Finished(_) => {
375 *self = st;
376 Err(EncoderStateError::Ended)
377 }
378 Self::Recording(_) => {
379 *self = make_error_state(EncoderStateError::Unlocked);
380 Err(EncoderStateError::Unlocked)
381 }
382 st @ Self::Consumed => {
383 *self = st;
384 Err(EncoderStateError::Ended)
385 }
386 st @ Self::Error(_) => {
387 *self = st;
390 Ok(())
391 }
392 Self::Transitioning => unreachable!(),
393 }
394 }
395
396 fn finish(&mut self) -> Self {
397 match mem::replace(self, Self::Consumed) {
400 Self::Recording(inner) => {
401 if inner.encoder.api != EncodingApi::Raw {
404 assert!(!inner.encoder.is_open);
405 }
406 Self::Finished(inner)
407 }
408 Self::Consumed | Self::Finished(_) => make_error_state(EncoderStateError::Ended),
409 Self::Locked(_) => make_error_state(EncoderStateError::Locked),
410 st @ Self::Error(_) => st,
411 Self::Transitioning => unreachable!(),
412 }
413 }
414
415 fn invalidate<E: Clone + Into<CommandEncoderError>>(&mut self, err: E) -> E {
423 #[cfg(feature = "trace")]
424 let trace_commands = match self {
425 Self::Recording(cmd_buf_data) => Some(
426 mem::take(&mut cmd_buf_data.commands)
427 .into_iter()
428 .map(crate::device::trace::IntoTrace::into_trace)
429 .collect(),
430 ),
431 _ => None,
432 };
433
434 let enc_err = err.clone().into();
435 api_log!("Invalidating command encoder: {enc_err:?}");
436 *self = Self::Error(EncoderErrorState {
437 error: enc_err,
438 #[cfg(feature = "trace")]
439 trace_commands,
440 });
441 err
442 }
443}
444
445pub(crate) struct RecordingGuard<'a> {
458 inner: &'a mut CommandEncoderStatus,
459}
460
461impl<'a> RecordingGuard<'a> {
462 pub(crate) fn mark_successful(self) {
463 mem::forget(self)
464 }
465
466 fn record<
467 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
468 E: Clone + Into<CommandEncoderError>,
469 >(
470 mut self,
471 f: F,
472 ) {
473 match f(&mut self) {
474 Ok(()) => self.mark_successful(),
475 Err(err) => {
476 self.inner.invalidate(err);
477 }
478 }
479 }
480
481 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
484 mut self,
485 f: F,
486 ) -> T {
487 let res = f(Some(&mut self));
488 self.mark_successful();
489 res
490 }
491}
492
493impl<'a> Drop for RecordingGuard<'a> {
494 fn drop(&mut self) {
495 if matches!(*self.inner, CommandEncoderStatus::Error(_)) {
496 return;
498 }
499 self.inner.invalidate(EncoderStateError::Invalid);
500 }
501}
502
503impl<'a> ops::Deref for RecordingGuard<'a> {
504 type Target = CommandBufferMutable;
505
506 fn deref(&self) -> &Self::Target {
507 match &*self.inner {
508 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
509 _ => unreachable!(),
510 }
511 }
512}
513
514impl<'a> ops::DerefMut for RecordingGuard<'a> {
515 fn deref_mut(&mut self) -> &mut Self::Target {
516 match self.inner {
517 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
518 _ => unreachable!(),
519 }
520 }
521}
522
523pub struct CommandEncoder {
524 pub(crate) device: Arc<Device>,
525
526 pub(crate) label: String,
527
528 pub(crate) data: Mutex<CommandEncoderStatus>,
530}
531
532crate::impl_resource_type!(CommandEncoder);
533crate::impl_labeled!(CommandEncoder);
534crate::impl_parent_device!(CommandEncoder);
535crate::impl_storage_item!(CommandEncoder);
536
537impl Drop for CommandEncoder {
538 fn drop(&mut self) {
539 resource_log!("Drop {}", self.error_ident());
540 }
541}
542
543#[derive(Copy, Clone, Debug, Eq, PartialEq)]
547pub enum EncodingApi {
548 Wgpu,
550
551 Raw,
553
554 Undecided,
556
557 InternalUse,
559}
560
561impl EncodingApi {
562 pub(crate) fn set(&mut self, api: EncodingApi) {
563 match *self {
564 EncodingApi::Undecided => {
565 *self = api;
566 }
567 self_api if self_api != api => {
568 panic!("Mixing the wgpu encoding API with the raw encoding API is not permitted");
569 }
570 _ => {}
571 }
572 }
573}
574
575pub(crate) struct InnerCommandEncoder {
591 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
599
600 pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
612
613 pub(crate) device: Arc<Device>,
614
615 pub(crate) is_open: bool,
622
623 pub(crate) api: EncodingApi,
629
630 pub(crate) label: String,
631}
632
633impl InnerCommandEncoder {
634 fn close_and_swap(&mut self) -> Result<(), DeviceError> {
666 self.close_and_insert_at(self.list.len() - 1)
667 }
668
669 pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> {
686 self.close_and_insert_at(0)
687 }
688
689 pub(crate) fn close_and_insert_at(&mut self, index: usize) -> Result<(), DeviceError> {
706 assert!(self.is_open);
707 self.is_open = false;
708
709 let cmd_buf =
710 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
711 self.list.insert(index, cmd_buf);
712
713 Ok(())
714 }
715
716 pub(crate) fn close(&mut self) -> Result<(), DeviceError> {
727 assert!(self.is_open);
728 self.is_open = false;
729
730 let cmd_buf =
731 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
732 self.list.push(cmd_buf);
733
734 Ok(())
735 }
736
737 fn close_if_open(&mut self) -> Result<(), DeviceError> {
748 if self.is_open {
749 self.is_open = false;
750 let cmd_buf =
751 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
752 self.list.push(cmd_buf);
753 }
754
755 Ok(())
756 }
757
758 fn open_if_closed(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
764 if !self.is_open {
765 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
766 unsafe { self.raw.begin_encoding(hal_label) }
767 .map_err(|e| self.device.handle_hal_error(e))?;
768 self.is_open = true;
769 }
770
771 Ok(self.raw.as_mut())
772 }
773
774 pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
778 if !self.is_open {
779 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
780 unsafe { self.raw.begin_encoding(hal_label) }
781 .map_err(|e| self.device.handle_hal_error(e))?;
782 self.is_open = true;
783 }
784
785 Ok(self.raw.as_mut())
786 }
787
788 pub(crate) fn open_pass(
797 &mut self,
798 label: Option<&str>,
799 ) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
800 assert!(!self.is_open);
801
802 let hal_label = hal_label(label, self.device.instance_flags);
803 unsafe { self.raw.begin_encoding(hal_label) }
804 .map_err(|e| self.device.handle_hal_error(e))?;
805 self.is_open = true;
806
807 Ok(self.raw.as_mut())
808 }
809}
810
811impl Drop for InnerCommandEncoder {
812 fn drop(&mut self) {
813 if self.is_open {
814 unsafe { self.raw.discard_encoding() };
815 }
816 unsafe {
817 self.raw.reset_all(mem::take(&mut self.list));
818 }
819 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
821 self.device.command_allocator.release_encoder(raw);
822 }
823}
824
825pub(crate) struct BakedCommands {
828 pub(crate) encoder: InnerCommandEncoder,
829 pub(crate) trackers: Tracker,
830 pub(crate) temp_resources: Vec<TempResource>,
831 pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
832 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
833 texture_memory_actions: CommandBufferTextureMemoryActions,
834 pub(crate) query_set_writes: query::QuerySetWrites,
835 pub(crate) deferred_query_set_resolves: Vec<query::DeferredQuerySetResolve>,
836}
837
838pub struct CommandBufferMutable {
840 pub(crate) encoder: InnerCommandEncoder,
845
846 pub(crate) trackers: Tracker,
848
849 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
856 texture_memory_actions: CommandBufferTextureMemoryActions,
857
858 as_actions: Vec<AsAction>,
859 temp_resources: Vec<TempResource>,
860
861 indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
862
863 pub(crate) commands: Vec<Command<ArcReferences>>,
864
865 #[cfg(feature = "trace")]
868 pub(crate) trace_commands: Option<Vec<Command<PointerReferences>>>,
869
870 pub(crate) query_set_writes: query::QuerySetWrites,
872 pub(crate) deferred_query_set_resolves: Vec<query::DeferredQuerySetResolve>,
874}
875
876impl CommandBufferMutable {
877 pub(crate) fn into_baked_commands(self) -> BakedCommands {
878 BakedCommands {
879 encoder: self.encoder,
880 trackers: self.trackers,
881 temp_resources: self.temp_resources,
882 indirect_draw_validation_resources: self.indirect_draw_validation_resources,
883 buffer_memory_init_actions: self.buffer_memory_init_actions,
884 texture_memory_actions: self.texture_memory_actions,
885 query_set_writes: self.query_set_writes,
886 deferred_query_set_resolves: self.deferred_query_set_resolves,
887 }
888 }
889}
890
891pub struct CommandBuffer {
897 pub(crate) device: Arc<Device>,
898 label: String,
900
901 pub(crate) data: Mutex<CommandEncoderStatus>,
903}
904
905impl Drop for CommandBuffer {
906 fn drop(&mut self) {
907 resource_log!("Drop {}", self.error_ident());
908 }
909}
910
911impl CommandEncoder {
912 pub(crate) fn new(
913 encoder: Box<dyn hal::DynCommandEncoder>,
914 device: &Arc<Device>,
915 label: &Label,
916 ) -> Self {
917 CommandEncoder {
918 device: device.clone(),
919 label: label.to_string(),
920 data: Mutex::new(
921 rank::COMMAND_BUFFER_DATA,
922 CommandEncoderStatus::Recording(CommandBufferMutable {
923 encoder: InnerCommandEncoder {
924 raw: ManuallyDrop::new(encoder),
925 list: Vec::new(),
926 device: device.clone(),
927 is_open: false,
928 api: EncodingApi::Undecided,
929 label: label.to_string(),
930 },
931 trackers: Tracker::new(
932 device.ordered_buffer_usages,
933 device.ordered_texture_usages,
934 ),
935 buffer_memory_init_actions: Default::default(),
936 texture_memory_actions: Default::default(),
937 as_actions: Default::default(),
938 temp_resources: Default::default(),
939 indirect_draw_validation_resources:
940 crate::indirect_validation::DrawResources::new(device.clone()),
941 commands: Vec::new(),
942 query_set_writes: Default::default(),
943 deferred_query_set_resolves: Default::default(),
944 #[cfg(feature = "trace")]
945 trace_commands: if device.trace.lock().is_some() {
946 Some(Vec::new())
947 } else {
948 None
949 },
950 }),
951 ),
952 }
953 }
954
955 pub(crate) fn new_invalid(
956 device: &Arc<Device>,
957 label: &Label,
958 err: CommandEncoderError,
959 ) -> Self {
960 CommandEncoder {
961 device: device.clone(),
962 label: label.to_string(),
963 data: Mutex::new(rank::COMMAND_BUFFER_DATA, make_error_state(err)),
964 }
965 }
966
967 pub(crate) fn validate_pass_timestamp_writes<E>(
968 device: &Device,
969 timestamp_writes: &PassTimestampWrites<Arc<QuerySet>>,
970 ) -> Result<ArcPassTimestampWrites, E>
971 where
972 E: From<TimestampWritesError>
973 + From<QueryUseError>
974 + From<DeviceError>
975 + From<MissingFeatures>
976 + From<InvalidResourceError>,
977 {
978 let &PassTimestampWrites {
979 ref query_set,
980 beginning_of_pass_write_index,
981 end_of_pass_write_index,
982 } = timestamp_writes;
983
984 device.require_features(wgt::Features::TIMESTAMP_QUERY)?;
985
986 query_set.check_is_valid()?;
987 query_set.same_device(device)?;
988
989 for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
990 .into_iter()
991 .flatten()
992 {
993 query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
994 }
995
996 if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
997 if begin == end {
998 return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());
999 }
1000 }
1001
1002 if beginning_of_pass_write_index
1003 .or(end_of_pass_write_index)
1004 .is_none()
1005 {
1006 return Err(TimestampWritesError::IndicesMissing.into());
1007 }
1008
1009 Ok(ArcPassTimestampWrites {
1010 query_set: query_set.clone(),
1011 beginning_of_pass_write_index,
1012 end_of_pass_write_index,
1013 })
1014 }
1015
1016 pub(crate) fn insert_barriers_from_tracker(
1017 raw: &mut dyn hal::DynCommandEncoder,
1018 base: &mut Tracker,
1019 head: &Tracker,
1020 snatch_guard: &SnatchGuard,
1021 ) {
1022 profiling::scope!("insert_barriers");
1023
1024 base.buffers.set_from_tracker(&head.buffers);
1025 base.textures.set_from_tracker(&head.textures);
1026
1027 Self::drain_barriers(raw, base, snatch_guard);
1028 }
1029
1030 pub(crate) fn insert_barriers_from_scope(
1031 raw: &mut dyn hal::DynCommandEncoder,
1032 base: &mut Tracker,
1033 head: &UsageScope,
1034 snatch_guard: &SnatchGuard,
1035 ) {
1036 profiling::scope!("insert_barriers");
1037
1038 base.buffers.set_from_usage_scope(&head.buffers);
1039 base.textures.set_from_usage_scope(&head.textures);
1040
1041 Self::drain_barriers(raw, base, snatch_guard);
1042 }
1043
1044 pub(crate) fn drain_barriers(
1045 raw: &mut dyn hal::DynCommandEncoder,
1046 base: &mut Tracker,
1047 snatch_guard: &SnatchGuard,
1048 ) {
1049 profiling::scope!("drain_barriers");
1050
1051 let buffer_barriers = base
1052 .buffers
1053 .drain_transitions(snatch_guard)
1054 .collect::<Vec<_>>();
1055 let (transitions, textures) = base.textures.drain_transitions(snatch_guard);
1056 let texture_barriers = transitions
1057 .into_iter()
1058 .enumerate()
1059 .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))
1060 .collect::<Vec<_>>();
1061
1062 unsafe {
1063 raw.transition_buffers(&buffer_barriers);
1064 raw.transition_textures(&texture_barriers);
1065 }
1066 }
1067
1068 pub(crate) fn insert_barriers_from_device_tracker(
1069 raw: &mut dyn hal::DynCommandEncoder,
1070 base: &mut DeviceTracker,
1071 head: &Tracker,
1072 snatch_guard: &SnatchGuard,
1073 ) {
1074 profiling::scope!("insert_barriers_from_device_tracker");
1075
1076 let buffer_barriers = base
1077 .buffers
1078 .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)
1079 .collect::<Vec<_>>();
1080
1081 let texture_barriers = base
1082 .textures
1083 .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)
1084 .collect::<Vec<_>>();
1085
1086 unsafe {
1087 raw.transition_buffers(&buffer_barriers);
1088 raw.transition_textures(&texture_barriers);
1089 }
1090 }
1091
1092 fn encode_commands(
1093 device: &Arc<Device>,
1094 cmd_buf_data: &mut CommandBufferMutable,
1095 ) -> Result<(), CommandEncoderError> {
1096 device.check_is_valid()?;
1097 let snatch_guard = device.snatchable_lock.read();
1098 let mut debug_scope_depth = 0;
1099
1100 if cmd_buf_data.encoder.api == EncodingApi::Raw {
1101 assert!(cmd_buf_data.commands.is_empty());
1104 }
1105
1106 let commands = mem::take(&mut cmd_buf_data.commands);
1107
1108 #[cfg(feature = "trace")]
1109 if device.trace.lock().is_some() {
1110 cmd_buf_data.trace_commands = Some(
1111 commands
1112 .iter()
1113 .map(crate::device::trace::IntoTrace::to_trace)
1114 .collect(),
1115 );
1116 }
1117
1118 for command in commands {
1119 if matches!(
1120 command,
1121 ArcCommand::RunRenderPass { .. }
1122 | ArcCommand::RunComputePass { .. }
1123 | ArcCommand::ResolveQuerySet { .. }
1124 ) {
1125 let mut state = EncodingState {
1131 device,
1132 raw_encoder: &mut cmd_buf_data.encoder,
1133 tracker: &mut cmd_buf_data.trackers,
1134 buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1135 texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1136 as_actions: &mut cmd_buf_data.as_actions,
1137 temp_resources: &mut cmd_buf_data.temp_resources,
1138 indirect_draw_validation_resources: &mut cmd_buf_data
1139 .indirect_draw_validation_resources,
1140 snatch_guard: &snatch_guard,
1141 debug_scope_depth: &mut debug_scope_depth,
1142 query_set_writes: &mut cmd_buf_data.query_set_writes,
1143 deferred_query_set_resolves: &mut cmd_buf_data.deferred_query_set_resolves,
1144 };
1145
1146 match command {
1147 ArcCommand::RunRenderPass {
1148 pass,
1149 color_attachments,
1150 depth_stencil_attachment,
1151 timestamp_writes,
1152 occlusion_query_set,
1153 multiview_mask,
1154 } => {
1155 api_log!(
1156 "Begin encoding render pass with '{}' label",
1157 pass.label.as_deref().unwrap_or("")
1158 );
1159 let res = render::encode_render_pass(
1160 &mut state,
1161 pass,
1162 color_attachments,
1163 depth_stencil_attachment,
1164 timestamp_writes,
1165 occlusion_query_set,
1166 multiview_mask,
1167 );
1168 match res.as_ref() {
1169 Err(err) => {
1170 api_log!("Finished encoding render pass ({err:?})")
1171 }
1172 Ok(_) => {
1173 api_log!("Finished encoding render pass (success)")
1174 }
1175 }
1176 res?;
1177 }
1178 ArcCommand::RunComputePass {
1179 pass,
1180 timestamp_writes,
1181 } => {
1182 api_log!(
1183 "Begin encoding compute pass with '{}' label",
1184 pass.label.as_deref().unwrap_or("")
1185 );
1186 let res = compute::encode_compute_pass(&mut state, pass, timestamp_writes);
1187 match res.as_ref() {
1188 Err(err) => {
1189 api_log!("Finished encoding compute pass ({err:?})")
1190 }
1191 Ok(_) => {
1192 api_log!("Finished encoding compute pass (success)")
1193 }
1194 }
1195 res?;
1196 }
1197 ArcCommand::ResolveQuerySet {
1198 query_set,
1199 start_query,
1200 query_count,
1201 destination,
1202 destination_offset,
1203 } => {
1204 query::resolve_query_set(
1205 &mut state,
1206 query_set,
1207 start_query,
1208 query_count,
1209 destination,
1210 destination_offset,
1211 )?;
1212 }
1213 _ => unreachable!(),
1214 }
1215 } else {
1216 let raw_encoder = cmd_buf_data.encoder.open_if_closed()?;
1222 let mut state = EncodingState {
1223 device,
1224 raw_encoder,
1225 tracker: &mut cmd_buf_data.trackers,
1226 buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1227 texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1228 as_actions: &mut cmd_buf_data.as_actions,
1229 temp_resources: &mut cmd_buf_data.temp_resources,
1230 indirect_draw_validation_resources: &mut cmd_buf_data
1231 .indirect_draw_validation_resources,
1232 snatch_guard: &snatch_guard,
1233 debug_scope_depth: &mut debug_scope_depth,
1234 query_set_writes: &mut cmd_buf_data.query_set_writes,
1235 deferred_query_set_resolves: &mut cmd_buf_data.deferred_query_set_resolves,
1236 };
1237 match command {
1238 ArcCommand::CopyBufferToBuffer {
1239 src,
1240 src_offset,
1241 dst,
1242 dst_offset,
1243 size,
1244 } => {
1245 transfer::copy_buffer_to_buffer(
1246 &mut state, &src, src_offset, &dst, dst_offset, size,
1247 )?;
1248 }
1249 ArcCommand::CopyBufferToTexture { src, dst, size } => {
1250 transfer::copy_buffer_to_texture(&mut state, &src, &dst, &size)?;
1251 }
1252 ArcCommand::CopyTextureToBuffer { src, dst, size } => {
1253 transfer::copy_texture_to_buffer(&mut state, &src, &dst, &size)?;
1254 }
1255 ArcCommand::CopyTextureToTexture { src, dst, size } => {
1256 transfer::copy_texture_to_texture(&mut state, &src, &dst, &size)?;
1257 }
1258 ArcCommand::ClearBuffer { dst, offset, size } => {
1259 clear::clear_buffer(&mut state, dst, offset, size)?;
1260 }
1261 ArcCommand::ClearTexture {
1262 dst,
1263 subresource_range,
1264 } => {
1265 clear::clear_texture_cmd(&mut state, dst, &subresource_range)?;
1266 }
1267 ArcCommand::WriteTimestamp {
1268 query_set,
1269 query_index,
1270 } => {
1271 query::write_timestamp(&mut state, query_set, query_index)?;
1272 }
1273 ArcCommand::PushDebugGroup(label) => {
1274 push_debug_group(&mut state, &label)?;
1275 }
1276 ArcCommand::PopDebugGroup => {
1277 pop_debug_group(&mut state)?;
1278 }
1279 ArcCommand::InsertDebugMarker(label) => {
1280 insert_debug_marker(&mut state, &label)?;
1281 }
1282 ArcCommand::BuildAccelerationStructures { blas, tlas } => {
1283 ray_tracing::build_acceleration_structures(&mut state, blas, tlas)?;
1284 }
1285 ArcCommand::TransitionResources {
1286 buffer_transitions,
1287 texture_transitions,
1288 } => {
1289 transition_resources::transition_resources(
1290 &mut state,
1291 buffer_transitions,
1292 texture_transitions,
1293 )?;
1294 }
1295 ArcCommand::RunComputePass { .. }
1296 | ArcCommand::RunRenderPass { .. }
1297 | ArcCommand::ResolveQuerySet { .. } => {
1298 unreachable!()
1299 }
1300 }
1301 }
1302 }
1303
1304 if debug_scope_depth > 0 {
1305 Err(CommandEncoderError::DebugGroupError(
1306 DebugGroupError::MissingPop,
1307 ))?;
1308 }
1309
1310 cmd_buf_data.encoder.close_if_open()?;
1312
1313 Ok(())
1317 }
1318
1319 fn finish(
1320 self: &Arc<Self>,
1321 desc: &wgt::CommandBufferDescriptor<Label>,
1322 ) -> (Arc<CommandBuffer>, Option<CommandEncoderError>) {
1323 let status = self.data.lock().finish();
1324
1325 let res = match status {
1326 CommandEncoderStatus::Finished(mut cmd_buf_data) => {
1327 match Self::encode_commands(&self.device, &mut cmd_buf_data) {
1328 Ok(()) => Ok(cmd_buf_data),
1329 Err(error) => Err(EncoderErrorState {
1330 error,
1331 #[cfg(feature = "trace")]
1332 trace_commands: mem::take(&mut cmd_buf_data.trace_commands),
1333 }),
1334 }
1335 }
1336 CommandEncoderStatus::Error(error_state) => Err(error_state),
1337 _ => unreachable!(),
1338 };
1339
1340 let (data, error) = match res {
1341 Err(EncoderErrorState {
1342 error,
1343 #[cfg(feature = "trace")]
1344 trace_commands,
1345 }) => {
1346 #[cfg(feature = "trace")]
1350 if let Some(trace) = self.device.trace.lock().as_mut() {
1351 use alloc::string::ToString;
1352
1353 trace.add(crate::device::trace::Action::FailedCommands {
1354 commands: trace_commands,
1355 failed_at_submit: None,
1356 error: error.to_string(),
1357 });
1358 }
1359
1360 if error.is_destroyed_error() {
1361 (make_error_state(error), None)
1364 } else {
1365 (make_error_state(error.clone()), Some(error))
1366 }
1367 }
1368
1369 Ok(data) => (CommandEncoderStatus::Finished(data), None),
1370 };
1371
1372 let cmd_buf = Arc::new(CommandBuffer {
1373 device: self.device.clone(),
1374 label: desc.label.to_string(),
1375 data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),
1376 });
1377
1378 (cmd_buf, error)
1379 }
1380}
1381
1382impl CommandBuffer {
1383 #[doc(hidden)]
1389 pub fn from_trace(device: &Arc<Device>, commands: Vec<Command<ArcReferences>>) -> Arc<Self> {
1390 let encoder = device.create_command_encoder(&None).unwrap();
1391 let mut cmd_enc_status = encoder.data.lock();
1392 cmd_enc_status.replay(commands);
1393 drop(cmd_enc_status);
1394
1395 let (cmd_buf, error) = encoder.finish(&wgt::CommandBufferDescriptor { label: None });
1396 if let Some(err) = error {
1397 panic!("CommandEncoder::finish failed: {err}");
1398 }
1399
1400 cmd_buf
1401 }
1402
1403 pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {
1404 use CommandEncoderStatus as St;
1405 match mem::replace(
1406 &mut *self.data.lock(),
1407 make_error_state(EncoderStateError::Submitted),
1408 ) {
1409 St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
1410 St::Error(EncoderErrorState {
1411 #[cfg(feature = "trace")]
1412 trace_commands: _,
1413 error,
1414 }) => Err(error),
1415 St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(),
1416 }
1417 }
1418}
1419
1420crate::impl_resource_type!(CommandBuffer);
1421crate::impl_labeled!(CommandBuffer);
1422crate::impl_parent_device!(CommandBuffer);
1423crate::impl_storage_item!(CommandBuffer);
1424
1425#[doc(hidden)]
1437#[derive(Debug, Clone)]
1438#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1439pub struct BasePass<C, E> {
1440 pub label: Option<String>,
1441
1442 #[cfg_attr(feature = "serde", serde(skip, default = "Option::default"))]
1450 pub error: Option<E>,
1451
1452 pub commands: Vec<C>,
1458
1459 pub dynamic_offsets: Vec<wgt::DynamicOffset>,
1464
1465 pub string_data: Vec<u8>,
1470
1471 pub immediates_data: Vec<u32>,
1476}
1477
1478impl<C: Clone, E: Clone> BasePass<C, E> {
1479 fn new(label: &Label) -> Self {
1480 Self {
1481 label: label.as_deref().map(str::to_owned),
1482 error: None,
1483 commands: Vec::new(),
1484 dynamic_offsets: Vec::new(),
1485 string_data: Vec::new(),
1486 immediates_data: Vec::new(),
1487 }
1488 }
1489
1490 fn new_invalid(label: &Label, err: E) -> Self {
1491 Self {
1492 label: label.as_deref().map(str::to_owned),
1493 error: Some(err),
1494 commands: Vec::new(),
1495 dynamic_offsets: Vec::new(),
1496 string_data: Vec::new(),
1497 immediates_data: Vec::new(),
1498 }
1499 }
1500
1501 fn take(&mut self) -> Result<BasePass<C, Infallible>, E> {
1508 match self.error.as_ref() {
1509 Some(err) => Err(err.clone()),
1510 None => Ok(BasePass {
1511 label: self.label.clone(),
1512 error: None,
1513 commands: mem::take(&mut self.commands),
1514 dynamic_offsets: mem::take(&mut self.dynamic_offsets),
1515 string_data: mem::take(&mut self.string_data),
1516 immediates_data: mem::take(&mut self.immediates_data),
1517 }),
1518 }
1519 }
1520}
1521
1522macro_rules! pass_base {
1545 ($pass:expr, $scope:expr $(,)?) => {
1546 match (&$pass.parent, &$pass.base.error) {
1547 (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),
1549 (&Some(_), &Some(_)) => return Ok(()),
1551 (&Some(_), &None) => &mut $pass.base,
1553 }
1554 };
1555}
1556pub(crate) use pass_base;
1557
1558macro_rules! pass_try {
1570 ($base:expr, $scope:expr, $res:expr $(,)?) => {
1571 match $res.map_pass_err($scope) {
1572 Ok(val) => val,
1573 Err(err) => {
1574 $base.error.get_or_insert(err);
1575 return Ok(());
1576 }
1577 }
1578 };
1579}
1580pub(crate) use pass_try;
1581
1582#[derive(Clone, Debug, Error)]
1587#[non_exhaustive]
1588pub enum EncoderStateError {
1589 #[error("Encoder is invalid")]
1594 Invalid,
1595
1596 #[error("Encoding must not have ended")]
1599 Ended,
1600
1601 #[error("Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
1607 Locked,
1608
1609 #[error(
1612 "Encoder is not currently locked. A pass can only be ended while the encoder is locked."
1613 )]
1614 Unlocked,
1615
1616 #[error("This command buffer has already been submitted.")]
1621 Submitted,
1622}
1623
1624impl WebGpuError for EncoderStateError {
1625 fn webgpu_error_type(&self) -> ErrorType {
1626 match self {
1627 EncoderStateError::Invalid
1628 | EncoderStateError::Ended
1629 | EncoderStateError::Locked
1630 | EncoderStateError::Unlocked
1631 | EncoderStateError::Submitted => ErrorType::Validation,
1632 }
1633 }
1634}
1635
1636#[derive(Clone, Debug, Error)]
1637#[non_exhaustive]
1638pub enum CommandEncoderError {
1639 #[error(transparent)]
1640 State(#[from] EncoderStateError),
1641 #[error(transparent)]
1642 Device(#[from] DeviceError),
1643 #[error(transparent)]
1644 InvalidResource(#[from] InvalidResourceError),
1645 #[error(transparent)]
1646 DestroyedResource(#[from] DestroyedResourceError),
1647 #[error(transparent)]
1648 ResourceUsage(#[from] ResourceUsageCompatibilityError),
1649 #[error(transparent)]
1650 DebugGroupError(#[from] DebugGroupError),
1651 #[error(transparent)]
1652 MissingFeatures(#[from] MissingFeatures),
1653 #[error(transparent)]
1654 Transfer(#[from] TransferError),
1655 #[error(transparent)]
1656 Clear(#[from] ClearError),
1657 #[error(transparent)]
1658 Query(#[from] QueryError),
1659 #[error(transparent)]
1660 BuildAccelerationStructure(#[from] BuildAccelerationStructureError),
1661 #[error(transparent)]
1662 TransitionResources(#[from] TransitionResourcesError),
1663 #[error(transparent)]
1664 ComputePass(#[from] ComputePassError),
1665 #[error(transparent)]
1666 RenderPass(#[from] RenderPassError),
1667}
1668
1669impl From<InvalidOrDestroyedResourceError> for CommandEncoderError {
1670 fn from(err: InvalidOrDestroyedResourceError) -> Self {
1671 match err {
1672 InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
1673 InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
1674 }
1675 }
1676}
1677
1678impl CommandEncoderError {
1679 fn is_destroyed_error(&self) -> bool {
1680 matches!(
1681 self,
1682 Self::DestroyedResource(_)
1683 | Self::Clear(ClearError::DestroyedResource(_))
1684 | Self::Query(QueryError::DestroyedResource(_))
1685 | Self::ComputePass(ComputePassError {
1686 inner: ComputePassErrorInner::DestroyedResource(_),
1687 ..
1688 })
1689 | Self::RenderPass(RenderPassError {
1690 inner: RenderPassErrorInner::DestroyedResource(_),
1691 ..
1692 })
1693 | Self::RenderPass(RenderPassError {
1694 inner: RenderPassErrorInner::RenderCommand(
1695 RenderCommandError::DestroyedResource(_)
1696 ),
1697 ..
1698 })
1699 | Self::RenderPass(RenderPassError {
1700 inner: RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError(
1701 BindingError::DestroyedResource(_)
1702 )),
1703 ..
1704 })
1705 )
1706 }
1707}
1708
1709impl WebGpuError for CommandEncoderError {
1710 fn webgpu_error_type(&self) -> ErrorType {
1711 match self {
1712 Self::Device(e) => e.webgpu_error_type(),
1713 Self::InvalidResource(e) => e.webgpu_error_type(),
1714 Self::DebugGroupError(e) => e.webgpu_error_type(),
1715 Self::MissingFeatures(e) => e.webgpu_error_type(),
1716 Self::State(e) => e.webgpu_error_type(),
1717 Self::DestroyedResource(e) => e.webgpu_error_type(),
1718 Self::Transfer(e) => e.webgpu_error_type(),
1719 Self::Clear(e) => e.webgpu_error_type(),
1720 Self::Query(e) => e.webgpu_error_type(),
1721 Self::BuildAccelerationStructure(e) => e.webgpu_error_type(),
1722 Self::TransitionResources(e) => e.webgpu_error_type(),
1723 Self::ResourceUsage(e) => e.webgpu_error_type(),
1724 Self::ComputePass(e) => e.webgpu_error_type(),
1725 Self::RenderPass(e) => e.webgpu_error_type(),
1726 }
1727 }
1728}
1729
1730#[derive(Clone, Debug, Error)]
1731#[non_exhaustive]
1732pub enum DebugGroupError {
1733 #[error("Cannot pop debug group, because number of pushed debug groups is zero")]
1734 InvalidPop,
1735 #[error("A debug group was not popped before the encoder was finished")]
1736 MissingPop,
1737}
1738
1739impl WebGpuError for DebugGroupError {
1740 fn webgpu_error_type(&self) -> ErrorType {
1741 match self {
1742 Self::InvalidPop | Self::MissingPop => ErrorType::Validation,
1743 }
1744 }
1745}
1746
1747#[derive(Clone, Debug, Error)]
1748#[non_exhaustive]
1749pub enum TimestampWritesError {
1750 #[error(
1751 "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
1752 )]
1753 IndicesEqual { idx: u32 },
1754 #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
1755 IndicesMissing,
1756}
1757
1758impl WebGpuError for TimestampWritesError {
1759 fn webgpu_error_type(&self) -> ErrorType {
1760 match self {
1761 Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,
1762 }
1763 }
1764}
1765
1766impl Global {
1767 fn resolve_buffer_id(
1768 &self,
1769 buffer_id: Id<id::markers::Buffer>,
1770 ) -> Result<Arc<crate::resource::Buffer>, InvalidResourceError> {
1771 self.hub.buffers.get(buffer_id).get()
1772 }
1773
1774 pub fn command_encoder_finish(
1782 &self,
1783 encoder_id: id::CommandEncoderId,
1784 desc: &wgt::CommandBufferDescriptor<Label>,
1785 id_in: Option<id::CommandBufferId>,
1786 ) -> (id::CommandBufferId, Option<(String, CommandEncoderError)>) {
1787 profiling::scope!("CommandEncoder::finish");
1788
1789 let hub = &self.hub;
1790 let cmd_enc = hub.command_encoders.get(encoder_id);
1791
1792 let (cmd_buf, opt_error) = cmd_enc.finish(desc);
1793 let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(cmd_buf);
1794
1795 (
1796 cmd_buf_id,
1797 opt_error.map(|error| (cmd_enc.label.clone(), error)),
1798 )
1799 }
1800
1801 pub fn command_encoder_push_debug_group(
1802 &self,
1803 encoder_id: id::CommandEncoderId,
1804 label: &str,
1805 ) -> Result<(), EncoderStateError> {
1806 profiling::scope!("CommandEncoder::push_debug_group");
1807 api_log!("CommandEncoder::push_debug_group {label}");
1808
1809 let hub = &self.hub;
1810
1811 let cmd_enc = hub.command_encoders.get(encoder_id);
1812 let mut cmd_buf_data = cmd_enc.data.lock();
1813
1814 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1815 Ok(ArcCommand::PushDebugGroup(label.to_owned()))
1816 })
1817 }
1818
1819 pub fn command_encoder_insert_debug_marker(
1820 &self,
1821 encoder_id: id::CommandEncoderId,
1822 label: &str,
1823 ) -> Result<(), EncoderStateError> {
1824 profiling::scope!("CommandEncoder::insert_debug_marker");
1825 api_log!("CommandEncoder::insert_debug_marker {label}");
1826
1827 let hub = &self.hub;
1828
1829 let cmd_enc = hub.command_encoders.get(encoder_id);
1830 let mut cmd_buf_data = cmd_enc.data.lock();
1831
1832 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1833 Ok(ArcCommand::InsertDebugMarker(label.to_owned()))
1834 })
1835 }
1836
1837 pub fn command_encoder_pop_debug_group(
1838 &self,
1839 encoder_id: id::CommandEncoderId,
1840 ) -> Result<(), EncoderStateError> {
1841 profiling::scope!("CommandEncoder::pop_debug_marker");
1842 api_log!("CommandEncoder::pop_debug_group");
1843
1844 let hub = &self.hub;
1845
1846 let cmd_enc = hub.command_encoders.get(encoder_id);
1847 let mut cmd_buf_data = cmd_enc.data.lock();
1848
1849 cmd_buf_data
1850 .push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::PopDebugGroup) })
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
1903fn immediates_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
1904where
1905 PushFn: FnMut(u32, &[u32]),
1906{
1907 let mut count_words = 0_u32;
1908 let size_words = size_bytes / wgt::IMMEDIATE_DATA_ALIGNMENT;
1909 while count_words < size_words {
1910 let count_bytes = count_words * wgt::IMMEDIATE_DATA_ALIGNMENT;
1911 let size_to_write_words =
1912 (size_words - count_words).min(IMMEDIATES_CLEAR_ARRAY.len() as u32);
1913
1914 push_fn(
1915 offset + count_bytes,
1916 &IMMEDIATES_CLEAR_ARRAY[0..size_to_write_words as usize],
1917 );
1918
1919 count_words += size_to_write_words;
1920 }
1921}
1922
1923#[derive(Debug, Copy, Clone)]
1924struct StateChange<T> {
1925 last_state: Option<T>,
1926}
1927
1928impl<T: Copy + PartialEq> StateChange<T> {
1929 fn new() -> Self {
1930 Self { last_state: None }
1931 }
1932 fn set_and_check_redundant(&mut self, new_state: T) -> bool {
1933 let already_set = self.last_state == Some(new_state);
1934 self.last_state = Some(new_state);
1935 already_set
1936 }
1937 fn reset(&mut self) {
1938 self.last_state = None;
1939 }
1940}
1941
1942impl<T: Copy + PartialEq> Default for StateChange<T> {
1943 fn default() -> Self {
1944 Self::new()
1945 }
1946}
1947
1948#[derive(Debug)]
1949struct BindGroupStateChange {
1950 last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],
1951}
1952
1953impl BindGroupStateChange {
1954 fn new() -> Self {
1955 Self {
1956 last_states: [StateChange::new(); hal::MAX_BIND_GROUPS],
1957 }
1958 }
1959
1960 fn set_and_check_redundant(
1961 &mut self,
1962 bind_group_id: Option<id::BindGroupId>,
1963 index: u32,
1964 dynamic_offsets: &mut Vec<u32>,
1965 offsets: &[wgt::DynamicOffset],
1966 ) -> bool {
1967 if offsets.is_empty() {
1969 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1972 if current_bind_group.set_and_check_redundant(bind_group_id) {
1974 return true;
1975 }
1976 }
1977 } else {
1978 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1982 current_bind_group.reset();
1983 }
1984 dynamic_offsets.extend_from_slice(offsets);
1985 }
1986 false
1987 }
1988 fn reset(&mut self) {
1989 self.last_states = [StateChange::new(); hal::MAX_BIND_GROUPS];
1990 }
1991}
1992
1993impl Default for BindGroupStateChange {
1994 fn default() -> Self {
1995 Self::new()
1996 }
1997}
1998
1999trait MapPassErr<T> {
2001 fn map_pass_err(self, scope: PassErrorScope) -> T;
2002}
2003
2004impl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>
2005where
2006 E: MapPassErr<F>,
2007{
2008 fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {
2009 self.map_err(|err| err.map_pass_err(scope))
2010 }
2011}
2012
2013impl MapPassErr<PassStateError> for EncoderStateError {
2014 fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {
2015 PassStateError { scope, inner: self }
2016 }
2017}
2018
2019#[derive(Clone, Copy, Debug)]
2020pub enum DrawKind {
2021 Draw,
2022 DrawIndirect,
2023 MultiDrawIndirect,
2024 MultiDrawIndirectCount,
2025}
2026
2027#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
2029#[derive(Clone, Copy, Debug, PartialEq, Eq)]
2030pub enum DrawCommandFamily {
2031 Draw,
2032 DrawIndexed,
2033 DrawMeshTasks,
2034}
2035
2036#[derive(Clone, Copy, Debug, Error)]
2046pub enum PassErrorScope {
2047 #[error("In a bundle parameter")]
2050 Bundle,
2051 #[error("In a pass parameter")]
2052 Pass,
2053 #[error("In a set_bind_group command")]
2054 SetBindGroup,
2055 #[error("In a set_pipeline command")]
2056 SetPipelineRender,
2057 #[error("In a set_pipeline command")]
2058 SetPipelineCompute,
2059 #[error("In a set_immediates command")]
2060 SetImmediate,
2061 #[error("In a set_vertex_buffer command")]
2062 SetVertexBuffer,
2063 #[error("In a set_index_buffer command")]
2064 SetIndexBuffer,
2065 #[error("In a set_blend_constant command")]
2066 SetBlendConstant,
2067 #[error("In a set_stencil_reference command")]
2068 SetStencilReference,
2069 #[error("In a set_viewport command")]
2070 SetViewport,
2071 #[error("In a set_scissor_rect command")]
2072 SetScissorRect,
2073 #[error("In a draw command, kind: {kind:?}")]
2074 Draw {
2075 kind: DrawKind,
2076 family: DrawCommandFamily,
2077 },
2078 #[error("In a write_timestamp command")]
2079 WriteTimestamp,
2080 #[error("In a begin_occlusion_query command")]
2081 BeginOcclusionQuery,
2082 #[error("In a end_occlusion_query command")]
2083 EndOcclusionQuery,
2084 #[error("In a begin_pipeline_statistics_query command")]
2085 BeginPipelineStatisticsQuery,
2086 #[error("In a end_pipeline_statistics_query command")]
2087 EndPipelineStatisticsQuery,
2088 #[error("In a transition_resources command")]
2089 TransitionResources,
2090 #[error("In a execute_bundle command")]
2091 ExecuteBundle,
2092 #[error("In a dispatch command, indirect:{indirect}")]
2093 Dispatch { indirect: bool },
2094 #[error("In a push_debug_group command")]
2095 PushDebugGroup,
2096 #[error("In a pop_debug_group command")]
2097 PopDebugGroup,
2098 #[error("In a insert_debug_marker command")]
2099 InsertDebugMarker,
2100}
2101
2102#[derive(Clone, Debug, Error)]
2104#[error("{scope}")]
2105pub struct PassStateError {
2106 pub scope: PassErrorScope,
2107 #[source]
2108 pub(super) inner: EncoderStateError,
2109}
2110
2111impl WebGpuError for PassStateError {
2112 fn webgpu_error_type(&self) -> ErrorType {
2113 let Self { scope: _, inner } = self;
2114 inner.webgpu_error_type()
2115 }
2116}