1mod allocator;
2mod bind;
3mod bundle;
4mod clear;
5mod compute;
6mod compute_command;
7mod draw;
8mod encoder;
9mod encoder_command;
10pub mod ffi;
11mod memory_init;
12mod pass;
13mod query;
14mod ray_tracing;
15mod render;
16mod render_command;
17mod timestamp_writes;
18mod transfer;
19mod transition_resources;
20
21use alloc::{borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec::Vec};
22use core::mem::{self, ManuallyDrop};
23use core::ops;
24
25pub(crate) use self::clear::clear_texture;
26pub use self::{
27 bundle::*, clear::ClearError, compute::*, compute_command::ComputeCommand, draw::*,
28 encoder_command::Command, query::*, render::*, render_command::RenderCommand, transfer::*,
29};
30pub(crate) use allocator::CommandAllocator;
31
32pub(crate) use timestamp_writes::ArcPassTimestampWrites;
33pub use timestamp_writes::PassTimestampWrites;
34
35use self::memory_init::CommandBufferTextureMemoryActions;
36
37use crate::binding_model::BindingError;
38use crate::command::transition_resources::TransitionResourcesError;
39use crate::device::queue::TempResource;
40use crate::device::{Device, DeviceError, MissingFeatures};
41use crate::id::Id;
42use crate::lock::{rank, Mutex};
43use crate::snatch::SnatchGuard;
44
45use crate::init_tracker::BufferInitTrackerAction;
46use crate::ray_tracing::{AsAction, BuildAccelerationStructureError};
47use crate::resource::{
48 DestroyedResourceError, Fallible, InvalidResourceError, Labeled, ParentDevice as _, QuerySet,
49};
50use crate::storage::Storage;
51use crate::track::{DeviceTracker, ResourceUsageCompatibilityError, Tracker, UsageScope};
52use crate::{api_log, global::Global, id, resource_log, Label};
53use crate::{hal_label, LabelHelpers};
54
55use wgt::error::{ErrorType, WebGpuError};
56
57use thiserror::Error;
58
59#[cfg(feature = "trace")]
60type TraceCommand = Command;
61
62pub type TexelCopyBufferInfo = ffi::TexelCopyBufferInfo;
64pub type TexelCopyTextureInfo = ffi::TexelCopyTextureInfo;
66pub type CopyExternalImageDestInfo = ffi::CopyExternalImageDestInfo;
68
69const PUSH_CONSTANT_CLEAR_ARRAY: &[u32] = &[0_u32; 64];
70
71pub(crate) enum CommandEncoderStatus {
77 Recording(CommandBufferMutable),
89
90 Locked(CommandBufferMutable),
99
100 Consumed,
101
102 Finished(CommandBufferMutable),
113
114 Error(CommandEncoderError),
119
120 Transitioning,
123}
124
125impl CommandEncoderStatus {
126 fn record_with<
141 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
142 E: Clone + Into<CommandEncoderError>,
143 >(
144 &mut self,
145 f: F,
146 ) -> Result<(), EncoderStateError> {
147 match self {
148 Self::Recording(_) => {
149 RecordingGuard { inner: self }.record(f);
150 Ok(())
151 }
152 Self::Locked(_) => {
153 self.invalidate(EncoderStateError::Locked);
156 Ok(())
157 }
158 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
161 Self::Consumed => Err(EncoderStateError::Ended),
162 Self::Error(_) => Ok(()),
165 Self::Transitioning => unreachable!(),
166 }
167 }
168
169 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
177 &mut self,
178 f: F,
179 ) -> T {
180 match self {
181 Self::Recording(_) => RecordingGuard { inner: self }.record_as_hal_mut(f),
182 Self::Locked(_) => {
183 self.invalidate(EncoderStateError::Locked);
184 f(None)
185 }
186 Self::Finished(_) => {
187 self.invalidate(EncoderStateError::Ended);
188 f(None)
189 }
190 Self::Consumed => f(None),
191 Self::Error(_) => f(None),
192 Self::Transitioning => unreachable!(),
193 }
194 }
195
196 #[cfg(feature = "trace")]
197 fn get_inner(&mut self) -> &mut CommandBufferMutable {
198 match self {
199 Self::Locked(inner) | Self::Finished(inner) | Self::Recording(inner) => inner,
200 Self::Consumed => unreachable!("command encoder is consumed"),
205 Self::Error(_) => unreachable!("passes in a trace do not store errors"),
206 Self::Transitioning => unreachable!(),
207 }
208 }
209
210 fn lock_encoder(&mut self) -> Result<(), EncoderStateError> {
216 match mem::replace(self, Self::Transitioning) {
217 Self::Recording(inner) => {
218 *self = Self::Locked(inner);
219 Ok(())
220 }
221 st @ Self::Finished(_) => {
222 *self = st;
226 Err(EncoderStateError::Ended)
227 }
228 Self::Locked(_) => Err(self.invalidate(EncoderStateError::Locked)),
229 st @ Self::Consumed => {
230 *self = st;
231 Err(EncoderStateError::Ended)
232 }
233 st @ Self::Error(_) => {
234 *self = st;
235 Err(EncoderStateError::Invalid)
236 }
237 Self::Transitioning => unreachable!(),
238 }
239 }
240
241 fn unlock_and_record<
257 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
258 E: Clone + Into<CommandEncoderError>,
259 >(
260 &mut self,
261 f: F,
262 ) -> Result<(), EncoderStateError> {
263 match mem::replace(self, Self::Transitioning) {
264 Self::Locked(inner) => {
265 *self = Self::Recording(inner);
266 RecordingGuard { inner: self }.record(f);
267 Ok(())
268 }
269 st @ Self::Finished(_) => {
270 *self = st;
271 Err(EncoderStateError::Ended)
272 }
273 Self::Recording(_) => {
274 *self = Self::Error(EncoderStateError::Unlocked.into());
275 Err(EncoderStateError::Unlocked)
276 }
277 st @ Self::Consumed => {
278 *self = st;
279 Err(EncoderStateError::Ended)
280 }
281 st @ Self::Error(_) => {
282 *self = st;
285 Ok(())
286 }
287 Self::Transitioning => unreachable!(),
288 }
289 }
290
291 fn finish(&mut self) -> Self {
292 match mem::replace(self, Self::Consumed) {
295 Self::Recording(mut inner) => {
296 if let Err(err) = inner.encoder.close_if_open() {
297 Self::Error(err.into())
298 } else if inner.debug_scope_depth > 0 {
299 Self::Error(CommandEncoderError::DebugGroupError(
300 DebugGroupError::MissingPop,
301 ))
302 } else {
303 Self::Finished(inner)
306 }
307 }
308 Self::Consumed | Self::Finished(_) => Self::Error(EncoderStateError::Ended.into()),
309 Self::Locked(_) => Self::Error(EncoderStateError::Locked.into()),
310 st @ Self::Error(_) => st,
311 Self::Transitioning => unreachable!(),
312 }
313 }
314
315 fn invalidate<E: Clone + Into<CommandEncoderError>>(&mut self, err: E) -> E {
321 let enc_err = err.clone().into();
322 api_log!("Invalidating command encoder: {enc_err:?}");
323 *self = Self::Error(enc_err);
324 err
325 }
326}
327
328pub(crate) struct RecordingGuard<'a> {
341 inner: &'a mut CommandEncoderStatus,
342}
343
344impl<'a> RecordingGuard<'a> {
345 pub(crate) fn mark_successful(self) {
346 mem::forget(self)
347 }
348
349 fn record<
350 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
351 E: Clone + Into<CommandEncoderError>,
352 >(
353 mut self,
354 f: F,
355 ) {
356 match f(&mut self) {
357 Ok(()) => self.mark_successful(),
358 Err(err) => {
359 self.inner.invalidate(err);
360 }
361 }
362 }
363
364 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
367 mut self,
368 f: F,
369 ) -> T {
370 let res = f(Some(&mut self));
371 self.mark_successful();
372 res
373 }
374}
375
376impl<'a> Drop for RecordingGuard<'a> {
377 fn drop(&mut self) {
378 if matches!(*self.inner, CommandEncoderStatus::Error(_)) {
379 return;
381 }
382 self.inner.invalidate(EncoderStateError::Invalid);
383 }
384}
385
386impl<'a> ops::Deref for RecordingGuard<'a> {
387 type Target = CommandBufferMutable;
388
389 fn deref(&self) -> &Self::Target {
390 match &*self.inner {
391 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
392 _ => unreachable!(),
393 }
394 }
395}
396
397impl<'a> ops::DerefMut for RecordingGuard<'a> {
398 fn deref_mut(&mut self) -> &mut Self::Target {
399 match self.inner {
400 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
401 _ => unreachable!(),
402 }
403 }
404}
405
406pub(crate) struct CommandEncoder {
407 pub(crate) device: Arc<Device>,
408
409 pub(crate) label: String,
410
411 pub(crate) data: Mutex<CommandEncoderStatus>,
413}
414
415crate::impl_resource_type!(CommandEncoder);
416crate::impl_labeled!(CommandEncoder);
417crate::impl_parent_device!(CommandEncoder);
418crate::impl_storage_item!(CommandEncoder);
419
420impl Drop for CommandEncoder {
421 fn drop(&mut self) {
422 resource_log!("Drop {}", self.error_ident());
423 }
424}
425
426pub(crate) struct InnerCommandEncoder {
442 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
450
451 pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
463
464 pub(crate) device: Arc<Device>,
465
466 pub(crate) is_open: bool,
473
474 pub(crate) label: String,
475}
476
477impl InnerCommandEncoder {
478 fn close_and_swap(&mut self) -> Result<(), DeviceError> {
504 assert!(self.is_open);
505 self.is_open = false;
506
507 let new =
508 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
509 self.list.insert(self.list.len() - 1, new);
510
511 Ok(())
512 }
513
514 pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> {
525 assert!(self.is_open);
526 self.is_open = false;
527
528 let new =
529 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
530 self.list.insert(0, new);
531
532 Ok(())
533 }
534
535 pub(crate) fn close(&mut self) -> Result<(), DeviceError> {
546 assert!(self.is_open);
547 self.is_open = false;
548
549 let cmd_buf =
550 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
551 self.list.push(cmd_buf);
552
553 Ok(())
554 }
555
556 fn close_if_open(&mut self) -> Result<(), DeviceError> {
567 if self.is_open {
568 self.is_open = false;
569 let cmd_buf =
570 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
571 self.list.push(cmd_buf);
572 }
573
574 Ok(())
575 }
576
577 pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
581 if !self.is_open {
582 self.is_open = true;
583 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
584 unsafe { self.raw.begin_encoding(hal_label) }
585 .map_err(|e| self.device.handle_hal_error(e))?;
586 }
587
588 Ok(self.raw.as_mut())
589 }
590
591 pub(crate) fn open_pass(
600 &mut self,
601 label: Option<&str>,
602 ) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
603 assert!(!self.is_open);
604 self.is_open = true;
605
606 let hal_label = hal_label(label, self.device.instance_flags);
607 unsafe { self.raw.begin_encoding(hal_label) }
608 .map_err(|e| self.device.handle_hal_error(e))?;
609
610 Ok(self.raw.as_mut())
611 }
612}
613
614impl Drop for InnerCommandEncoder {
615 fn drop(&mut self) {
616 if self.is_open {
617 unsafe { self.raw.discard_encoding() };
618 }
619 unsafe {
620 self.raw.reset_all(mem::take(&mut self.list));
621 }
622 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
624 self.device.command_allocator.release_encoder(raw);
625 }
626}
627
628pub(crate) struct BakedCommands {
631 pub(crate) encoder: InnerCommandEncoder,
632 pub(crate) trackers: Tracker,
633 pub(crate) temp_resources: Vec<TempResource>,
634 pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
635 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
636 texture_memory_actions: CommandBufferTextureMemoryActions,
637}
638
639pub struct CommandBufferMutable {
641 pub(crate) encoder: InnerCommandEncoder,
646
647 pub(crate) trackers: Tracker,
649
650 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
657 texture_memory_actions: CommandBufferTextureMemoryActions,
658
659 as_actions: Vec<AsAction>,
660 temp_resources: Vec<TempResource>,
661
662 indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
663
664 debug_scope_depth: u32,
665
666 #[cfg(feature = "trace")]
667 pub(crate) trace_commands: Option<Vec<TraceCommand>>,
668}
669
670impl CommandBufferMutable {
671 pub(crate) fn open_encoder_and_tracker(
672 &mut self,
673 ) -> Result<(&mut dyn hal::DynCommandEncoder, &mut Tracker), DeviceError> {
674 let encoder = self.encoder.open()?;
675 let tracker = &mut self.trackers;
676
677 Ok((encoder, tracker))
678 }
679
680 pub(crate) fn into_baked_commands(self) -> BakedCommands {
681 BakedCommands {
682 encoder: self.encoder,
683 trackers: self.trackers,
684 temp_resources: self.temp_resources,
685 indirect_draw_validation_resources: self.indirect_draw_validation_resources,
686 buffer_memory_init_actions: self.buffer_memory_init_actions,
687 texture_memory_actions: self.texture_memory_actions,
688 }
689 }
690}
691
692pub struct CommandBuffer {
698 pub(crate) device: Arc<Device>,
699 label: String,
701
702 pub(crate) data: Mutex<CommandEncoderStatus>,
704}
705
706impl Drop for CommandBuffer {
707 fn drop(&mut self) {
708 resource_log!("Drop {}", self.error_ident());
709 }
710}
711
712impl CommandEncoder {
713 pub(crate) fn new(
714 encoder: Box<dyn hal::DynCommandEncoder>,
715 device: &Arc<Device>,
716 label: &Label,
717 ) -> Self {
718 CommandEncoder {
719 device: device.clone(),
720 label: label.to_string(),
721 data: Mutex::new(
722 rank::COMMAND_BUFFER_DATA,
723 CommandEncoderStatus::Recording(CommandBufferMutable {
724 encoder: InnerCommandEncoder {
725 raw: ManuallyDrop::new(encoder),
726 list: Vec::new(),
727 device: device.clone(),
728 is_open: false,
729 label: label.to_string(),
730 },
731 trackers: Tracker::new(),
732 buffer_memory_init_actions: Default::default(),
733 texture_memory_actions: Default::default(),
734 as_actions: Default::default(),
735 temp_resources: Default::default(),
736 indirect_draw_validation_resources:
737 crate::indirect_validation::DrawResources::new(device.clone()),
738 debug_scope_depth: 0,
739 #[cfg(feature = "trace")]
740 trace_commands: if device.trace.lock().is_some() {
741 Some(Vec::new())
742 } else {
743 None
744 },
745 }),
746 ),
747 }
748 }
749
750 pub(crate) fn new_invalid(
751 device: &Arc<Device>,
752 label: &Label,
753 err: CommandEncoderError,
754 ) -> Self {
755 CommandEncoder {
756 device: device.clone(),
757 label: label.to_string(),
758 data: Mutex::new(rank::COMMAND_BUFFER_DATA, CommandEncoderStatus::Error(err)),
759 }
760 }
761
762 pub(crate) fn insert_barriers_from_tracker(
763 raw: &mut dyn hal::DynCommandEncoder,
764 base: &mut Tracker,
765 head: &Tracker,
766 snatch_guard: &SnatchGuard,
767 ) {
768 profiling::scope!("insert_barriers");
769
770 base.buffers.set_from_tracker(&head.buffers);
771 base.textures.set_from_tracker(&head.textures);
772
773 Self::drain_barriers(raw, base, snatch_guard);
774 }
775
776 pub(crate) fn insert_barriers_from_scope(
777 raw: &mut dyn hal::DynCommandEncoder,
778 base: &mut Tracker,
779 head: &UsageScope,
780 snatch_guard: &SnatchGuard,
781 ) {
782 profiling::scope!("insert_barriers");
783
784 base.buffers.set_from_usage_scope(&head.buffers);
785 base.textures.set_from_usage_scope(&head.textures);
786
787 Self::drain_barriers(raw, base, snatch_guard);
788 }
789
790 pub(crate) fn drain_barriers(
791 raw: &mut dyn hal::DynCommandEncoder,
792 base: &mut Tracker,
793 snatch_guard: &SnatchGuard,
794 ) {
795 profiling::scope!("drain_barriers");
796
797 let buffer_barriers = base
798 .buffers
799 .drain_transitions(snatch_guard)
800 .collect::<Vec<_>>();
801 let (transitions, textures) = base.textures.drain_transitions(snatch_guard);
802 let texture_barriers = transitions
803 .into_iter()
804 .enumerate()
805 .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))
806 .collect::<Vec<_>>();
807
808 unsafe {
809 raw.transition_buffers(&buffer_barriers);
810 raw.transition_textures(&texture_barriers);
811 }
812 }
813
814 pub(crate) fn insert_barriers_from_device_tracker(
815 raw: &mut dyn hal::DynCommandEncoder,
816 base: &mut DeviceTracker,
817 head: &Tracker,
818 snatch_guard: &SnatchGuard,
819 ) {
820 profiling::scope!("insert_barriers_from_device_tracker");
821
822 let buffer_barriers = base
823 .buffers
824 .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)
825 .collect::<Vec<_>>();
826
827 let texture_barriers = base
828 .textures
829 .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)
830 .collect::<Vec<_>>();
831
832 unsafe {
833 raw.transition_buffers(&buffer_barriers);
834 raw.transition_textures(&texture_barriers);
835 }
836 }
837}
838
839impl CommandBuffer {
840 pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {
841 use CommandEncoderStatus as St;
842 match mem::replace(
843 &mut *self.data.lock(),
844 CommandEncoderStatus::Error(EncoderStateError::Submitted.into()),
845 ) {
846 St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
847 St::Error(err) => Err(err),
848 St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(),
849 }
850 }
851}
852
853crate::impl_resource_type!(CommandBuffer);
854crate::impl_labeled!(CommandBuffer);
855crate::impl_parent_device!(CommandBuffer);
856crate::impl_storage_item!(CommandBuffer);
857
858#[doc(hidden)]
870#[derive(Debug, Clone)]
871#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
872pub struct BasePass<C, E> {
873 pub label: Option<String>,
874
875 #[cfg_attr(feature = "serde", serde(skip, default = "Option::default"))]
883 pub error: Option<E>,
884
885 pub commands: Vec<C>,
887
888 pub dynamic_offsets: Vec<wgt::DynamicOffset>,
893
894 pub string_data: Vec<u8>,
899
900 pub push_constant_data: Vec<u32>,
905}
906
907impl<C: Clone, E: Clone> BasePass<C, E> {
908 fn new(label: &Label) -> Self {
909 Self {
910 label: label.as_deref().map(str::to_owned),
911 error: None,
912 commands: Vec::new(),
913 dynamic_offsets: Vec::new(),
914 string_data: Vec::new(),
915 push_constant_data: Vec::new(),
916 }
917 }
918
919 fn new_invalid(label: &Label, err: E) -> Self {
920 Self {
921 label: label.as_deref().map(str::to_owned),
922 error: Some(err),
923 commands: Vec::new(),
924 dynamic_offsets: Vec::new(),
925 string_data: Vec::new(),
926 push_constant_data: Vec::new(),
927 }
928 }
929}
930
931macro_rules! pass_base {
954 ($pass:expr, $scope:expr $(,)?) => {
955 match (&$pass.parent, &$pass.base.error) {
956 (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),
958 (&Some(_), &Some(_)) => return Ok(()),
960 (&Some(_), &None) => &mut $pass.base,
962 }
963 };
964}
965pub(crate) use pass_base;
966
967macro_rules! pass_try {
979 ($base:expr, $scope:expr, $res:expr $(,)?) => {
980 match $res.map_pass_err($scope) {
981 Ok(val) => val,
982 Err(err) => {
983 $base.error.get_or_insert(err);
984 return Ok(());
985 }
986 }
987 };
988}
989pub(crate) use pass_try;
990
991#[derive(Clone, Debug, Error)]
996#[non_exhaustive]
997pub enum EncoderStateError {
998 #[error("Encoder is invalid")]
1003 Invalid,
1004
1005 #[error("Encoding must not have ended")]
1008 Ended,
1009
1010 #[error("Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
1016 Locked,
1017
1018 #[error(
1021 "Encoder is not currently locked. A pass can only be ended while the encoder is locked."
1022 )]
1023 Unlocked,
1024
1025 #[error("This command buffer has already been submitted.")]
1030 Submitted,
1031}
1032
1033impl WebGpuError for EncoderStateError {
1034 fn webgpu_error_type(&self) -> ErrorType {
1035 match self {
1036 EncoderStateError::Invalid
1037 | EncoderStateError::Ended
1038 | EncoderStateError::Locked
1039 | EncoderStateError::Unlocked
1040 | EncoderStateError::Submitted => ErrorType::Validation,
1041 }
1042 }
1043}
1044
1045#[derive(Clone, Debug, Error)]
1046#[non_exhaustive]
1047pub enum CommandEncoderError {
1048 #[error(transparent)]
1049 State(#[from] EncoderStateError),
1050 #[error(transparent)]
1051 Device(#[from] DeviceError),
1052 #[error(transparent)]
1053 InvalidResource(#[from] InvalidResourceError),
1054 #[error(transparent)]
1055 DestroyedResource(#[from] DestroyedResourceError),
1056 #[error(transparent)]
1057 ResourceUsage(#[from] ResourceUsageCompatibilityError),
1058 #[error(transparent)]
1059 DebugGroupError(#[from] DebugGroupError),
1060 #[error(transparent)]
1061 MissingFeatures(#[from] MissingFeatures),
1062 #[error(transparent)]
1063 Transfer(#[from] TransferError),
1064 #[error(transparent)]
1065 Clear(#[from] ClearError),
1066 #[error(transparent)]
1067 Query(#[from] QueryError),
1068 #[error(transparent)]
1069 BuildAccelerationStructure(#[from] BuildAccelerationStructureError),
1070 #[error(transparent)]
1071 TransitionResources(#[from] TransitionResourcesError),
1072 #[error(transparent)]
1073 ComputePass(#[from] ComputePassError),
1074 #[error(transparent)]
1075 RenderPass(#[from] RenderPassError),
1076}
1077
1078impl CommandEncoderError {
1079 fn is_destroyed_error(&self) -> bool {
1080 matches!(
1081 self,
1082 Self::DestroyedResource(_)
1083 | Self::Clear(ClearError::DestroyedResource(_))
1084 | Self::Query(QueryError::DestroyedResource(_))
1085 | Self::ComputePass(ComputePassError {
1086 inner: ComputePassErrorInner::DestroyedResource(_),
1087 ..
1088 })
1089 | Self::RenderPass(RenderPassError {
1090 inner: RenderPassErrorInner::DestroyedResource(_),
1091 ..
1092 })
1093 | Self::RenderPass(RenderPassError {
1094 inner: RenderPassErrorInner::RenderCommand(
1095 RenderCommandError::DestroyedResource(_)
1096 ),
1097 ..
1098 })
1099 | Self::RenderPass(RenderPassError {
1100 inner: RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError(
1101 BindingError::DestroyedResource(_)
1102 )),
1103 ..
1104 })
1105 )
1106 }
1107}
1108
1109impl WebGpuError for CommandEncoderError {
1110 fn webgpu_error_type(&self) -> ErrorType {
1111 let e: &dyn WebGpuError = match self {
1112 Self::Device(e) => e,
1113 Self::InvalidResource(e) => e,
1114 Self::DebugGroupError(e) => e,
1115 Self::MissingFeatures(e) => e,
1116 Self::State(e) => e,
1117 Self::DestroyedResource(e) => e,
1118 Self::Transfer(e) => e,
1119 Self::Clear(e) => e,
1120 Self::Query(e) => e,
1121 Self::BuildAccelerationStructure(e) => e,
1122 Self::TransitionResources(e) => e,
1123 Self::ResourceUsage(e) => e,
1124 Self::ComputePass(e) => e,
1125 Self::RenderPass(e) => e,
1126 };
1127 e.webgpu_error_type()
1128 }
1129}
1130
1131#[derive(Clone, Debug, Error)]
1132#[non_exhaustive]
1133pub enum DebugGroupError {
1134 #[error("Cannot pop debug group, because number of pushed debug groups is zero")]
1135 InvalidPop,
1136 #[error("A debug group was not popped before the encoder was finished")]
1137 MissingPop,
1138}
1139
1140impl WebGpuError for DebugGroupError {
1141 fn webgpu_error_type(&self) -> ErrorType {
1142 match self {
1143 Self::InvalidPop | Self::MissingPop => ErrorType::Validation,
1144 }
1145 }
1146}
1147
1148#[derive(Clone, Debug, Error)]
1149#[non_exhaustive]
1150pub enum TimestampWritesError {
1151 #[error(
1152 "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
1153 )]
1154 IndicesEqual { idx: u32 },
1155 #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
1156 IndicesMissing,
1157}
1158
1159impl WebGpuError for TimestampWritesError {
1160 fn webgpu_error_type(&self) -> ErrorType {
1161 match self {
1162 Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,
1163 }
1164 }
1165}
1166
1167impl Global {
1168 fn resolve_buffer_id(
1169 &self,
1170 buffer_id: Id<id::markers::Buffer>,
1171 ) -> Result<Arc<crate::resource::Buffer>, InvalidResourceError> {
1172 let hub = &self.hub;
1173 let buffer = hub.buffers.get(buffer_id).get()?;
1174
1175 Ok(buffer)
1176 }
1177
1178 fn resolve_query_set(
1179 &self,
1180 query_set_id: Id<id::markers::QuerySet>,
1181 ) -> Result<Arc<QuerySet>, InvalidResourceError> {
1182 let hub = &self.hub;
1183 let query_set = hub.query_sets.get(query_set_id).get()?;
1184
1185 Ok(query_set)
1186 }
1187
1188 pub fn command_encoder_finish(
1189 &self,
1190 encoder_id: id::CommandEncoderId,
1191 desc: &wgt::CommandBufferDescriptor<Label>,
1192 id_in: Option<id::CommandBufferId>,
1193 ) -> (id::CommandBufferId, Option<CommandEncoderError>) {
1194 profiling::scope!("CommandEncoder::finish");
1195
1196 let hub = &self.hub;
1197
1198 let cmd_enc = hub.command_encoders.get(encoder_id);
1199
1200 let data = cmd_enc.data.lock().finish();
1201
1202 let error = match data {
1205 CommandEncoderStatus::Error(ref e) if !e.is_destroyed_error() => Some(e.clone()),
1206 _ => None,
1207 };
1208
1209 let cmd_buf = CommandBuffer {
1210 device: cmd_enc.device.clone(),
1211 label: desc.label.to_string(),
1212 data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),
1213 };
1214
1215 let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(Arc::new(cmd_buf));
1216
1217 (cmd_buf_id, error)
1218 }
1219
1220 pub fn command_encoder_push_debug_group(
1221 &self,
1222 encoder_id: id::CommandEncoderId,
1223 label: &str,
1224 ) -> Result<(), EncoderStateError> {
1225 profiling::scope!("CommandEncoder::push_debug_group");
1226 api_log!("CommandEncoder::push_debug_group {label}");
1227
1228 let hub = &self.hub;
1229
1230 let cmd_enc = hub.command_encoders.get(encoder_id);
1231 let mut cmd_buf_data = cmd_enc.data.lock();
1232 cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
1233 push_debug_group(cmd_buf_data, &cmd_enc, label)
1234 })
1235 }
1236
1237 pub fn command_encoder_insert_debug_marker(
1238 &self,
1239 encoder_id: id::CommandEncoderId,
1240 label: &str,
1241 ) -> Result<(), EncoderStateError> {
1242 profiling::scope!("CommandEncoder::insert_debug_marker");
1243 api_log!("CommandEncoder::insert_debug_marker {label}");
1244
1245 let hub = &self.hub;
1246
1247 let cmd_enc = hub.command_encoders.get(encoder_id);
1248 let mut cmd_buf_data = cmd_enc.data.lock();
1249 cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
1250 insert_debug_marker(cmd_buf_data, &cmd_enc, label)
1251 })
1252 }
1253
1254 pub fn command_encoder_pop_debug_group(
1255 &self,
1256 encoder_id: id::CommandEncoderId,
1257 ) -> Result<(), EncoderStateError> {
1258 profiling::scope!("CommandEncoder::pop_debug_marker");
1259 api_log!("CommandEncoder::pop_debug_group");
1260
1261 let hub = &self.hub;
1262
1263 let cmd_enc = hub.command_encoders.get(encoder_id);
1264 let mut cmd_buf_data = cmd_enc.data.lock();
1265 cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
1266 pop_debug_group(cmd_buf_data, &cmd_enc)
1267 })
1268 }
1269
1270 fn validate_pass_timestamp_writes<E>(
1271 device: &Device,
1272 query_sets: &Storage<Fallible<QuerySet>>,
1273 timestamp_writes: &PassTimestampWrites,
1274 ) -> Result<ArcPassTimestampWrites, E>
1275 where
1276 E: From<TimestampWritesError>
1277 + From<QueryUseError>
1278 + From<DeviceError>
1279 + From<MissingFeatures>
1280 + From<InvalidResourceError>,
1281 {
1282 let &PassTimestampWrites {
1283 query_set,
1284 beginning_of_pass_write_index,
1285 end_of_pass_write_index,
1286 } = timestamp_writes;
1287
1288 device.require_features(wgt::Features::TIMESTAMP_QUERY)?;
1289
1290 let query_set = query_sets.get(query_set).get()?;
1291
1292 query_set.same_device(device)?;
1293
1294 for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
1295 .into_iter()
1296 .flatten()
1297 {
1298 query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
1299 }
1300
1301 if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
1302 if begin == end {
1303 return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());
1304 }
1305 }
1306
1307 if beginning_of_pass_write_index
1308 .or(end_of_pass_write_index)
1309 .is_none()
1310 {
1311 return Err(TimestampWritesError::IndicesMissing.into());
1312 }
1313
1314 Ok(ArcPassTimestampWrites {
1315 query_set,
1316 beginning_of_pass_write_index,
1317 end_of_pass_write_index,
1318 })
1319 }
1320}
1321
1322pub(crate) fn push_debug_group(
1323 cmd_buf_data: &mut CommandBufferMutable,
1324 cmd_enc: &Arc<CommandEncoder>,
1325 label: &str,
1326) -> Result<(), CommandEncoderError> {
1327 cmd_buf_data.debug_scope_depth += 1;
1328
1329 #[cfg(feature = "trace")]
1330 if let Some(ref mut list) = cmd_buf_data.trace_commands {
1331 list.push(TraceCommand::PushDebugGroup(label.to_owned()));
1332 }
1333
1334 cmd_enc.device.check_is_valid()?;
1335
1336 let cmd_buf_raw = cmd_buf_data.encoder.open()?;
1337 if !cmd_enc
1338 .device
1339 .instance_flags
1340 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1341 {
1342 unsafe {
1343 cmd_buf_raw.begin_debug_marker(label);
1344 }
1345 }
1346
1347 Ok(())
1348}
1349
1350pub(crate) fn insert_debug_marker(
1351 cmd_buf_data: &mut CommandBufferMutable,
1352 cmd_enc: &Arc<CommandEncoder>,
1353 label: &str,
1354) -> Result<(), CommandEncoderError> {
1355 #[cfg(feature = "trace")]
1356 if let Some(ref mut list) = cmd_buf_data.trace_commands {
1357 list.push(TraceCommand::InsertDebugMarker(label.to_owned()));
1358 }
1359
1360 cmd_enc.device.check_is_valid()?;
1361
1362 if !cmd_enc
1363 .device
1364 .instance_flags
1365 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1366 {
1367 let cmd_buf_raw = cmd_buf_data.encoder.open()?;
1368 unsafe {
1369 cmd_buf_raw.insert_debug_marker(label);
1370 }
1371 }
1372
1373 Ok(())
1374}
1375
1376pub(crate) fn pop_debug_group(
1377 cmd_buf_data: &mut CommandBufferMutable,
1378 cmd_enc: &Arc<CommandEncoder>,
1379) -> Result<(), CommandEncoderError> {
1380 if cmd_buf_data.debug_scope_depth == 0 {
1381 return Err(DebugGroupError::InvalidPop.into());
1382 }
1383 cmd_buf_data.debug_scope_depth -= 1;
1384
1385 #[cfg(feature = "trace")]
1386 if let Some(ref mut list) = cmd_buf_data.trace_commands {
1387 list.push(TraceCommand::PopDebugGroup);
1388 }
1389
1390 cmd_enc.device.check_is_valid()?;
1391
1392 let cmd_buf_raw = cmd_buf_data.encoder.open()?;
1393 if !cmd_enc
1394 .device
1395 .instance_flags
1396 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1397 {
1398 unsafe {
1399 cmd_buf_raw.end_debug_marker();
1400 }
1401 }
1402
1403 Ok(())
1404}
1405
1406fn push_constant_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
1407where
1408 PushFn: FnMut(u32, &[u32]),
1409{
1410 let mut count_words = 0_u32;
1411 let size_words = size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT;
1412 while count_words < size_words {
1413 let count_bytes = count_words * wgt::PUSH_CONSTANT_ALIGNMENT;
1414 let size_to_write_words =
1415 (size_words - count_words).min(PUSH_CONSTANT_CLEAR_ARRAY.len() as u32);
1416
1417 push_fn(
1418 offset + count_bytes,
1419 &PUSH_CONSTANT_CLEAR_ARRAY[0..size_to_write_words as usize],
1420 );
1421
1422 count_words += size_to_write_words;
1423 }
1424}
1425
1426#[derive(Debug, Copy, Clone)]
1427struct StateChange<T> {
1428 last_state: Option<T>,
1429}
1430
1431impl<T: Copy + PartialEq> StateChange<T> {
1432 fn new() -> Self {
1433 Self { last_state: None }
1434 }
1435 fn set_and_check_redundant(&mut self, new_state: T) -> bool {
1436 let already_set = self.last_state == Some(new_state);
1437 self.last_state = Some(new_state);
1438 already_set
1439 }
1440 fn reset(&mut self) {
1441 self.last_state = None;
1442 }
1443}
1444
1445impl<T: Copy + PartialEq> Default for StateChange<T> {
1446 fn default() -> Self {
1447 Self::new()
1448 }
1449}
1450
1451#[derive(Debug)]
1452struct BindGroupStateChange {
1453 last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],
1454}
1455
1456impl BindGroupStateChange {
1457 fn new() -> Self {
1458 Self {
1459 last_states: [StateChange::new(); hal::MAX_BIND_GROUPS],
1460 }
1461 }
1462
1463 fn set_and_check_redundant(
1464 &mut self,
1465 bind_group_id: Option<id::BindGroupId>,
1466 index: u32,
1467 dynamic_offsets: &mut Vec<u32>,
1468 offsets: &[wgt::DynamicOffset],
1469 ) -> bool {
1470 if offsets.is_empty() {
1472 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1475 if current_bind_group.set_and_check_redundant(bind_group_id) {
1477 return true;
1478 }
1479 }
1480 } else {
1481 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1485 current_bind_group.reset();
1486 }
1487 dynamic_offsets.extend_from_slice(offsets);
1488 }
1489 false
1490 }
1491 fn reset(&mut self) {
1492 self.last_states = [StateChange::new(); hal::MAX_BIND_GROUPS];
1493 }
1494}
1495
1496impl Default for BindGroupStateChange {
1497 fn default() -> Self {
1498 Self::new()
1499 }
1500}
1501
1502trait MapPassErr<T> {
1504 fn map_pass_err(self, scope: PassErrorScope) -> T;
1505}
1506
1507impl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>
1508where
1509 E: MapPassErr<F>,
1510{
1511 fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {
1512 self.map_err(|err| err.map_pass_err(scope))
1513 }
1514}
1515
1516impl MapPassErr<PassStateError> for EncoderStateError {
1517 fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {
1518 PassStateError { scope, inner: self }
1519 }
1520}
1521
1522#[derive(Clone, Copy, Debug)]
1523pub enum DrawKind {
1524 Draw,
1525 DrawIndirect,
1526 MultiDrawIndirect,
1527 MultiDrawIndirectCount,
1528}
1529
1530#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1532#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1533pub enum DrawCommandFamily {
1534 Draw,
1535 DrawIndexed,
1536 DrawMeshTasks,
1537}
1538
1539#[derive(Clone, Copy, Debug, Error)]
1549pub enum PassErrorScope {
1550 #[error("In a bundle parameter")]
1553 Bundle,
1554 #[error("In a pass parameter")]
1555 Pass,
1556 #[error("In a set_bind_group command")]
1557 SetBindGroup,
1558 #[error("In a set_pipeline command")]
1559 SetPipelineRender,
1560 #[error("In a set_pipeline command")]
1561 SetPipelineCompute,
1562 #[error("In a set_push_constant command")]
1563 SetPushConstant,
1564 #[error("In a set_vertex_buffer command")]
1565 SetVertexBuffer,
1566 #[error("In a set_index_buffer command")]
1567 SetIndexBuffer,
1568 #[error("In a set_blend_constant command")]
1569 SetBlendConstant,
1570 #[error("In a set_stencil_reference command")]
1571 SetStencilReference,
1572 #[error("In a set_viewport command")]
1573 SetViewport,
1574 #[error("In a set_scissor_rect command")]
1575 SetScissorRect,
1576 #[error("In a draw command, kind: {kind:?}")]
1577 Draw {
1578 kind: DrawKind,
1579 family: DrawCommandFamily,
1580 },
1581 #[error("In a write_timestamp command")]
1582 WriteTimestamp,
1583 #[error("In a begin_occlusion_query command")]
1584 BeginOcclusionQuery,
1585 #[error("In a end_occlusion_query command")]
1586 EndOcclusionQuery,
1587 #[error("In a begin_pipeline_statistics_query command")]
1588 BeginPipelineStatisticsQuery,
1589 #[error("In a end_pipeline_statistics_query command")]
1590 EndPipelineStatisticsQuery,
1591 #[error("In a execute_bundle command")]
1592 ExecuteBundle,
1593 #[error("In a dispatch command, indirect:{indirect}")]
1594 Dispatch { indirect: bool },
1595 #[error("In a push_debug_group command")]
1596 PushDebugGroup,
1597 #[error("In a pop_debug_group command")]
1598 PopDebugGroup,
1599 #[error("In a insert_debug_marker command")]
1600 InsertDebugMarker,
1601}
1602
1603#[derive(Clone, Debug, Error)]
1605#[error("{scope}")]
1606pub struct PassStateError {
1607 pub scope: PassErrorScope,
1608 #[source]
1609 pub(super) inner: EncoderStateError,
1610}
1611
1612impl WebGpuError for PassStateError {
1613 fn webgpu_error_type(&self) -> ErrorType {
1614 let Self { scope: _, inner } = self;
1615 inner.webgpu_error_type()
1616 }
1617}