1mod allocator;
12mod bind;
13mod bundle;
14mod clear;
15mod compute;
16mod compute_command;
17mod draw;
18mod encoder;
19mod encoder_command;
20mod memory_init;
21mod pass;
22mod query;
23mod ray_tracing;
24mod render;
25mod render_command;
26mod timestamp_writes;
27mod transfer;
28mod transition_resources;
29
30use alloc::{borrow::ToOwned as _, boxed::Box, string::String, sync::Arc, vec::Vec};
31use core::convert::Infallible;
32use core::mem::{self, ManuallyDrop};
33use core::{ops, panic};
34
35#[cfg(feature = "serde")]
36pub(crate) use self::encoder_command::serde_object_reference_struct;
37#[cfg(any(feature = "trace", feature = "replay"))]
38#[doc(hidden)]
39pub use self::encoder_command::PointerReferences;
40pub use self::{
44 bundle::{
45 CreateRenderBundleError, ExecutionError, RenderBundle, RenderBundleDescriptor,
46 RenderBundleEncoder, RenderBundleEncoderDescriptor, RenderBundleError,
47 RenderBundleErrorInner,
48 },
49 clear::ClearError,
50 compute::{
51 ComputeBasePass, ComputePass, ComputePassDescriptor, ComputePassError,
52 ComputePassErrorInner, DispatchError,
53 },
54 compute_command::ArcComputeCommand,
55 draw::{DrawError, Rect, RenderCommandError},
56 encoder_command::{ArcCommand, ArcReferences, Command, ReferenceType},
57 query::{QueryError, QueryUseError, ResolveError, SimplifiedQueryType},
58 render::{
59 AttachmentError, AttachmentErrorLocation, ColorAttachmentError, ColorAttachments, LoadOp,
60 PassChannel, RenderBasePass, RenderPass, RenderPassColorAttachment,
61 RenderPassDepthStencilAttachment, RenderPassError, RenderPassErrorInner,
62 ResolvedPassChannel, ResolvedRenderPassDepthStencilAttachment,
63 ResolvedRenderPassDescriptor, StoreOp,
64 },
65 render_command::ArcRenderCommand,
66 transfer::{CopySide, TransferError},
67 transition_resources::TransitionResourcesError,
68};
69pub(crate) use self::{
70 clear::clear_texture,
71 encoder::EncodingState,
72 memory_init::CommandBufferTextureMemoryActions,
73 render::{get_dst_stride_of_indirect_args, get_src_stride_of_indirect_args, VertexState},
74 transfer::{
75 extract_texture_selector, validate_linear_texture_data, validate_texture_buffer_copy,
76 validate_texture_copy_dst_format, validate_texture_copy_range,
77 },
78};
79
80pub(crate) use allocator::CommandAllocator;
81
82pub use self::{compute_command::ComputeCommand, render_command::RenderCommand};
84
85pub use timestamp_writes::PassTimestampWrites;
86
87use crate::binding_model::{BindGroup, BindingError};
88use crate::device::queue::TempResource;
89use crate::device::{Device, DeviceError, MissingFeatures};
90use crate::lock::{rank, Mutex};
91use crate::snatch::SnatchGuard;
92
93use crate::init_tracker::BufferInitTrackerAction;
94use crate::ray_tracing::{AsAction, BuildAccelerationStructureError};
95use crate::resource::{
96 DestroyedResourceError, InvalidOrDestroyedResourceError, InvalidResourceError, Labeled,
97 ParentDevice as _, QuerySet,
98};
99use crate::track::{DeviceTracker, ResourceUsageCompatibilityError, Tracker, UsageScope};
100use crate::{api_log, resource_log, Label};
101use crate::{hal_label, LabelHelpers};
102
103use wgt::error::{ErrorType, WebGpuError};
104
105use thiserror::Error;
106
107pub(crate) struct EncoderErrorState {
108 error: CommandEncoderError,
109
110 #[cfg(feature = "trace")]
111 trace_commands: Option<Vec<Command<PointerReferences>>>,
112}
113
114fn make_error_state<E: Into<CommandEncoderError>>(error: E) -> CommandEncoderStatus {
124 CommandEncoderStatus::Error(EncoderErrorState {
125 error: error.into(),
126
127 #[cfg(feature = "trace")]
128 trace_commands: None,
129 })
130}
131
132pub(crate) enum CommandEncoderStatus {
138 Recording(CommandBufferMutable),
147
148 Locked(CommandBufferMutable),
157
158 Consumed,
159
160 Finished(CommandBufferMutable),
173
174 Error(EncoderErrorState),
179
180 Transitioning,
183}
184
185impl CommandEncoderStatus {
186 #[doc(hidden)]
187 fn replay(&mut self, commands: Vec<Command<ArcReferences>>) {
188 let Self::Recording(cmd_buf_data) = self else {
189 panic!("encoder should be in the recording state");
190 };
191 cmd_buf_data.commands.extend(commands);
192 }
193
194 fn push_with<F: FnOnce() -> Result<ArcCommand, E>, E: Clone + Into<CommandEncoderError>>(
210 &mut self,
211 f: F,
212 ) -> Result<(), EncoderStateError> {
213 match self {
214 Self::Recording(cmd_buf_data) => {
215 cmd_buf_data.encoder.api.set(EncodingApi::Wgpu);
216 match f() {
217 Ok(cmd) => cmd_buf_data.commands.push(cmd),
218 Err(err) => {
219 self.invalidate(err);
220 }
221 }
222 Ok(())
223 }
224 Self::Locked(_) => {
225 self.invalidate(EncoderStateError::Locked);
228 Ok(())
229 }
230 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
233 Self::Consumed => Err(EncoderStateError::Ended),
234 Self::Error(_) => Ok(()),
237 Self::Transitioning => unreachable!(),
238 }
239 }
240
241 fn with_buffer<
255 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
256 E: Clone + Into<CommandEncoderError>,
257 >(
258 &mut self,
259 api: EncodingApi,
260 f: F,
261 ) -> Result<(), EncoderStateError> {
262 match self {
263 Self::Recording(inner) => {
264 inner.encoder.api.set(api);
265 RecordingGuard { inner: self }.record(f);
266 Ok(())
267 }
268 Self::Locked(_) => {
269 self.invalidate(EncoderStateError::Locked);
272 Ok(())
273 }
274 Self::Finished(_) => Err(self.invalidate(EncoderStateError::Ended)),
277 Self::Consumed => Err(EncoderStateError::Ended),
278 Self::Error(_) => Ok(()),
281 Self::Transitioning => unreachable!(),
282 }
283 }
284
285 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
293 &mut self,
294 f: F,
295 ) -> T {
296 match self {
297 Self::Recording(inner) => {
298 inner.encoder.api.set(EncodingApi::Raw);
299 RecordingGuard { inner: self }.record_as_hal_mut(f)
300 }
301 Self::Locked(_) => {
302 self.invalidate(EncoderStateError::Locked);
303 f(None)
304 }
305 Self::Finished(_) => {
306 self.invalidate(EncoderStateError::Ended);
307 f(None)
308 }
309 Self::Consumed => f(None),
310 Self::Error(_) => f(None),
311 Self::Transitioning => unreachable!(),
312 }
313 }
314
315 fn lock_encoder(&mut self) -> Result<(), EncoderStateError> {
321 match mem::replace(self, Self::Transitioning) {
322 Self::Recording(inner) => {
323 *self = Self::Locked(inner);
324 Ok(())
325 }
326 st @ Self::Finished(_) => {
327 *self = st;
331 Err(EncoderStateError::Ended)
332 }
333 Self::Locked(_) => Err(self.invalidate(EncoderStateError::Locked)),
334 st @ Self::Consumed => {
335 *self = st;
336 Err(EncoderStateError::Ended)
337 }
338 st @ Self::Error(_) => {
339 *self = st;
340 Err(EncoderStateError::Invalid)
341 }
342 Self::Transitioning => unreachable!(),
343 }
344 }
345
346 fn unlock_encoder(&mut self) -> Result<(), EncoderStateError> {
356 match mem::replace(self, Self::Transitioning) {
357 Self::Locked(inner) => {
358 *self = Self::Recording(inner);
359 Ok(())
360 }
361 st @ Self::Finished(_) => {
362 *self = st;
363 Err(EncoderStateError::Ended)
364 }
365 Self::Recording(_) => {
366 *self = make_error_state(EncoderStateError::Unlocked);
367 Err(EncoderStateError::Unlocked)
368 }
369 st @ Self::Consumed => {
370 *self = st;
371 Err(EncoderStateError::Ended)
372 }
373 st @ Self::Error(_) => {
374 *self = st;
377 Ok(())
378 }
379 Self::Transitioning => unreachable!(),
380 }
381 }
382
383 fn finish(&mut self) -> Self {
384 match mem::replace(self, Self::Consumed) {
387 Self::Recording(inner) => {
388 if inner.encoder.api != EncodingApi::Raw {
391 assert!(!inner.encoder.is_open);
392 }
393 Self::Finished(inner)
394 }
395 Self::Consumed | Self::Finished(_) => make_error_state(EncoderStateError::Ended),
396 Self::Locked(_) => make_error_state(EncoderStateError::Locked),
397 st @ Self::Error(_) => st,
398 Self::Transitioning => unreachable!(),
399 }
400 }
401
402 fn invalidate<E: Clone + Into<CommandEncoderError>>(&mut self, err: E) -> E {
410 #[cfg(feature = "trace")]
411 let trace_commands = match self {
412 Self::Recording(cmd_buf_data) => Some(
413 mem::take(&mut cmd_buf_data.commands)
414 .into_iter()
415 .map(crate::device::trace::IntoTrace::into_trace)
416 .collect(),
417 ),
418 _ => None,
419 };
420
421 let enc_err = err.clone().into();
422 api_log!("Invalidating command encoder: {enc_err:?}");
423 *self = Self::Error(EncoderErrorState {
424 error: enc_err,
425 #[cfg(feature = "trace")]
426 trace_commands,
427 });
428 err
429 }
430}
431
432pub(crate) struct RecordingGuard<'a> {
445 inner: &'a mut CommandEncoderStatus,
446}
447
448impl<'a> RecordingGuard<'a> {
449 pub(crate) fn mark_successful(self) {
450 mem::forget(self)
451 }
452
453 fn record<
454 F: FnOnce(&mut CommandBufferMutable) -> Result<(), E>,
455 E: Clone + Into<CommandEncoderError>,
456 >(
457 mut self,
458 f: F,
459 ) {
460 match f(&mut self) {
461 Ok(()) => self.mark_successful(),
462 Err(err) => {
463 self.inner.invalidate(err);
464 }
465 }
466 }
467
468 pub(crate) fn record_as_hal_mut<T, F: FnOnce(Option<&mut CommandBufferMutable>) -> T>(
471 mut self,
472 f: F,
473 ) -> T {
474 let res = f(Some(&mut self));
475 self.mark_successful();
476 res
477 }
478}
479
480impl<'a> Drop for RecordingGuard<'a> {
481 fn drop(&mut self) {
482 if matches!(*self.inner, CommandEncoderStatus::Error(_)) {
483 return;
485 }
486 self.inner.invalidate(EncoderStateError::Invalid);
487 }
488}
489
490impl<'a> ops::Deref for RecordingGuard<'a> {
491 type Target = CommandBufferMutable;
492
493 fn deref(&self) -> &Self::Target {
494 match &*self.inner {
495 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
496 _ => unreachable!(),
497 }
498 }
499}
500
501impl<'a> ops::DerefMut for RecordingGuard<'a> {
502 fn deref_mut(&mut self) -> &mut Self::Target {
503 match self.inner {
504 CommandEncoderStatus::Recording(command_buffer_mutable) => command_buffer_mutable,
505 _ => unreachable!(),
506 }
507 }
508}
509
510pub struct CommandEncoder {
511 pub(crate) device: Arc<Device>,
512
513 pub(crate) label: String,
514
515 pub(crate) data: Mutex<CommandEncoderStatus>,
517}
518
519crate::impl_resource_type!(CommandEncoder);
520crate::impl_labeled!(CommandEncoder);
521crate::impl_parent_device!(CommandEncoder);
522crate::impl_storage_item!(CommandEncoder);
523
524impl Drop for CommandEncoder {
525 #[allow(trivial_casts)]
526 fn drop(&mut self) {
527 profiling::scope!("CommandEncoder::drop");
528 api_log!("CommandEncoder::drop {:?}", self as *const _);
529 resource_log!("Drop {}", self.error_ident());
530 }
531}
532
533#[derive(Copy, Clone, Debug, Eq, PartialEq)]
537pub enum EncodingApi {
538 Wgpu,
540
541 Raw,
543
544 Undecided,
546
547 InternalUse,
549}
550
551impl EncodingApi {
552 pub(crate) fn set(&mut self, api: EncodingApi) {
553 match *self {
554 EncodingApi::Undecided => {
555 *self = api;
556 }
557 self_api if self_api != api => {
558 panic!("Mixing the wgpu encoding API with the raw encoding API is not permitted");
559 }
560 _ => {}
561 }
562 }
563}
564
565pub(crate) struct InnerCommandEncoder {
581 pub(crate) raw: ManuallyDrop<Box<dyn hal::DynCommandEncoder>>,
589
590 pub(crate) list: Vec<Box<dyn hal::DynCommandBuffer>>,
602
603 pub(crate) device: Arc<Device>,
604
605 pub(crate) is_open: bool,
612
613 pub(crate) api: EncodingApi,
619
620 pub(crate) label: String,
621}
622
623impl InnerCommandEncoder {
624 fn close_and_swap(&mut self) -> Result<(), DeviceError> {
656 self.close_and_insert_at(self.list.len() - 1)
657 }
658
659 pub(crate) fn close_and_push_front(&mut self) -> Result<(), DeviceError> {
676 self.close_and_insert_at(0)
677 }
678
679 pub(crate) fn close_and_insert_at(&mut self, index: usize) -> Result<(), DeviceError> {
696 assert!(self.is_open);
697 self.is_open = false;
698
699 let cmd_buf =
700 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
701 self.list.insert(index, cmd_buf);
702
703 Ok(())
704 }
705
706 pub(crate) fn close(&mut self) -> Result<(), DeviceError> {
717 assert!(self.is_open);
718 self.is_open = false;
719
720 let cmd_buf =
721 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
722 self.list.push(cmd_buf);
723
724 Ok(())
725 }
726
727 fn close_if_open(&mut self) -> Result<(), DeviceError> {
738 if self.is_open {
739 self.is_open = false;
740 let cmd_buf =
741 unsafe { self.raw.end_encoding() }.map_err(|e| self.device.handle_hal_error(e))?;
742 self.list.push(cmd_buf);
743 }
744
745 Ok(())
746 }
747
748 fn open_if_closed(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
754 if !self.is_open {
755 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
756 unsafe { self.raw.begin_encoding(hal_label) }
757 .map_err(|e| self.device.handle_hal_error(e))?;
758 self.is_open = true;
759 }
760
761 Ok(self.raw.as_mut())
762 }
763
764 pub(crate) fn open(&mut self) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
768 if !self.is_open {
769 let hal_label = hal_label(Some(self.label.as_str()), self.device.instance_flags);
770 unsafe { self.raw.begin_encoding(hal_label) }
771 .map_err(|e| self.device.handle_hal_error(e))?;
772 self.is_open = true;
773 }
774
775 Ok(self.raw.as_mut())
776 }
777
778 pub(crate) fn open_pass(
787 &mut self,
788 label: Option<&str>,
789 ) -> Result<&mut dyn hal::DynCommandEncoder, DeviceError> {
790 assert!(!self.is_open);
791
792 let hal_label = hal_label(label, self.device.instance_flags);
793 unsafe { self.raw.begin_encoding(hal_label) }
794 .map_err(|e| self.device.handle_hal_error(e))?;
795 self.is_open = true;
796
797 Ok(self.raw.as_mut())
798 }
799}
800
801impl Drop for InnerCommandEncoder {
802 fn drop(&mut self) {
803 if self.is_open {
804 unsafe { self.raw.discard_encoding() };
805 }
806 unsafe {
807 self.raw.reset_all(mem::take(&mut self.list));
808 }
809 let raw = unsafe { ManuallyDrop::take(&mut self.raw) };
811 self.device.command_allocator.release_encoder(raw);
812 }
813}
814
815pub(crate) struct BakedCommands {
818 pub(crate) encoder: InnerCommandEncoder,
819 pub(crate) trackers: Tracker,
820 pub(crate) temp_resources: Vec<TempResource>,
821 pub(crate) indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
822 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
823 texture_memory_actions: CommandBufferTextureMemoryActions,
824 pub(crate) query_set_writes: query::QuerySetWrites,
825 pub(crate) deferred_query_set_resolves: Vec<query::DeferredQuerySetResolve>,
826}
827
828pub struct CommandBufferMutable {
830 pub(crate) encoder: InnerCommandEncoder,
835
836 pub(crate) trackers: Tracker,
838
839 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
846 texture_memory_actions: CommandBufferTextureMemoryActions,
847
848 as_actions: Vec<AsAction>,
849 temp_resources: Vec<TempResource>,
850
851 indirect_draw_validation_resources: crate::indirect_validation::DrawResources,
852
853 pub(crate) commands: Vec<Command<ArcReferences>>,
854
855 #[cfg(feature = "trace")]
858 pub(crate) trace_commands: Option<Vec<Command<PointerReferences>>>,
859
860 pub(crate) query_set_writes: query::QuerySetWrites,
862 pub(crate) deferred_query_set_resolves: Vec<query::DeferredQuerySetResolve>,
864}
865
866impl CommandBufferMutable {
867 pub(crate) fn into_baked_commands(self) -> BakedCommands {
868 BakedCommands {
869 encoder: self.encoder,
870 trackers: self.trackers,
871 temp_resources: self.temp_resources,
872 indirect_draw_validation_resources: self.indirect_draw_validation_resources,
873 buffer_memory_init_actions: self.buffer_memory_init_actions,
874 texture_memory_actions: self.texture_memory_actions,
875 query_set_writes: self.query_set_writes,
876 deferred_query_set_resolves: self.deferred_query_set_resolves,
877 }
878 }
879}
880
881pub struct CommandBuffer {
887 pub(crate) device: Arc<Device>,
888 label: String,
890
891 pub(crate) data: Mutex<CommandEncoderStatus>,
893}
894
895impl Drop for CommandBuffer {
896 #[allow(trivial_casts)]
897 fn drop(&mut self) {
898 profiling::scope!("CommandBuffer::drop");
899 api_log!("CommandBuffer::drop {:?}", self as *const _);
900 resource_log!("Drop {}", self.error_ident());
901 }
902}
903
904impl CommandEncoder {
905 pub(crate) fn new(
906 encoder: Box<dyn hal::DynCommandEncoder>,
907 device: &Arc<Device>,
908 label: &Label,
909 ) -> Self {
910 CommandEncoder {
911 device: device.clone(),
912 label: label.to_string(),
913 data: Mutex::new(
914 rank::COMMAND_BUFFER_DATA,
915 CommandEncoderStatus::Recording(CommandBufferMutable {
916 encoder: InnerCommandEncoder {
917 raw: ManuallyDrop::new(encoder),
918 list: Vec::new(),
919 device: device.clone(),
920 is_open: false,
921 api: EncodingApi::Undecided,
922 label: label.to_string(),
923 },
924 trackers: Tracker::new(
925 device.ordered_buffer_usages,
926 device.ordered_texture_usages,
927 ),
928 buffer_memory_init_actions: Default::default(),
929 texture_memory_actions: Default::default(),
930 as_actions: Default::default(),
931 temp_resources: Default::default(),
932 indirect_draw_validation_resources:
933 crate::indirect_validation::DrawResources::new(device.clone()),
934 commands: Vec::new(),
935 query_set_writes: Default::default(),
936 deferred_query_set_resolves: Default::default(),
937 #[cfg(feature = "trace")]
938 trace_commands: if device.trace.lock().is_some() {
939 Some(Vec::new())
940 } else {
941 None
942 },
943 }),
944 ),
945 }
946 }
947
948 pub(crate) fn new_invalid(
949 device: &Arc<Device>,
950 label: &Label,
951 err: CommandEncoderError,
952 ) -> Arc<Self> {
953 Arc::new(CommandEncoder {
954 device: device.clone(),
955 label: label.to_string(),
956 data: Mutex::new(rank::COMMAND_BUFFER_DATA, make_error_state(err)),
957 })
958 }
959
960 pub(crate) fn validate_pass_timestamp_writes<E>(
961 device: &Device,
962 timestamp_writes: &PassTimestampWrites<Arc<QuerySet>>,
963 ) -> Result<PassTimestampWrites, E>
964 where
965 E: From<TimestampWritesError>
966 + From<QueryUseError>
967 + From<DeviceError>
968 + From<MissingFeatures>
969 + From<InvalidResourceError>,
970 {
971 let &PassTimestampWrites {
972 ref query_set,
973 beginning_of_pass_write_index,
974 end_of_pass_write_index,
975 } = timestamp_writes;
976
977 device.require_features(wgt::Features::TIMESTAMP_QUERY)?;
978
979 query_set.check_is_valid()?;
980 query_set.same_device(device)?;
981
982 for idx in [beginning_of_pass_write_index, end_of_pass_write_index]
983 .into_iter()
984 .flatten()
985 {
986 query_set.validate_query(SimplifiedQueryType::Timestamp, idx, None)?;
987 }
988
989 if let Some((begin, end)) = beginning_of_pass_write_index.zip(end_of_pass_write_index) {
990 if begin == end {
991 return Err(TimestampWritesError::IndicesEqual { idx: begin }.into());
992 }
993 }
994
995 if beginning_of_pass_write_index
996 .or(end_of_pass_write_index)
997 .is_none()
998 {
999 return Err(TimestampWritesError::IndicesMissing.into());
1000 }
1001
1002 Ok(PassTimestampWrites {
1003 query_set: query_set.clone(),
1004 beginning_of_pass_write_index,
1005 end_of_pass_write_index,
1006 })
1007 }
1008
1009 pub(crate) fn insert_barriers_from_tracker(
1010 raw: &mut dyn hal::DynCommandEncoder,
1011 base: &mut Tracker,
1012 head: &Tracker,
1013 snatch_guard: &SnatchGuard,
1014 ) {
1015 profiling::scope!("insert_barriers");
1016
1017 base.buffers.set_from_tracker(&head.buffers);
1018 base.textures.set_from_tracker(&head.textures);
1019
1020 Self::drain_barriers(raw, base, snatch_guard);
1021 }
1022
1023 pub(crate) fn insert_barriers_from_scope(
1024 raw: &mut dyn hal::DynCommandEncoder,
1025 base: &mut Tracker,
1026 head: &UsageScope,
1027 snatch_guard: &SnatchGuard,
1028 ) {
1029 profiling::scope!("insert_barriers");
1030
1031 base.buffers.set_from_usage_scope(&head.buffers);
1032 base.textures.set_from_usage_scope(&head.textures);
1033
1034 Self::drain_barriers(raw, base, snatch_guard);
1035 }
1036
1037 pub(crate) fn drain_barriers(
1038 raw: &mut dyn hal::DynCommandEncoder,
1039 base: &mut Tracker,
1040 snatch_guard: &SnatchGuard,
1041 ) {
1042 profiling::scope!("drain_barriers");
1043
1044 let buffer_barriers = base
1045 .buffers
1046 .drain_transitions(snatch_guard)
1047 .collect::<Vec<_>>();
1048 let (transitions, textures) = base.textures.drain_transitions(snatch_guard);
1049 let texture_barriers = transitions
1050 .into_iter()
1051 .enumerate()
1052 .map(|(i, p)| p.into_hal(textures[i].unwrap().raw()))
1053 .collect::<Vec<_>>();
1054
1055 unsafe {
1056 raw.transition_buffers(&buffer_barriers);
1057 raw.transition_textures(&texture_barriers);
1058 }
1059 }
1060
1061 pub(crate) fn insert_barriers_from_device_tracker(
1062 raw: &mut dyn hal::DynCommandEncoder,
1063 base: &mut DeviceTracker,
1064 head: &Tracker,
1065 snatch_guard: &SnatchGuard,
1066 ) {
1067 profiling::scope!("insert_barriers_from_device_tracker");
1068
1069 let buffer_barriers = base
1070 .buffers
1071 .set_from_tracker_and_drain_transitions(&head.buffers, snatch_guard)
1072 .collect::<Vec<_>>();
1073
1074 let texture_barriers = base
1075 .textures
1076 .set_from_tracker_and_drain_transitions(&head.textures, snatch_guard)
1077 .collect::<Vec<_>>();
1078
1079 unsafe {
1080 raw.transition_buffers(&buffer_barriers);
1081 raw.transition_textures(&texture_barriers);
1082 }
1083 }
1084
1085 fn encode_commands(
1086 device: &Arc<Device>,
1087 cmd_buf_data: &mut CommandBufferMutable,
1088 ) -> Result<(), CommandEncoderError> {
1089 device.check_is_valid()?;
1090 let snatch_guard = device.snatchable_lock.read();
1091 let mut debug_scope_depth = 0;
1092
1093 if cmd_buf_data.encoder.api == EncodingApi::Raw {
1094 assert!(cmd_buf_data.commands.is_empty());
1097 }
1098
1099 let commands = mem::take(&mut cmd_buf_data.commands);
1100
1101 #[cfg(feature = "trace")]
1102 if device.trace.lock().is_some() {
1103 cmd_buf_data.trace_commands = Some(
1104 commands
1105 .iter()
1106 .map(crate::device::trace::IntoTrace::to_trace)
1107 .collect(),
1108 );
1109 }
1110
1111 for command in commands {
1112 if matches!(
1113 command,
1114 ArcCommand::RunRenderPass { .. }
1115 | ArcCommand::RunComputePass { .. }
1116 | ArcCommand::ResolveQuerySet { .. }
1117 ) {
1118 let mut state = EncodingState {
1124 device,
1125 raw_encoder: &mut cmd_buf_data.encoder,
1126 tracker: &mut cmd_buf_data.trackers,
1127 buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1128 texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1129 as_actions: &mut cmd_buf_data.as_actions,
1130 temp_resources: &mut cmd_buf_data.temp_resources,
1131 indirect_draw_validation_resources: &mut cmd_buf_data
1132 .indirect_draw_validation_resources,
1133 snatch_guard: &snatch_guard,
1134 debug_scope_depth: &mut debug_scope_depth,
1135 query_set_writes: &mut cmd_buf_data.query_set_writes,
1136 deferred_query_set_resolves: &mut cmd_buf_data.deferred_query_set_resolves,
1137 };
1138
1139 match command {
1140 ArcCommand::RunRenderPass {
1141 pass,
1142 color_attachments,
1143 depth_stencil_attachment,
1144 timestamp_writes,
1145 occlusion_query_set,
1146 multiview_mask,
1147 } => {
1148 api_log!(
1149 "Begin encoding render pass with '{}' label",
1150 pass.label.as_deref().unwrap_or("")
1151 );
1152 let res = render::encode_render_pass(
1153 &mut state,
1154 pass,
1155 color_attachments,
1156 depth_stencil_attachment,
1157 timestamp_writes,
1158 occlusion_query_set,
1159 multiview_mask,
1160 );
1161 match res.as_ref() {
1162 Err(err) => {
1163 api_log!("Finished encoding render pass ({err:?})")
1164 }
1165 Ok(_) => {
1166 api_log!("Finished encoding render pass (success)")
1167 }
1168 }
1169 res?;
1170 }
1171 ArcCommand::RunComputePass {
1172 pass,
1173 timestamp_writes,
1174 } => {
1175 api_log!(
1176 "Begin encoding compute pass with '{}' label",
1177 pass.label.as_deref().unwrap_or("")
1178 );
1179 let res = compute::encode_compute_pass(&mut state, pass, timestamp_writes);
1180 match res.as_ref() {
1181 Err(err) => {
1182 api_log!("Finished encoding compute pass ({err:?})")
1183 }
1184 Ok(_) => {
1185 api_log!("Finished encoding compute pass (success)")
1186 }
1187 }
1188 res?;
1189 }
1190 ArcCommand::ResolveQuerySet {
1191 query_set,
1192 start_query,
1193 query_count,
1194 destination,
1195 destination_offset,
1196 } => {
1197 query::resolve_query_set(
1198 &mut state,
1199 query_set,
1200 start_query,
1201 query_count,
1202 destination,
1203 destination_offset,
1204 )?;
1205 }
1206 _ => unreachable!(),
1207 }
1208 } else {
1209 let raw_encoder = cmd_buf_data.encoder.open_if_closed()?;
1215 let mut state = EncodingState {
1216 device,
1217 raw_encoder,
1218 tracker: &mut cmd_buf_data.trackers,
1219 buffer_memory_init_actions: &mut cmd_buf_data.buffer_memory_init_actions,
1220 texture_memory_actions: &mut cmd_buf_data.texture_memory_actions,
1221 as_actions: &mut cmd_buf_data.as_actions,
1222 temp_resources: &mut cmd_buf_data.temp_resources,
1223 indirect_draw_validation_resources: &mut cmd_buf_data
1224 .indirect_draw_validation_resources,
1225 snatch_guard: &snatch_guard,
1226 debug_scope_depth: &mut debug_scope_depth,
1227 query_set_writes: &mut cmd_buf_data.query_set_writes,
1228 deferred_query_set_resolves: &mut cmd_buf_data.deferred_query_set_resolves,
1229 };
1230 match command {
1231 ArcCommand::CopyBufferToBuffer {
1232 src,
1233 src_offset,
1234 dst,
1235 dst_offset,
1236 size,
1237 } => {
1238 transfer::copy_buffer_to_buffer(
1239 &mut state, &src, src_offset, &dst, dst_offset, size,
1240 )?;
1241 }
1242 ArcCommand::CopyBufferToTexture { src, dst, size } => {
1243 transfer::copy_buffer_to_texture(&mut state, &src, &dst, &size)?;
1244 }
1245 ArcCommand::CopyTextureToBuffer { src, dst, size } => {
1246 transfer::copy_texture_to_buffer(&mut state, &src, &dst, &size)?;
1247 }
1248 ArcCommand::CopyTextureToTexture { src, dst, size } => {
1249 transfer::copy_texture_to_texture(&mut state, &src, &dst, &size)?;
1250 }
1251 ArcCommand::ClearBuffer { dst, offset, size } => {
1252 clear::clear_buffer(&mut state, dst, offset, size)?;
1253 }
1254 ArcCommand::ClearTexture {
1255 dst,
1256 subresource_range,
1257 } => {
1258 clear::clear_texture_cmd(&mut state, dst, &subresource_range)?;
1259 }
1260 ArcCommand::WriteTimestamp {
1261 query_set,
1262 query_index,
1263 } => {
1264 query::write_timestamp(&mut state, query_set, query_index)?;
1265 }
1266 ArcCommand::PushDebugGroup(label) => {
1267 push_debug_group(&mut state, &label)?;
1268 }
1269 ArcCommand::PopDebugGroup => {
1270 pop_debug_group(&mut state)?;
1271 }
1272 ArcCommand::InsertDebugMarker(label) => {
1273 insert_debug_marker(&mut state, &label)?;
1274 }
1275 ArcCommand::BuildAccelerationStructures { blas, tlas } => {
1276 ray_tracing::build_acceleration_structures(&mut state, blas, tlas)?;
1277 }
1278 ArcCommand::TransitionResources {
1279 buffer_transitions,
1280 texture_transitions,
1281 } => {
1282 transition_resources::transition_resources(
1283 &mut state,
1284 buffer_transitions,
1285 texture_transitions,
1286 )?;
1287 }
1288 ArcCommand::RunComputePass { .. }
1289 | ArcCommand::RunRenderPass { .. }
1290 | ArcCommand::ResolveQuerySet { .. } => {
1291 unreachable!()
1292 }
1293 }
1294 }
1295 }
1296
1297 if debug_scope_depth > 0 {
1298 Err(CommandEncoderError::DebugGroupError(
1299 DebugGroupError::MissingPop,
1300 ))?;
1301 }
1302
1303 cmd_buf_data.encoder.close_if_open()?;
1305
1306 Ok(())
1310 }
1311
1312 fn finish_inner(
1315 self: &Arc<Self>,
1316 desc: &wgt::CommandBufferDescriptor<Label>,
1317 ) -> (Arc<CommandBuffer>, Option<CommandEncoderError>) {
1318 profiling::scope!("CommandEncoder::finish");
1319
1320 let status = self.data.lock().finish();
1321
1322 let res = match status {
1323 CommandEncoderStatus::Finished(mut cmd_buf_data) => {
1324 match Self::encode_commands(&self.device, &mut cmd_buf_data) {
1325 Ok(()) => Ok(cmd_buf_data),
1326 Err(error) => Err(EncoderErrorState {
1327 error,
1328 #[cfg(feature = "trace")]
1329 trace_commands: mem::take(&mut cmd_buf_data.trace_commands),
1330 }),
1331 }
1332 }
1333 CommandEncoderStatus::Error(error_state) => Err(error_state),
1334 _ => unreachable!(),
1335 };
1336
1337 let (data, error) = match res {
1338 Err(EncoderErrorState {
1339 error,
1340 #[cfg(feature = "trace")]
1341 trace_commands,
1342 }) => {
1343 #[cfg(feature = "trace")]
1347 if let Some(trace) = self.device.trace.lock().as_mut() {
1348 use alloc::string::ToString;
1349
1350 trace.add(crate::device::trace::Action::FailedCommands {
1351 commands: trace_commands,
1352 failed_at_submit: None,
1353 error: error.to_string(),
1354 });
1355 }
1356
1357 if error.is_destroyed_error() {
1358 (make_error_state(error), None)
1361 } else {
1362 (make_error_state(error.clone()), Some(error))
1363 }
1364 }
1365
1366 Ok(data) => (CommandEncoderStatus::Finished(data), None),
1367 };
1368
1369 let cmd_buf = Arc::new(CommandBuffer {
1370 device: self.device.clone(),
1371 label: desc.label.to_string(),
1372 data: Mutex::new(rank::COMMAND_BUFFER_DATA, data),
1373 });
1374
1375 (cmd_buf, error)
1376 }
1377
1378 pub fn finish(
1379 self: &Arc<Self>,
1380 desc: &wgt::CommandBufferDescriptor<Label>,
1381 ) -> Arc<CommandBuffer> {
1382 let (cmd_buf, error) = self.finish_inner(desc);
1383 if let Some(error) = error {
1384 self.device
1385 .handle_error(error, Some(self.label()), "CommandEncoder::finish");
1386 }
1387 cmd_buf
1388 }
1389}
1390
1391impl CommandBuffer {
1392 #[doc(hidden)]
1398 pub fn from_trace(device: &Arc<Device>, commands: Vec<Command<ArcReferences>>) -> Arc<Self> {
1399 let encoder = device.create_command_encoder(&wgt::CommandEncoderDescriptor { label: None });
1400 let mut cmd_enc_status = encoder.data.lock();
1401 cmd_enc_status.replay(commands);
1402 drop(cmd_enc_status);
1403
1404 encoder.finish(&wgt::CommandBufferDescriptor { label: None })
1405 }
1406
1407 pub fn take_finished(&self) -> Result<CommandBufferMutable, CommandEncoderError> {
1408 use CommandEncoderStatus as St;
1409 match mem::replace(
1410 &mut *self.data.lock(),
1411 make_error_state(EncoderStateError::Submitted),
1412 ) {
1413 St::Finished(command_buffer_mutable) => Ok(command_buffer_mutable),
1414 St::Error(EncoderErrorState {
1415 #[cfg(feature = "trace")]
1416 trace_commands: _,
1417 error,
1418 }) => Err(error),
1419 St::Recording(_) | St::Locked(_) | St::Consumed | St::Transitioning => unreachable!(),
1420 }
1421 }
1422}
1423
1424crate::impl_resource_type!(CommandBuffer);
1425crate::impl_labeled!(CommandBuffer);
1426crate::impl_parent_device!(CommandBuffer);
1427crate::impl_storage_item!(CommandBuffer);
1428
1429#[doc(hidden)]
1441#[derive(Debug, Clone)]
1442#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1443pub struct BasePass<C, E> {
1444 pub label: Option<String>,
1445
1446 #[cfg_attr(feature = "serde", serde(skip, default = "Option::default"))]
1454 pub error: Option<E>,
1455
1456 pub commands: Vec<C>,
1462
1463 pub dynamic_offsets: Vec<wgt::DynamicOffset>,
1468
1469 pub string_data: Vec<u8>,
1474}
1475
1476impl<C: Clone, E: Clone> BasePass<C, E> {
1477 fn new(label: &Label) -> Self {
1478 Self {
1479 label: label.as_deref().map(str::to_owned),
1480 error: None,
1481 commands: Vec::new(),
1482 dynamic_offsets: Vec::new(),
1483 string_data: Vec::new(),
1484 }
1485 }
1486
1487 fn new_invalid(label: &Label, err: E) -> Self {
1488 Self {
1489 label: label.as_deref().map(str::to_owned),
1490 error: Some(err),
1491 commands: Vec::new(),
1492 dynamic_offsets: Vec::new(),
1493 string_data: Vec::new(),
1494 }
1495 }
1496
1497 fn take(&mut self) -> Result<BasePass<C, Infallible>, E> {
1504 match self.error.as_ref() {
1505 Some(err) => Err(err.clone()),
1506 None => Ok(BasePass {
1507 label: self.label.clone(),
1508 error: None,
1509 commands: mem::take(&mut self.commands),
1510 dynamic_offsets: mem::take(&mut self.dynamic_offsets),
1511 string_data: mem::take(&mut self.string_data),
1512 }),
1513 }
1514 }
1515}
1516
1517macro_rules! pass_base {
1540 ($pass:expr, $scope:expr $(,)?) => {
1541 match (&$pass.parent, &$pass.base.error) {
1542 (&None, _) => return Err(EncoderStateError::Ended).map_pass_err($scope),
1544 (&Some(_), &Some(_)) => return Ok(()),
1546 (&Some(_), &None) => &mut $pass.base,
1548 }
1549 };
1550}
1551pub(crate) use pass_base;
1552
1553macro_rules! pass_try {
1565 ($base:expr, $scope:expr, $res:expr $(,)?) => {
1566 match $res.map_pass_err($scope) {
1567 Ok(val) => val,
1568 Err(err) => {
1569 $base.error.get_or_insert(err);
1570 return Ok(());
1571 }
1572 }
1573 };
1574}
1575pub(crate) use pass_try;
1576
1577#[derive(Clone, Debug, Error)]
1582#[non_exhaustive]
1583pub enum EncoderStateError {
1584 #[error("Encoder is invalid")]
1589 Invalid,
1590
1591 #[error("Encoding must not have ended")]
1594 Ended,
1595
1596 #[error("Encoder is locked by a previously created render/compute pass. Before recording any new commands, the pass must be ended.")]
1602 Locked,
1603
1604 #[error(
1607 "Encoder is not currently locked. A pass can only be ended while the encoder is locked."
1608 )]
1609 Unlocked,
1610
1611 #[error("This command buffer has already been submitted.")]
1616 Submitted,
1617}
1618
1619impl WebGpuError for EncoderStateError {
1620 fn webgpu_error_type(&self) -> ErrorType {
1621 match self {
1622 EncoderStateError::Invalid
1623 | EncoderStateError::Ended
1624 | EncoderStateError::Locked
1625 | EncoderStateError::Unlocked
1626 | EncoderStateError::Submitted => ErrorType::Validation,
1627 }
1628 }
1629}
1630
1631#[derive(Clone, Debug, Error)]
1632#[non_exhaustive]
1633pub enum CommandEncoderError {
1634 #[error(transparent)]
1635 State(#[from] EncoderStateError),
1636 #[error(transparent)]
1637 Device(#[from] DeviceError),
1638 #[error(transparent)]
1639 InvalidResource(#[from] InvalidResourceError),
1640 #[error(transparent)]
1641 DestroyedResource(#[from] DestroyedResourceError),
1642 #[error(transparent)]
1643 ResourceUsage(#[from] ResourceUsageCompatibilityError),
1644 #[error(transparent)]
1645 DebugGroupError(#[from] DebugGroupError),
1646 #[error(transparent)]
1647 MissingFeatures(#[from] MissingFeatures),
1648 #[error(transparent)]
1649 Transfer(#[from] TransferError),
1650 #[error(transparent)]
1651 Clear(#[from] ClearError),
1652 #[error(transparent)]
1653 Query(#[from] QueryError),
1654 #[error(transparent)]
1655 BuildAccelerationStructure(#[from] BuildAccelerationStructureError),
1656 #[error(transparent)]
1657 TransitionResources(#[from] TransitionResourcesError),
1658 #[error(transparent)]
1659 ComputePass(#[from] ComputePassError),
1660 #[error(transparent)]
1661 RenderPass(#[from] RenderPassError),
1662}
1663
1664impl From<InvalidOrDestroyedResourceError> for CommandEncoderError {
1665 fn from(err: InvalidOrDestroyedResourceError) -> Self {
1666 match err {
1667 InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
1668 InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
1669 }
1670 }
1671}
1672
1673impl CommandEncoderError {
1674 fn is_destroyed_error(&self) -> bool {
1675 matches!(
1676 self,
1677 Self::DestroyedResource(_)
1678 | Self::Clear(ClearError::DestroyedResource(_))
1679 | Self::Query(QueryError::DestroyedResource(_))
1680 | Self::ComputePass(ComputePassError {
1681 inner: ComputePassErrorInner::DestroyedResource(_),
1682 ..
1683 })
1684 ) || if let Self::RenderPass(pass_error) = self {
1685 matches!(
1686 pass_error.inner.as_ref(),
1687 RenderPassErrorInner::DestroyedResource(_)
1688 | RenderPassErrorInner::RenderCommand(RenderCommandError::DestroyedResource(_))
1689 | RenderPassErrorInner::RenderCommand(RenderCommandError::BindingError(
1690 BindingError::DestroyedResource(_),
1691 ))
1692 )
1693 } else {
1694 false
1695 }
1696 }
1697}
1698
1699impl WebGpuError for CommandEncoderError {
1700 fn webgpu_error_type(&self) -> ErrorType {
1701 match self {
1702 Self::Device(e) => e.webgpu_error_type(),
1703 Self::InvalidResource(e) => e.webgpu_error_type(),
1704 Self::DebugGroupError(e) => e.webgpu_error_type(),
1705 Self::MissingFeatures(e) => e.webgpu_error_type(),
1706 Self::State(e) => e.webgpu_error_type(),
1707 Self::DestroyedResource(e) => e.webgpu_error_type(),
1708 Self::Transfer(e) => e.webgpu_error_type(),
1709 Self::Clear(e) => e.webgpu_error_type(),
1710 Self::Query(e) => e.webgpu_error_type(),
1711 Self::BuildAccelerationStructure(e) => e.webgpu_error_type(),
1712 Self::TransitionResources(e) => e.webgpu_error_type(),
1713 Self::ResourceUsage(e) => e.webgpu_error_type(),
1714 Self::ComputePass(e) => e.webgpu_error_type(),
1715 Self::RenderPass(e) => e.webgpu_error_type(),
1716 }
1717 }
1718}
1719
1720#[derive(Clone, Debug, Error)]
1721#[non_exhaustive]
1722pub enum DebugGroupError {
1723 #[error("Cannot pop debug group, because number of pushed debug groups is zero")]
1724 InvalidPop,
1725 #[error("A debug group was not popped before the encoder was finished")]
1726 MissingPop,
1727}
1728
1729impl WebGpuError for DebugGroupError {
1730 fn webgpu_error_type(&self) -> ErrorType {
1731 match self {
1732 Self::InvalidPop | Self::MissingPop => ErrorType::Validation,
1733 }
1734 }
1735}
1736
1737#[derive(Clone, Debug, Error)]
1738#[non_exhaustive]
1739pub enum TimestampWritesError {
1740 #[error(
1741 "begin and end indices of pass timestamp writes are both set to {idx}, which is not allowed"
1742 )]
1743 IndicesEqual { idx: u32 },
1744 #[error("no begin or end indices were specified for pass timestamp writes, expected at least one to be set")]
1745 IndicesMissing,
1746}
1747
1748impl WebGpuError for TimestampWritesError {
1749 fn webgpu_error_type(&self) -> ErrorType {
1750 match self {
1751 Self::IndicesEqual { .. } | Self::IndicesMissing => ErrorType::Validation,
1752 }
1753 }
1754}
1755
1756impl CommandEncoder {
1757 fn push_debug_group_inner(self: &Arc<Self>, label: &str) -> Result<(), EncoderStateError> {
1758 profiling::scope!("CommandEncoder::push_debug_group");
1759 api_log!("CommandEncoder::push_debug_group {label}");
1760
1761 let mut cmd_buf_data = self.data.lock();
1762
1763 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1764 Ok(ArcCommand::PushDebugGroup(label.to_owned()))
1765 })
1766 }
1767
1768 pub fn push_debug_group(self: &Arc<Self>, label: &str) {
1769 if let Err(err) = self.push_debug_group_inner(label) {
1770 self.device
1771 .handle_error(err, Some(self.label()), "CommandEncoder::push_debug_group");
1772 }
1773 }
1774
1775 fn insert_debug_marker_inner(self: &Arc<Self>, label: &str) -> Result<(), EncoderStateError> {
1776 profiling::scope!("CommandEncoder::insert_debug_marker");
1777 api_log!("CommandEncoder::insert_debug_marker {label}");
1778
1779 let mut cmd_buf_data = self.data.lock();
1780
1781 cmd_buf_data.push_with(|| -> Result<_, CommandEncoderError> {
1782 Ok(ArcCommand::InsertDebugMarker(label.to_owned()))
1783 })
1784 }
1785
1786 pub fn insert_debug_marker(self: &Arc<Self>, label: &str) {
1787 if let Err(err) = self.insert_debug_marker_inner(label) {
1788 self.device.handle_error(
1789 err,
1790 Some(self.label()),
1791 "CommandEncoder::insert_debug_marker",
1792 );
1793 }
1794 }
1795
1796 fn pop_debug_group_inner(self: &Arc<Self>) -> Result<(), EncoderStateError> {
1797 profiling::scope!("CommandEncoder::pop_debug_marker");
1798 api_log!("CommandEncoder::pop_debug_group");
1799
1800 let mut cmd_buf_data = self.data.lock();
1801
1802 cmd_buf_data
1803 .push_with(|| -> Result<_, CommandEncoderError> { Ok(ArcCommand::PopDebugGroup) })
1804 }
1805
1806 pub fn pop_debug_group(self: &Arc<Self>) {
1807 if let Err(err) = self.pop_debug_group_inner() {
1808 self.device
1809 .handle_error(err, Some(self.label()), "CommandEncoder::pop_debug_group");
1810 }
1811 }
1812}
1813
1814pub(crate) fn push_debug_group(
1815 state: &mut EncodingState,
1816 label: &str,
1817) -> Result<(), CommandEncoderError> {
1818 *state.debug_scope_depth += 1;
1819
1820 if !state
1821 .device
1822 .instance_flags
1823 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1824 {
1825 unsafe { state.raw_encoder.begin_debug_marker(label) };
1826 }
1827
1828 Ok(())
1829}
1830
1831pub(crate) fn insert_debug_marker(
1832 state: &mut EncodingState,
1833 label: &str,
1834) -> Result<(), CommandEncoderError> {
1835 if !state
1836 .device
1837 .instance_flags
1838 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1839 {
1840 unsafe { state.raw_encoder.insert_debug_marker(label) };
1841 }
1842
1843 Ok(())
1844}
1845
1846pub(crate) fn pop_debug_group(state: &mut EncodingState) -> Result<(), CommandEncoderError> {
1847 if *state.debug_scope_depth == 0 {
1848 return Err(DebugGroupError::InvalidPop.into());
1849 }
1850 *state.debug_scope_depth -= 1;
1851
1852 if !state
1853 .device
1854 .instance_flags
1855 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS)
1856 {
1857 unsafe { state.raw_encoder.end_debug_marker() };
1858 }
1859
1860 Ok(())
1861}
1862
1863#[derive(Debug, Copy, Clone)]
1864struct StateChange<T> {
1865 last_state: Option<T>,
1866}
1867
1868impl<T: Clone + PartialEq_> StateChange<T> {
1869 const fn new() -> Self {
1870 Self { last_state: None }
1871 }
1872
1873 fn set_and_check_redundant(&mut self, new_state: &T) -> bool {
1874 let already_set = self.last_state.as_ref().is_some_and(|s| s.eq(new_state));
1875 if !already_set {
1876 self.last_state = Some(new_state.clone());
1877 }
1878 already_set
1879 }
1880
1881 fn reset(&mut self) {
1882 self.last_state = None;
1883 }
1884}
1885
1886impl<T: Clone + PartialEq_> Default for StateChange<T> {
1887 fn default() -> Self {
1888 Self::new()
1889 }
1890}
1891
1892trait PartialEq_ {
1893 fn eq(&self, other: &Self) -> bool;
1894}
1895
1896impl<T> PartialEq_ for Arc<T> {
1897 fn eq(&self, other: &Self) -> bool {
1898 Arc::ptr_eq(self, other)
1899 }
1900}
1901
1902impl<T: PartialEq_> PartialEq_ for Option<T> {
1903 fn eq(&self, other: &Self) -> bool {
1904 match (self, other) {
1905 (Some(a), Some(b)) => a.eq(b),
1906 (None, None) => true,
1907 _ => false,
1908 }
1909 }
1910}
1911
1912#[derive(Debug)]
1913struct BindGroupStateChange {
1914 last_states: [StateChange<Option<Arc<BindGroup>>>; hal::MAX_BIND_GROUPS],
1915}
1916
1917impl BindGroupStateChange {
1918 fn new() -> Self {
1919 Self {
1920 last_states: [const { StateChange::new() }; hal::MAX_BIND_GROUPS],
1921 }
1922 }
1923
1924 fn set_and_check_redundant(
1925 &mut self,
1926 bind_group: &Option<Arc<BindGroup>>,
1927 index: u32,
1928 dynamic_offsets: &mut Vec<u32>,
1929 offsets: &[wgt::DynamicOffset],
1930 ) -> bool {
1931 if offsets.is_empty() {
1933 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1936 if current_bind_group.set_and_check_redundant(bind_group) {
1938 return true;
1939 }
1940 }
1941 } else {
1942 if let Some(current_bind_group) = self.last_states.get_mut(index as usize) {
1946 current_bind_group.reset();
1947 }
1948 dynamic_offsets.extend_from_slice(offsets);
1949 }
1950 false
1951 }
1952 fn reset(&mut self) {
1953 self.last_states = [const { StateChange::new() }; hal::MAX_BIND_GROUPS];
1954 }
1955}
1956
1957impl Default for BindGroupStateChange {
1958 fn default() -> Self {
1959 Self::new()
1960 }
1961}
1962
1963trait MapPassErr<T> {
1965 fn map_pass_err(self, scope: PassErrorScope) -> T;
1966}
1967
1968impl<T, E, F> MapPassErr<Result<T, F>> for Result<T, E>
1969where
1970 E: MapPassErr<F>,
1971{
1972 fn map_pass_err(self, scope: PassErrorScope) -> Result<T, F> {
1973 self.map_err(|err| err.map_pass_err(scope))
1974 }
1975}
1976
1977impl MapPassErr<PassStateError> for EncoderStateError {
1978 fn map_pass_err(self, scope: PassErrorScope) -> PassStateError {
1979 PassStateError { scope, inner: self }
1980 }
1981}
1982
1983#[derive(Clone, Copy, Debug)]
1984pub enum DrawKind {
1985 Draw,
1986 DrawIndirect,
1987 MultiDrawIndirect,
1988 MultiDrawIndirectCount,
1989}
1990
1991#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
1993#[derive(Clone, Copy, Debug, PartialEq, Eq)]
1994pub enum DrawCommandFamily {
1995 Draw,
1996 DrawIndexed,
1997 DrawMeshTasks,
1998}
1999
2000#[derive(Clone, Copy, Debug, Error)]
2010pub enum PassErrorScope {
2011 #[error("In a bundle parameter")]
2014 Bundle,
2015 #[error("In a pass parameter")]
2016 Pass,
2017 #[error("In a set_bind_group command")]
2018 SetBindGroup,
2019 #[error("In a set_pipeline command")]
2020 SetPipelineRender,
2021 #[error("In a set_pipeline command")]
2022 SetPipelineCompute,
2023 #[error("In a set_immediates command")]
2024 SetImmediate,
2025 #[error("In a set_vertex_buffer command")]
2026 SetVertexBuffer,
2027 #[error("In a set_index_buffer command")]
2028 SetIndexBuffer,
2029 #[error("In a set_blend_constant command")]
2030 SetBlendConstant,
2031 #[error("In a set_stencil_reference command")]
2032 SetStencilReference,
2033 #[error("In a set_viewport command")]
2034 SetViewport,
2035 #[error("In a set_scissor_rect command")]
2036 SetScissorRect,
2037 #[error("In a draw command, kind: {kind:?}")]
2038 Draw {
2039 kind: DrawKind,
2040 family: DrawCommandFamily,
2041 },
2042 #[error("In a write_timestamp command")]
2043 WriteTimestamp,
2044 #[error("In a begin_occlusion_query command")]
2045 BeginOcclusionQuery,
2046 #[error("In a end_occlusion_query command")]
2047 EndOcclusionQuery,
2048 #[error("In a begin_pipeline_statistics_query command")]
2049 BeginPipelineStatisticsQuery,
2050 #[error("In a end_pipeline_statistics_query command")]
2051 EndPipelineStatisticsQuery,
2052 #[error("In a transition_resources command")]
2053 TransitionResources,
2054 #[error("In a execute_bundle command")]
2055 ExecuteBundle,
2056 #[error("In a dispatch command, indirect:{indirect}")]
2057 Dispatch { indirect: bool },
2058 #[error("In a push_debug_group command")]
2059 PushDebugGroup,
2060 #[error("In a pop_debug_group command")]
2061 PopDebugGroup,
2062 #[error("In a insert_debug_marker command")]
2063 InsertDebugMarker,
2064}
2065
2066#[derive(Clone, Debug, Error)]
2068#[error("{scope}")]
2069pub struct PassStateError {
2070 pub scope: PassErrorScope,
2071 #[source]
2072 pub(super) inner: EncoderStateError,
2073}
2074
2075impl WebGpuError for PassStateError {
2076 fn webgpu_error_type(&self) -> ErrorType {
2077 let Self { scope: _, inner } = self;
2078 inner.webgpu_error_type()
2079 }
2080}