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