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 if inner.debug_scope_depth > 0 {
288 Self::Error(CommandEncoderError::DebugGroupError(
289 DebugGroupError::MissingPop,
290 ))
291 } else {
292 Self::Finished(inner)
295 }
296 }
297 Self::Consumed | Self::Finished(_) => Self::Error(EncoderStateError::Ended.into()),
298 Self::Locked(_) => Self::Error(EncoderStateError::Locked.into()),
299 st @ Self::Error(_) => st,
300 Self::Transitioning => unreachable!(),
301 }
302 }
303
304 fn invalidate<E: Clone + Into<CommandEncoderError>>(&mut self, err: E) -> E {
310 let enc_err = err.clone().into();
311 api_log!("Invalidating command encoder: {enc_err:?}");
312 *self = Self::Error(enc_err);
313 err
314 }
315}
316
317pub(crate) struct RecordingGuard<'a> {
330 inner: &'a mut CommandEncoderStatus,
331}
332
333impl<'a> RecordingGuard<'a> {
334 pub(crate) fn mark_successful(self) {
335 mem::forget(self)
336 }
337
338 fn record<
339 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
340 E: Clone + Into<CommandEncoderError>,
341 >(
342 mut self,
343 f: F,
344 ) {
345 match f(&mut self) {
346 Ok(()) => self.mark_successful(),
347 Err(err) => {
348 self.inner.invalidate(err);
349 }
350 }
351 }
352
353 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
356 mut self,
357 f: F,
358 ) -> T {
359 let res = f(Some(&mut self));
360 self.mark_successful();
361 res
362 }
363}
364
365impl<'a> Drop for RecordingGuard<'a> {
366 fn drop(&mut self) {
367 if matches!(*self.inner, CommandEncoderStatus::Error(_)) {
368 return;
370 }
371 self.inner.invalidate(EncoderStateError::Invalid);
372 }
373}
374
375impl<'a> ops::Deref for RecordingGuard<'a> {
376 type Target = CommandBufferMutable;
377
378 fn deref(&self) -> &Self::Target {
379 match &*self.inner {
380 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
381 _ => unreachable!(),
382 }
383 }
384}
385
386impl<'a> ops::DerefMut for RecordingGuard<'a> {
387 fn deref_mut(&mut self) -> &mut Self::Target {
388 match self.inner {
389 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
390 _ => unreachable!(),
391 }
392 }
393}
394
395pub(crate) struct CommandEncoder {
396 pub(crate) device: Arc<Device>,
397
398 pub(crate) label: String,
399
400 pub(crate) data: Mutex<CommandEncoderStatus>,
402}
403
404crate::impl_resource_type!(CommandEncoder);
405crate::impl_labeled!(CommandEncoder);
406crate::impl_parent_device!(CommandEncoder);
407crate::impl_storage_item!(CommandEncoder);
408
409impl Drop for CommandEncoder {
410 fn drop(&mut self) {
411 resource_log!("Drop {}", self.error_ident());
412 }
413}
414
415pub(crate) struct InnerCommandEncoder {
431 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
439
440 pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
452
453 pub(crate) device: Arc<Device>,
454
455 pub(crate) is_open: bool,
462
463 pub(crate) label: String,
464}
465
466impl InnerCommandEncoder {
467 fn close_and_swap(&mut self) -> Result<(), DeviceError> {
493 assert!(self.is_open);
494 self.is_open = false;
495
496 let new =
497 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
498 self.list.insert(self.list.len() - 1, new);
499
500 Ok(())
501 }
502
503 pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> {
514 assert!(self.is_open);
515 self.is_open = false;
516
517 let new =
518 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
519 self.list.insert(0, new);
520
521 Ok(())
522 }
523
524 pub(crate) fn close(&mut self) -> Result<(), DeviceError> {
535 assert!(self.is_open);
536 self.is_open = false;
537
538 let cmd_buf =
539 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
540 self.list.push(cmd_buf);
541
542 Ok(())
543 }
544
545 fn close_if_open(&mut self) -> Result<(), DeviceError> {
556 if self.is_open {
557 self.is_open = false;
558 let cmd_buf =
559 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
560 self.list.push(cmd_buf);
561 }
562
563 Ok(())
564 }
565
566 pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
570 if !self.is_open {
571 self.is_open = true;
572 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
573 unsafe { self.raw.begin_encoding(hal_label) }
574 .map_err(|e| self.device.handle_hal_error(e))?;
575 }
576
577 Ok(self.raw.as_mut())
578 }
579
580 pub(crate) fn open_pass(
589 &mut self,
590 label: Option<&str>,
591 ) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
592 assert!(!self.is_open);
593 self.is_open = true;
594
595 let hal_label = hal_label(label, self.device.instance_flags);
596 unsafe { self.raw.begin_encoding(hal_label) }
597 .map_err(|e| self.device.handle_hal_error(e))?;
598
599 Ok(self.raw.as_mut())
600 }
601}
602
603impl Drop for InnerCommandEncoder {
604 fn drop(&mut self) {
605 if self.is_open {
606 unsafe { self.raw.discard_encoding() };
607 }
608 unsafe {
609 self.raw.reset_all(mem::take(&mut self.list));
610 }
611 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
613 self.device.command_allocator.release_encoder(raw);
614 }
615}
616
617pub(crate) struct BakedCommands {
620 pub(crate) encoder: InnerCommandEncoder,
621 pub(crate) trackers: Tracker,
622 pub(crate) temp_resources: Vec<TempResource>,
623 pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
624 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
625 texture_memory_actions: CommandBufferTextureMemoryActions,
626}
627
628pub struct CommandBufferMutable {
630 pub(crate) encoder: InnerCommandEncoder,
635
636 pub(crate) trackers: Tracker,
638
639 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
646 texture_memory_actions: CommandBufferTextureMemoryActions,
647
648 pub(crate) pending_query_resets: QueryResetMap,
649
650 as_actions: Vec<AsAction>,
651 temp_resources: Vec<TempResource>,
652
653 indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
654
655 debug_scope_depth: u32,
656
657 #[cfg(feature = "trace")]
658 pub(crate) commands: Option<Vec<TraceCommand>>,
659}
660
661impl CommandBufferMutable {
662 pub(crate) fn open_encoder_and_tracker(
663 &mut self,
664 ) -> Result<(&mut dyn hal::DynCommandEncoder, &mut Tracker), DeviceError> {
665 let encoder = self.encoder.open()?;
666 let tracker = &mut self.trackers;
667
668 Ok((encoder, tracker))
669 }
670
671 pub(crate) fn into_baked_commands(self) -> BakedCommands {
672 BakedCommands {
673 encoder: self.encoder,
674 trackers: self.trackers,
675 temp_resources: self.temp_resources,
676 indirect_draw_validation_resources: self.indirect_draw_validation_resources,
677 buffer_memory_init_actions: self.buffer_memory_init_actions,
678 texture_memory_actions: self.texture_memory_actions,
679 }
680 }
681}
682
683pub struct CommandBuffer {
689 pub(crate) device: Arc<Device>,
690 label: String,
692
693 pub(crate) data: Mutex<CommandEncoderStatus>,
695}
696
697impl Drop for CommandBuffer {
698 fn drop(&mut self) {
699 resource_log!("Drop {}", self.error_ident());
700 }
701}
702
703impl CommandEncoder {
704 pub(crate) fn new(
705 encoder: Box<dyn hal::DynCommandEncoder>,
706 device: &Arc<Device>,
707 label: &Label,
708 ) -> Self {
709 CommandEncoder {
710 device: device.clone(),
711 label: label.to_string(),
712 data: Mutex::new(
713 rank::COMMAND_BUFFER_DATA,
714 CommandEncoderStatus::Recording(CommandBufferMutable {
715 encoder: InnerCommandEncoder {
716 raw: ManuallyDrop::new(encoder),
717 list: Vec::new(),
718 device: device.clone(),
719 is_open: false,
720 label: label.to_string(),
721 },
722 trackers: Tracker::new(),
723 buffer_memory_init_actions: Default::default(),
724 texture_memory_actions: Default::default(),
725 pending_query_resets: QueryResetMap::new(),
726 as_actions: Default::default(),
727 temp_resources: Default::default(),
728 indirect_draw_validation_resources:
729 crate::indirect_validation::DrawResources::new(device.clone()),
730 debug_scope_depth: 0,
731 #[cfg(feature = "trace")]
732 commands: if device.trace.lock().is_some() {
733 Some(Vec::new())
734 } else {
735 None
736 },
737 }),
738 ),
739 }
740 }
741
742 pub(crate) fn new_invalid(
743 device: &Arc<Device>,
744 label: &Label,
745 err: CommandEncoderError,
746 ) -> Self {
747 CommandEncoder {
748 device: device.clone(),
749 label: label.to_string(),
750 data: Mutex::new(rank::COMMAND_BUFFER_DATA, CommandEncoderStatus::Error(err)),
751 }
752 }
753
754 pub(crate) fn insert_barriers_from_tracker(
755 raw: &mut dyn hal::DynCommandEncoder,
756 base: &mut Tracker,
757 head: &Tracker,
758 snatch_guard: &SnatchGuard,
759 ) {
760 profiling::scope!("insert_barriers");
761
762 base.buffers.set_from_tracker(&head.buffers);
763 base.textures.set_from_tracker(&head.textures);
764
765 Self::drain_barriers(raw, base, snatch_guard);
766 }
767
768 pub(crate) fn insert_barriers_from_scope(
769 raw: &mut dyn hal::DynCommandEncoder,
770 base: &mut Tracker,
771 head: &UsageScope,
772 snatch_guard: &SnatchGuard,
773 ) {
774 profiling::scope!("insert_barriers");
775
776 base.buffers.set_from_usage_scope(&head.buffers);
777 base.textures.set_from_usage_scope(&head.textures);
778
779 Self::drain_barriers(raw, base, snatch_guard);
780 }
781
782 pub(crate) fn drain_barriers(
783 raw: &mut dyn hal::DynCommandEncoder,
784 base: &mut Tracker,
785 snatch_guard: &SnatchGuard,
786 ) {
787 profiling::scope!("drain_barriers");
788
789 let buffer_barriers = base
790 .buffers
791 .drain_transitions(snatch_guard)
792 .collect::<Vec<_>>();
793 let (transitions, textures) = base.textures.drain_transitions(snatch_guard);
794 let texture_barriers = transitions
795 .into_iter()
796 .enumerate()
797 .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))
798 .collect::<Vec<_>>();
799
800 unsafe {
801 raw.transition_buffers(&buffer_barriers);
802 raw.transition_textures(&texture_barriers);
803 }
804 }
805
806 pub(crate) fn insert_barriers_from_device_tracker(
807 raw: &mut dyn hal::DynCommandEncoder,
808 base: &mut DeviceTracker,
809 head: &Tracker,
810 snatch_guard: &SnatchGuard,
811 ) {
812 profiling::scope!("insert_barriers_from_device_tracker");
813
814 let buffer_barriers = base
815 .buffers
816 .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)
817 .collect::<Vec<_>>();
818
819 let texture_barriers = base
820 .textures
821 .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)
822 .collect::<Vec<_>>();
823
824 unsafe {
825 raw.transition_buffers(&buffer_barriers);
826 raw.transition_textures(&texture_barriers);
827 }
828 }
829}
830
831impl CommandBuffer {
832 pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {
833 use CommandEncoderStatus as St;
834 match mem::replace(
835 &mut *self.data.lock(),
836 CommandEncoderStatus::Error(EncoderStateError::Submitted.into()),
837 ) {
838 St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
839 St::Error(err) => Err(err),
840 St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(),
841 }
842 }
843}
844
845crate::impl_resource_type!(CommandBuffer);
846crate::impl_labeled!(CommandBuffer);
847crate::impl_parent_device!(CommandBuffer);
848crate::impl_storage_item!(CommandBuffer);
849
850#[doc(hidden)]
862#[derive(Debug, Clone)]
863#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
864pub struct BasePass<C, E> {
865 pub label: Option<String>,
866
867 #[cfg_attr(feature = "serde", serde(skip, default = "Option::default"))]
875 pub error: Option<E>,
876
877 pub commands: Vec<C>,
879
880 pub dynamic_offsets: Vec<wgt::DynamicOffset>,
885
886 pub string_data: Vec<u8>,
891
892 pub push_constant_data: Vec<u32>,
897}
898
899impl<C: Clone, E: Clone> BasePass<C, E> {
900 fn new(label: &Label) -> Self {
901 Self {
902 label: label.as_deref().map(str::to_owned),
903 error: None,
904 commands: Vec::new(),
905 dynamic_offsets: Vec::new(),
906 string_data: Vec::new(),
907 push_constant_data: Vec::new(),
908 }
909 }
910
911 fn new_invalid(label: &Label, err: E) -> Self {
912 Self {
913 label: label.as_deref().map(str::to_owned),
914 error: Some(err),
915 commands: Vec::new(),
916 dynamic_offsets: Vec::new(),
917 string_data: Vec::new(),
918 push_constant_data: Vec::new(),
919 }
920 }
921}
922
923macro_rules! pass_base {
946 ($pass:expr, $scope:expr $(,)?) => {
947 match (&$pass.parent, &$pass.base.error) {
948 (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),
950 (&Some(_), &Some(_)) => return Ok(()),
952 (&Some(_), &None) => &mut $pass.base,
954 }
955 };
956}
957pub(crate) use pass_base;
958
959macro_rules! pass_try {
971 ($base:expr, $scope:expr, $res:expr $(,)?) => {
972 match $res.map_pass_err($scope) {
973 Ok(val) => val,
974 Err(err) => {
975 $base.error.get_or_insert(err);
976 return Ok(());
977 }
978 }
979 };
980}
981pub(crate) use pass_try;
982
983#[derive(Clone, Debug, Error)]
988#[non_exhaustive]
989pub enum EncoderStateError {
990 #[error("Encoder is invalid")]
995 Invalid,
996
997 #[error("Encoding must not have ended")]
1000 Ended,
1001
1002 #[error("Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
1008 Locked,
1009
1010 #[error(
1013 "Encoder is not currently locked. A pass can only be ended while the encoder is locked."
1014 )]
1015 Unlocked,
1016
1017 #[error("This command buffer has already been submitted.")]
1022 Submitted,
1023}
1024
1025impl WebGpuError for EncoderStateError {
1026 fn webgpu_error_type(&self) -> ErrorType {
1027 match self {
1028 EncoderStateError::Invalid
1029 | EncoderStateError::Ended
1030 | EncoderStateError::Locked
1031 | EncoderStateError::Unlocked
1032 | EncoderStateError::Submitted => ErrorType::Validation,
1033 }
1034 }
1035}
1036
1037#[derive(Clone, Debug, Error)]
1038#[non_exhaustive]
1039pub enum CommandEncoderError {
1040 #[error(transparent)]
1041 State(#[from] EncoderStateError),
1042 #[error(transparent)]
1043 Device(#[from] DeviceError),
1044 #[error(transparent)]
1045 InvalidResource(#[from] InvalidResourceError),
1046 #[error(transparent)]
1047 DestroyedResource(#[from] DestroyedResourceError),
1048 #[error(transparent)]
1049 ResourceUsage(#[from] ResourceUsageCompatibilityError),
1050 #[error(transparent)]
1051 DebugGroupError(#[from] DebugGroupError),
1052 #[error(transparent)]
1053 MissingFeatures(#[from] MissingFeatures),
1054 #[error(transparent)]
1055 Transfer(#[from] TransferError),
1056 #[error(transparent)]
1057 Clear(#[from] ClearError),
1058 #[error(transparent)]
1059 Query(#[from] QueryError),
1060 #[error(transparent)]
1061 BuildAccelerationStructure(#[from] BuildAccelerationStructureError),
1062 #[error(transparent)]
1063 TransitionResources(#[from] TransitionResourcesError),
1064 #[error(transparent)]
1065 ComputePass(#[from] ComputePassError),
1066 #[error(transparent)]
1067 RenderPass(#[from] RenderPassError),
1068}
1069
1070impl CommandEncoderError {
1071 fn is_destroyed_error(&self) -> bool {
1072 matches!(
1073 self,
1074 Self::DestroyedResource(_)
1075 | Self::Clear(ClearError::DestroyedResource(_))
1076 | Self::Query(QueryError::DestroyedResource(_))
1077 | Self::ComputePass(ComputePassError {
1078 inner: ComputePassErrorInner::DestroyedResource(_),
1079 ..
1080 })
1081 | Self::RenderPass(RenderPassError {
1082 inner: RenderPassErrorInner::DestroyedResource(_),
1083 ..
1084 })
1085 | Self::RenderPass(RenderPassError {
1086 inner: RenderPassErrorInner::RenderCommand(
1087 RenderCommandError::DestroyedResource(_)
1088 ),
1089 ..
1090 })
1091 | Self::RenderPass(RenderPassError {
1092 inner: RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError(
1093 BindingError::DestroyedResource(_)
1094 )),
1095 ..
1096 })
1097 )
1098 }
1099}
1100
1101impl WebGpuError for CommandEncoderError {
1102 fn webgpu_error_type(&self) -> ErrorType {
1103 let e: &dyn WebGpuError = match self {
1104 Self::Device(e) => e,
1105 Self::InvalidResource(e) => e,
1106 Self::DebugGroupError(e) => e,
1107 Self::MissingFeatures(e) => e,
1108 Self::State(e) => e,
1109 Self::DestroyedResource(e) => e,
1110 Self::Transfer(e) => e,
1111 Self::Clear(e) => e,
1112 Self::Query(e) => e,
1113 Self::BuildAccelerationStructure(e) => e,
1114 Self::TransitionResources(e) => e,
1115 Self::ResourceUsage(e) => e,
1116 Self::ComputePass(e) => e,
1117 Self::RenderPass(e) => e,
1118 };
1119 e.webgpu_error_type()
1120 }
1121}
1122
1123#[derive(Clone, Debug, Error)]
1124#[non_exhaustive]
1125pub enum DebugGroupError {
1126 #[error("Cannot pop debug group, because number of pushed debug groups is zero")]
1127 InvalidPop,
1128 #[error("A debug group was not popped before the encoder was finished")]
1129 MissingPop,
1130}
1131
1132impl WebGpuError for DebugGroupError {
1133 fn webgpu_error_type(&self) -> ErrorType {
1134 match self {
1135 Self::InvalidPop | Self::MissingPop => ErrorType::Validation,
1136 }
1137 }
1138}
1139
1140#[derive(Clone, Debug, Error)]
1141#[non_exhaustive]
1142pub enum TimestampWritesError {
1143 #[error(
1144 "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
1145 )]
1146 IndicesEqual { idx: u32 },
1147 #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
1148 IndicesMissing,
1149}
1150
1151impl WebGpuError for TimestampWritesError {
1152 fn webgpu_error_type(&self) -> ErrorType {
1153 match self {
1154 Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,
1155 }
1156 }
1157}
1158
1159impl Global {
1160 pub fn command_encoder_finish(
1161 &self,
1162 encoder_id: id::CommandEncoderId,
1163 desc: &wgt::CommandBufferDescriptor<Label>,
1164 id_in: Option<id::CommandBufferId>,
1165 ) -> (id::CommandBufferId, Option<CommandEncoderError>) {
1166 profiling::scope!("CommandEncoder::finish");
1167
1168 let hub = &self.hub;
1169
1170 let cmd_enc = hub.command_encoders.get(encoder_id);
1171
1172 let data = cmd_enc.data.lock().finish();
1173
1174 let error = match data {
1177 CommandEncoderStatus::Error(ref e) if !e.is_destroyed_error() => Some(e.clone()),
1178 _ => None,
1179 };
1180
1181 let cmd_buf = CommandBuffer {
1182 device: cmd_enc.device.clone(),
1183 label: desc.label.to_string(),
1184 data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),
1185 };
1186
1187 let cmd_buf_id = hub.command_buffers.prepare(id_in).assign(Arc::new(cmd_buf));
1188
1189 (cmd_buf_id, error)
1190 }
1191
1192 pub fn command_encoder_push_debug_group(
1193 &self,
1194 encoder_id: id::CommandEncoderId,
1195 label: &str,
1196 ) -> Result<(), EncoderStateError> {
1197 profiling::scope!("CommandEncoder::push_debug_group");
1198 api_log!("CommandEncoder::push_debug_group {label}");
1199
1200 let hub = &self.hub;
1201
1202 let cmd_enc = hub.command_encoders.get(encoder_id);
1203 let mut cmd_buf_data = cmd_enc.data.lock();
1204 cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
1205 cmd_buf_data.debug_scope_depth += 1;
1206
1207 #[cfg(feature = "trace")]
1208 if let Some(ref mut list) = cmd_buf_data.commands {
1209 list.push(TraceCommand::PushDebugGroup(label.to_owned()));
1210 }
1211
1212 cmd_enc.device.check_is_valid()?;
1213
1214 let cmd_buf_raw = cmd_buf_data.encoder.open()?;
1215 if !cmd_enc
1216 .device
1217 .instance_flags
1218 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1219 {
1220 unsafe {
1221 cmd_buf_raw.begin_debug_marker(label);
1222 }
1223 }
1224
1225 Ok(())
1226 })
1227 }
1228
1229 pub fn command_encoder_insert_debug_marker(
1230 &self,
1231 encoder_id: id::CommandEncoderId,
1232 label: &str,
1233 ) -> Result<(), EncoderStateError> {
1234 profiling::scope!("CommandEncoder::insert_debug_marker");
1235 api_log!("CommandEncoder::insert_debug_marker {label}");
1236
1237 let hub = &self.hub;
1238
1239 let cmd_enc = hub.command_encoders.get(encoder_id);
1240 let mut cmd_buf_data = cmd_enc.data.lock();
1241 cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
1242 #[cfg(feature = "trace")]
1243 if let Some(ref mut list) = cmd_buf_data.commands {
1244 list.push(TraceCommand::InsertDebugMarker(label.to_owned()));
1245 }
1246
1247 cmd_enc.device.check_is_valid()?;
1248
1249 if !cmd_enc
1250 .device
1251 .instance_flags
1252 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1253 {
1254 let cmd_buf_raw = cmd_buf_data.encoder.open()?;
1255 unsafe {
1256 cmd_buf_raw.insert_debug_marker(label);
1257 }
1258 }
1259
1260 Ok(())
1261 })
1262 }
1263
1264 pub fn command_encoder_pop_debug_group(
1265 &self,
1266 encoder_id: id::CommandEncoderId,
1267 ) -> Result<(), EncoderStateError> {
1268 profiling::scope!("CommandEncoder::pop_debug_marker");
1269 api_log!("CommandEncoder::pop_debug_group");
1270
1271 let hub = &self.hub;
1272
1273 let cmd_enc = hub.command_encoders.get(encoder_id);
1274 let mut cmd_buf_data = cmd_enc.data.lock();
1275 cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
1276 if cmd_buf_data.debug_scope_depth == 0 {
1277 return Err(DebugGroupError::InvalidPop.into());
1278 }
1279 cmd_buf_data.debug_scope_depth -= 1;
1280
1281 #[cfg(feature = "trace")]
1282 if let Some(ref mut list) = cmd_buf_data.commands {
1283 list.push(TraceCommand::PopDebugGroup);
1284 }
1285
1286 cmd_enc.device.check_is_valid()?;
1287
1288 let cmd_buf_raw = cmd_buf_data.encoder.open()?;
1289 if !cmd_enc
1290 .device
1291 .instance_flags
1292 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1293 {
1294 unsafe {
1295 cmd_buf_raw.end_debug_marker();
1296 }
1297 }
1298
1299 Ok(())
1300 })
1301 }
1302
1303 fn validate_pass_timestamp_writes<E>(
1304 device: &Device,
1305 query_sets: &Storage<Fallible<QuerySet>>,
1306 timestamp_writes: &PassTimestampWrites,
1307 ) -> Result<ArcPassTimestampWrites, E>
1308 where
1309 E: From<TimestampWritesError>
1310 + From<QueryUseError>
1311 + From<DeviceError>
1312 + From<MissingFeatures>
1313 + From<InvalidResourceError>,
1314 {
1315 let &PassTimestampWrites {
1316 query_set,
1317 beginning_of_pass_write_index,
1318 end_of_pass_write_index,
1319 } = timestamp_writes;
1320
1321 device.require_features(wgt::Features::TIMESTAMP_QUERY)?;
1322
1323 let query_set = query_sets.get(query_set).get()?;
1324
1325 query_set.same_device(device)?;
1326
1327 for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
1328 .into_iter()
1329 .flatten()
1330 {
1331 query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
1332 }
1333
1334 if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
1335 if begin == end {
1336 return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());
1337 }
1338 }
1339
1340 if beginning_of_pass_write_index
1341 .or(end_of_pass_write_index)
1342 .is_none()
1343 {
1344 return Err(TimestampWritesError::IndicesMissing.into());
1345 }
1346
1347 Ok(ArcPassTimestampWrites {
1348 query_set,
1349 beginning_of_pass_write_index,
1350 end_of_pass_write_index,
1351 })
1352 }
1353}
1354
1355fn push_constant_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
1356where
1357 PushFn: FnMut(u32, &[u32]),
1358{
1359 let mut count_words = 0_u32;
1360 let size_words = size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT;
1361 while count_words < size_words {
1362 let count_bytes = count_words * wgt::PUSH_CONSTANT_ALIGNMENT;
1363 let size_to_write_words =
1364 (size_words - count_words).min(PUSH_CONSTANT_CLEAR_ARRAY.len() as u32);
1365
1366 push_fn(
1367 offset + count_bytes,
1368 &PUSH_CONSTANT_CLEAR_ARRAY[0..size_to_write_words as usize],
1369 );
1370
1371 count_words += size_to_write_words;
1372 }
1373}
1374
1375#[derive(Debug, Copy, Clone)]
1376struct StateChange<T> {
1377 last_state: Option<T>,
1378}
1379
1380impl<T: Copy + PartialEq> StateChange<T> {
1381 fn new() -> Self {
1382 Self { last_state: None }
1383 }
1384 fn set_and_check_redundant(&mut self, new_state: T) -> bool {
1385 let already_set = self.last_state == Some(new_state);
1386 self.last_state = Some(new_state);
1387 already_set
1388 }
1389 fn reset(&mut self) {
1390 self.last_state = None;
1391 }
1392}
1393
1394impl<T: Copy + PartialEq> Default for StateChange<T> {
1395 fn default() -> Self {
1396 Self::new()
1397 }
1398}
1399
1400#[derive(Debug)]
1401struct BindGroupStateChange {
1402 last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],
1403}
1404
1405impl BindGroupStateChange {
1406 fn new() -> Self {
1407 Self {
1408 last_states: [StateChange::new(); hal::MAX_BIND_GROUPS],
1409 }
1410 }
1411
1412 fn set_and_check_redundant(
1413 &mut self,
1414 bind_group_id: Option<id::BindGroupId>,
1415 index: u32,
1416 dynamic_offsets: &mut Vec<u32>,
1417 offsets: &[wgt::DynamicOffset],
1418 ) -> bool {
1419 if offsets.is_empty() {
1421 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1424 if current_bind_group.set_and_check_redundant(bind_group_id) {
1426 return true;
1427 }
1428 }
1429 } else {
1430 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1434 current_bind_group.reset();
1435 }
1436 dynamic_offsets.extend_from_slice(offsets);
1437 }
1438 false
1439 }
1440 fn reset(&mut self) {
1441 self.last_states = [StateChange::new(); hal::MAX_BIND_GROUPS];
1442 }
1443}
1444
1445impl Default for BindGroupStateChange {
1446 fn default() -> Self {
1447 Self::new()
1448 }
1449}
1450
1451trait MapPassErr<T> {
1453 fn map_pass_err(self, scope: PassErrorScope) -> T;
1454}
1455
1456impl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>
1457where
1458 E: MapPassErr<F>,
1459{
1460 fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {
1461 self.map_err(|err| err.map_pass_err(scope))
1462 }
1463}
1464
1465impl MapPassErr<PassStateError> for EncoderStateError {
1466 fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {
1467 PassStateError { scope, inner: self }
1468 }
1469}
1470
1471#[derive(Clone, Copy, Debug)]
1472pub enum DrawKind {
1473 Draw,
1474 DrawIndirect,
1475 MultiDrawIndirect,
1476 MultiDrawIndirectCount,
1477}
1478
1479#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1481#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1482pub enum DrawCommandFamily {
1483 Draw,
1484 DrawIndexed,
1485 DrawMeshTasks,
1486}
1487
1488#[derive(Clone, Copy, Debug, Error)]
1498pub enum PassErrorScope {
1499 #[error("In a bundle parameter")]
1502 Bundle,
1503 #[error("In a pass parameter")]
1504 Pass,
1505 #[error("In a set_bind_group command")]
1506 SetBindGroup,
1507 #[error("In a set_pipeline command")]
1508 SetPipelineRender,
1509 #[error("In a set_pipeline command")]
1510 SetPipelineCompute,
1511 #[error("In a set_push_constant command")]
1512 SetPushConstant,
1513 #[error("In a set_vertex_buffer command")]
1514 SetVertexBuffer,
1515 #[error("In a set_index_buffer command")]
1516 SetIndexBuffer,
1517 #[error("In a set_blend_constant command")]
1518 SetBlendConstant,
1519 #[error("In a set_stencil_reference command")]
1520 SetStencilReference,
1521 #[error("In a set_viewport command")]
1522 SetViewport,
1523 #[error("In a set_scissor_rect command")]
1524 SetScissorRect,
1525 #[error("In a draw command, kind: {kind:?}")]
1526 Draw {
1527 kind: DrawKind,
1528 family: DrawCommandFamily,
1529 },
1530 #[error("In a write_timestamp command")]
1531 WriteTimestamp,
1532 #[error("In a begin_occlusion_query command")]
1533 BeginOcclusionQuery,
1534 #[error("In a end_occlusion_query command")]
1535 EndOcclusionQuery,
1536 #[error("In a begin_pipeline_statistics_query command")]
1537 BeginPipelineStatisticsQuery,
1538 #[error("In a end_pipeline_statistics_query command")]
1539 EndPipelineStatisticsQuery,
1540 #[error("In a execute_bundle command")]
1541 ExecuteBundle,
1542 #[error("In a dispatch command, indirect:{indirect}")]
1543 Dispatch { indirect: bool },
1544 #[error("In a push_debug_group command")]
1545 PushDebugGroup,
1546 #[error("In a pop_debug_group command")]
1547 PopDebugGroup,
1548 #[error("In a insert_debug_marker command")]
1549 InsertDebugMarker,
1550}
1551
1552#[derive(Clone, Debug, Error)]
1554#[error("{scope}")]
1555pub struct PassStateError {
1556 pub scope: PassErrorScope,
1557 #[source]
1558 pub(super) inner: EncoderStateError,
1559}
1560
1561impl WebGpuError for PassStateError {
1562 fn webgpu_error_type(&self) -> ErrorType {
1563 let Self { scope: _, inner } = self;
1564 inner.webgpu_error_type()
1565 }
1566}