wgpu_core/command/
compute.rs

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