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