1use parking_lot::Mutex;
2use thiserror::Error;
3use wgt::{
4 error::{ErrorType, WebGpuError},
5 BufferAddress, DynamicOffset,
6};
7
8use alloc::{borrow::Cow, boxed::Box, sync::Arc, vec::Vec};
9use core::{convert::Infallible, fmt, str};
10
11use crate::{
12 api_log,
13 binding_model::{BindError, BindGroup, ImmediateUploadError, LateMinBufferBindingSizeMismatch},
14 command::{
15 bind::{Binder, BinderError},
16 compute_command::ArcComputeCommand,
17 encoder::EncodingState,
18 memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState},
19 pass::{self, flush_bindings_helper, ImmediateState},
20 pass_base, pass_try,
21 query::{
22 end_pipeline_statistics_query, record_pass_timestamp_writes,
23 validate_and_begin_pipeline_statistics_query,
24 },
25 ArcCommand, ArcPassTimestampWrites, BasePass, BindGroupStateChange, CommandEncoder,
26 CommandEncoderError, DebugGroupError, EncoderStateError, InnerCommandEncoder, MapPassErr,
27 PassErrorScope, PassStateError, PassTimestampWrites, QueryUseError, StateChange,
28 TimestampWritesError, TransitionResourcesError,
29 },
30 device::{Device, DeviceError, MissingDownlevelFlags, MissingFeatures},
31 global::Global,
32 hal_label, id, impl_resource_type,
33 init_tracker::MemoryInitKind,
34 pipeline::ComputePipeline,
35 resource::{
36 Buffer, DestroyedResourceError, InvalidOrDestroyedResourceError, InvalidResourceError,
37 Labeled, MissingBufferUsageError, ParentDevice, QuerySet, RawResourceAccess, TextureView,
38 Trackable,
39 },
40 track::{ResourceUsageCompatibilityError, TextureViewBindGroupState, Tracker},
41 Label,
42};
43
44pub type ComputeBasePass = BasePass<ArcComputeCommand, ComputePassError>;
45
46pub struct ComputePass {
54 base: ComputeBasePass,
56
57 parent: Option<Arc<CommandEncoder>>,
63
64 timestamp_writes: Option<ArcPassTimestampWrites>,
65
66 current_bind_groups: BindGroupStateChange<Arc<BindGroup>>,
68 current_pipeline: StateChange<Arc<ComputePipeline>>,
69}
70
71impl_resource_type!(ComputePass);
72
73impl crate::storage::StorageItem for ComputePass {
74 type Marker = id::markers::ComputePassEncoder;
75}
76
77impl ComputePass {
78 fn new(parent: Arc<CommandEncoder>, desc: ArcComputePassDescriptor) -> Self {
80 let ArcComputePassDescriptor {
81 label,
82 timestamp_writes,
83 } = desc;
84
85 Self {
86 base: BasePass::new(&label),
87 parent: Some(parent),
88 timestamp_writes,
89
90 current_bind_groups: BindGroupStateChange::new(),
91 current_pipeline: StateChange::new(),
92 }
93 }
94
95 fn new_invalid(parent: Arc<CommandEncoder>, label: &Label, err: ComputePassError) -> Self {
96 Self {
97 base: BasePass::new_invalid(label, err),
98 parent: Some(parent),
99 timestamp_writes: None,
100 current_bind_groups: BindGroupStateChange::new(),
101 current_pipeline: StateChange::new(),
102 }
103 }
104
105 #[inline]
106 pub fn label(&self) -> Option<&str> {
107 self.base.label.as_deref()
108 }
109}
110
111impl fmt::Debug for ComputePass {
112 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
113 match self.parent {
114 Some(ref cmd_enc) => write!(f, "ComputePass {{ parent: {} }}", cmd_enc.error_ident()),
115 None => write!(f, "ComputePass {{ parent: None }}"),
116 }
117 }
118}
119
120#[derive(Clone, Debug, Default)]
121#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
122pub struct ComputePassDescriptor<'a, PTW = PassTimestampWrites> {
123 pub label: Label<'a>,
124 pub timestamp_writes: Option<PTW>,
126}
127
128type ArcComputePassDescriptor<'a> = ComputePassDescriptor<'a, ArcPassTimestampWrites>;
130
131#[derive(Clone, Debug, Error)]
132#[non_exhaustive]
133pub enum DispatchError {
134 #[error("Compute pipeline must be set")]
135 MissingPipeline(pass::MissingPipeline),
136 #[error(transparent)]
137 IncompatibleBindGroup(#[from] Box<BinderError>),
138 #[error(
139 "Each current dispatch group size dimension ({current:?}) must be less or equal to {limit}"
140 )]
141 InvalidGroupSize { current: [u32; 3], limit: u32 },
142 #[error(transparent)]
143 BindingSizeTooSmall(#[from] LateMinBufferBindingSizeMismatch),
144 #[error("Not all immediate data required by the pipeline has been set via set_immediates (missing byte ranges: {missing})")]
145 MissingImmediateData {
146 missing: naga::valid::ImmediateSlots,
147 },
148}
149
150impl WebGpuError for DispatchError {
151 fn webgpu_error_type(&self) -> ErrorType {
152 ErrorType::Validation
153 }
154}
155
156#[derive(Clone, Debug, Error)]
158pub enum ComputePassErrorInner {
159 #[error(transparent)]
160 Device(#[from] DeviceError),
161 #[error(transparent)]
162 EncoderState(#[from] EncoderStateError),
163 #[error("Parent encoder is invalid")]
164 InvalidParentEncoder,
165 #[error(transparent)]
166 DebugGroupError(#[from] DebugGroupError),
167 #[error(transparent)]
168 BindGroupIndexOutOfRange(#[from] pass::BindGroupIndexOutOfRange),
169 #[error(transparent)]
170 DestroyedResource(#[from] DestroyedResourceError),
171 #[error("Indirect buffer offset {0:?} is not a multiple of 4")]
172 UnalignedIndirectBufferOffset(BufferAddress),
173 #[error("Indirect buffer of {args_size} bytes starting at offset {offset} would overrun buffer of size {buffer_size}")]
174 IndirectBufferOverrun {
175 args_size: u64,
176 offset: u64,
177 buffer_size: u64,
178 },
179 #[error(transparent)]
180 ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),
181 #[error(transparent)]
182 MissingBufferUsage(#[from] MissingBufferUsageError),
183 #[error(transparent)]
184 Dispatch(#[from] DispatchError),
185 #[error(transparent)]
186 Bind(#[from] BindError),
187 #[error(transparent)]
188 ImmediateData(#[from] ImmediateUploadError),
189 #[error(transparent)]
190 QueryUse(#[from] QueryUseError),
191 #[error(transparent)]
192 TransitionResources(#[from] TransitionResourcesError),
193 #[error(transparent)]
194 MissingFeatures(#[from] MissingFeatures),
195 #[error(transparent)]
196 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
197 #[error("The compute pass has already been ended and no further commands can be recorded")]
198 PassEnded,
199 #[error(transparent)]
200 InvalidResource(#[from] InvalidResourceError),
201 #[error(transparent)]
202 TimestampWrites(#[from] TimestampWritesError),
203}
204
205impl From<InvalidOrDestroyedResourceError> for ComputePassErrorInner {
206 fn from(value: InvalidOrDestroyedResourceError) -> Self {
207 match value {
208 InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
209 InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
210 }
211 }
212}
213
214#[derive(Clone, Debug, Error)]
217#[error("{scope}")]
218pub struct ComputePassError {
219 pub scope: PassErrorScope,
220 #[source]
221 pub(super) inner: ComputePassErrorInner,
222}
223
224impl From<pass::MissingPipeline> for ComputePassErrorInner {
225 fn from(value: pass::MissingPipeline) -> Self {
226 Self::Dispatch(DispatchError::MissingPipeline(value))
227 }
228}
229
230impl<E> MapPassErr<ComputePassError> for E
231where
232 E: Into<ComputePassErrorInner>,
233{
234 fn map_pass_err(self, scope: PassErrorScope) -> ComputePassError {
235 ComputePassError {
236 scope,
237 inner: self.into(),
238 }
239 }
240}
241
242impl WebGpuError for ComputePassError {
243 fn webgpu_error_type(&self) -> ErrorType {
244 let Self { scope: _, inner } = self;
245 match inner {
246 ComputePassErrorInner::Device(e) => e.webgpu_error_type(),
247 ComputePassErrorInner::EncoderState(e) => e.webgpu_error_type(),
248 ComputePassErrorInner::DebugGroupError(e) => e.webgpu_error_type(),
249 ComputePassErrorInner::DestroyedResource(e) => e.webgpu_error_type(),
250 ComputePassErrorInner::ResourceUsageCompatibility(e) => e.webgpu_error_type(),
251 ComputePassErrorInner::MissingBufferUsage(e) => e.webgpu_error_type(),
252 ComputePassErrorInner::Dispatch(e) => e.webgpu_error_type(),
253 ComputePassErrorInner::Bind(e) => e.webgpu_error_type(),
254 ComputePassErrorInner::ImmediateData(e) => e.webgpu_error_type(),
255 ComputePassErrorInner::QueryUse(e) => e.webgpu_error_type(),
256 ComputePassErrorInner::TransitionResources(e) => e.webgpu_error_type(),
257 ComputePassErrorInner::MissingFeatures(e) => e.webgpu_error_type(),
258 ComputePassErrorInner::MissingDownlevelFlags(e) => e.webgpu_error_type(),
259 ComputePassErrorInner::InvalidResource(e) => e.webgpu_error_type(),
260 ComputePassErrorInner::TimestampWrites(e) => e.webgpu_error_type(),
261
262 ComputePassErrorInner::InvalidParentEncoder
263 | ComputePassErrorInner::BindGroupIndexOutOfRange { .. }
264 | ComputePassErrorInner::UnalignedIndirectBufferOffset(_)
265 | ComputePassErrorInner::IndirectBufferOverrun { .. }
266 | ComputePassErrorInner::PassEnded => ErrorType::Validation,
267 }
268 }
269}
270
271struct State<'scope, 'snatch_guard, 'cmd_enc> {
272 pipeline: Option<Arc<ComputePipeline>>,
273
274 pass: pass::PassState<'scope, 'snatch_guard, 'cmd_enc>,
275
276 active_query: Option<(Arc<QuerySet>, u32)>,
277
278 intermediate_trackers: Tracker,
279}
280
281impl<'scope, 'snatch_guard, 'cmd_enc> State<'scope, 'snatch_guard, 'cmd_enc> {
282 fn is_ready(&self) -> Result<(), DispatchError> {
283 if let Some(pipeline) = self.pipeline.as_ref() {
284 self.pass.binder.check_compatibility(pipeline.as_ref())?;
285 self.pass.binder.check_late_buffer_bindings()?;
286 if !self
287 .pass
288 .immediate_state
289 .immediate_slots_set
290 .contains(pipeline.immediate_slots_required)
291 {
292 return Err(DispatchError::MissingImmediateData {
293 missing: pipeline
294 .immediate_slots_required
295 .difference(self.pass.immediate_state.immediate_slots_set),
296 });
297 }
298 Ok(())
299 } else {
300 Err(DispatchError::MissingPipeline(pass::MissingPipeline))
301 }
302 }
303
304 fn flush_immediates(&mut self) {
305 let pipeline = self.pipeline.as_ref().unwrap();
306 let layout = pipeline.layout().unwrap();
307 self.pass
308 .immediate_state
309 .flush_immediates(layout, self.pass.base.raw_encoder);
310 }
311
312 fn flush_bindings(
351 &mut self,
352 indirect_buffer: Option<&Arc<Buffer>>,
353 track_indirect_buffer: bool,
354 ) -> Result<(), ComputePassErrorInner> {
355 for bind_group in self.pass.binder.list_active() {
356 unsafe { self.pass.scope.merge_bind_group(&bind_group.used)? };
357 }
358
359 if let Some(buffer) = indirect_buffer {
363 self.pass
364 .scope
365 .buffers
366 .merge_single(buffer, wgt::BufferUses::INDIRECT)?;
367 }
368
369 for bind_group in self.pass.binder.list_active() {
375 self.intermediate_trackers
376 .set_and_remove_from_usage_scope_sparse(&mut self.pass.scope, &bind_group.used);
377 }
378
379 if track_indirect_buffer {
380 self.intermediate_trackers
381 .buffers
382 .set_and_remove_from_usage_scope_sparse(
383 &mut self.pass.scope.buffers,
384 indirect_buffer.map(|buf| buf.tracker_index()),
385 );
386 } else if let Some(buffer) = indirect_buffer {
387 self.pass
388 .scope
389 .buffers
390 .remove_usage(buffer, wgt::BufferUses::INDIRECT);
391 }
392
393 flush_bindings_helper(&mut self.pass)?;
394
395 CommandEncoder::drain_barriers(
396 self.pass.base.raw_encoder,
397 &mut self.intermediate_trackers,
398 self.pass.base.snatch_guard,
399 );
400 Ok(())
401 }
402}
403
404fn transition_resources(
407 state: &mut State,
408 buffer_transitions: Vec<wgt::BufferTransition<Arc<Buffer>>>,
409 texture_transitions: Vec<wgt::TextureTransition<Arc<TextureView>>>,
410) -> Result<(), TransitionResourcesError> {
411 let indices = &state.pass.base.device.tracker_indices;
412 state.pass.scope.buffers.set_size(indices.buffers.size());
413 state.pass.scope.textures.set_size(indices.textures.size());
414
415 let mut buffer_ids = Vec::with_capacity(buffer_transitions.len());
416 let mut textures = TextureViewBindGroupState::new();
417
418 for buffer_transition in buffer_transitions {
420 buffer_transition
421 .buffer
422 .same_device(state.pass.base.device)?;
423
424 state
425 .pass
426 .scope
427 .buffers
428 .merge_single(&buffer_transition.buffer, buffer_transition.state)?;
429 buffer_ids.push(buffer_transition.buffer.tracker_index());
430 }
431
432 state
433 .intermediate_trackers
434 .buffers
435 .set_and_remove_from_usage_scope_sparse(&mut state.pass.scope.buffers, buffer_ids);
436
437 for texture_transition in texture_transitions {
439 texture_transition
440 .texture
441 .same_device(state.pass.base.device)?;
442
443 unsafe {
444 state.pass.scope.textures.merge_single(
445 &texture_transition.texture.parent,
446 texture_transition.selector,
447 texture_transition.state,
448 )
449 }?;
450
451 textures.insert_single(texture_transition.texture, texture_transition.state);
452 }
453
454 state
455 .intermediate_trackers
456 .textures
457 .set_and_remove_from_usage_scope_sparse(&mut state.pass.scope.textures, &textures);
458
459 CommandEncoder::drain_barriers(
461 state.pass.base.raw_encoder,
462 &mut state.intermediate_trackers,
463 state.pass.base.snatch_guard,
464 );
465 Ok(())
466}
467
468impl CommandEncoder {
469 fn begin_compute_pass(
470 self: &Arc<Self>,
471 desc: &ComputePassDescriptor<'_, PassTimestampWrites<Arc<QuerySet>>>,
472 ) -> (ComputePass, Option<CommandEncoderError>) {
473 use EncoderStateError as SErr;
474
475 let scope = PassErrorScope::Pass;
476
477 let label = desc.label.as_deref().map(Cow::Borrowed);
478
479 let mut cmd_buf_data = self.data.lock();
480
481 match cmd_buf_data.lock_encoder() {
482 Ok(()) => {
483 drop(cmd_buf_data);
484 if let Err(err) = self.device.check_is_valid() {
485 return (
486 ComputePass::new_invalid(Arc::clone(self), &label, err.map_pass_err(scope)),
487 None,
488 );
489 }
490
491 match desc
492 .timestamp_writes
493 .as_ref()
494 .map(|tw| {
495 Self::validate_pass_timestamp_writes::<ComputePassErrorInner>(
496 &self.device,
497 tw,
498 )
499 })
500 .transpose()
501 {
502 Ok(timestamp_writes) => {
503 let arc_desc = ArcComputePassDescriptor {
504 label,
505 timestamp_writes,
506 };
507 (ComputePass::new(Arc::clone(self), arc_desc), None)
508 }
509 Err(err) => (
510 ComputePass::new_invalid(Arc::clone(self), &label, err.map_pass_err(scope)),
511 None,
512 ),
513 }
514 }
515 Err(err @ SErr::Locked) => {
516 cmd_buf_data.invalidate(err.clone());
520 drop(cmd_buf_data);
521 (
522 ComputePass::new_invalid(Arc::clone(self), &label, err.map_pass_err(scope)),
523 None,
524 )
525 }
526 Err(err @ (SErr::Ended | SErr::Submitted)) => {
527 drop(cmd_buf_data);
530 (
531 ComputePass::new_invalid(
532 Arc::clone(self),
533 &label,
534 err.clone().map_pass_err(scope),
535 ),
536 Some(err.into()),
537 )
538 }
539 Err(err @ SErr::Invalid) => {
540 drop(cmd_buf_data);
546 (
547 ComputePass::new_invalid(Arc::clone(self), &label, err.map_pass_err(scope)),
548 None,
549 )
550 }
551 Err(SErr::Unlocked) => {
552 unreachable!("lock_encoder cannot fail due to the encoder being unlocked")
553 }
554 }
555 }
556}
557
558impl ComputePass {
560 fn end(&mut self) -> Result<(), EncoderStateError> {
561 profiling::scope!(
562 "CommandEncoder::run_compute_pass {}",
563 self.base.label.as_deref().unwrap_or("")
564 );
565
566 let cmd_enc = self.parent.take().ok_or(EncoderStateError::Ended)?;
567 let mut cmd_buf_data = cmd_enc.data.lock();
568
569 cmd_buf_data.unlock_encoder()?;
570
571 let base = self.base.take();
572
573 if let Err(ComputePassError {
574 inner:
575 ComputePassErrorInner::EncoderState(
576 err @ (EncoderStateError::Locked | EncoderStateError::Ended),
577 ),
578 scope: _,
579 }) = base
580 {
581 return Err(err.clone());
588 }
589
590 cmd_buf_data.push_with(|| -> Result<_, ComputePassError> {
591 Ok(ArcCommand::RunComputePass {
592 pass: base?,
593 timestamp_writes: self.timestamp_writes.take(),
594 })
595 })
596 }
597}
598
599impl Global {
600 pub fn command_encoder_begin_compute_pass(
611 &self,
612 encoder_id: id::CommandEncoderId,
613 desc: &ComputePassDescriptor<'_>,
614 ) -> (ComputePass, Option<CommandEncoderError>) {
615 let hub = &self.hub;
616
617 let cmd_enc = hub.command_encoders.get(encoder_id);
618
619 let desc = ComputePassDescriptor {
620 label: desc.label.as_deref().map(Cow::Borrowed),
621 timestamp_writes: desc
622 .timestamp_writes
623 .as_ref()
624 .map(|tw| PassTimestampWrites {
625 query_set: hub.query_sets.get(tw.query_set),
626 beginning_of_pass_write_index: tw.beginning_of_pass_write_index,
627 end_of_pass_write_index: tw.end_of_pass_write_index,
628 }),
629 };
630
631 cmd_enc.begin_compute_pass(&desc)
632 }
633
634 pub fn command_encoder_begin_compute_pass_with_id(
635 &self,
636 encoder_id: id::CommandEncoderId,
637 desc: &ComputePassDescriptor<'_>,
638 id_in: Option<id::ComputePassEncoderId>,
639 ) -> (id::ComputePassEncoderId, Option<CommandEncoderError>) {
640 let fid = self.hub.compute_passes.prepare(id_in);
641
642 let (pass, err) = self.command_encoder_begin_compute_pass(encoder_id, desc);
643
644 let id = fid.assign(Arc::new(Mutex::new(pass)));
648
649 (id, err)
650 }
651
652 pub fn compute_pass_end(&self, pass: &mut ComputePass) -> Result<(), EncoderStateError> {
653 pass.end()
654 }
655
656 pub fn compute_pass_end_with_id(
657 &self,
658 pass_id: id::ComputePassEncoderId,
659 ) -> Result<(), EncoderStateError> {
660 let pass = self.hub.compute_passes.get(pass_id);
661 let mut pass = pass
662 .try_lock()
663 .expect("ComputePasses should not be accessed concurrently");
664 self.compute_pass_end(&mut pass)
665 }
666
667 pub fn compute_pass_drop(&self, pass_id: id::ComputePassEncoderId) {
668 self.hub.compute_passes.remove(pass_id);
669 }
670}
671
672pub(super) fn encode_compute_pass(
673 parent_state: &mut EncodingState<InnerCommandEncoder>,
674 mut base: BasePass<ArcComputeCommand, Infallible>,
675 mut timestamp_writes: Option<ArcPassTimestampWrites>,
676) -> Result<(), ComputePassError> {
677 let pass_scope = PassErrorScope::Pass;
678
679 let device = parent_state.device;
680
681 parent_state
685 .raw_encoder
686 .close_if_open()
687 .map_pass_err(pass_scope)?;
688 let raw_encoder = parent_state
689 .raw_encoder
690 .open_pass(base.label.as_deref())
691 .map_pass_err(pass_scope)?;
692
693 let mut debug_scope_depth = 0;
694
695 let mut state = State {
696 pipeline: None,
697
698 pass: pass::PassState {
699 base: EncodingState {
700 device,
701 raw_encoder,
702 tracker: parent_state.tracker,
703 buffer_memory_init_actions: parent_state.buffer_memory_init_actions,
704 texture_memory_actions: parent_state.texture_memory_actions,
705 as_actions: parent_state.as_actions,
706 temp_resources: parent_state.temp_resources,
707 indirect_draw_validation_resources: parent_state.indirect_draw_validation_resources,
708 snatch_guard: parent_state.snatch_guard,
709 debug_scope_depth: &mut debug_scope_depth,
710 query_set_writes: parent_state.query_set_writes,
711 deferred_query_set_resolves: parent_state.deferred_query_set_resolves,
712 },
713 binder: Binder::new(),
714 temp_offsets: Vec::new(),
715 dynamic_offset_count: 0,
716 pending_discard_init_fixups: SurfacesInDiscardState::new(),
717 scope: device.new_usage_scope(),
718 string_offset: 0,
719 immediate_state: ImmediateState::default(),
720 },
721 active_query: None,
722
723 intermediate_trackers: Tracker::new(
724 device.ordered_buffer_usages,
725 device.ordered_texture_usages,
726 ),
727 };
728
729 let indices = &device.tracker_indices;
730 state
731 .pass
732 .base
733 .tracker
734 .buffers
735 .set_size(indices.buffers.size());
736 state
737 .pass
738 .base
739 .tracker
740 .textures
741 .set_size(indices.textures.size());
742
743 let timestamp_writes: Option<hal::PassTimestampWrites<'_, dyn hal::DynQuerySet>> =
744 if let Some(tw) = timestamp_writes.take() {
745 tw.query_set.same_device(device).map_pass_err(pass_scope)?;
746
747 record_pass_timestamp_writes(&tw, state.pass.base.query_set_writes);
748
749 let query_set = state
750 .pass
751 .base
752 .tracker
753 .query_sets
754 .insert_single(tw.query_set);
755
756 let range = if let (Some(index_a), Some(index_b)) =
759 (tw.beginning_of_pass_write_index, tw.end_of_pass_write_index)
760 {
761 Some(index_a.min(index_b)..index_a.max(index_b) + 1)
762 } else {
763 tw.beginning_of_pass_write_index
764 .or(tw.end_of_pass_write_index)
765 .map(|i| i..i + 1)
766 };
767 let raw_query_set = query_set
768 .try_raw(parent_state.snatch_guard)
769 .map_pass_err(pass_scope)?;
770 if let Some(range) = range {
773 unsafe {
774 state
775 .pass
776 .base
777 .raw_encoder
778 .reset_queries(raw_query_set, range);
779 }
780 }
781
782 Some(hal::PassTimestampWrites {
783 query_set: raw_query_set,
784 beginning_of_pass_write_index: tw.beginning_of_pass_write_index,
785 end_of_pass_write_index: tw.end_of_pass_write_index,
786 })
787 } else {
788 None
789 };
790
791 let hal_desc = hal::ComputePassDescriptor {
792 label: hal_label(base.label.as_deref(), device.instance_flags),
793 timestamp_writes,
794 };
795
796 unsafe {
797 state.pass.base.raw_encoder.begin_compute_pass(&hal_desc);
798 }
799
800 for command in base.commands.drain(..) {
801 match command {
802 ArcComputeCommand::SetBindGroup {
803 index,
804 num_dynamic_offsets,
805 bind_group,
806 } => {
807 let scope = PassErrorScope::SetBindGroup;
808 pass::set_bind_group::<ComputePassErrorInner>(
809 &mut state.pass,
810 device,
811 &base.dynamic_offsets,
812 index,
813 num_dynamic_offsets,
814 bind_group,
815 false,
816 )
817 .map_pass_err(scope)?;
818 }
819 ArcComputeCommand::SetPipeline(pipeline) => {
820 let scope = PassErrorScope::SetPipelineCompute;
821 set_pipeline(&mut state, device, pipeline).map_pass_err(scope)?;
822 }
823 ArcComputeCommand::SetImmediate { offset, data } => {
824 let scope = PassErrorScope::SetImmediate;
825 state
826 .pass
827 .immediate_state
828 .set_immediates::<ComputePassErrorInner>(
829 &state.pass.base.device.limits,
830 offset,
831 &data,
832 )
833 .map_pass_err(scope)?;
834 }
835 ArcComputeCommand::DispatchWorkgroups(groups) => {
836 let scope = PassErrorScope::Dispatch { indirect: false };
837 dispatch_workgroups(&mut state, groups).map_pass_err(scope)?;
838 }
839 ArcComputeCommand::DispatchWorkgroupsIndirect { buffer, offset } => {
840 let scope = PassErrorScope::Dispatch { indirect: true };
841 dispatch_workgroups_indirect(&mut state, device, buffer, offset)
842 .map_pass_err(scope)?;
843 }
844 ArcComputeCommand::PushDebugGroup { color: _, len } => {
845 pass::push_debug_group(&mut state.pass, &base.string_data, len);
846 }
847 ArcComputeCommand::PopDebugGroup => {
848 let scope = PassErrorScope::PopDebugGroup;
849 pass::pop_debug_group::<ComputePassErrorInner>(&mut state.pass)
850 .map_pass_err(scope)?;
851 }
852 ArcComputeCommand::InsertDebugMarker { color: _, len } => {
853 pass::insert_debug_marker(&mut state.pass, &base.string_data, len);
854 }
855 ArcComputeCommand::WriteTimestamp {
856 query_set,
857 query_index,
858 } => {
859 let scope = PassErrorScope::WriteTimestamp;
860 pass::write_timestamp::<ComputePassErrorInner>(
861 &mut state.pass,
862 device,
863 None, query_set,
865 query_index,
866 )
867 .map_pass_err(scope)?;
868 }
869 ArcComputeCommand::BeginPipelineStatisticsQuery {
870 query_set,
871 query_index,
872 } => {
873 let scope = PassErrorScope::BeginPipelineStatisticsQuery;
874 validate_and_begin_pipeline_statistics_query(
875 query_set,
876 state.pass.base.raw_encoder,
877 &mut state.pass.base.tracker.query_sets,
878 device,
879 query_index,
880 None,
881 &mut state.active_query,
882 state.pass.base.snatch_guard,
883 )
884 .map_pass_err(scope)?;
885 }
886 ArcComputeCommand::EndPipelineStatisticsQuery => {
887 let scope = PassErrorScope::EndPipelineStatisticsQuery;
888 end_pipeline_statistics_query(
889 state.pass.base.raw_encoder,
890 &mut state.active_query,
891 state.pass.base.snatch_guard,
892 state.pass.base.query_set_writes,
893 )
894 .map_pass_err(scope)?;
895 }
896 ArcComputeCommand::TransitionResources {
897 buffer_transitions,
898 texture_transitions,
899 } => {
900 let scope = PassErrorScope::TransitionResources;
901 transition_resources(&mut state, buffer_transitions, texture_transitions)
902 .map_pass_err(scope)?;
903 }
904 }
905 }
906
907 if *state.pass.base.debug_scope_depth > 0 {
908 Err(
909 ComputePassErrorInner::DebugGroupError(DebugGroupError::MissingPop)
910 .map_pass_err(pass_scope),
911 )?;
912 }
913
914 unsafe {
915 state.pass.base.raw_encoder.end_compute_pass();
916 }
917
918 let State {
919 pass: pass::PassState {
920 pending_discard_init_fixups,
921 ..
922 },
923 intermediate_trackers,
924 ..
925 } = state;
926
927 parent_state.raw_encoder.close().map_pass_err(pass_scope)?;
929
930 let transit = parent_state
934 .raw_encoder
935 .open_pass(hal_label(
936 Some("(wgpu internal) Pre Pass"),
937 device.instance_flags,
938 ))
939 .map_pass_err(pass_scope)?;
940 fixup_discarded_surfaces(
941 pending_discard_init_fixups.into_iter(),
942 transit,
943 &mut parent_state.tracker.textures,
944 device,
945 parent_state.snatch_guard,
946 );
947 CommandEncoder::insert_barriers_from_tracker(
948 transit,
949 parent_state.tracker,
950 &intermediate_trackers,
951 parent_state.snatch_guard,
952 );
953 parent_state
955 .raw_encoder
956 .close_and_swap()
957 .map_pass_err(pass_scope)?;
958
959 Ok(())
960}
961
962fn set_pipeline(
963 state: &mut State,
964 device: &Arc<Device>,
965 pipeline: Arc<ComputePipeline>,
966) -> Result<(), ComputePassErrorInner> {
967 pipeline.same_device(device)?;
968
969 state.pipeline = Some(pipeline.clone());
970
971 let pipeline = state
972 .pass
973 .base
974 .tracker
975 .compute_pipelines
976 .insert_single(pipeline)
977 .clone();
978
979 unsafe {
980 state
981 .pass
982 .base
983 .raw_encoder
984 .set_compute_pipeline(pipeline.raw()?);
985 }
986
987 let pipeline_layout = pipeline.layout()?;
989 pass::change_pipeline_layout::<ComputePassErrorInner>(
990 &mut state.pass,
991 pipeline_layout,
992 &pipeline.late_sized_buffer_groups,
993 )
994}
995
996fn dispatch_workgroups(state: &mut State, groups: [u32; 3]) -> Result<(), ComputePassErrorInner> {
997 api_log!("ComputePass::dispatch {groups:?}");
998
999 state.is_ready()?;
1000
1001 state.flush_bindings(None, false)?;
1002 state.flush_immediates();
1003
1004 let groups_size_limit = state
1005 .pass
1006 .base
1007 .device
1008 .limits
1009 .max_compute_workgroups_per_dimension;
1010
1011 if groups.iter().copied().any(|g| g > groups_size_limit) {
1012 return Err(ComputePassErrorInner::Dispatch(
1013 DispatchError::InvalidGroupSize {
1014 current: groups,
1015 limit: groups_size_limit,
1016 },
1017 ));
1018 }
1019
1020 unsafe {
1021 state.pass.base.raw_encoder.dispatch_workgroups(groups);
1022 }
1023 Ok(())
1024}
1025
1026fn dispatch_workgroups_indirect(
1027 state: &mut State,
1028 device: &Arc<Device>,
1029 buffer: Arc<Buffer>,
1030 offset: u64,
1031) -> Result<(), ComputePassErrorInner> {
1032 api_log!("ComputePass::dispatch_indirect");
1033
1034 buffer.same_device(device)?;
1035
1036 state.is_ready()?;
1037
1038 state
1039 .pass
1040 .base
1041 .device
1042 .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
1043
1044 buffer.check_usage(wgt::BufferUsages::INDIRECT)?;
1045
1046 if !offset.is_multiple_of(4) {
1047 return Err(ComputePassErrorInner::UnalignedIndirectBufferOffset(offset));
1048 }
1049
1050 let args_size = size_of::<wgt::DispatchIndirectArgs>() as u64;
1051 if buffer.size < args_size || buffer.size - args_size < offset {
1052 return Err(ComputePassErrorInner::IndirectBufferOverrun {
1053 args_size,
1054 offset,
1055 buffer_size: buffer.size,
1056 });
1057 }
1058
1059 buffer.check_destroyed(state.pass.base.snatch_guard)?;
1060
1061 let stride = 3 * 4; state.pass.base.buffer_memory_init_actions.extend(
1063 buffer.initialization_status.read().create_action(
1064 &buffer,
1065 offset..(offset + stride),
1066 MemoryInitKind::NeedsInitializedMemory,
1067 ),
1068 );
1069
1070 if let Some(ref indirect_validation) = state.pass.base.device.indirect_validation {
1071 let params = indirect_validation.dispatch.params(
1072 &state.pass.base.device.limits,
1073 offset,
1074 buffer.size,
1075 );
1076
1077 unsafe {
1078 state
1079 .pass
1080 .base
1081 .raw_encoder
1082 .set_compute_pipeline(params.pipeline);
1083 }
1084
1085 unsafe {
1086 state.pass.base.raw_encoder.set_immediates(
1087 params.pipeline_layout,
1088 0,
1089 &[params.offset_remainder as u32 / 4],
1090 );
1091 }
1092
1093 unsafe {
1094 state.pass.base.raw_encoder.set_bind_group(
1095 params.pipeline_layout,
1096 0,
1097 params.dst_bind_group,
1098 &[],
1099 );
1100 }
1101 unsafe {
1102 state.pass.base.raw_encoder.set_bind_group(
1103 params.pipeline_layout,
1104 1,
1105 buffer
1106 .indirect_validation_bind_groups
1107 .get(state.pass.base.snatch_guard)
1108 .unwrap()
1109 .dispatch
1110 .as_ref(),
1111 &[params.aligned_offset as u32],
1112 );
1113 }
1114
1115 let src_transition = state
1116 .intermediate_trackers
1117 .buffers
1118 .set_single(&buffer, wgt::BufferUses::STORAGE_READ_ONLY);
1119 let src_barrier = src_transition
1120 .map(|transition| transition.into_hal(&buffer, state.pass.base.snatch_guard));
1121 unsafe {
1122 state
1123 .pass
1124 .base
1125 .raw_encoder
1126 .transition_buffers(src_barrier.as_slice());
1127 }
1128
1129 unsafe {
1130 state
1131 .pass
1132 .base
1133 .raw_encoder
1134 .transition_buffers(&[hal::BufferBarrier {
1135 buffer: params.dst_buffer,
1136 usage: hal::StateTransition {
1137 from: wgt::BufferUses::INDIRECT,
1138 to: wgt::BufferUses::STORAGE_READ_WRITE,
1139 },
1140 }]);
1141 }
1142
1143 unsafe {
1144 state.pass.base.raw_encoder.dispatch_workgroups([1, 1, 1]);
1145 }
1146
1147 {
1149 unsafe {
1150 state
1151 .pass
1152 .base
1153 .raw_encoder
1154 .set_compute_pipeline(state.pipeline.as_ref().unwrap().raw()?);
1155 }
1156
1157 state.pass.immediate_state.immediates_dirty = true;
1159 state.flush_immediates();
1160
1161 for (i, group, dynamic_offsets) in state.pass.binder.list_valid() {
1162 let raw_bg = group.try_raw(state.pass.base.snatch_guard)?;
1163 unsafe {
1164 state.pass.base.raw_encoder.set_bind_group(
1165 state.pipeline.as_ref().unwrap().layout()?.raw()?,
1166 i as u32,
1167 raw_bg,
1168 dynamic_offsets,
1169 );
1170 }
1171 }
1172 }
1173
1174 unsafe {
1175 state
1176 .pass
1177 .base
1178 .raw_encoder
1179 .transition_buffers(&[hal::BufferBarrier {
1180 buffer: params.dst_buffer,
1181 usage: hal::StateTransition {
1182 from: wgt::BufferUses::STORAGE_READ_WRITE,
1183 to: wgt::BufferUses::INDIRECT,
1184 },
1185 }]);
1186 }
1187
1188 state.flush_bindings(Some(&buffer), false)?;
1189 unsafe {
1190 state
1191 .pass
1192 .base
1193 .raw_encoder
1194 .dispatch_workgroups_indirect(params.dst_buffer, 0);
1195 }
1196 } else {
1197 state.flush_bindings(Some(&buffer), true)?;
1198 state.flush_immediates();
1199 let buf_raw = buffer.try_raw(state.pass.base.snatch_guard)?;
1200 unsafe {
1201 state
1202 .pass
1203 .base
1204 .raw_encoder
1205 .dispatch_workgroups_indirect(buf_raw, offset);
1206 }
1207 }
1208
1209 Ok(())
1210}
1211
1212impl ComputePass {
1225 pub fn set_bind_group(
1226 &mut self,
1227 index: u32,
1228 bind_group: Option<Arc<BindGroup>>,
1229 offsets: &[DynamicOffset],
1230 ) -> Result<(), PassStateError> {
1231 let scope = PassErrorScope::SetBindGroup;
1232
1233 let base = pass_base!(self, scope);
1237
1238 if self.current_bind_groups.set_and_check_redundant(
1239 &bind_group,
1240 index,
1241 &mut base.dynamic_offsets,
1242 offsets,
1243 ) {
1244 return Ok(());
1245 }
1246
1247 let bind_group = if let Some(bind_group) = bind_group {
1248 pass_try!(base, scope, bind_group.check_is_valid());
1249 Some(bind_group)
1250 } else {
1251 None
1252 };
1253
1254 base.commands.push(ArcComputeCommand::SetBindGroup {
1255 index,
1256 num_dynamic_offsets: offsets.len(),
1257 bind_group,
1258 });
1259
1260 Ok(())
1261 }
1262
1263 pub fn set_pipeline(
1264 &mut self,
1265 compute_pipeline: Arc<ComputePipeline>,
1266 ) -> Result<(), PassStateError> {
1267 let redundant = self
1268 .current_pipeline
1269 .set_and_check_redundant(&compute_pipeline);
1270
1271 let scope = PassErrorScope::SetPipelineCompute;
1272
1273 let base = pass_base!(self, scope);
1276
1277 if redundant {
1278 return Ok(());
1279 }
1280
1281 pass_try!(base, scope, compute_pipeline.check_valid());
1282
1283 base.commands
1284 .push(ArcComputeCommand::SetPipeline(compute_pipeline));
1285
1286 Ok(())
1287 }
1288
1289 pub fn set_immediates(&mut self, offset: u32, data: &[u8]) -> Result<(), PassStateError> {
1290 let scope = PassErrorScope::SetImmediate;
1291 let base = pass_base!(self, scope);
1292
1293 pass_try!(
1294 base,
1295 scope,
1296 pass::validate_immediates_alignment(offset, data.len())
1297 );
1298
1299 base.commands.push(ArcComputeCommand::SetImmediate {
1300 offset,
1301 data: data
1302 .chunks_exact(size_of::<u32>())
1303 .map(|ck| u32::from_le_bytes(ck.try_into().unwrap()))
1304 .collect(),
1305 });
1306
1307 Ok(())
1308 }
1309
1310 pub fn dispatch_workgroups(
1311 &mut self,
1312 groups_x: u32,
1313 groups_y: u32,
1314 groups_z: u32,
1315 ) -> Result<(), PassStateError> {
1316 let scope = PassErrorScope::Dispatch { indirect: false };
1317
1318 pass_base!(self, scope)
1319 .commands
1320 .push(ArcComputeCommand::DispatchWorkgroups([
1321 groups_x, groups_y, groups_z,
1322 ]));
1323
1324 Ok(())
1325 }
1326
1327 pub fn dispatch_workgroups_indirect(
1328 &mut self,
1329 buffer: Arc<Buffer>,
1330 offset: BufferAddress,
1331 ) -> Result<(), PassStateError> {
1332 let scope = PassErrorScope::Dispatch { indirect: true };
1333 let base = pass_base!(self, scope);
1334
1335 pass_try!(base, scope, buffer.check_is_valid());
1336
1337 base.commands
1338 .push(ArcComputeCommand::DispatchWorkgroupsIndirect { buffer, offset });
1339
1340 Ok(())
1341 }
1342
1343 pub fn push_debug_group(&mut self, label: &str, color: u32) -> Result<(), PassStateError> {
1344 let base = pass_base!(self, PassErrorScope::PushDebugGroup);
1345
1346 let bytes = label.as_bytes();
1347 base.string_data.extend_from_slice(bytes);
1348
1349 base.commands.push(ArcComputeCommand::PushDebugGroup {
1350 color,
1351 len: bytes.len(),
1352 });
1353
1354 Ok(())
1355 }
1356
1357 pub fn pop_debug_group(&mut self) -> Result<(), PassStateError> {
1358 let base = pass_base!(self, PassErrorScope::PopDebugGroup);
1359
1360 base.commands.push(ArcComputeCommand::PopDebugGroup);
1361
1362 Ok(())
1363 }
1364
1365 pub fn insert_debug_marker(&mut self, label: &str, color: u32) -> Result<(), PassStateError> {
1366 let base = pass_base!(self, PassErrorScope::InsertDebugMarker);
1367
1368 let bytes = label.as_bytes();
1369 base.string_data.extend_from_slice(bytes);
1370
1371 base.commands.push(ArcComputeCommand::InsertDebugMarker {
1372 color,
1373 len: bytes.len(),
1374 });
1375
1376 Ok(())
1377 }
1378
1379 pub fn write_timestamp(
1380 &mut self,
1381 query_set: Arc<QuerySet>,
1382 query_index: u32,
1383 ) -> Result<(), PassStateError> {
1384 let scope = PassErrorScope::WriteTimestamp;
1385 let base = pass_base!(self, scope);
1386
1387 pass_try!(base, scope, query_set.check_is_valid());
1388
1389 base.commands.push(ArcComputeCommand::WriteTimestamp {
1390 query_set,
1391 query_index,
1392 });
1393
1394 Ok(())
1395 }
1396
1397 pub fn begin_pipeline_statistics_query(
1398 &mut self,
1399 query_set: Arc<QuerySet>,
1400 query_index: u32,
1401 ) -> Result<(), PassStateError> {
1402 let scope = PassErrorScope::BeginPipelineStatisticsQuery;
1403 let base = pass_base!(self, scope);
1404
1405 pass_try!(base, scope, query_set.check_is_valid());
1406
1407 base.commands
1408 .push(ArcComputeCommand::BeginPipelineStatisticsQuery {
1409 query_set,
1410 query_index,
1411 });
1412
1413 Ok(())
1414 }
1415
1416 pub fn end_pipeline_statistics_query(&mut self) -> Result<(), PassStateError> {
1417 pass_base!(self, PassErrorScope::EndPipelineStatisticsQuery)
1418 .commands
1419 .push(ArcComputeCommand::EndPipelineStatisticsQuery);
1420
1421 Ok(())
1422 }
1423
1424 pub fn transition_resources(
1425 &mut self,
1426 buffer_transitions: impl Iterator<Item = wgt::BufferTransition<Arc<Buffer>>>,
1427 texture_transitions: impl Iterator<Item = wgt::TextureTransition<Arc<TextureView>>>,
1428 ) -> Result<(), PassStateError> {
1429 let scope = PassErrorScope::TransitionResources;
1430 let base = pass_base!(self, scope);
1431
1432 let buffer_transitions = pass_try!(
1433 base,
1434 scope,
1435 buffer_transitions
1436 .map(|buffer_transition| -> Result<_, InvalidResourceError> {
1437 let buffer = buffer_transition.buffer;
1438 buffer.check_is_valid()?;
1439 Ok(wgt::BufferTransition {
1440 buffer,
1441 state: buffer_transition.state,
1442 })
1443 })
1444 .collect::<Result<Vec<_>, _>>()
1445 );
1446
1447 let texture_transitions = pass_try!(
1448 base,
1449 scope,
1450 texture_transitions
1451 .map(|texture_transition| -> Result<_, InvalidResourceError> {
1452 let texture_view = texture_transition.texture;
1453 texture_view.check_valid()?;
1454 Ok(wgt::TextureTransition {
1455 texture: texture_view,
1456 selector: texture_transition.selector,
1457 state: texture_transition.state,
1458 })
1459 })
1460 .collect::<Result<Vec<_>, _>>()
1461 );
1462
1463 base.commands.push(ArcComputeCommand::TransitionResources {
1464 buffer_transitions,
1465 texture_transitions,
1466 });
1467
1468 Ok(())
1469 }
1470}
1471
1472impl Global {
1485 pub fn compute_pass_set_bind_group(
1486 &self,
1487 pass: &mut ComputePass,
1488 index: u32,
1489 bind_group_id: Option<id::BindGroupId>,
1490 offsets: &[DynamicOffset],
1491 ) -> Result<(), PassStateError> {
1492 pass.set_bind_group(
1493 index,
1494 bind_group_id.map(|bind_group_id| self.hub.bind_groups.get(bind_group_id)),
1495 offsets,
1496 )
1497 }
1498
1499 pub fn compute_pass_set_bind_group_with_id(
1500 &self,
1501 pass_id: id::ComputePassEncoderId,
1502 index: u32,
1503 bind_group_id: Option<id::BindGroupId>,
1504 offsets: &[DynamicOffset],
1505 ) -> Result<(), PassStateError> {
1506 let pass = self.hub.compute_passes.get(pass_id);
1507 let mut pass = pass
1508 .try_lock()
1509 .expect("ComputePasses should not be accessed concurrently");
1510 self.compute_pass_set_bind_group(&mut pass, index, bind_group_id, offsets)
1511 }
1512
1513 pub fn compute_pass_set_pipeline(
1514 &self,
1515 pass: &mut ComputePass,
1516 pipeline_id: id::ComputePipelineId,
1517 ) -> Result<(), PassStateError> {
1518 let pipeline = self.hub.compute_pipelines.get(pipeline_id);
1519 pass.set_pipeline(pipeline)
1520 }
1521
1522 pub fn compute_pass_set_pipeline_with_id(
1523 &self,
1524 pass_id: id::ComputePassEncoderId,
1525 pipeline_id: id::ComputePipelineId,
1526 ) -> Result<(), PassStateError> {
1527 let pass = self.hub.compute_passes.get(pass_id);
1528 let mut pass = pass
1529 .try_lock()
1530 .expect("ComputePasses should not be accessed concurrently");
1531 self.compute_pass_set_pipeline(&mut pass, pipeline_id)
1532 }
1533
1534 pub fn compute_pass_set_immediates(
1535 &self,
1536 pass: &mut ComputePass,
1537 offset: u32,
1538 data: &[u8],
1539 ) -> Result<(), PassStateError> {
1540 pass.set_immediates(offset, data)
1541 }
1542
1543 pub fn compute_pass_set_immediates_with_id(
1544 &self,
1545 pass_id: id::ComputePassEncoderId,
1546 offset: u32,
1547 data: &[u8],
1548 ) -> Result<(), PassStateError> {
1549 let pass = self.hub.compute_passes.get(pass_id);
1550 let mut pass = pass
1551 .try_lock()
1552 .expect("ComputePasses should not be accessed concurrently");
1553 self.compute_pass_set_immediates(&mut pass, offset, data)
1554 }
1555
1556 pub fn compute_pass_dispatch_workgroups(
1557 &self,
1558 pass: &mut ComputePass,
1559 groups_x: u32,
1560 groups_y: u32,
1561 groups_z: u32,
1562 ) -> Result<(), PassStateError> {
1563 pass.dispatch_workgroups(groups_x, groups_y, groups_z)
1564 }
1565
1566 pub fn compute_pass_dispatch_workgroups_with_id(
1567 &self,
1568 pass_id: id::ComputePassEncoderId,
1569 groups_x: u32,
1570 groups_y: u32,
1571 groups_z: u32,
1572 ) -> Result<(), PassStateError> {
1573 let pass = self.hub.compute_passes.get(pass_id);
1574 let mut pass = pass
1575 .try_lock()
1576 .expect("ComputePasses should not be accessed concurrently");
1577 self.compute_pass_dispatch_workgroups(&mut pass, groups_x, groups_y, groups_z)
1578 }
1579
1580 pub fn compute_pass_dispatch_workgroups_indirect(
1581 &self,
1582 pass: &mut ComputePass,
1583 buffer_id: id::BufferId,
1584 offset: BufferAddress,
1585 ) -> Result<(), PassStateError> {
1586 pass.dispatch_workgroups_indirect(self.hub.buffers.get(buffer_id), offset)
1587 }
1588
1589 pub fn compute_pass_dispatch_workgroups_indirect_with_id(
1590 &self,
1591 pass_id: id::ComputePassEncoderId,
1592 buffer_id: id::BufferId,
1593 offset: BufferAddress,
1594 ) -> Result<(), PassStateError> {
1595 let pass = self.hub.compute_passes.get(pass_id);
1596 let mut pass = pass
1597 .try_lock()
1598 .expect("ComputePasses should not be accessed concurrently");
1599 self.compute_pass_dispatch_workgroups_indirect(&mut pass, buffer_id, offset)
1600 }
1601
1602 pub fn compute_pass_push_debug_group(
1603 &self,
1604 pass: &mut ComputePass,
1605 label: &str,
1606 color: u32,
1607 ) -> Result<(), PassStateError> {
1608 pass.push_debug_group(label, color)
1609 }
1610
1611 pub fn compute_pass_push_debug_group_with_id(
1612 &self,
1613 pass_id: id::ComputePassEncoderId,
1614 label: &str,
1615 color: u32,
1616 ) -> Result<(), PassStateError> {
1617 let pass = self.hub.compute_passes.get(pass_id);
1618 let mut pass = pass
1619 .try_lock()
1620 .expect("ComputePasses should not be accessed concurrently");
1621 self.compute_pass_push_debug_group(&mut pass, label, color)
1622 }
1623
1624 pub fn compute_pass_pop_debug_group(
1625 &self,
1626 pass: &mut ComputePass,
1627 ) -> Result<(), PassStateError> {
1628 pass.pop_debug_group()
1629 }
1630
1631 pub fn compute_pass_pop_debug_group_with_id(
1632 &self,
1633 pass_id: id::ComputePassEncoderId,
1634 ) -> Result<(), PassStateError> {
1635 let pass = self.hub.compute_passes.get(pass_id);
1636 let mut pass = pass
1637 .try_lock()
1638 .expect("ComputePasses should not be accessed concurrently");
1639 self.compute_pass_pop_debug_group(&mut pass)
1640 }
1641
1642 pub fn compute_pass_insert_debug_marker(
1643 &self,
1644 pass: &mut ComputePass,
1645 label: &str,
1646 color: u32,
1647 ) -> Result<(), PassStateError> {
1648 pass.insert_debug_marker(label, color)
1649 }
1650
1651 pub fn compute_pass_insert_debug_marker_with_id(
1652 &self,
1653 pass_id: id::ComputePassEncoderId,
1654 label: &str,
1655 color: u32,
1656 ) -> Result<(), PassStateError> {
1657 let pass = self.hub.compute_passes.get(pass_id);
1658 let mut pass = pass
1659 .try_lock()
1660 .expect("ComputePasses should not be accessed concurrently");
1661 self.compute_pass_insert_debug_marker(&mut pass, label, color)
1662 }
1663
1664 pub fn compute_pass_write_timestamp(
1665 &self,
1666 pass: &mut ComputePass,
1667 query_set_id: id::QuerySetId,
1668 query_index: u32,
1669 ) -> Result<(), PassStateError> {
1670 let query_set = self.hub.query_sets.get(query_set_id);
1671 pass.write_timestamp(query_set, query_index)
1672 }
1673
1674 pub fn compute_pass_write_timestamp_with_id(
1675 &self,
1676 pass_id: id::ComputePassEncoderId,
1677 query_set_id: id::QuerySetId,
1678 query_index: u32,
1679 ) -> Result<(), PassStateError> {
1680 let pass = self.hub.compute_passes.get(pass_id);
1681 let mut pass = pass
1682 .try_lock()
1683 .expect("ComputePasses should not be accessed concurrently");
1684 self.compute_pass_write_timestamp(&mut pass, query_set_id, query_index)
1685 }
1686
1687 pub fn compute_pass_begin_pipeline_statistics_query(
1688 &self,
1689 pass: &mut ComputePass,
1690 query_set_id: id::QuerySetId,
1691 query_index: u32,
1692 ) -> Result<(), PassStateError> {
1693 let query_set = self.hub.query_sets.get(query_set_id);
1694 pass.begin_pipeline_statistics_query(query_set, query_index)
1695 }
1696
1697 pub fn compute_pass_begin_pipeline_statistics_query_with_id(
1698 &self,
1699 pass_id: id::ComputePassEncoderId,
1700 query_set_id: id::QuerySetId,
1701 query_index: u32,
1702 ) -> Result<(), PassStateError> {
1703 let pass = self.hub.compute_passes.get(pass_id);
1704 let mut pass = pass
1705 .try_lock()
1706 .expect("ComputePasses should not be accessed concurrently");
1707 self.compute_pass_begin_pipeline_statistics_query(&mut pass, query_set_id, query_index)
1708 }
1709
1710 pub fn compute_pass_end_pipeline_statistics_query(
1711 &self,
1712 pass: &mut ComputePass,
1713 ) -> Result<(), PassStateError> {
1714 pass.end_pipeline_statistics_query()
1715 }
1716
1717 pub fn compute_pass_end_pipeline_statistics_query_with_id(
1718 &self,
1719 pass_id: id::ComputePassEncoderId,
1720 ) -> Result<(), PassStateError> {
1721 let pass = self.hub.compute_passes.get(pass_id);
1722 let mut pass = pass
1723 .try_lock()
1724 .expect("ComputePasses should not be accessed concurrently");
1725 self.compute_pass_end_pipeline_statistics_query(&mut pass)
1726 }
1727
1728 pub fn compute_pass_transition_resources(
1729 &self,
1730 pass: &mut ComputePass,
1731 buffer_transitions: impl Iterator<Item = wgt::BufferTransition<id::BufferId>>,
1732 texture_transitions: impl Iterator<Item = wgt::TextureTransition<id::TextureViewId>>,
1733 ) -> Result<(), PassStateError> {
1734 pass.transition_resources(
1735 buffer_transitions.map(|bt| wgt::BufferTransition {
1736 buffer: self.hub.buffers.get(bt.buffer),
1737 state: bt.state,
1738 }),
1739 texture_transitions.map(|tt| wgt::TextureTransition {
1740 texture: self.hub.texture_views.get(tt.texture),
1741 selector: tt.selector,
1742 state: tt.state,
1743 }),
1744 )
1745 }
1746}