wgpu_core/command/
compute.rs

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
46/// A pass's [encoder state](https://www.w3.org/TR/webgpu/#encoder-state) and
47/// its validity are two distinct conditions, i.e., the full matrix of
48/// (open, ended) x (valid, invalid) is possible.
49///
50/// The presence or absence of the `parent` `Option` indicates the pass's state.
51/// The presence or absence of an error in `base.error` indicates the pass's
52/// validity.
53pub struct ComputePass {
54    /// All pass data & records is stored here.
55    base: ComputeBasePass,
56
57    /// Parent command encoder that this pass records commands into.
58    ///
59    /// If this is `Some`, then the pass is in WebGPU's "open" state. If it is
60    /// `None`, then the pass is in the "ended" state.
61    /// See <https://www.w3.org/TR/webgpu/#encoder-state>
62    parent: Option<Arc<CommandEncoder>>,
63
64    timestamp_writes: Option<PassTimestampWrites>,
65
66    // Resource binding dedupe state.
67    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    /// If the parent command encoder is invalid, the returned pass will be invalid.
79    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))]
122/// cbindgen:ignore
123pub struct ComputePassDescriptor<'a, PTW = PassTimestampWrites> {
124    pub label: Label<'a>,
125    /// Defines where and when timestamp values will be written for this pass.
126    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/// Error encountered when performing a compute pass.
155#[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/// Error encountered when performing a compute pass, stored for later reporting
213/// when encoding ends.
214#[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    /// Flush binding state in preparation for a dispatch.
311    ///
312    /// # Differences between render and compute passes
313    ///
314    /// There are differences between the `flush_bindings` implementations for
315    /// render and compute passes, because render passes have a single usage
316    /// scope for the entire pass, and compute passes have a separate usage
317    /// scope for each dispatch.
318    ///
319    /// For compute passes, bind groups are merged into a fresh usage scope
320    /// here, not into the pass usage scope within calls to `set_bind_group`. As
321    /// specified by WebGPU, for compute passes, we merge only the bind groups
322    /// that are actually used by the pipeline, unlike render passes, which
323    /// merge every bind group that is ever set, even if it is not ultimately
324    /// used by the pipeline.
325    ///
326    /// For compute passes, we call `drain_barriers` here, because barriers may
327    /// be needed before each dispatch if a previous dispatch had a conflicting
328    /// usage. For render passes, barriers are emitted once at the start of the
329    /// render pass.
330    ///
331    /// # Indirect buffer handling
332    ///
333    /// The `indirect_buffer` argument should be passed for any indirect
334    /// dispatch (with or without validation). It will be checked for
335    /// conflicting usages according to WebGPU rules. For the purpose of
336    /// these rules, the fact that we have actually processed the buffer in
337    /// the validation pass is an implementation detail.
338    ///
339    /// The `track_indirect_buffer` argument should be set when doing indirect
340    /// dispatch *without* validation. In this case, the indirect buffer will
341    /// be added to the tracker in order to generate any necessary transitions
342    /// for that usage.
343    ///
344    /// When doing indirect dispatch *with* validation, the indirect buffer is
345    /// processed by the validation pass and is not used by the actual dispatch.
346    /// The indirect validation code handles transitions for the validation
347    /// pass.
348    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        // Add the indirect buffer. Because usage scopes are per-dispatch, this
358        // is the only place where INDIRECT usage could be added, and it is safe
359        // for us to remove it below.
360        if let Some(buffer) = indirect_buffer {
361            self.pass
362                .scope
363                .buffers
364                .merge_single(buffer, wgt::BufferUses::INDIRECT)?;
365        }
366
367        // For compute, usage scopes are associated with each dispatch and not
368        // with the pass as a whole. However, because the cost of creating and
369        // dropping `UsageScope`s is significant (even with the pool), we
370        // add and then remove usage from a single usage scope.
371
372        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
402/// Compute pass version of [`command::transition_resources`](crate::command::transition_resources).
403/// See also `State::flush_bindings` for details on the implementation.
404fn 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    // Process buffer transitions
417    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    // Process texture transitions
436    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    // Record any needed barriers based on tracker data
458    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                // Attempting to open a new pass while the encoder is locked
515                // invalidates the encoder, but does not generate a validation
516                // error.
517                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                // Attempting to open a new pass after the encode has ended
526                // generates an immediate validation error.
527                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                // Passes can be opened even on an invalid encoder. Such passes
539                // are even valid, but since there's no visible side-effect of
540                // the pass being valid and there's no point in storing recorded
541                // commands that will ultimately be discarded, we open an
542                // invalid pass to save that work.
543                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
556// Running the compute pass.
557impl 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            // Most encoding errors are detected and raised within `finish()`.
580            //
581            // However, we raise a validation error here if the pass was opened
582            // within another pass, or on a finished encoder. The latter is
583            // particularly important, because in that case reporting errors via
584            // `CommandEncoder::finish` is not possible.
585            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    /// Creates a compute pass.
599    ///
600    /// If creation fails, an invalid pass is returned. Attempting to record
601    /// commands into an invalid pass is permitted, but a validation error will
602    /// ultimately be generated when the parent encoder is finished, and it is
603    /// not possible to run any commands from the invalid pass.
604    ///
605    /// If successful, puts the encoder into the [`Locked`] state.
606    ///
607    /// [`Locked`]: crate::command::CommandEncoderStatus::Locked
608    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        // no lock rank here because only one thread should be using compute pass
643        // and it's only used by id variants of compute pass methods on global
644        // so no deadlock (or concurrent lock) should happen in practise
645        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    // We automatically keep extending command buffers over time, and because
680    // we want to insert a command buffer _before_ what we're about to record,
681    // we need to make sure to close the previous one.
682    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            // Unlike in render passes we can't delay resetting the query sets since
755            // there is no auxiliary pass.
756            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            // Range should always be Some, both values being None should lead to a validation error.
769            // But no point in erroring over that nuance here!
770            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, // compute passes do not attempt to coalesce query resets
862                    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    // Stop the current command encoder.
926    parent_state.raw_encoder.close().map_pass_err(pass_scope)?;
927
928    // Create a new command encoder, which we will insert _before_ the body of the compute pass.
929    //
930    // Use that buffer to insert barriers and clear discarded images.
931    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    // Close the command encoder, and swap it with the previous.
952    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    // Rebind resources
986    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; // 3 integers, x/y/z group size
1060    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        // reset state
1146        {
1147            unsafe {
1148                state
1149                    .pass
1150                    .base
1151                    .raw_encoder
1152                    .set_compute_pipeline(state.pipeline.as_ref().unwrap().raw()?);
1153            }
1154
1155            // Immediates are dirty because we used them for the validation pipeline
1156            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
1210// Recording a compute pass.
1211//
1212// The only error that should be returned from these methods is
1213// `EncoderStateError::Ended`, when the pass has already ended and an immediate
1214// validation error is raised.
1215//
1216// All other errors should be stored in the pass for later reporting when
1217// `CommandEncoder.finish()` is called.
1218//
1219// The `pass_try!` macro should be used to handle errors appropriately. Note
1220// that the `pass_try!` and `pass_base!` macros may return early from the
1221// function that invokes them, like the `?` operator.
1222impl 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        // This statement will return an error if the pass is ended. It's
1232        // important the error check comes before the early-out for
1233        // `set_and_check_redundant`.
1234        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        // This statement will return an error if the pass is ended.
1272        // Its important the error check comes before the early-out for `redundant`.
1273        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
1470// Recording a compute pass.
1471//
1472// The only error that should be returned from these methods is
1473// `EncoderStateError::Ended`, when the pass has already ended and an immediate
1474// validation error is raised.
1475//
1476// All other errors should be stored in the pass for later reporting when
1477// `CommandEncoder.finish()` is called.
1478//
1479// The `pass_try!` macro should be used to handle errors appropriately. Note
1480// that the `pass_try!` and `pass_base!` macros may return early from the
1481// function that invokes them, like the `?` operator.
1482impl 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}