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::command::transition_resources::TransitionResourcesError;
35use crate::device::queue::TempResource;
36use crate::device::{Device, DeviceError, MissingFeatures};
37use crate::lock::{rank, Mutex};
38use crate::snatch::SnatchGuard;
39
40use crate::init_tracker::BufferInitTrackerAction;
41use crate::ray_tracing::{AsAction, BuildAccelerationStructureError};
42use crate::resource::{
43 DestroyedResourceError, Fallible, InvalidResourceError, Labeled, ParentDevice as _, QuerySet,
44};
45use crate::storage::Storage;
46use crate::track::{DeviceTracker, ResourceUsageCompatibilityError, Tracker, UsageScope};
47use crate::{api_log, global::Global, id, resource_log, Label};
48use crate::{hal_label, LabelHelpers};
49
50use wgt::error::{ErrorType, WebGpuError};
51
52use thiserror::Error;
53
54#[cfg(feature = "trace")]
55use crate::device::trace::Command as TraceCommand;
56
57const PUSH_CONSTANT_CLEAR_ARRAY: &[u32] = &[0_u32; 64];
58
59pub(crate) enum CommandEncoderStatus {
65 Recording(CommandBufferMutable),
77
78 Locked(CommandBufferMutable),
87
88 Finished(CommandBufferMutable),
99
100 Error(CommandEncoderError),
105
106 Transitioning,
109}
110
111impl CommandEncoderStatus {
112 fn record_with<
127 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
128 E: Clone + Into<CommandEncoderError>,
129 >(
130 &mut self,
131 f: F,
132 ) -> Result<(), EncoderStateError> {
133 match self {
134 Self::Recording(_) => {
135 RecordingGuard { inner: self }.record(f);
136 Ok(())
137 }
138 Self::Locked(_) => {
139 self.invalidate(EncoderStateError::Locked);
142 Ok(())
143 }
144 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
147 Self::Error(_) => Ok(()),
150 Self::Transitioning => unreachable!(),
151 }
152 }
153
154 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
162 &mut self,
163 f: F,
164 ) -> T {
165 match self {
166 Self::Recording(_) => RecordingGuard { inner: self }.record_as_hal_mut(f),
167 Self::Locked(_) => {
168 self.invalidate(EncoderStateError::Locked);
169 f(None)
170 }
171 Self::Finished(_) => {
172 self.invalidate(EncoderStateError::Ended);
173 f(None)
174 }
175 Self::Error(_) => f(None),
176 Self::Transitioning => unreachable!(),
177 }
178 }
179
180 #[cfg(feature = "trace")]
181 fn get_inner(&mut self) -> &mut CommandBufferMutable {
182 match self {
183 Self::Locked(inner) | Self::Finished(inner) | Self::Recording(inner) => inner,
184 Self::Error(_) => unreachable!("passes in a trace do not store errors"),
189 Self::Transitioning => unreachable!(),
190 }
191 }
192
193 fn lock_encoder(&mut self) -> Result<(), EncoderStateError> {
199 match mem::replace(self, Self::Transitioning) {
200 Self::Recording(inner) => {
201 *self = Self::Locked(inner);
202 Ok(())
203 }
204 st @ Self::Finished(_) => {
205 *self = st;
209 Err(EncoderStateError::Ended)
210 }
211 Self::Locked(_) => Err(self.invalidate(EncoderStateError::Locked)),
212 st @ Self::Error(_) => {
213 *self = st;
214 Err(EncoderStateError::Invalid)
215 }
216 Self::Transitioning => unreachable!(),
217 }
218 }
219
220 fn unlock_and_record<
236 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
237 E: Clone + Into<CommandEncoderError>,
238 >(
239 &mut self,
240 f: F,
241 ) -> Result<(), EncoderStateError> {
242 match mem::replace(self, Self::Transitioning) {
243 Self::Locked(inner) => {
244 *self = Self::Recording(inner);
245 RecordingGuard { inner: self }.record(f);
246 Ok(())
247 }
248 st @ Self::Finished(_) => {
249 *self = st;
250 Err(EncoderStateError::Ended)
251 }
252 Self::Recording(_) => {
253 *self = Self::Error(EncoderStateError::Unlocked.into());
254 Err(EncoderStateError::Unlocked)
255 }
256 st @ Self::Error(_) => {
257 *self = st;
260 Ok(())
261 }
262 Self::Transitioning => unreachable!(),
263 }
264 }
265
266 fn finish(&mut self) -> Result<(), CommandEncoderError> {
267 match mem::replace(self, Self::Transitioning) {
268 Self::Recording(mut inner) => {
269 if let Err(e) = inner.encoder.close_if_open() {
270 Err(self.invalidate(e.into()))
271 } else {
272 *self = Self::Finished(inner);
273 Ok(())
276 }
277 }
278 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended.into())),
279 Self::Locked(_) => Err(self.invalidate(EncoderStateError::Locked.into())),
280 Self::Error(err) => Err(self.invalidate(err)),
281 Self::Transitioning => unreachable!(),
282 }
283 }
284
285 fn invalidate<E: Clone + Into<CommandEncoderError>>(&mut self, err: E) -> E {
291 *self = Self::Error(err.clone().into());
292 err
293 }
294}
295
296pub(crate) struct RecordingGuard<'a> {
309 inner: &'a mut CommandEncoderStatus,
310}
311
312impl<'a> RecordingGuard<'a> {
313 pub(crate) fn mark_successful(self) {
314 mem::forget(self)
315 }
316
317 fn record<
318 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
319 E: Clone + Into<CommandEncoderError>,
320 >(
321 mut self,
322 f: F,
323 ) {
324 match f(&mut self) {
325 Ok(()) => self.mark_successful(),
326 Err(err) => {
327 self.inner.invalidate(err);
328 }
329 }
330 }
331
332 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
335 mut self,
336 f: F,
337 ) -> T {
338 let res = f(Some(&mut self));
339 self.mark_successful();
340 res
341 }
342}
343
344impl<'a> Drop for RecordingGuard<'a> {
345 fn drop(&mut self) {
346 if matches!(*self.inner, CommandEncoderStatus::Error(_)) {
347 return;
349 }
350 self.inner.invalidate(EncoderStateError::Invalid);
351 }
352}
353
354impl<'a> ops::Deref for RecordingGuard<'a> {
355 type Target = CommandBufferMutable;
356
357 fn deref(&self) -> &Self::Target {
358 match &*self.inner {
359 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
360 _ => unreachable!(),
361 }
362 }
363}
364
365impl<'a> ops::DerefMut for RecordingGuard<'a> {
366 fn deref_mut(&mut self) -> &mut Self::Target {
367 match self.inner {
368 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
369 _ => unreachable!(),
370 }
371 }
372}
373
374pub(crate) struct CommandEncoder {
395 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
403
404 pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
416
417 pub(crate) device: Arc<Device>,
418
419 pub(crate) is_open: bool,
426
427 pub(crate) hal_label: Option<String>,
428}
429
430impl CommandEncoder {
431 fn close_and_swap(&mut self) -> Result<(), DeviceError> {
457 assert!(self.is_open);
458 self.is_open = false;
459
460 let new =
461 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
462 self.list.insert(self.list.len() - 1, new);
463
464 Ok(())
465 }
466
467 pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> {
478 assert!(self.is_open);
479 self.is_open = false;
480
481 let new =
482 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
483 self.list.insert(0, new);
484
485 Ok(())
486 }
487
488 pub(crate) fn close(&mut self) -> Result<(), DeviceError> {
499 assert!(self.is_open);
500 self.is_open = false;
501
502 let cmd_buf =
503 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
504 self.list.push(cmd_buf);
505
506 Ok(())
507 }
508
509 fn close_if_open(&mut self) -> Result<(), DeviceError> {
520 if self.is_open {
521 self.is_open = false;
522 let cmd_buf =
523 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
524 self.list.push(cmd_buf);
525 }
526
527 Ok(())
528 }
529
530 pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
534 if !self.is_open {
535 self.is_open = true;
536 let hal_label = self.hal_label.as_deref();
537 unsafe { self.raw.begin_encoding(hal_label) }
538 .map_err(|e| self.device.handle_hal_error(e))?;
539 }
540
541 Ok(self.raw.as_mut())
542 }
543
544 pub(crate) fn open_pass(
553 &mut self,
554 label: Option<&str>,
555 ) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
556 assert!(!self.is_open);
557 self.is_open = true;
558
559 let hal_label = hal_label(label, self.device.instance_flags);
560 unsafe { self.raw.begin_encoding(hal_label) }
561 .map_err(|e| self.device.handle_hal_error(e))?;
562
563 Ok(self.raw.as_mut())
564 }
565}
566
567impl Drop for CommandEncoder {
568 fn drop(&mut self) {
569 if self.is_open {
570 unsafe { self.raw.discard_encoding() };
571 }
572 unsafe {
573 self.raw.reset_all(mem::take(&mut self.list));
574 }
575 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
577 self.device.command_allocator.release_encoder(raw);
578 }
579}
580
581pub(crate) struct BakedCommands {
584 pub(crate) encoder: CommandEncoder,
585 pub(crate) trackers: Tracker,
586 pub(crate) temp_resources: Vec<TempResource>,
587 pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
588 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
589 texture_memory_actions: CommandBufferTextureMemoryActions,
590}
591
592pub struct CommandBufferMutable {
594 pub(crate) encoder: CommandEncoder,
599
600 pub(crate) trackers: Tracker,
602
603 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
610 texture_memory_actions: CommandBufferTextureMemoryActions,
611
612 pub(crate) pending_query_resets: QueryResetMap,
613
614 as_actions: Vec<AsAction>,
615 temp_resources: Vec<TempResource>,
616
617 indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
618
619 #[cfg(feature = "trace")]
620 pub(crate) commands: Option<Vec<TraceCommand>>,
621}
622
623impl CommandBufferMutable {
624 pub(crate) fn open_encoder_and_tracker(
625 &mut self,
626 ) -> Result<(&mut dyn hal::DynCommandEncoder, &mut Tracker), DeviceError> {
627 let encoder = self.encoder.open()?;
628 let tracker = &mut self.trackers;
629
630 Ok((encoder, tracker))
631 }
632
633 pub(crate) fn into_baked_commands(self) -> BakedCommands {
634 BakedCommands {
635 encoder: self.encoder,
636 trackers: self.trackers,
637 temp_resources: self.temp_resources,
638 indirect_draw_validation_resources: self.indirect_draw_validation_resources,
639 buffer_memory_init_actions: self.buffer_memory_init_actions,
640 texture_memory_actions: self.texture_memory_actions,
641 }
642 }
643}
644
645pub struct CommandBuffer {
664 pub(crate) device: Arc<Device>,
665 support_clear_texture: bool,
666 label: String,
668
669 pub(crate) data: Mutex<CommandEncoderStatus>,
671}
672
673impl Drop for CommandBuffer {
674 fn drop(&mut self) {
675 resource_log!("Drop {}", self.error_ident());
676 }
677}
678
679impl CommandBuffer {
680 pub(crate) fn new(
681 encoder: Box<dyn hal::DynCommandEncoder>,
682 device: &Arc<Device>,
683 label: &Label,
684 ) -> Self {
685 CommandBuffer {
686 device: device.clone(),
687 support_clear_texture: device.features.contains(wgt::Features::CLEAR_TEXTURE),
688 label: label.to_string(),
689 data: Mutex::new(
690 rank::COMMAND_BUFFER_DATA,
691 CommandEncoderStatus::Recording(CommandBufferMutable {
692 encoder: CommandEncoder {
693 raw: ManuallyDrop::new(encoder),
694 list: Vec::new(),
695 device: device.clone(),
696 is_open: false,
697 hal_label: label.to_hal(device.instance_flags).map(str::to_owned),
698 },
699 trackers: Tracker::new(),
700 buffer_memory_init_actions: Default::default(),
701 texture_memory_actions: Default::default(),
702 pending_query_resets: QueryResetMap::new(),
703 as_actions: Default::default(),
704 temp_resources: Default::default(),
705 indirect_draw_validation_resources:
706 crate::indirect_validation::DrawResources::new(device.clone()),
707 #[cfg(feature = "trace")]
708 commands: if device.trace.lock().is_some() {
709 Some(Vec::new())
710 } else {
711 None
712 },
713 }),
714 ),
715 }
716 }
717
718 pub(crate) fn new_invalid(
719 device: &Arc<Device>,
720 label: &Label,
721 err: CommandEncoderError,
722 ) -> Self {
723 CommandBuffer {
724 device: device.clone(),
725 support_clear_texture: device.features.contains(wgt::Features::CLEAR_TEXTURE),
726 label: label.to_string(),
727 data: Mutex::new(rank::COMMAND_BUFFER_DATA, CommandEncoderStatus::Error(err)),
728 }
729 }
730
731 pub(crate) fn insert_barriers_from_tracker(
732 raw: &mut dyn hal::DynCommandEncoder,
733 base: &mut Tracker,
734 head: &Tracker,
735 snatch_guard: &SnatchGuard,
736 ) {
737 profiling::scope!("insert_barriers");
738
739 base.buffers.set_from_tracker(&head.buffers);
740 base.textures.set_from_tracker(&head.textures);
741
742 Self::drain_barriers(raw, base, snatch_guard);
743 }
744
745 pub(crate) fn insert_barriers_from_scope(
746 raw: &mut dyn hal::DynCommandEncoder,
747 base: &mut Tracker,
748 head: &UsageScope,
749 snatch_guard: &SnatchGuard,
750 ) {
751 profiling::scope!("insert_barriers");
752
753 base.buffers.set_from_usage_scope(&head.buffers);
754 base.textures.set_from_usage_scope(&head.textures);
755
756 Self::drain_barriers(raw, base, snatch_guard);
757 }
758
759 pub(crate) fn drain_barriers(
760 raw: &mut dyn hal::DynCommandEncoder,
761 base: &mut Tracker,
762 snatch_guard: &SnatchGuard,
763 ) {
764 profiling::scope!("drain_barriers");
765
766 let buffer_barriers = base
767 .buffers
768 .drain_transitions(snatch_guard)
769 .collect::<Vec<_>>();
770 let (transitions, textures) = base.textures.drain_transitions(snatch_guard);
771 let texture_barriers = transitions
772 .into_iter()
773 .enumerate()
774 .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))
775 .collect::<Vec<_>>();
776
777 unsafe {
778 raw.transition_buffers(&buffer_barriers);
779 raw.transition_textures(&texture_barriers);
780 }
781 }
782
783 pub(crate) fn insert_barriers_from_device_tracker(
784 raw: &mut dyn hal::DynCommandEncoder,
785 base: &mut DeviceTracker,
786 head: &Tracker,
787 snatch_guard: &SnatchGuard,
788 ) {
789 profiling::scope!("insert_barriers_from_device_tracker");
790
791 let buffer_barriers = base
792 .buffers
793 .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)
794 .collect::<Vec<_>>();
795
796 let texture_barriers = base
797 .textures
798 .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)
799 .collect::<Vec<_>>();
800
801 unsafe {
802 raw.transition_buffers(&buffer_barriers);
803 raw.transition_textures(&texture_barriers);
804 }
805 }
806}
807
808impl CommandBuffer {
809 pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {
810 use CommandEncoderStatus as St;
811 match mem::replace(
812 &mut *self.data.lock(),
813 CommandEncoderStatus::Error(EncoderStateError::Submitted.into()),
814 ) {
815 St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
816 St::Error(err) => Err(err),
817 St::Recording(_) | St::Locked(_) => {
818 Err(InvalidResourceError(self.error_ident()).into())
819 }
820 St::Transitioning => unreachable!(),
821 }
822 }
823}
824
825crate::impl_resource_type!(CommandBuffer);
826crate::impl_labeled!(CommandBuffer);
827crate::impl_parent_device!(CommandBuffer);
828crate::impl_storage_item!(CommandBuffer);
829
830#[doc(hidden)]
842#[derive(Debug, Clone)]
843#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
844pub struct BasePass<C, E> {
845 pub label: Option<String>,
846
847 #[cfg_attr(feature = "serde", serde(skip, default = "Option::default"))]
855 pub error: Option<E>,
856
857 pub commands: Vec<C>,
859
860 pub dynamic_offsets: Vec<wgt::DynamicOffset>,
865
866 pub string_data: Vec<u8>,
871
872 pub push_constant_data: Vec<u32>,
877}
878
879impl<C: Clone, E: Clone> BasePass<C, E> {
880 fn new(label: &Label) -> Self {
881 Self {
882 label: label.as_deref().map(str::to_owned),
883 error: None,
884 commands: Vec::new(),
885 dynamic_offsets: Vec::new(),
886 string_data: Vec::new(),
887 push_constant_data: Vec::new(),
888 }
889 }
890
891 fn new_invalid(label: &Label, err: E) -> Self {
892 Self {
893 label: label.as_deref().map(str::to_owned),
894 error: Some(err),
895 commands: Vec::new(),
896 dynamic_offsets: Vec::new(),
897 string_data: Vec::new(),
898 push_constant_data: Vec::new(),
899 }
900 }
901}
902
903macro_rules! pass_base {
926 ($pass:expr, $scope:expr $(,)?) => {
927 match (&$pass.parent, &$pass.base.error) {
928 (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),
930 (&Some(_), &Some(_)) => return Ok(()),
932 (&Some(_), &None) => &mut $pass.base,
934 }
935 };
936}
937pub(crate) use pass_base;
938
939macro_rules! pass_try {
951 ($base:expr, $scope:expr, $res:expr $(,)?) => {
952 match $res.map_pass_err($scope) {
953 Ok(val) => val,
954 Err(err) => {
955 $base.error.get_or_insert(err);
956 return Ok(());
957 }
958 }
959 };
960}
961pub(crate) use pass_try;
962
963#[derive(Clone, Debug, Error)]
968#[non_exhaustive]
969pub enum EncoderStateError {
970 #[error("Encoder is invalid")]
975 Invalid,
976
977 #[error("Encoding must not have ended")]
980 Ended,
981
982 #[error("Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
988 Locked,
989
990 #[error(
993 "Encoder is not currently locked. A pass can only be ended while the encoder is locked."
994 )]
995 Unlocked,
996
997 #[error("This command buffer has already been submitted.")]
1002 Submitted,
1003}
1004
1005impl WebGpuError for EncoderStateError {
1006 fn webgpu_error_type(&self) -> ErrorType {
1007 match self {
1008 EncoderStateError::Invalid
1009 | EncoderStateError::Ended
1010 | EncoderStateError::Locked
1011 | EncoderStateError::Unlocked
1012 | EncoderStateError::Submitted => ErrorType::Validation,
1013 }
1014 }
1015}
1016
1017#[derive(Clone, Debug, Error)]
1018#[non_exhaustive]
1019pub enum CommandEncoderError {
1020 #[error(transparent)]
1021 State(#[from] EncoderStateError),
1022 #[error(transparent)]
1023 Device(#[from] DeviceError),
1024 #[error(transparent)]
1025 InvalidResource(#[from] InvalidResourceError),
1026 #[error(transparent)]
1027 DestroyedResource(#[from] DestroyedResourceError),
1028 #[error(transparent)]
1029 ResourceUsage(#[from] ResourceUsageCompatibilityError),
1030 #[error(transparent)]
1031 MissingFeatures(#[from] MissingFeatures),
1032 #[error(transparent)]
1033 Transfer(#[from] TransferError),
1034 #[error(transparent)]
1035 Clear(#[from] ClearError),
1036 #[error(transparent)]
1037 Query(#[from] QueryError),
1038 #[error(transparent)]
1039 BuildAccelerationStructure(#[from] BuildAccelerationStructureError),
1040 #[error(transparent)]
1041 TransitionResources(#[from] TransitionResourcesError),
1042 #[error(transparent)]
1043 ComputePass(#[from] ComputePassError),
1044 #[error(transparent)]
1045 RenderPass(#[from] RenderPassError),
1046}
1047
1048impl CommandEncoderError {
1049 fn is_destroyed_error(&self) -> bool {
1050 matches!(
1051 self,
1052 Self::DestroyedResource(_)
1053 | Self::Clear(ClearError::DestroyedResource(_))
1054 | Self::Query(QueryError::DestroyedResource(_))
1055 | Self::ComputePass(ComputePassError {
1056 inner: ComputePassErrorInner::DestroyedResource(_),
1057 ..
1058 })
1059 | Self::RenderPass(RenderPassError {
1060 inner: RenderPassErrorInner::DestroyedResource(_),
1061 ..
1062 })
1063 )
1064 }
1065}
1066
1067impl WebGpuError for CommandEncoderError {
1068 fn webgpu_error_type(&self) -> ErrorType {
1069 let e: &dyn WebGpuError = match self {
1070 Self::Device(e) => e,
1071 Self::InvalidResource(e) => e,
1072 Self::MissingFeatures(e) => e,
1073 Self::State(e) => e,
1074 Self::DestroyedResource(e) => e,
1075 Self::Transfer(e) => e,
1076 Self::Clear(e) => e,
1077 Self::Query(e) => e,
1078 Self::BuildAccelerationStructure(e) => e,
1079 Self::TransitionResources(e) => e,
1080 Self::ResourceUsage(e) => e,
1081 Self::ComputePass(e) => e,
1082 Self::RenderPass(e) => e,
1083 };
1084 e.webgpu_error_type()
1085 }
1086}
1087
1088#[derive(Clone, Debug, Error)]
1089#[non_exhaustive]
1090pub enum TimestampWritesError {
1091 #[error(
1092 "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
1093 )]
1094 IndicesEqual { idx: u32 },
1095 #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
1096 IndicesMissing,
1097}
1098
1099impl WebGpuError for TimestampWritesError {
1100 fn webgpu_error_type(&self) -> ErrorType {
1101 match self {
1102 Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,
1103 }
1104 }
1105}
1106
1107impl Global {
1108 pub fn command_encoder_finish(
1109 &self,
1110 encoder_id: id::CommandEncoderId,
1111 _desc: &wgt::CommandBufferDescriptor<Label>,
1112 ) -> (id::CommandBufferId, Option<CommandEncoderError>) {
1113 profiling::scope!("CommandEncoder::finish");
1114
1115 let hub = &self.hub;
1116
1117 let cmd_buf = hub.command_buffers.get(encoder_id.into_command_buffer_id());
1118
1119 let error = match cmd_buf.data.lock().finish() {
1122 Err(e) if !e.is_destroyed_error() => Some(e),
1123 _ => None,
1124 };
1125
1126 (encoder_id.into_command_buffer_id(), error)
1127 }
1128
1129 pub fn command_encoder_push_debug_group(
1130 &self,
1131 encoder_id: id::CommandEncoderId,
1132 label: &str,
1133 ) -> Result<(), EncoderStateError> {
1134 profiling::scope!("CommandEncoder::push_debug_group");
1135 api_log!("CommandEncoder::push_debug_group {label}");
1136
1137 let hub = &self.hub;
1138
1139 let cmd_buf = hub.command_buffers.get(encoder_id.into_command_buffer_id());
1140 let mut cmd_buf_data = cmd_buf.data.lock();
1141 cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
1142 #[cfg(feature = "trace")]
1143 if let Some(ref mut list) = cmd_buf_data.commands {
1144 list.push(TraceCommand::PushDebugGroup(label.to_owned()));
1145 }
1146
1147 cmd_buf.device.check_is_valid()?;
1148
1149 let cmd_buf_raw = cmd_buf_data.encoder.open()?;
1150 if !cmd_buf
1151 .device
1152 .instance_flags
1153 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1154 {
1155 unsafe {
1156 cmd_buf_raw.begin_debug_marker(label);
1157 }
1158 }
1159
1160 Ok(())
1161 })
1162 }
1163
1164 pub fn command_encoder_insert_debug_marker(
1165 &self,
1166 encoder_id: id::CommandEncoderId,
1167 label: &str,
1168 ) -> Result<(), EncoderStateError> {
1169 profiling::scope!("CommandEncoder::insert_debug_marker");
1170 api_log!("CommandEncoder::insert_debug_marker {label}");
1171
1172 let hub = &self.hub;
1173
1174 let cmd_buf = hub.command_buffers.get(encoder_id.into_command_buffer_id());
1175 let mut cmd_buf_data = cmd_buf.data.lock();
1176 cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
1177 #[cfg(feature = "trace")]
1178 if let Some(ref mut list) = cmd_buf_data.commands {
1179 list.push(TraceCommand::InsertDebugMarker(label.to_owned()));
1180 }
1181
1182 cmd_buf.device.check_is_valid()?;
1183
1184 if !cmd_buf
1185 .device
1186 .instance_flags
1187 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1188 {
1189 let cmd_buf_raw = cmd_buf_data.encoder.open()?;
1190 unsafe {
1191 cmd_buf_raw.insert_debug_marker(label);
1192 }
1193 }
1194
1195 Ok(())
1196 })
1197 }
1198
1199 pub fn command_encoder_pop_debug_group(
1200 &self,
1201 encoder_id: id::CommandEncoderId,
1202 ) -> Result<(), EncoderStateError> {
1203 profiling::scope!("CommandEncoder::pop_debug_marker");
1204 api_log!("CommandEncoder::pop_debug_group");
1205
1206 let hub = &self.hub;
1207
1208 let cmd_buf = hub.command_buffers.get(encoder_id.into_command_buffer_id());
1209 let mut cmd_buf_data = cmd_buf.data.lock();
1210 cmd_buf_data.record_with(|cmd_buf_data| -> Result<(), CommandEncoderError> {
1211 #[cfg(feature = "trace")]
1212 if let Some(ref mut list) = cmd_buf_data.commands {
1213 list.push(TraceCommand::PopDebugGroup);
1214 }
1215
1216 cmd_buf.device.check_is_valid()?;
1217
1218 let cmd_buf_raw = cmd_buf_data.encoder.open()?;
1219 if !cmd_buf
1220 .device
1221 .instance_flags
1222 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1223 {
1224 unsafe {
1225 cmd_buf_raw.end_debug_marker();
1226 }
1227 }
1228
1229 Ok(())
1230 })
1231 }
1232
1233 fn validate_pass_timestamp_writes<E>(
1234 device: &Device,
1235 query_sets: &Storage<Fallible<QuerySet>>,
1236 timestamp_writes: &PassTimestampWrites,
1237 ) -> Result<ArcPassTimestampWrites, E>
1238 where
1239 E: From<TimestampWritesError>
1240 + From<QueryUseError>
1241 + From<DeviceError>
1242 + From<MissingFeatures>
1243 + From<InvalidResourceError>,
1244 {
1245 let &PassTimestampWrites {
1246 query_set,
1247 beginning_of_pass_write_index,
1248 end_of_pass_write_index,
1249 } = timestamp_writes;
1250
1251 device.require_features(wgt::Features::TIMESTAMP_QUERY)?;
1252
1253 let query_set = query_sets.get(query_set).get()?;
1254
1255 query_set.same_device(device)?;
1256
1257 for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
1258 .into_iter()
1259 .flatten()
1260 {
1261 query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
1262 }
1263
1264 if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
1265 if begin == end {
1266 return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());
1267 }
1268 }
1269
1270 if beginning_of_pass_write_index
1271 .or(end_of_pass_write_index)
1272 .is_none()
1273 {
1274 return Err(TimestampWritesError::IndicesMissing.into());
1275 }
1276
1277 Ok(ArcPassTimestampWrites {
1278 query_set,
1279 beginning_of_pass_write_index,
1280 end_of_pass_write_index,
1281 })
1282 }
1283}
1284
1285fn push_constant_clear<PushFn>(offset: u32, size_bytes: u32, mut push_fn: PushFn)
1286where
1287 PushFn: FnMut(u32, &[u32]),
1288{
1289 let mut count_words = 0_u32;
1290 let size_words = size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT;
1291 while count_words < size_words {
1292 let count_bytes = count_words * wgt::PUSH_CONSTANT_ALIGNMENT;
1293 let size_to_write_words =
1294 (size_words - count_words).min(PUSH_CONSTANT_CLEAR_ARRAY.len() as u32);
1295
1296 push_fn(
1297 offset + count_bytes,
1298 &PUSH_CONSTANT_CLEAR_ARRAY[0..size_to_write_words as usize],
1299 );
1300
1301 count_words += size_to_write_words;
1302 }
1303}
1304
1305#[derive(Debug, Copy, Clone)]
1306struct StateChange<T> {
1307 last_state: Option<T>,
1308}
1309
1310impl<T: Copy + PartialEq> StateChange<T> {
1311 fn new() -> Self {
1312 Self { last_state: None }
1313 }
1314 fn set_and_check_redundant(&mut self, new_state: T) -> bool {
1315 let already_set = self.last_state == Some(new_state);
1316 self.last_state = Some(new_state);
1317 already_set
1318 }
1319 fn reset(&mut self) {
1320 self.last_state = None;
1321 }
1322}
1323
1324impl<T: Copy + PartialEq> Default for StateChange<T> {
1325 fn default() -> Self {
1326 Self::new()
1327 }
1328}
1329
1330#[derive(Debug)]
1331struct BindGroupStateChange {
1332 last_states: [StateChange<Option<id::BindGroupId>>; hal::MAX_BIND_GROUPS],
1333}
1334
1335impl BindGroupStateChange {
1336 fn new() -> Self {
1337 Self {
1338 last_states: [StateChange::new(); hal::MAX_BIND_GROUPS],
1339 }
1340 }
1341
1342 fn set_and_check_redundant(
1343 &mut self,
1344 bind_group_id: Option<id::BindGroupId>,
1345 index: u32,
1346 dynamic_offsets: &mut Vec<u32>,
1347 offsets: &[wgt::DynamicOffset],
1348 ) -> bool {
1349 if offsets.is_empty() {
1351 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1354 if current_bind_group.set_and_check_redundant(bind_group_id) {
1356 return true;
1357 }
1358 }
1359 } else {
1360 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1364 current_bind_group.reset();
1365 }
1366 dynamic_offsets.extend_from_slice(offsets);
1367 }
1368 false
1369 }
1370 fn reset(&mut self) {
1371 self.last_states = [StateChange::new(); hal::MAX_BIND_GROUPS];
1372 }
1373}
1374
1375impl Default for BindGroupStateChange {
1376 fn default() -> Self {
1377 Self::new()
1378 }
1379}
1380
1381trait MapPassErr<T> {
1383 fn map_pass_err(self, scope: PassErrorScope) -> T;
1384}
1385
1386impl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>
1387where
1388 E: MapPassErr<F>,
1389{
1390 fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {
1391 self.map_err(|err| err.map_pass_err(scope))
1392 }
1393}
1394
1395impl MapPassErr<PassStateError> for EncoderStateError {
1396 fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {
1397 PassStateError { scope, inner: self }
1398 }
1399}
1400
1401#[derive(Clone, Copy, Debug)]
1402pub enum DrawKind {
1403 Draw,
1404 DrawIndirect,
1405 MultiDrawIndirect,
1406 MultiDrawIndirectCount,
1407}
1408
1409#[derive(Clone, Copy, Debug, Error)]
1419pub enum PassErrorScope {
1420 #[error("In a bundle parameter")]
1423 Bundle,
1424 #[error("In a pass parameter")]
1425 Pass,
1426 #[error("In a set_bind_group command")]
1427 SetBindGroup,
1428 #[error("In a set_pipeline command")]
1429 SetPipelineRender,
1430 #[error("In a set_pipeline command")]
1431 SetPipelineCompute,
1432 #[error("In a set_push_constant command")]
1433 SetPushConstant,
1434 #[error("In a set_vertex_buffer command")]
1435 SetVertexBuffer,
1436 #[error("In a set_index_buffer command")]
1437 SetIndexBuffer,
1438 #[error("In a set_blend_constant command")]
1439 SetBlendConstant,
1440 #[error("In a set_stencil_reference command")]
1441 SetStencilReference,
1442 #[error("In a set_viewport command")]
1443 SetViewport,
1444 #[error("In a set_scissor_rect command")]
1445 SetScissorRect,
1446 #[error("In a draw command, kind: {kind:?}")]
1447 Draw { kind: DrawKind, indexed: bool },
1448 #[error("In a write_timestamp command")]
1449 WriteTimestamp,
1450 #[error("In a begin_occlusion_query command")]
1451 BeginOcclusionQuery,
1452 #[error("In a end_occlusion_query command")]
1453 EndOcclusionQuery,
1454 #[error("In a begin_pipeline_statistics_query command")]
1455 BeginPipelineStatisticsQuery,
1456 #[error("In a end_pipeline_statistics_query command")]
1457 EndPipelineStatisticsQuery,
1458 #[error("In a execute_bundle command")]
1459 ExecuteBundle,
1460 #[error("In a dispatch command, indirect:{indirect}")]
1461 Dispatch { indirect: bool },
1462 #[error("In a push_debug_group command")]
1463 PushDebugGroup,
1464 #[error("In a pop_debug_group command")]
1465 PopDebugGroup,
1466 #[error("In a insert_debug_marker command")]
1467 InsertDebugMarker,
1468}
1469
1470#[derive(Clone, Debug, Error)]
1472#[error("{scope}")]
1473pub struct PassStateError {
1474 pub scope: PassErrorScope,
1475 #[source]
1476 pub(super) inner: EncoderStateError,
1477}
1478
1479impl WebGpuError for PassStateError {
1480 fn webgpu_error_type(&self) -> ErrorType {
1481 let Self { scope: _, inner } = self;
1482 inner.webgpu_error_type()
1483 }
1484}