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