1mod allocator;
2mod bind;
3mod bundle;
4mod clear;
5mod compute;
6mod compute_command;
7mod draw;
8mod memory_init;
9mod pass;
10mod query;
11mod ray_tracing;
12mod render;
13mod render_command;
14mod timestamp_writes;
15mod transfer;
16mod transition_resources;
17
18use alloc::{borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec::Vec};
19use core::mem::{self, ManuallyDrop};
20use core::ops;
21
22pub(crate) use self::clear::clear_texture;
23pub use self::{
24 bundle::*, clear::ClearError, compute::*, compute_command::ComputeCommand, draw::*, query::*,
25 render::*, render_command::RenderCommand, transfer::*,
26};
27pub(crate) use allocator::CommandAllocator;
28
29pub(crate) use timestamp_writes::ArcPassTimestampWrites;
30pub use timestamp_writes::PassTimestampWrites;
31
32use self::memory_init::CommandBufferTextureMemoryActions;
33
34use crate::binding_model::BindingError;
35use crate::command::transition_resources::TransitionResourcesError;
36use crate::device::queue::TempResource;
37use crate::device::{Device, DeviceError, MissingFeatures};
38use crate::lock::{rank, Mutex};
39use crate::snatch::SnatchGuard;
40
41use crate::init_tracker::BufferInitTrackerAction;
42use crate::ray_tracing::{AsAction, BuildAccelerationStructureError};
43use crate::resource::{
44 DestroyedResourceError, Fallible, InvalidResourceError, Labeled, ParentDevice as _, QuerySet,
45};
46use crate::storage::Storage;
47use crate::track::{DeviceTracker, ResourceUsageCompatibilityError, Tracker, UsageScope};
48use crate::{api_log, global::Global, id, resource_log, Label};
49use crate::{hal_label, LabelHelpers};
50
51use wgt::error::{ErrorType, WebGpuError};
52
53use thiserror::Error;
54
55#[cfg(feature = "trace")]
56use crate::device::trace::Command as TraceCommand;
57
58const PUSH_CONSTANT_CLEAR_ARRAY: &[u32] = &[0_u32; 64];
59
60pub(crate) enum CommandEncoderStatus {
66 Recording(CommandBufferMutable),
78
79 Locked(CommandBufferMutable),
88
89 Consumed,
90
91 Finished(CommandBufferMutable),
102
103 Error(CommandEncoderError),
108
109 Transitioning,
112}
113
114impl CommandEncoderStatus {
115 fn record_with<
130 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
131 E: Clone + Into<CommandEncoderError>,
132 >(
133 &mut self,
134 f: F,
135 ) -> Result<(), EncoderStateError> {
136 match self {
137 Self::Recording(_) => {
138 RecordingGuard { inner: self }.record(f);
139 Ok(())
140 }
141 Self::Locked(_) => {
142 self.invalidate(EncoderStateError::Locked);
145 Ok(())
146 }
147 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
150 Self::Consumed => Err(EncoderStateError::Ended),
151 Self::Error(_) => Ok(()),
154 Self::Transitioning => unreachable!(),
155 }
156 }
157
158 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
166 &mut self,
167 f: F,
168 ) -> T {
169 match self {
170 Self::Recording(_) => RecordingGuard { inner: self }.record_as_hal_mut(f),
171 Self::Locked(_) => {
172 self.invalidate(EncoderStateError::Locked);
173 f(None)
174 }
175 Self::Finished(_) => {
176 self.invalidate(EncoderStateError::Ended);
177 f(None)
178 }
179 Self::Consumed => f(None),
180 Self::Error(_) => f(None),
181 Self::Transitioning => unreachable!(),
182 }
183 }
184
185 #[cfg(feature = "trace")]
186 fn get_inner(&mut self) -> &mut CommandBufferMutable {
187 match self {
188 Self::Locked(inner) | Self::Finished(inner) | Self::Recording(inner) => inner,
189 Self::Consumed => unreachable!("command encoder is consumed"),
194 Self::Error(_) => unreachable!("passes in a trace do not store errors"),
195 Self::Transitioning => unreachable!(),
196 }
197 }
198
199 fn lock_encoder(&mut self) -> Result<(), EncoderStateError> {
205 match mem::replace(self, Self::Transitioning) {
206 Self::Recording(inner) => {
207 *self = Self::Locked(inner);
208 Ok(())
209 }
210 st @ Self::Finished(_) => {
211 *self = st;
215 Err(EncoderStateError::Ended)
216 }
217 Self::Locked(_) => Err(self.invalidate(EncoderStateError::Locked)),
218 st @ Self::Consumed => {
219 *self = st;
220 Err(EncoderStateError::Ended)
221 }
222 st @ Self::Error(_) => {
223 *self = st;
224 Err(EncoderStateError::Invalid)
225 }
226 Self::Transitioning => unreachable!(),
227 }
228 }
229
230 fn unlock_and_record<
246 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
247 E: Clone + Into<CommandEncoderError>,
248 >(
249 &mut self,
250 f: F,
251 ) -> Result<(), EncoderStateError> {
252 match mem::replace(self, Self::Transitioning) {
253 Self::Locked(inner) => {
254 *self = Self::Recording(inner);
255 RecordingGuard { inner: self }.record(f);
256 Ok(())
257 }
258 st @ Self::Finished(_) => {
259 *self = st;
260 Err(EncoderStateError::Ended)
261 }
262 Self::Recording(_) => {
263 *self = Self::Error(EncoderStateError::Unlocked.into());
264 Err(EncoderStateError::Unlocked)
265 }
266 st @ Self::Consumed => {
267 *self = st;
268 Err(EncoderStateError::Ended)
269 }
270 st @ Self::Error(_) => {
271 *self = st;
274 Ok(())
275 }
276 Self::Transitioning => unreachable!(),
277 }
278 }
279
280 fn finish(&mut self) -> Self {
281 match mem::replace(self, Self::Consumed) {
284 Self::Recording(mut inner) => {
285 if let Err(err) = inner.encoder.close_if_open() {
286 Self::Error(err.into())
287 } else {
288 Self::Finished(inner)
291 }
292 }
293 Self::Consumed | Self::Finished(_) => Self::Error(EncoderStateError::Ended.into()),
294 Self::Locked(_) => Self::Error(EncoderStateError::Locked.into()),
295 st @ Self::Error(_) => st,
296 Self::Transitioning => unreachable!(),
297 }
298 }
299
300 fn invalidate<E: Clone + Into<CommandEncoderError>>(&mut self, err: E) -> E {
306 let enc_err = err.clone().into();
307 api_log!("Invalidating command encoder: {enc_err:?}");
308 *self = Self::Error(enc_err);
309 err
310 }
311}
312
313pub(crate) struct RecordingGuard<'a> {
326 inner: &'a mut CommandEncoderStatus,
327}
328
329impl<'a> RecordingGuard<'a> {
330 pub(crate) fn mark_successful(self) {
331 mem::forget(self)
332 }
333
334 fn record<
335 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
336 E: Clone + Into<CommandEncoderError>,
337 >(
338 mut self,
339 f: F,
340 ) {
341 match f(&mut self) {
342 Ok(()) => self.mark_successful(),
343 Err(err) => {
344 self.inner.invalidate(err);
345 }
346 }
347 }
348
349 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
352 mut self,
353 f: F,
354 ) -> T {
355 let res = f(Some(&mut self));
356 self.mark_successful();
357 res
358 }
359}
360
361impl<'a> Drop for RecordingGuard<'a> {
362 fn drop(&mut self) {
363 if matches!(*self.inner, CommandEncoderStatus::Error(_)) {
364 return;
366 }
367 self.inner.invalidate(EncoderStateError::Invalid);
368 }
369}
370
371impl<'a> ops::Deref for RecordingGuard<'a> {
372 type Target = CommandBufferMutable;
373
374 fn deref(&self) -> &Self::Target {
375 match &*self.inner {
376 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
377 _ => unreachable!(),
378 }
379 }
380}
381
382impl<'a> ops::DerefMut for RecordingGuard<'a> {
383 fn deref_mut(&mut self) -> &mut Self::Target {
384 match self.inner {
385 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
386 _ => unreachable!(),
387 }
388 }
389}
390
391pub(crate) struct CommandEncoder {
392 pub(crate) device: Arc<Device>,
393
394 pub(crate) label: String,
395
396 pub(crate) data: Mutex<CommandEncoderStatus>,
398}
399
400crate::impl_resource_type!(CommandEncoder);
401crate::impl_labeled!(CommandEncoder);
402crate::impl_parent_device!(CommandEncoder);
403crate::impl_storage_item!(CommandEncoder);
404
405impl Drop for CommandEncoder {
406 fn drop(&mut self) {
407 resource_log!("Drop {}", self.error_ident());
408 }
409}
410
411pub(crate) struct InnerCommandEncoder {
427 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
435
436 pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
448
449 pub(crate) device: Arc<Device>,
450
451 pub(crate) is_open: bool,
458
459 pub(crate) label: String,
460}
461
462impl InnerCommandEncoder {
463 fn close_and_swap(&mut self) -> Result<(), DeviceError> {
489 assert!(self.is_open);
490 self.is_open = false;
491
492 let new =
493 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
494 self.list.insert(self.list.len() - 1, new);
495
496 Ok(())
497 }
498
499 pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> {
510 assert!(self.is_open);
511 self.is_open = false;
512
513 let new =
514 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
515 self.list.insert(0, new);
516
517 Ok(())
518 }
519
520 pub(crate) fn close(&mut self) -> Result<(), DeviceError> {
531 assert!(self.is_open);
532 self.is_open = false;
533
534 let cmd_buf =
535 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
536 self.list.push(cmd_buf);
537
538 Ok(())
539 }
540
541 fn close_if_open(&mut self) -> Result<(), DeviceError> {
552 if self.is_open {
553 self.is_open = false;
554 let cmd_buf =
555 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
556 self.list.push(cmd_buf);
557 }
558
559 Ok(())
560 }
561
562 pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
566 if !self.is_open {
567 self.is_open = true;
568 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
569 unsafe { self.raw.begin_encoding(hal_label) }
570 .map_err(|e| self.device.handle_hal_error(e))?;
571 }
572
573 Ok(self.raw.as_mut())
574 }
575
576 pub(crate) fn open_pass(
585 &mut self,
586 label: Option<&str>,
587 ) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
588 assert!(!self.is_open);
589 self.is_open = true;
590
591 let hal_label = hal_label(label, self.device.instance_flags);
592 unsafe { self.raw.begin_encoding(hal_label) }
593 .map_err(|e| self.device.handle_hal_error(e))?;
594
595 Ok(self.raw.as_mut())
596 }
597}
598
599impl Drop for InnerCommandEncoder {
600 fn drop(&mut self) {
601 if self.is_open {
602 unsafe { self.raw.discard_encoding() };
603 }
604 unsafe {
605 self.raw.reset_all(mem::take(&mut self.list));
606 }
607 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
609 self.device.command_allocator.release_encoder(raw);
610 }
611}
612
613pub(crate) struct BakedCommands {
616 pub(crate) encoder: InnerCommandEncoder,
617 pub(crate) trackers: Tracker,
618 pub(crate) temp_resources: Vec<TempResource>,
619 pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
620 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
621 texture_memory_actions: CommandBufferTextureMemoryActions,
622}
623
624pub struct CommandBufferMutable {
626 pub(crate) encoder: InnerCommandEncoder,
631
632 pub(crate) trackers: Tracker,
634
635 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
642 texture_memory_actions: CommandBufferTextureMemoryActions,
643
644 pub(crate) pending_query_resets: QueryResetMap,
645
646 as_actions: Vec<AsAction>,
647 temp_resources: Vec<TempResource>,
648
649 indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
650
651 #[cfg(feature = "trace")]
652 pub(crate) commands: Option<Vec<TraceCommand>>,
653}
654
655impl CommandBufferMutable {
656 pub(crate) fn open_encoder_and_tracker(
657 &mut self,
658 ) -> Result<(&mut dyn hal::DynCommandEncoder, &mut Tracker), DeviceError> {
659 let encoder = self.encoder.open()?;
660 let tracker = &mut self.trackers;
661
662 Ok((encoder, tracker))
663 }
664
665 pub(crate) fn into_baked_commands(self) -> BakedCommands {
666 BakedCommands {
667 encoder: self.encoder,
668 trackers: self.trackers,
669 temp_resources: self.temp_resources,
670 indirect_draw_validation_resources: self.indirect_draw_validation_resources,
671 buffer_memory_init_actions: self.buffer_memory_init_actions,
672 texture_memory_actions: self.texture_memory_actions,
673 }
674 }
675}
676
677pub struct CommandBuffer {
683 pub(crate) device: Arc<Device>,
684 label: String,
686
687 pub(crate) data: Mutex<CommandEncoderStatus>,
689}
690
691impl Drop for CommandBuffer {
692 fn drop(&mut self) {
693 resource_log!("Drop {}", self.error_ident());
694 }
695}
696
697impl CommandEncoder {
698 pub(crate) fn new(
699 encoder: Box<dyn hal::DynCommandEncoder>,
700 device: &Arc<Device>,
701 label: &Label,
702 ) -> Self {
703 CommandEncoder {
704 device: device.clone(),
705 label: label.to_string(),
706 data: Mutex::new(
707 rank::COMMAND_BUFFER_DATA,
708 CommandEncoderStatus::Recording(CommandBufferMutable {
709 encoder: InnerCommandEncoder {
710 raw: ManuallyDrop::new(encoder),
711 list: Vec::new(),
712 device: device.clone(),
713 is_open: false,
714 label: label.to_string(),
715 },
716 trackers: Tracker::new(),
717 buffer_memory_init_actions: Default::default(),
718 texture_memory_actions: Default::default(),
719 pending_query_resets: QueryResetMap::new(),
720 as_actions: Default::default(),
721 temp_resources: Default::default(),
722 indirect_draw_validation_resources:
723 crate::indirect_validation::DrawResources::new(device.clone()),
724 #[cfg(feature = "trace")]
725 commands: if device.trace.lock().is_some() {
726 Some(Vec::new())
727 } else {
728 None
729 },
730 }),
731 ),
732 }
733 }
734
735 pub(crate) fn new_invalid(
736 device: &Arc<Device>,
737 label: &Label,
738 err: CommandEncoderError,
739 ) -> Self {
740 CommandEncoder {
741 device: device.clone(),
742 label: label.to_string(),
743 data: Mutex::new(rank::COMMAND_BUFFER_DATA, CommandEncoderStatus::Error(err)),
744 }
745 }
746
747 pub(crate) fn insert_barriers_from_tracker(
748 raw: &mut dyn hal::DynCommandEncoder,
749 base: &mut Tracker,
750 head: &Tracker,
751 snatch_guard: &SnatchGuard,
752 ) {
753 profiling::scope!("insert_barriers");
754
755 base.buffers.set_from_tracker(&head.buffers);
756 base.textures.set_from_tracker(&head.textures);
757
758 Self::drain_barriers(raw, base, snatch_guard);
759 }
760
761 pub(crate) fn insert_barriers_from_scope(
762 raw: &mut dyn hal::DynCommandEncoder,
763 base: &mut Tracker,
764 head: &UsageScope,
765 snatch_guard: &SnatchGuard,
766 ) {
767 profiling::scope!("insert_barriers");
768
769 base.buffers.set_from_usage_scope(&head.buffers);
770 base.textures.set_from_usage_scope(&head.textures);
771
772 Self::drain_barriers(raw, base, snatch_guard);
773 }
774
775 pub(crate) fn drain_barriers(
776 raw: &mut dyn hal::DynCommandEncoder,
777 base: &mut Tracker,
778 snatch_guard: &SnatchGuard,
779 ) {
780 profiling::scope!("drain_barriers");
781
782 let buffer_barriers = base
783 .buffers
784 .drain_transitions(snatch_guard)
785 .collect::<Vec<_>>();
786 let (transitions, textures) = base.textures.drain_transitions(snatch_guard);
787 let texture_barriers = transitions
788 .into_iter()
789 .enumerate()
790 .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))
791 .collect::<Vec<_>>();
792
793 unsafe {
794 raw.transition_buffers(&buffer_barriers);
795 raw.transition_textures(&texture_barriers);
796 }
797 }
798
799 pub(crate) fn insert_barriers_from_device_tracker(
800 raw: &mut dyn hal::DynCommandEncoder,
801 base: &mut DeviceTracker,
802 head: &Tracker,
803 snatch_guard: &SnatchGuard,
804 ) {
805 profiling::scope!("insert_barriers_from_device_tracker");
806
807 let buffer_barriers = base
808 .buffers
809 .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)
810 .collect::<Vec<_>>();
811
812 let texture_barriers = base
813 .textures
814 .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)
815 .collect::<Vec<_>>();
816
817 unsafe {
818 raw.transition_buffers(&buffer_barriers);
819 raw.transition_textures(&texture_barriers);
820 }
821 }
822}
823
824impl CommandBuffer {
825 pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {
826 use CommandEncoderStatus as St;
827 match mem::replace(
828 &mut *self.data.lock(),
829 CommandEncoderStatus::Error(EncoderStateError::Submitted.into()),
830 ) {
831 St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
832 St::Error(err) => Err(err),
833 St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(),
834 }
835 }
836}
837
838crate::impl_resource_type!(CommandBuffer);
839crate::impl_labeled!(CommandBuffer);
840crate::impl_parent_device!(CommandBuffer);
841crate::impl_storage_item!(CommandBuffer);
842
843#[doc(hidden)]
855#[derive(Debug, Clone)]
856#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
857pub struct BasePass<C, E> {
858 pub label: Option<String>,
859
860 #[cfg_attr(feature = "serde", serde(skip, default = "Option::default"))]
868 pub error: Option<E>,
869
870 pub commands: Vec<C>,
872
873 pub dynamic_offsets: Vec<wgt::DynamicOffset>,
878
879 pub string_data: Vec<u8>,
884
885 pub push_constant_data: Vec<u32>,
890}
891
892impl<C: Clone, E: Clone> BasePass<C, E> {
893 fn new(label: &Label) -> Self {
894 Self {
895 label: label.as_deref().map(str::to_owned),
896 error: None,
897 commands: Vec::new(),
898 dynamic_offsets: Vec::new(),
899 string_data: Vec::new(),
900 push_constant_data: Vec::new(),
901 }
902 }
903
904 fn new_invalid(label: &Label, err: E) -> Self {
905 Self {
906 label: label.as_deref().map(str::to_owned),
907 error: Some(err),
908 commands: Vec::new(),
909 dynamic_offsets: Vec::new(),
910 string_data: Vec::new(),
911 push_constant_data: Vec::new(),
912 }
913 }
914}
915
916macro_rules! pass_base {
939 ($pass:expr, $scope:expr $(,)?) => {
940 match (&$pass.parent, &$pass.base.error) {
941 (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),
943 (&Some(_), &Some(_)) => return Ok(()),
945 (&Some(_), &None) => &mut $pass.base,
947 }
948 };
949}
950pub(crate) use pass_base;
951
952macro_rules! pass_try {
964 ($base:expr, $scope:expr, $res:expr $(,)?) => {
965 match $res.map_pass_err($scope) {
966 Ok(val) => val,
967 Err(err) => {
968 $base.error.get_or_insert(err);
969 return Ok(());
970 }
971 }
972 };
973}
974pub(crate) use pass_try;
975
976#[derive(Clone, Debug, Error)]
981#[non_exhaustive]
982pub enum EncoderStateError {
983 #[error("Encoder is invalid")]
988 Invalid,
989
990 #[error("Encoding must not have ended")]
993 Ended,
994
995 #[error("Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
1001 Locked,
1002
1003 #[error(
1006 "Encoder is not currently locked. A pass can only be ended while the encoder is locked."
1007 )]
1008 Unlocked,
1009
1010 #[error("This command buffer has already been submitted.")]
1015 Submitted,
1016}
1017
1018impl WebGpuError for EncoderStateError {
1019 fn webgpu_error_type(&self) -> ErrorType {
1020 match self {
1021 EncoderStateError::Invalid
1022 | EncoderStateError::Ended
1023 | EncoderStateError::Locked
1024 | EncoderStateError::Unlocked
1025 | EncoderStateError::Submitted => ErrorType::Validation,
1026 }
1027 }
1028}
1029
1030#[derive(Clone, Debug, Error)]
1031#[non_exhaustive]
1032pub enum CommandEncoderError {
1033 #[error(transparent)]
1034 State(#[from] EncoderStateError),
1035 #[error(transparent)]
1036 Device(#[from] DeviceError),
1037 #[error(transparent)]
1038 InvalidResource(#[from] InvalidResourceError),
1039 #[error(transparent)]
1040 DestroyedResource(#[from] DestroyedResourceError),
1041 #[error(transparent)]
1042 ResourceUsage(#[from] ResourceUsageCompatibilityError),
1043 #[error(transparent)]
1044 MissingFeatures(#[from] MissingFeatures),
1045 #[error(transparent)]
1046 Transfer(#[from] TransferError),
1047 #[error(transparent)]
1048 Clear(#[from] ClearError),
1049 #[error(transparent)]
1050 Query(#[from] QueryError),
1051 #[error(transparent)]
1052 BuildAccelerationStructure(#[from] BuildAccelerationStructureError),
1053 #[error(transparent)]
1054 TransitionResources(#[from] TransitionResourcesError),
1055 #[error(transparent)]
1056 ComputePass(#[from] ComputePassError),
1057 #[error(transparent)]
1058 RenderPass(#[from] RenderPassError),
1059}
1060
1061impl CommandEncoderError {
1062 fn is_destroyed_error(&self) -> bool {
1063 matches!(
1064 self,
1065 Self::DestroyedResource(_)
1066 | Self::Clear(ClearError::DestroyedResource(_))
1067 | Self::Query(QueryError::DestroyedResource(_))
1068 | Self::ComputePass(ComputePassError {
1069 inner: ComputePassErrorInner::DestroyedResource(_),
1070 ..
1071 })
1072 | Self::RenderPass(RenderPassError {
1073 inner: RenderPassErrorInner::DestroyedResource(_),
1074 ..
1075 })
1076 | Self::RenderPass(RenderPassError {
1077 inner: RenderPassErrorInner::RenderCommand(
1078 RenderCommandError::DestroyedResource(_)
1079 ),
1080 ..
1081 })
1082 | Self::RenderPass(RenderPassError {
1083 inner: RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError(
1084 BindingError::DestroyedResource(_)
1085 )),
1086 ..
1087 })
1088 )
1089 }
1090}
1091
1092impl WebGpuError for CommandEncoderError {
1093 fn webgpu_error_type(&self) -> ErrorType {
1094 let e: &dyn WebGpuError = match self {
1095 Self::Device(e) => e,
1096 Self::InvalidResource(e) => e,
1097 Self::MissingFeatures(e) => e,
1098 Self::State(e) => e,
1099 Self::DestroyedResource(e) => e,
1100 Self::Transfer(e) => e,
1101 Self::Clear(e) => e,
1102 Self::Query(e) => e,
1103 Self::BuildAccelerationStructure(e) => e,
1104 Self::TransitionResources(e) => e,
1105 Self::ResourceUsage(e) => e,
1106 Self::ComputePass(e) => e,
1107 Self::RenderPass(e) => e,
1108 };
1109 e.webgpu_error_type()
1110 }
1111}
1112
1113#[derive(Clone, Debug, Error)]
1114#[non_exhaustive]
1115pub enum TimestampWritesError {
1116 #[error(
1117 "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
1118 )]
1119 IndicesEqual { idx: u32 },
1120 #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
1121 IndicesMissing,
1122}
1123
1124impl WebGpuError for TimestampWritesError {
1125 fn webgpu_error_type(&self) -> ErrorType {
1126 match self {
1127 Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,
1128 }
1129 }
1130}
1131
1132impl Global {
1133 pub fn command_encoder_finish(
1134 &self,
1135 encoder_id: id::CommandEncoderId,
1136 desc: &wgt::CommandBufferDescriptor<Label>,
1137 id_in: Option<id::CommandBufferId>,
1138 ) -> (id::CommandBufferId, Option<CommandEncoderError>) {
1139 profiling::scope!("CommandEncoder::finish");
1140
1141 let hub = &self.hub;
1142
1143 let cmd_enc = hub.command_encoders.get(encoder_id);
1144
1145 let data = cmd_enc.data.lock().finish();
1146
1147 let error = match data {
1150 CommandEncoderStatus::Error(ref e) if !e.is_destroyed_error() => Some(e.clone()),
1151 _ => None,
1152 };
1153
1154 let cmd_buf = CommandBuffer {
1155 device: cmd_enc.device.clone(),
1156 label: desc.label.to_string(),
1157 data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),
1158 };
1159
1160 let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(Arc::new(cmd_buf));
1161
1162 (cmd_buf_id, error)
1163 }
1164
1165 pub fn command_encoder_push_debug_group(
1166 &self,
1167 encoder_id: id::CommandEncoderId,
1168 label: &str,
1169 ) -> Result<(), EncoderStateError> {
1170 profiling::scope!("CommandEncoder::push_debug_group");
1171 api_log!("CommandEncoder::push_debug_group {label}");
1172
1173 let hub = &self.hub;
1174
1175 let cmd_enc = hub.command_encoders.get(encoder_id);
1176 let mut cmd_buf_data = cmd_enc.data.lock();
1177 cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
1178 #[cfg(feature = "trace")]
1179 if let Some(ref mut list) = cmd_buf_data.commands {
1180 list.push(TraceCommand::PushDebugGroup(label.to_owned()));
1181 }
1182
1183 cmd_enc.device.check_is_valid()?;
1184
1185 let cmd_buf_raw = cmd_buf_data.encoder.open()?;
1186 if !cmd_enc
1187 .device
1188 .instance_flags
1189 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1190 {
1191 unsafe {
1192 cmd_buf_raw.begin_debug_marker(label);
1193 }
1194 }
1195
1196 Ok(())
1197 })
1198 }
1199
1200 pub fn command_encoder_insert_debug_marker(
1201 &self,
1202 encoder_id: id::CommandEncoderId,
1203 label: &str,
1204 ) -> Result<(), EncoderStateError> {
1205 profiling::scope!("CommandEncoder::insert_debug_marker");
1206 api_log!("CommandEncoder::insert_debug_marker {label}");
1207
1208 let hub = &self.hub;
1209
1210 let cmd_enc = hub.command_encoders.get(encoder_id);
1211 let mut cmd_buf_data = cmd_enc.data.lock();
1212 cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
1213 #[cfg(feature = "trace")]
1214 if let Some(ref mut list) = cmd_buf_data.commands {
1215 list.push(TraceCommand::InsertDebugMarker(label.to_owned()));
1216 }
1217
1218 cmd_enc.device.check_is_valid()?;
1219
1220 if !cmd_enc
1221 .device
1222 .instance_flags
1223 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1224 {
1225 let cmd_buf_raw = cmd_buf_data.encoder.open()?;
1226 unsafe {
1227 cmd_buf_raw.insert_debug_marker(label);
1228 }
1229 }
1230
1231 Ok(())
1232 })
1233 }
1234
1235 pub fn command_encoder_pop_debug_group(
1236 &self,
1237 encoder_id: id::CommandEncoderId,
1238 ) -> Result<(), EncoderStateError> {
1239 profiling::scope!("CommandEncoder::pop_debug_marker");
1240 api_log!("CommandEncoder::pop_debug_group");
1241
1242 let hub = &self.hub;
1243
1244 let cmd_enc = hub.command_encoders.get(encoder_id);
1245 let mut cmd_buf_data = cmd_enc.data.lock();
1246 cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
1247 #[cfg(feature = "trace")]
1248 if let Some(ref mut list) = cmd_buf_data.commands {
1249 list.push(TraceCommand::PopDebugGroup);
1250 }
1251
1252 cmd_enc.device.check_is_valid()?;
1253
1254 let cmd_buf_raw = cmd_buf_data.encoder.open()?;
1255 if !cmd_enc
1256 .device
1257 .instance_flags
1258 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1259 {
1260 unsafe {
1261 cmd_buf_raw.end_debug_marker();
1262 }
1263 }
1264
1265 Ok(())
1266 })
1267 }
1268
1269 fn validate_pass_timestamp_writes<E>(
1270 device: &Device,
1271 query_sets: &Storage<Fallible<QuerySet>>,
1272 timestamp_writes: &PassTimestampWrites,
1273 ) -> Result<ArcPassTimestampWrites, E>
1274 where
1275 E: From<TimestampWritesError>
1276 + From<QueryUseError>
1277 + From<DeviceError>
1278 + From<MissingFeatures>
1279 + From<InvalidResourceError>,
1280 {
1281 let &PassTimestampWrites {
1282 query_set,
1283 beginning_of_pass_write_index,
1284 end_of_pass_write_index,
1285 } = timestamp_writes;
1286
1287 device.require_features(wgt::Features::TIMESTAMP_QUERY)?;
1288
1289 let query_set = query_sets.get(query_set).get()?;
1290
1291 query_set.same_device(device)?;
1292
1293 for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
1294 .into_iter()
1295 .flatten()
1296 {
1297 query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
1298 }
1299
1300 if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
1301 if begin == end {
1302 return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());
1303 }
1304 }
1305
1306 if beginning_of_pass_write_index
1307 .or(end_of_pass_write_index)
1308 .is_none()
1309 {
1310 return Err(TimestampWritesError::IndicesMissing.into());
1311 }
1312
1313 Ok(ArcPassTimestampWrites {
1314 query_set,
1315 beginning_of_pass_write_index,
1316 end_of_pass_write_index,
1317 })
1318 }
1319}
1320
1321fn push_constant_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
1322where
1323 PushFn: FnMut(u32, &[u32]),
1324{
1325 let mut count_words = 0_u32;
1326 let size_words = size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT;
1327 while count_words < size_words {
1328 let count_bytes = count_words * wgt::PUSH_CONSTANT_ALIGNMENT;
1329 let size_to_write_words =
1330 (size_words - count_words).min(PUSH_CONSTANT_CLEAR_ARRAY.len() as u32);
1331
1332 push_fn(
1333 offset + count_bytes,
1334 &PUSH_CONSTANT_CLEAR_ARRAY[0..size_to_write_words as usize],
1335 );
1336
1337 count_words += size_to_write_words;
1338 }
1339}
1340
1341#[derive(Debug, Copy, Clone)]
1342struct StateChange<T> {
1343 last_state: Option<T>,
1344}
1345
1346impl<T: Copy + PartialEq> StateChange<T> {
1347 fn new() -> Self {
1348 Self { last_state: None }
1349 }
1350 fn set_and_check_redundant(&mut self, new_state: T) -> bool {
1351 let already_set = self.last_state == Some(new_state);
1352 self.last_state = Some(new_state);
1353 already_set
1354 }
1355 fn reset(&mut self) {
1356 self.last_state = None;
1357 }
1358}
1359
1360impl<T: Copy + PartialEq> Default for StateChange<T> {
1361 fn default() -> Self {
1362 Self::new()
1363 }
1364}
1365
1366#[derive(Debug)]
1367struct BindGroupStateChange {
1368 last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],
1369}
1370
1371impl BindGroupStateChange {
1372 fn new() -> Self {
1373 Self {
1374 last_states: [StateChange::new(); hal::MAX_BIND_GROUPS],
1375 }
1376 }
1377
1378 fn set_and_check_redundant(
1379 &mut self,
1380 bind_group_id: Option<id::BindGroupId>,
1381 index: u32,
1382 dynamic_offsets: &mut Vec<u32>,
1383 offsets: &[wgt::DynamicOffset],
1384 ) -> bool {
1385 if offsets.is_empty() {
1387 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1390 if current_bind_group.set_and_check_redundant(bind_group_id) {
1392 return true;
1393 }
1394 }
1395 } else {
1396 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1400 current_bind_group.reset();
1401 }
1402 dynamic_offsets.extend_from_slice(offsets);
1403 }
1404 false
1405 }
1406 fn reset(&mut self) {
1407 self.last_states = [StateChange::new(); hal::MAX_BIND_GROUPS];
1408 }
1409}
1410
1411impl Default for BindGroupStateChange {
1412 fn default() -> Self {
1413 Self::new()
1414 }
1415}
1416
1417trait MapPassErr<T> {
1419 fn map_pass_err(self, scope: PassErrorScope) -> T;
1420}
1421
1422impl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>
1423where
1424 E: MapPassErr<F>,
1425{
1426 fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {
1427 self.map_err(|err| err.map_pass_err(scope))
1428 }
1429}
1430
1431impl MapPassErr<PassStateError> for EncoderStateError {
1432 fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {
1433 PassStateError { scope, inner: self }
1434 }
1435}
1436
1437#[derive(Clone, Copy, Debug)]
1438pub enum DrawKind {
1439 Draw,
1440 DrawIndirect,
1441 MultiDrawIndirect,
1442 MultiDrawIndirectCount,
1443}
1444
1445#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1447#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1448pub enum DrawCommandFamily {
1449 Draw,
1450 DrawIndexed,
1451 DrawMeshTasks,
1452}
1453
1454#[derive(Clone, Copy, Debug, Error)]
1464pub enum PassErrorScope {
1465 #[error("In a bundle parameter")]
1468 Bundle,
1469 #[error("In a pass parameter")]
1470 Pass,
1471 #[error("In a set_bind_group command")]
1472 SetBindGroup,
1473 #[error("In a set_pipeline command")]
1474 SetPipelineRender,
1475 #[error("In a set_pipeline command")]
1476 SetPipelineCompute,
1477 #[error("In a set_push_constant command")]
1478 SetPushConstant,
1479 #[error("In a set_vertex_buffer command")]
1480 SetVertexBuffer,
1481 #[error("In a set_index_buffer command")]
1482 SetIndexBuffer,
1483 #[error("In a set_blend_constant command")]
1484 SetBlendConstant,
1485 #[error("In a set_stencil_reference command")]
1486 SetStencilReference,
1487 #[error("In a set_viewport command")]
1488 SetViewport,
1489 #[error("In a set_scissor_rect command")]
1490 SetScissorRect,
1491 #[error("In a draw command, kind: {kind:?}")]
1492 Draw {
1493 kind: DrawKind,
1494 family: DrawCommandFamily,
1495 },
1496 #[error("In a write_timestamp command")]
1497 WriteTimestamp,
1498 #[error("In a begin_occlusion_query command")]
1499 BeginOcclusionQuery,
1500 #[error("In a end_occlusion_query command")]
1501 EndOcclusionQuery,
1502 #[error("In a begin_pipeline_statistics_query command")]
1503 BeginPipelineStatisticsQuery,
1504 #[error("In a end_pipeline_statistics_query command")]
1505 EndPipelineStatisticsQuery,
1506 #[error("In a execute_bundle command")]
1507 ExecuteBundle,
1508 #[error("In a dispatch command, indirect:{indirect}")]
1509 Dispatch { indirect: bool },
1510 #[error("In a push_debug_group command")]
1511 PushDebugGroup,
1512 #[error("In a pop_debug_group command")]
1513 PopDebugGroup,
1514 #[error("In a insert_debug_marker command")]
1515 InsertDebugMarker,
1516}
1517
1518#[derive(Clone, Debug, Error)]
1520#[error("{scope}")]
1521pub struct PassStateError {
1522 pub scope: PassErrorScope,
1523 #[source]
1524 pub(super) inner: EncoderStateError,
1525}
1526
1527impl WebGpuError for PassStateError {
1528 fn webgpu_error_type(&self) -> ErrorType {
1529 let Self { scope: _, inner } = self;
1530 inner.webgpu_error_type()
1531 }
1532}