wgpu_core/command/
render.rs

1use alloc::{borrow::Cow, sync::Arc, vec::Vec};
2use core::{convert::Infallible, fmt, num::NonZeroU32, ops::Range, str};
3use smallvec::SmallVec;
4
5use arrayvec::ArrayVec;
6use thiserror::Error;
7use wgt::{
8    error::{ErrorType, WebGpuError},
9    BufferAddress, BufferSize, BufferUsages, Color, DynamicOffset, IndexFormat, TextureSelector,
10    TextureUsages, TextureViewDimension, VertexStepMode,
11};
12
13use crate::{
14    api_log,
15    binding_model::{BindError, ImmediateUploadError},
16    command::{
17        bind::Binder,
18        memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState, TextureSurfaceDiscard},
19        pass::{self, flush_bindings_helper},
20        pass_base, pass_try,
21        query::{
22            end_occlusion_query, end_pipeline_statistics_query, validate_and_begin_occlusion_query,
23            validate_and_begin_pipeline_statistics_query, QueryResetMap,
24        },
25        render_command::ArcRenderCommand,
26        ArcCommand, ArcPassTimestampWrites, BasePass, BindGroupStateChange,
27        CommandBufferTextureMemoryActions, CommandEncoder, CommandEncoderError, DebugGroupError,
28        DrawCommandFamily, DrawError, DrawKind, EncoderStateError, EncodingState, ExecutionError,
29        InnerCommandEncoder, MapPassErr, PassErrorScope, PassStateError, PassTimestampWrites,
30        QueryUseError, Rect, RenderCommandError, StateChange, TimestampWritesError,
31    },
32    device::{
33        AttachmentData, Device, DeviceError, MissingDownlevelFlags, MissingFeatures,
34        RenderPassCompatibilityError, RenderPassContext,
35    },
36    global::Global,
37    hal_label, id,
38    init_tracker::{MemoryInitKind, TextureInitRange, TextureInitTrackerAction},
39    pipeline::{PipelineFlags, RenderPipeline, VertexStep},
40    resource::{
41        Buffer, DestroyedResourceError, InvalidResourceError, Labeled, MissingBufferUsageError,
42        MissingTextureUsageError, ParentDevice, QuerySet, RawResourceAccess, ResourceErrorIdent,
43        Texture, TextureView, TextureViewNotRenderableReason,
44    },
45    snatch::SnatchGuard,
46    track::{ResourceUsageCompatibilityError, Tracker, UsageScope},
47    validation::{self, check_workgroup_sizes},
48    Label,
49};
50
51#[cfg(feature = "serde")]
52use serde::Deserialize;
53#[cfg(feature = "serde")]
54use serde::Serialize;
55
56pub use wgt::{LoadOp, StoreOp};
57
58fn load_hal_ops<V>(load: LoadOp<V>) -> hal::AttachmentOps {
59    match load {
60        LoadOp::Load => hal::AttachmentOps::LOAD,
61        LoadOp::Clear(_) => hal::AttachmentOps::LOAD_CLEAR,
62        LoadOp::DontCare(_) => hal::AttachmentOps::LOAD_DONT_CARE,
63    }
64}
65
66fn store_hal_ops(store: StoreOp) -> hal::AttachmentOps {
67    match store {
68        StoreOp::Store => hal::AttachmentOps::STORE,
69        StoreOp::Discard => hal::AttachmentOps::STORE_DISCARD,
70    }
71}
72
73/// Describes an individual channel within a render pass, such as color, depth, or stencil.
74///
75/// A channel must either be read-only, or it must specify both load and store
76/// operations. See [`ResolvedPassChannel`] for a validated version.
77#[repr(C)]
78#[derive(Clone, Debug, Eq, PartialEq)]
79#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
80pub struct PassChannel<V> {
81    /// Operation to perform to the output attachment at the start of a
82    /// renderpass.
83    ///
84    /// This must be clear if it is the first renderpass rendering to a swap
85    /// chain image.
86    pub load_op: Option<LoadOp<V>>,
87    /// Operation to perform to the output attachment at the end of a renderpass.
88    pub store_op: Option<StoreOp>,
89    /// If true, the relevant channel is not changed by a renderpass, and the
90    /// corresponding attachment can be used inside the pass by other read-only
91    /// usages.
92    pub read_only: bool,
93}
94
95impl<V: Copy + Default> PassChannel<Option<V>> {
96    fn resolve(
97        &self,
98        handle_clear: impl Fn(Option<V>) -> Result<V, AttachmentError>,
99    ) -> Result<ResolvedPassChannel<V>, AttachmentError> {
100        if self.read_only {
101            if self.load_op.is_some() {
102                return Err(AttachmentError::ReadOnlyWithLoad);
103            }
104            if self.store_op.is_some() {
105                return Err(AttachmentError::ReadOnlyWithStore);
106            }
107            Ok(ResolvedPassChannel::ReadOnly)
108        } else {
109            Ok(ResolvedPassChannel::Operational(wgt::Operations {
110                load: match self.load_op.ok_or(AttachmentError::NoLoad)? {
111                    LoadOp::Clear(clear_value) => LoadOp::Clear(handle_clear(clear_value)?),
112                    LoadOp::DontCare(token) => LoadOp::DontCare(token),
113                    LoadOp::Load => LoadOp::Load,
114                },
115                store: self.store_op.ok_or(AttachmentError::NoStore)?,
116            }))
117        }
118    }
119}
120
121/// Describes an individual channel within a render pass, such as color, depth, or stencil.
122///
123/// Unlike [`PassChannel`], this version uses the Rust type system to guarantee
124/// a valid specification.
125#[derive(Clone, Debug)]
126#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
127pub enum ResolvedPassChannel<V> {
128    ReadOnly,
129    Operational(wgt::Operations<V>),
130}
131
132impl<V: Copy + Default> ResolvedPassChannel<V> {
133    fn load_op(&self) -> LoadOp<V> {
134        match self {
135            ResolvedPassChannel::ReadOnly => LoadOp::Load,
136            ResolvedPassChannel::Operational(wgt::Operations { load, .. }) => *load,
137        }
138    }
139
140    fn store_op(&self) -> StoreOp {
141        match self {
142            ResolvedPassChannel::ReadOnly => StoreOp::Store,
143            ResolvedPassChannel::Operational(wgt::Operations { store, .. }) => *store,
144        }
145    }
146
147    fn clear_value(&self) -> V {
148        match self {
149            Self::Operational(wgt::Operations {
150                load: LoadOp::Clear(clear_value),
151                ..
152            }) => *clear_value,
153            _ => Default::default(),
154        }
155    }
156
157    fn is_readonly(&self) -> bool {
158        matches!(self, Self::ReadOnly)
159    }
160
161    fn hal_ops(&self) -> hal::AttachmentOps {
162        load_hal_ops(self.load_op()) | store_hal_ops(self.store_op())
163    }
164}
165
166/// Describes a color attachment to a render pass.
167#[repr(C)]
168#[derive(Clone, Debug, PartialEq)]
169#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
170pub struct RenderPassColorAttachment<TV = id::TextureViewId> {
171    /// The view to use as an attachment.
172    pub view: TV,
173    /// The depth slice index of a 3D view. It must not be provided if the view is not 3D.
174    pub depth_slice: Option<u32>,
175    /// The view that will receive the resolved output if multisampling is used.
176    pub resolve_target: Option<TV>,
177    /// Operation to perform to the output attachment at the start of a
178    /// renderpass.
179    ///
180    /// This must be clear if it is the first renderpass rendering to a swap
181    /// chain image.
182    pub load_op: LoadOp<Color>,
183    /// Operation to perform to the output attachment at the end of a renderpass.
184    pub store_op: StoreOp,
185}
186
187pub type ArcRenderPassColorAttachment = RenderPassColorAttachment<Arc<TextureView>>;
188
189// Avoid allocation in the common case that there is only one color attachment,
190// but don't bloat `ArcCommand::RunRenderPass` excessively.
191pub type ColorAttachments<TV = Arc<TextureView>> =
192    SmallVec<[Option<RenderPassColorAttachment<TV>>; 1]>;
193
194impl ArcRenderPassColorAttachment {
195    fn hal_ops(&self) -> hal::AttachmentOps {
196        load_hal_ops(self.load_op) | store_hal_ops(self.store_op)
197    }
198
199    fn clear_value(&self) -> Color {
200        match self.load_op {
201            LoadOp::Clear(clear_value) => clear_value,
202            LoadOp::DontCare(_) | LoadOp::Load => Color::default(),
203        }
204    }
205}
206
207/// Describes a depth/stencil attachment to a render pass.
208///
209/// This version uses the unvalidated [`PassChannel`].
210#[repr(C)]
211#[derive(Clone, Debug, PartialEq)]
212#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
213pub struct RenderPassDepthStencilAttachment<TV> {
214    /// The view to use as an attachment.
215    pub view: TV,
216    /// What operations will be performed on the depth part of the attachment.
217    pub depth: PassChannel<Option<f32>>,
218    /// What operations will be performed on the stencil part of the attachment.
219    pub stencil: PassChannel<Option<u32>>,
220}
221
222/// Describes a depth/stencil attachment to a render pass.
223///
224/// This version uses the validated [`ResolvedPassChannel`].
225#[derive(Clone, Debug)]
226#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
227pub struct ResolvedRenderPassDepthStencilAttachment<TV> {
228    /// The view to use as an attachment.
229    pub view: TV,
230    /// What operations will be performed on the depth part of the attachment.
231    pub depth: ResolvedPassChannel<f32>,
232    /// What operations will be performed on the stencil part of the attachment.
233    pub stencil: ResolvedPassChannel<u32>,
234}
235
236/// Describes the attachments of a render pass.
237#[derive(Clone, Debug, Default, PartialEq)]
238pub struct RenderPassDescriptor<'a> {
239    pub label: Label<'a>,
240    /// The color attachments of the render pass.
241    pub color_attachments: Cow<'a, [Option<RenderPassColorAttachment>]>,
242    /// The depth and stencil attachment of the render pass, if any.
243    pub depth_stencil_attachment: Option<&'a RenderPassDepthStencilAttachment<id::TextureViewId>>,
244    /// Defines where and when timestamp values will be written for this pass.
245    pub timestamp_writes: Option<&'a PassTimestampWrites>,
246    /// Defines where the occlusion query results will be stored for this pass.
247    pub occlusion_query_set: Option<id::QuerySetId>,
248    /// The multiview array layers that will be used
249    pub multiview_mask: Option<NonZeroU32>,
250}
251
252/// Describes the attachments of a render pass.
253struct ArcRenderPassDescriptor<'a> {
254    pub label: &'a Label<'a>,
255    /// The color attachments of the render pass.
256    pub color_attachments:
257        ArrayVec<Option<ArcRenderPassColorAttachment>, { hal::MAX_COLOR_ATTACHMENTS }>,
258    /// The depth and stencil attachment of the render pass, if any.
259    pub depth_stencil_attachment:
260        Option<ResolvedRenderPassDepthStencilAttachment<Arc<TextureView>>>,
261    /// Defines where and when timestamp values will be written for this pass.
262    pub timestamp_writes: Option<ArcPassTimestampWrites>,
263    /// Defines where the occlusion query results will be stored for this pass.
264    pub occlusion_query_set: Option<Arc<QuerySet>>,
265    /// The multiview array layers that will be used
266    pub multiview_mask: Option<NonZeroU32>,
267}
268
269pub type RenderBasePass = BasePass<ArcRenderCommand, RenderPassError>;
270
271/// A pass's [encoder state](https://www.w3.org/TR/webgpu/#encoder-state) and
272/// its validity are two distinct conditions, i.e., the full matrix of
273/// (open, ended) x (valid, invalid) is possible.
274///
275/// The presence or absence of the `parent` `Option` indicates the pass's state.
276/// The presence or absence of an error in `base.error` indicates the pass's
277/// validity.
278pub struct RenderPass {
279    /// All pass data & records is stored here.
280    base: BasePass<ArcRenderCommand, RenderPassError>,
281
282    /// Parent command encoder that this pass records commands into.
283    ///
284    /// If this is `Some`, then the pass is in WebGPU's "open" state. If it is
285    /// `None`, then the pass is in the "ended" state.
286    /// See <https://www.w3.org/TR/webgpu/#encoder-state>
287    parent: Option<Arc<CommandEncoder>>,
288
289    color_attachments:
290        ArrayVec<Option<ArcRenderPassColorAttachment>, { hal::MAX_COLOR_ATTACHMENTS }>,
291    depth_stencil_attachment: Option<ResolvedRenderPassDepthStencilAttachment<Arc<TextureView>>>,
292    timestamp_writes: Option<ArcPassTimestampWrites>,
293    occlusion_query_set: Option<Arc<QuerySet>>,
294    multiview_mask: Option<NonZeroU32>,
295
296    // Resource binding dedupe state.
297    current_bind_groups: BindGroupStateChange,
298    current_pipeline: StateChange<id::RenderPipelineId>,
299}
300
301impl RenderPass {
302    /// If the parent command encoder is invalid, the returned pass will be invalid.
303    fn new(parent: Arc<CommandEncoder>, desc: ArcRenderPassDescriptor) -> Self {
304        let ArcRenderPassDescriptor {
305            label,
306            timestamp_writes,
307            color_attachments,
308            depth_stencil_attachment,
309            occlusion_query_set,
310            multiview_mask,
311        } = desc;
312
313        Self {
314            base: BasePass::new(label),
315            parent: Some(parent),
316            color_attachments,
317            depth_stencil_attachment,
318            timestamp_writes,
319            occlusion_query_set,
320            multiview_mask,
321
322            current_bind_groups: BindGroupStateChange::new(),
323            current_pipeline: StateChange::new(),
324        }
325    }
326
327    fn new_invalid(parent: Arc<CommandEncoder>, label: &Label, err: RenderPassError) -> Self {
328        Self {
329            base: BasePass::new_invalid(label, err),
330            parent: Some(parent),
331            color_attachments: ArrayVec::new(),
332            depth_stencil_attachment: None,
333            timestamp_writes: None,
334            occlusion_query_set: None,
335            multiview_mask: None,
336            current_bind_groups: BindGroupStateChange::new(),
337            current_pipeline: StateChange::new(),
338        }
339    }
340
341    #[inline]
342    pub fn label(&self) -> Option<&str> {
343        self.base.label.as_deref()
344    }
345}
346
347impl fmt::Debug for RenderPass {
348    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
349        f.debug_struct("RenderPass")
350            .field("label", &self.label())
351            .field("color_attachments", &self.color_attachments)
352            .field("depth_stencil_target", &self.depth_stencil_attachment)
353            .field("command count", &self.base.commands.len())
354            .field("dynamic offset count", &self.base.dynamic_offsets.len())
355            .field("immediate data u32 count", &self.base.immediates_data.len())
356            .field("multiview mask", &self.multiview_mask)
357            .finish()
358    }
359}
360
361#[derive(Debug, PartialEq)]
362enum OptionalState {
363    Unused,
364    Required,
365    Set,
366}
367
368impl OptionalState {
369    fn require(&mut self, require: bool) {
370        if require && *self == Self::Unused {
371            *self = Self::Required;
372        }
373    }
374}
375
376#[derive(Debug, Default)]
377struct IndexState {
378    buffer_format: Option<IndexFormat>,
379    limit: u64,
380}
381
382impl IndexState {
383    fn update_buffer(&mut self, range: Range<BufferAddress>, format: IndexFormat) {
384        self.buffer_format = Some(format);
385        let shift = match format {
386            IndexFormat::Uint16 => 1,
387            IndexFormat::Uint32 => 2,
388        };
389        self.limit = (range.end - range.start) >> shift;
390    }
391
392    fn reset(&mut self) {
393        self.buffer_format = None;
394        self.limit = 0;
395    }
396}
397
398#[derive(Debug, Default)]
399pub(crate) struct VertexLimits {
400    /// Length of the shortest vertex rate vertex buffer
401    pub(crate) vertex_limit: u64,
402    /// Buffer slot which the shortest vertex rate vertex buffer is bound to
403    vertex_limit_slot: u32,
404    /// Length of the shortest instance rate vertex buffer
405    pub(crate) instance_limit: u64,
406    /// Buffer slot which the shortest instance rate vertex buffer is bound to
407    instance_limit_slot: u32,
408}
409
410impl VertexLimits {
411    pub(crate) fn new(
412        buffer_sizes: impl ExactSizeIterator<Item = Option<BufferAddress>>,
413        pipeline_steps: &[Option<VertexStep>],
414    ) -> Self {
415        // Implements the validation from https://gpuweb.github.io/gpuweb/#dom-gpurendercommandsmixin-draw
416        // Except that the formula is shuffled to extract the number of vertices in order
417        // to carry the bulk of the computation when changing states instead of when producing
418        // draws. Draw calls tend to happen at a higher frequency. Here we determine vertex
419        // limits that can be cheaply checked for each draw call.
420
421        let mut vertex_limit = u64::MAX;
422        let mut vertex_limit_slot = 0;
423        let mut instance_limit = u64::MAX;
424        let mut instance_limit_slot = 0;
425
426        for (idx, (buffer_size, step)) in buffer_sizes.zip(pipeline_steps).enumerate() {
427            let Some(step) = step else {
428                continue;
429            };
430
431            let Some(buffer_size) = buffer_size else {
432                // Missing required vertex buffer
433                return Self::default();
434            };
435
436            let limit = if buffer_size < step.last_stride {
437                // The buffer cannot fit the last vertex.
438                0
439            } else {
440                if step.stride == 0 {
441                    // We already checked that the last stride fits, the same
442                    // vertex will be repeated so this slot can accommodate any number of
443                    // vertices.
444                    continue;
445                }
446
447                // The general case.
448                (buffer_size - step.last_stride) / step.stride + 1
449            };
450
451            match step.mode {
452                VertexStepMode::Vertex => {
453                    if limit < vertex_limit {
454                        vertex_limit = limit;
455                        vertex_limit_slot = idx as _;
456                    }
457                }
458                VertexStepMode::Instance => {
459                    if limit < instance_limit {
460                        instance_limit = limit;
461                        instance_limit_slot = idx as _;
462                    }
463                }
464            }
465        }
466
467        Self {
468            vertex_limit,
469            vertex_limit_slot,
470            instance_limit,
471            instance_limit_slot,
472        }
473    }
474
475    pub(crate) fn validate_vertex_limit(
476        &self,
477        first_vertex: u32,
478        vertex_count: u32,
479    ) -> Result<(), DrawError> {
480        let last_vertex = first_vertex as u64 + vertex_count as u64;
481        let vertex_limit = self.vertex_limit;
482        if last_vertex > vertex_limit {
483            return Err(DrawError::VertexBeyondLimit {
484                last_vertex,
485                vertex_limit,
486                slot: self.vertex_limit_slot,
487            });
488        }
489
490        Ok(())
491    }
492
493    pub(crate) fn validate_instance_limit(
494        &self,
495        first_instance: u32,
496        instance_count: u32,
497    ) -> Result<(), DrawError> {
498        let last_instance = first_instance as u64 + instance_count as u64;
499        let instance_limit = self.instance_limit;
500        if last_instance > instance_limit {
501            return Err(DrawError::InstanceBeyondLimit {
502                last_instance,
503                instance_limit,
504                slot: self.instance_limit_slot,
505            });
506        }
507
508        Ok(())
509    }
510}
511
512/// State of a single vertex buffer slot.
513#[derive(Debug)]
514pub(crate) struct VertexSlot {
515    pub(crate) buffer: Arc<Buffer>,
516    pub(crate) range: Range<BufferAddress>,
517    pub(crate) is_dirty: bool,
518}
519
520/// Vertex buffer tracking state, shared between render passes and render bundles.
521///
522/// Tracks which vertex buffer slots are set, and caches the vertex and instance limits
523/// derived from those buffers and the current pipeline, avoiding recomputation on each draw.
524#[derive(Debug, Default)]
525pub(crate) struct VertexState {
526    slots: [Option<VertexSlot>; hal::MAX_VERTEX_BUFFERS],
527    pub(crate) limits: VertexLimits,
528}
529
530impl VertexState {
531    /// Set a vertex buffer slot, marking it dirty.
532    pub(crate) fn set_buffer(
533        &mut self,
534        slot: usize,
535        buffer: Arc<Buffer>,
536        range: Range<BufferAddress>,
537    ) {
538        self.slots[slot] = Some(VertexSlot {
539            buffer,
540            range,
541            is_dirty: true,
542        });
543    }
544
545    /// Clear a vertex buffer slot.
546    pub(crate) fn clear_buffer(&mut self, slot: usize) {
547        self.slots[slot] = None;
548    }
549
550    /// Recompute the cached vertex and instance limits based on the current slots and pipeline.
551    pub(crate) fn update_limits(&mut self, pipeline_steps: &[Option<VertexStep>]) {
552        self.limits = VertexLimits::new(
553            self.slots
554                .iter()
555                .map(|s| s.as_ref().map(|s| s.range.end - s.range.start)),
556            pipeline_steps,
557        );
558    }
559
560    fn last_assigned_index(&self) -> Option<usize> {
561        self.slots
562            .iter()
563            .enumerate()
564            .filter_map(|(i, s)| s.as_ref().map(|_| i))
565            .next_back()
566    }
567
568    pub(super) fn validate(
569        &self,
570        pipeline: &RenderPipeline,
571        binder: &Binder,
572    ) -> Result<(), DrawError> {
573        // Check all needed vertex buffers have been bound
574        for index in pipeline
575            .vertex_steps
576            .iter()
577            .enumerate()
578            .filter_map(|(index, step)| step.map(|_| index))
579        {
580            if self.slots[index].is_none() {
581                return Err(DrawError::MissingVertexBuffer {
582                    pipeline: pipeline.error_ident(),
583                    index,
584                });
585            }
586        }
587
588        let bind_group_space_used = binder.last_assigned_index().map_or(0, |i| i + 1);
589        let vertex_buffer_space_used = self.last_assigned_index().map_or(0, |i| i + 1);
590
591        let bind_groups_plus_vertex_buffers =
592            u32::try_from(bind_group_space_used + vertex_buffer_space_used).unwrap();
593        if bind_groups_plus_vertex_buffers
594            > pipeline.device.limits.max_bind_groups_plus_vertex_buffers
595        {
596            return Err(DrawError::TooManyBindGroupsPlusVertexBuffers {
597                given: bind_groups_plus_vertex_buffers,
598                limit: pipeline.device.limits.max_bind_groups_plus_vertex_buffers,
599            });
600        }
601
602        Ok(())
603    }
604
605    /// Call `f` for each dirty slot with `(slot_index, buffer, offset, size)` and mark them clean.
606    pub(crate) fn flush<F>(&mut self, mut f: F)
607    where
608        F: FnMut(u32, &Arc<Buffer>, BufferAddress, Option<BufferSize>),
609    {
610        for (i, slot) in self.slots.iter_mut().enumerate() {
611            let Some(slot) = slot.as_mut() else { continue };
612            if !slot.is_dirty {
613                continue;
614            }
615            slot.is_dirty = false;
616            let size = slot.range.end - slot.range.start;
617            f(
618                i as u32,
619                &slot.buffer,
620                slot.range.start,
621                BufferSize::new(size),
622            );
623        }
624    }
625}
626
627struct State<'scope, 'snatch_guard, 'cmd_enc> {
628    pipeline_flags: PipelineFlags,
629    blend_constant: OptionalState,
630    stencil_reference: u32,
631    pipeline: Option<Arc<RenderPipeline>>,
632    index: IndexState,
633    vertex: VertexState,
634
635    info: RenderPassInfo,
636
637    pass: pass::PassState<'scope, 'snatch_guard, 'cmd_enc>,
638
639    /// A bitmask, tracking which 4-byte slots have been written via `set_immediates`.
640    /// Checked against the pipeline's required slots before each draw call.
641    immediate_slots_set: naga::valid::ImmediateSlots,
642
643    active_occlusion_query: Option<(Arc<QuerySet>, u32)>,
644    active_pipeline_statistics_query: Option<(Arc<QuerySet>, u32)>,
645}
646
647impl<'scope, 'snatch_guard, 'cmd_enc> State<'scope, 'snatch_guard, 'cmd_enc> {
648    fn is_ready(&self, family: DrawCommandFamily) -> Result<(), DrawError> {
649        if let Some(pipeline) = self.pipeline.as_ref() {
650            self.pass.binder.check_compatibility(pipeline.as_ref())?;
651            self.pass.binder.check_late_buffer_bindings()?;
652
653            if self.blend_constant == OptionalState::Required {
654                return Err(DrawError::MissingBlendConstant);
655            }
656
657            self.vertex.validate(pipeline.as_ref(), &self.pass.binder)?;
658
659            if family == DrawCommandFamily::DrawIndexed {
660                // Pipeline expects an index buffer
661                // We have a buffer bound
662                let buffer_index_format = self
663                    .index
664                    .buffer_format
665                    .ok_or(DrawError::MissingIndexBuffer)?;
666
667                if pipeline.topology.is_strip()
668                    && pipeline.strip_index_format != Some(buffer_index_format)
669                {
670                    return Err(DrawError::UnmatchedStripIndexFormat {
671                        pipeline: pipeline.error_ident(),
672                        strip_index_format: pipeline.strip_index_format,
673                        buffer_format: buffer_index_format,
674                    });
675                }
676            }
677            if (family == DrawCommandFamily::DrawMeshTasks) != pipeline.is_mesh {
678                return Err(DrawError::WrongPipelineType {
679                    wanted_mesh_pipeline: !pipeline.is_mesh,
680                });
681            }
682            if !self
683                .immediate_slots_set
684                .contains(pipeline.immediate_slots_required)
685            {
686                return Err(DrawError::MissingImmediateData {
687                    missing: pipeline
688                        .immediate_slots_required
689                        .difference(self.immediate_slots_set),
690                });
691            }
692            Ok(())
693        } else {
694            Err(DrawError::MissingPipeline(pass::MissingPipeline))
695        }
696    }
697
698    /// Flush binding state in preparation for a draw call.
699    ///
700    /// See the compute pass version for an explanation of some ways that
701    /// `flush_bindings` differs between the two types of passes.
702    fn flush_bindings(&mut self) -> Result<(), RenderPassErrorInner> {
703        flush_bindings_helper(&mut self.pass)?;
704        Ok(())
705    }
706
707    /// Reset the `RenderBundle`-related states.
708    fn reset_bundle(&mut self) {
709        self.pass.binder.reset();
710        self.pipeline = None;
711        self.index.reset();
712        self.vertex = Default::default();
713        self.immediate_slots_set = Default::default();
714    }
715
716    /// Flush dirty vertex buffer slots to the HAL encoder in preparation for a draw call.
717    fn flush_vertex_buffers(&mut self) -> Result<(), RenderPassErrorInner> {
718        let vertex = &mut self.vertex;
719        let raw_encoder: &mut dyn hal::DynCommandEncoder = self.pass.base.raw_encoder;
720        let snatch_guard = self.pass.base.snatch_guard;
721        let mut result = Ok(());
722        vertex.flush(|slot, buffer, offset, size| {
723            if result.is_err() {
724                return;
725            }
726            match buffer.try_raw(snatch_guard) {
727                Ok(raw) => unsafe {
728                    // SAFETY: The offset and size were validated in set_vertex_buffer.
729                    raw_encoder.set_vertex_buffer(
730                        slot,
731                        hal::BufferBinding::new_unchecked(raw, offset, size),
732                    );
733                },
734                Err(e) => result = Err(e.into()),
735            }
736        });
737        result
738    }
739}
740
741/// Describes an attachment location in words.
742///
743/// Can be used as "the {loc} has..." or "{loc} has..."
744#[derive(Debug, Copy, Clone)]
745pub enum AttachmentErrorLocation {
746    Color { index: usize, resolve: bool },
747    Depth,
748}
749
750impl fmt::Display for AttachmentErrorLocation {
751    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
752        match *self {
753            AttachmentErrorLocation::Color {
754                index,
755                resolve: false,
756            } => write!(f, "color attachment at index {index}'s texture view"),
757            AttachmentErrorLocation::Color {
758                index,
759                resolve: true,
760            } => write!(
761                f,
762                "color attachment at index {index}'s resolve texture view"
763            ),
764            AttachmentErrorLocation::Depth => write!(f, "depth attachment's texture view"),
765        }
766    }
767}
768
769#[derive(Clone, Debug, Error)]
770#[non_exhaustive]
771pub enum ColorAttachmentError {
772    #[error("Attachment format {0:?} is not a color format")]
773    InvalidFormat(wgt::TextureFormat),
774    #[error("The number of color attachments {given} exceeds the limit {limit}")]
775    TooMany { given: usize, limit: usize },
776    #[error("The total number of bytes per sample in color attachments {total} exceeds the limit {limit}")]
777    TooManyBytesPerSample { total: u32, limit: u32 },
778    #[error("Depth slice must be less than {limit} but is {given}")]
779    DepthSliceLimit { given: u32, limit: u32 },
780    #[error("Color attachment's view is 3D and requires depth slice to be provided")]
781    MissingDepthSlice,
782    #[error("Depth slice was provided but the color attachment's view is not 3D")]
783    UnneededDepthSlice,
784    #[error("{view}'s subresource at mip {mip_level} and depth/array layer {depth_or_array_layer} is already attached to this render pass")]
785    SubresourceOverlap {
786        view: ResourceErrorIdent,
787        mip_level: u32,
788        depth_or_array_layer: u32,
789    },
790    #[error("Color attachment's usage contains {0:?}. This can only be used with StoreOp::{1:?}, but StoreOp::{2:?} was provided")]
791    InvalidUsageForStoreOp(TextureUsages, StoreOp, StoreOp),
792}
793
794impl WebGpuError for ColorAttachmentError {
795    fn webgpu_error_type(&self) -> ErrorType {
796        ErrorType::Validation
797    }
798}
799
800#[derive(Clone, Debug, Error)]
801#[non_exhaustive]
802pub enum AttachmentError {
803    #[error("The format of the depth-stencil attachment ({0:?}) is not a depth-or-stencil format")]
804    InvalidDepthStencilAttachmentFormat(wgt::TextureFormat),
805    #[error("LoadOp must be None for read-only attachments")]
806    ReadOnlyWithLoad,
807    #[error("StoreOp must be None for read-only attachments")]
808    ReadOnlyWithStore,
809    #[error("Attachment without load")]
810    NoLoad,
811    #[error("Attachment without store")]
812    NoStore,
813    #[error("LoadOp is `Clear` but no clear value was provided")]
814    NoClearValue,
815    #[error("Clear value ({0}) must be between 0.0 and 1.0, inclusive")]
816    ClearValueOutOfRange(f32),
817}
818
819impl WebGpuError for AttachmentError {
820    fn webgpu_error_type(&self) -> ErrorType {
821        ErrorType::Validation
822    }
823}
824
825/// Error encountered when performing a render pass.
826#[derive(Clone, Debug, Error)]
827pub enum RenderPassErrorInner {
828    #[error(transparent)]
829    Device(#[from] DeviceError),
830    #[error(transparent)]
831    ColorAttachment(#[from] ColorAttachmentError),
832    #[error(transparent)]
833    InvalidAttachment(#[from] AttachmentError),
834    #[error(transparent)]
835    EncoderState(#[from] EncoderStateError),
836    #[error("Parent encoder is invalid")]
837    InvalidParentEncoder,
838    #[error(transparent)]
839    DebugGroupError(#[from] DebugGroupError),
840    #[error("The format of the {location} ({format:?}) is not resolvable")]
841    UnsupportedResolveTargetFormat {
842        location: AttachmentErrorLocation,
843        format: wgt::TextureFormat,
844    },
845    #[error("No color attachments or depth attachments were provided, at least one attachment of any kind must be provided")]
846    MissingAttachments,
847    #[error("The {location} is not renderable:")]
848    TextureViewIsNotRenderable {
849        location: AttachmentErrorLocation,
850        #[source]
851        reason: TextureViewNotRenderableReason,
852    },
853    #[error("Attachments have differing sizes: the {expected_location} has extent {expected_extent:?} but is followed by the {actual_location} which has {actual_extent:?}")]
854    AttachmentsDimensionMismatch {
855        expected_location: AttachmentErrorLocation,
856        expected_extent: wgt::Extent3d,
857        actual_location: AttachmentErrorLocation,
858        actual_extent: wgt::Extent3d,
859    },
860    #[error("Attachments have differing sample counts: the {expected_location} has count {expected_samples:?} but is followed by the {actual_location} which has count {actual_samples:?}")]
861    AttachmentSampleCountMismatch {
862        expected_location: AttachmentErrorLocation,
863        expected_samples: u32,
864        actual_location: AttachmentErrorLocation,
865        actual_samples: u32,
866    },
867    #[error("The resolve source, {location}, must be multi-sampled (has {src} samples) while the resolve destination must not be multisampled (has {dst} samples)")]
868    InvalidResolveSampleCounts {
869        location: AttachmentErrorLocation,
870        src: u32,
871        dst: u32,
872    },
873    #[error(
874        "Resource source, {location}, format ({src:?}) must match the resolve destination format ({dst:?})"
875    )]
876    MismatchedResolveTextureFormat {
877        location: AttachmentErrorLocation,
878        src: wgt::TextureFormat,
879        dst: wgt::TextureFormat,
880    },
881    #[error("Unable to clear non-present/read-only depth")]
882    InvalidDepthOps,
883    #[error("Unable to clear non-present/read-only stencil")]
884    InvalidStencilOps,
885    #[error(transparent)]
886    InvalidValuesOffset(#[from] pass::InvalidValuesOffset),
887    #[error(transparent)]
888    MissingFeatures(#[from] MissingFeatures),
889    #[error(transparent)]
890    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
891    #[error("Indirect buffer offset {0:?} is not a multiple of 4")]
892    UnalignedIndirectBufferOffset(BufferAddress),
893    #[error("Indirect draw arguments of {args_size} bytes (count = {count}) starting at {offset} would overrun buffer size of {buffer_size}")]
894    IndirectBufferOverrun {
895        count: u32,
896        offset: u64,
897        args_size: u64,
898        buffer_size: u64,
899    },
900    #[error("Indirect draw count of {count_bytes} bytes starting at {begin_count_offset} would overrun buffer of size {count_buffer_size}")]
901    IndirectCountBufferOverrun {
902        count_bytes: u64,
903        begin_count_offset: u64,
904        count_buffer_size: u64,
905    },
906    #[error(transparent)]
907    ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),
908    #[error("Render bundle has incompatible targets, {0}")]
909    IncompatibleBundleTargets(#[from] RenderPassCompatibilityError),
910    #[error(
911        "Render bundle has incompatible read-only flags: \
912             bundle has flags depth = {bundle_depth} and stencil = {bundle_stencil}, \
913             while the pass has flags depth = {pass_depth} and stencil = {pass_stencil}. \
914             Read-only renderpasses are only compatible with read-only bundles for that aspect."
915    )]
916    IncompatibleBundleReadOnlyDepthStencil {
917        pass_depth: bool,
918        pass_stencil: bool,
919        bundle_depth: bool,
920        bundle_stencil: bool,
921    },
922    #[error(transparent)]
923    RenderCommand(#[from] RenderCommandError),
924    #[error(transparent)]
925    Draw(#[from] DrawError),
926    #[error(transparent)]
927    Bind(#[from] BindError),
928    #[error("Immediate data offset must be aligned to 4 bytes")]
929    ImmediateOffsetAlignment,
930    #[error("Immediate data size must be aligned to 4 bytes")]
931    ImmediateDataizeAlignment,
932    #[error("Ran out of immediate data space. Don't set 4gb of immediates per ComputePass.")]
933    ImmediateOutOfMemory,
934    #[error(transparent)]
935    QueryUse(#[from] QueryUseError),
936    #[error("Multiview layer count must match")]
937    MultiViewMismatch,
938    #[error(
939        "Multiview pass texture views with more than one array layer must have D2Array dimension"
940    )]
941    MultiViewDimensionMismatch,
942    #[error("Multiview view count limit violated")]
943    TooManyMultiviewViews,
944    #[error("missing occlusion query set")]
945    MissingOcclusionQuerySet,
946    #[error(transparent)]
947    DestroyedResource(#[from] DestroyedResourceError),
948    #[error("The compute pass has already been ended and no further commands can be recorded")]
949    PassEnded,
950    #[error(transparent)]
951    InvalidResource(#[from] InvalidResourceError),
952    #[error(transparent)]
953    TimestampWrites(#[from] TimestampWritesError),
954}
955
956impl From<MissingBufferUsageError> for RenderPassErrorInner {
957    fn from(error: MissingBufferUsageError) -> Self {
958        Self::RenderCommand(error.into())
959    }
960}
961
962impl From<MissingTextureUsageError> for RenderPassErrorInner {
963    fn from(error: MissingTextureUsageError) -> Self {
964        Self::RenderCommand(error.into())
965    }
966}
967
968impl From<pass::BindGroupIndexOutOfRange> for RenderPassErrorInner {
969    fn from(error: pass::BindGroupIndexOutOfRange) -> Self {
970        Self::RenderCommand(RenderCommandError::BindGroupIndexOutOfRange(error))
971    }
972}
973
974impl From<pass::MissingPipeline> for RenderPassErrorInner {
975    fn from(error: pass::MissingPipeline) -> Self {
976        Self::Draw(DrawError::MissingPipeline(error))
977    }
978}
979
980impl From<ImmediateUploadError> for RenderPassErrorInner {
981    fn from(error: ImmediateUploadError) -> Self {
982        Self::RenderCommand(error.into())
983    }
984}
985
986/// Error encountered when performing a render pass.
987#[derive(Clone, Debug, Error)]
988#[error("{scope}")]
989pub struct RenderPassError {
990    pub scope: PassErrorScope,
991    #[source]
992    pub(super) inner: RenderPassErrorInner,
993}
994
995impl<E: Into<RenderPassErrorInner>> MapPassErr<RenderPassError> for E {
996    fn map_pass_err(self, scope: PassErrorScope) -> RenderPassError {
997        RenderPassError {
998            scope,
999            inner: self.into(),
1000        }
1001    }
1002}
1003
1004impl WebGpuError for RenderPassError {
1005    fn webgpu_error_type(&self) -> ErrorType {
1006        let Self { scope: _, inner } = self;
1007        match inner {
1008            RenderPassErrorInner::Device(e) => e.webgpu_error_type(),
1009            RenderPassErrorInner::ColorAttachment(e) => e.webgpu_error_type(),
1010            RenderPassErrorInner::EncoderState(e) => e.webgpu_error_type(),
1011            RenderPassErrorInner::DebugGroupError(e) => e.webgpu_error_type(),
1012            RenderPassErrorInner::MissingFeatures(e) => e.webgpu_error_type(),
1013            RenderPassErrorInner::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1014            RenderPassErrorInner::RenderCommand(e) => e.webgpu_error_type(),
1015            RenderPassErrorInner::Draw(e) => e.webgpu_error_type(),
1016            RenderPassErrorInner::Bind(e) => e.webgpu_error_type(),
1017            RenderPassErrorInner::QueryUse(e) => e.webgpu_error_type(),
1018            RenderPassErrorInner::DestroyedResource(e) => e.webgpu_error_type(),
1019            RenderPassErrorInner::InvalidResource(e) => e.webgpu_error_type(),
1020            RenderPassErrorInner::IncompatibleBundleTargets(e) => e.webgpu_error_type(),
1021            RenderPassErrorInner::InvalidAttachment(e) => e.webgpu_error_type(),
1022            RenderPassErrorInner::TimestampWrites(e) => e.webgpu_error_type(),
1023            RenderPassErrorInner::InvalidValuesOffset(e) => e.webgpu_error_type(),
1024
1025            RenderPassErrorInner::InvalidParentEncoder
1026            | RenderPassErrorInner::UnsupportedResolveTargetFormat { .. }
1027            | RenderPassErrorInner::MissingAttachments
1028            | RenderPassErrorInner::TextureViewIsNotRenderable { .. }
1029            | RenderPassErrorInner::AttachmentsDimensionMismatch { .. }
1030            | RenderPassErrorInner::AttachmentSampleCountMismatch { .. }
1031            | RenderPassErrorInner::InvalidResolveSampleCounts { .. }
1032            | RenderPassErrorInner::MismatchedResolveTextureFormat { .. }
1033            | RenderPassErrorInner::InvalidDepthOps
1034            | RenderPassErrorInner::InvalidStencilOps
1035            | RenderPassErrorInner::UnalignedIndirectBufferOffset(..)
1036            | RenderPassErrorInner::IndirectBufferOverrun { .. }
1037            | RenderPassErrorInner::IndirectCountBufferOverrun { .. }
1038            | RenderPassErrorInner::ResourceUsageCompatibility(..)
1039            | RenderPassErrorInner::IncompatibleBundleReadOnlyDepthStencil { .. }
1040            | RenderPassErrorInner::ImmediateOffsetAlignment
1041            | RenderPassErrorInner::ImmediateDataizeAlignment
1042            | RenderPassErrorInner::ImmediateOutOfMemory
1043            | RenderPassErrorInner::MultiViewMismatch
1044            | RenderPassErrorInner::MultiViewDimensionMismatch
1045            | RenderPassErrorInner::TooManyMultiviewViews
1046            | RenderPassErrorInner::MissingOcclusionQuerySet
1047            | RenderPassErrorInner::PassEnded => ErrorType::Validation,
1048        }
1049    }
1050}
1051
1052struct RenderAttachment {
1053    texture: Arc<Texture>,
1054    selector: TextureSelector,
1055    usage: wgt::TextureUses,
1056}
1057
1058impl TextureView {
1059    fn to_render_attachment(&self, usage: wgt::TextureUses) -> RenderAttachment {
1060        RenderAttachment {
1061            texture: self.parent.clone(),
1062            selector: self.selector.clone(),
1063            usage,
1064        }
1065    }
1066}
1067
1068const MAX_TOTAL_ATTACHMENTS: usize = hal::MAX_COLOR_ATTACHMENTS + hal::MAX_COLOR_ATTACHMENTS + 1;
1069type AttachmentDataVec<T> = ArrayVec<T, MAX_TOTAL_ATTACHMENTS>;
1070
1071struct RenderPassInfo {
1072    context: RenderPassContext,
1073    /// All render attachments, including depth/stencil
1074    render_attachments: AttachmentDataVec<RenderAttachment>,
1075    is_depth_read_only: bool,
1076    is_stencil_read_only: bool,
1077    extent: wgt::Extent3d,
1078
1079    divergent_discarded_depth_stencil_aspect: Option<(wgt::TextureAspect, Arc<TextureView>)>,
1080    multiview_mask: Option<NonZeroU32>,
1081}
1082
1083impl RenderPassInfo {
1084    fn add_pass_texture_init_actions<V>(
1085        load_op: LoadOp<V>,
1086        store_op: StoreOp,
1087        texture_memory_actions: &mut CommandBufferTextureMemoryActions,
1088        view: &TextureView,
1089        pending_discard_init_fixups: &mut SurfacesInDiscardState,
1090    ) {
1091        if matches!(load_op, LoadOp::Load) {
1092            pending_discard_init_fixups.extend(texture_memory_actions.register_init_action(
1093                &TextureInitTrackerAction {
1094                    texture: view.parent.clone(),
1095                    range: TextureInitRange::from(view.selector.clone()),
1096                    // Note that this is needed even if the target is discarded,
1097                    kind: MemoryInitKind::NeedsInitializedMemory,
1098                },
1099            ));
1100        } else if store_op == StoreOp::Store {
1101            // Clear + Store
1102            texture_memory_actions.register_implicit_init(
1103                &view.parent,
1104                TextureInitRange::from(view.selector.clone()),
1105            );
1106        }
1107        if store_op == StoreOp::Discard {
1108            // the discard happens at the *end* of a pass, but recording the
1109            // discard right away be alright since the texture can't be used
1110            // during the pass anyways
1111            texture_memory_actions.discard(TextureSurfaceDiscard {
1112                texture: view.parent.clone(),
1113                mip_level: view.selector.mips.start,
1114                layer: view.selector.layers.start,
1115            });
1116        }
1117    }
1118
1119    fn start(
1120        device: &Arc<Device>,
1121        hal_label: Option<&str>,
1122        color_attachments: &[Option<ArcRenderPassColorAttachment>],
1123        mut depth_stencil_attachment: Option<
1124            ResolvedRenderPassDepthStencilAttachment<Arc<TextureView>>,
1125        >,
1126        mut timestamp_writes: Option<ArcPassTimestampWrites>,
1127        mut occlusion_query_set: Option<Arc<QuerySet>>,
1128        encoder: &mut dyn hal::DynCommandEncoder,
1129        trackers: &mut Tracker,
1130        texture_memory_actions: &mut CommandBufferTextureMemoryActions,
1131        pending_query_resets: &mut QueryResetMap,
1132        pending_discard_init_fixups: &mut SurfacesInDiscardState,
1133        snatch_guard: &SnatchGuard<'_>,
1134        multiview_mask: Option<NonZeroU32>,
1135    ) -> Result<Self, RenderPassErrorInner> {
1136        profiling::scope!("RenderPassInfo::start");
1137
1138        // We default to false intentionally, even if depth-stencil isn't used at all.
1139        // This allows us to use the primary raw pipeline in `RenderPipeline`,
1140        // instead of the special read-only one, which would be `None`.
1141        let mut is_depth_read_only = false;
1142        let mut is_stencil_read_only = false;
1143
1144        let mut render_attachments = AttachmentDataVec::<RenderAttachment>::new();
1145        let mut discarded_surfaces = AttachmentDataVec::new();
1146        let mut divergent_discarded_depth_stencil_aspect = None;
1147
1148        let mut attachment_location = AttachmentErrorLocation::Color {
1149            index: usize::MAX,
1150            resolve: false,
1151        };
1152        let mut extent = None;
1153        let mut sample_count = 0;
1154
1155        let mut detected_multiview: Option<Option<NonZeroU32>> = None;
1156
1157        let mut check_multiview = |view: &TextureView| {
1158            // Get the multiview configuration for this texture view
1159            let layers = view.selector.layers.end - view.selector.layers.start;
1160            let this_multiview = if layers >= 2 {
1161                // Trivially proven by the if above
1162                Some(unsafe { NonZeroU32::new_unchecked(layers) })
1163            } else {
1164                None
1165            };
1166
1167            // Make sure that if this view is a multiview, it is set to be an array
1168            if this_multiview.is_some() && view.desc.dimension != TextureViewDimension::D2Array {
1169                return Err(RenderPassErrorInner::MultiViewDimensionMismatch);
1170            }
1171
1172            // Validate matching first, or store the first one
1173            if let Some(multiview) = detected_multiview {
1174                if multiview != this_multiview {
1175                    return Err(RenderPassErrorInner::MultiViewMismatch);
1176                }
1177            } else {
1178                // Multiview is only supported if the feature is enabled
1179                if let Some(this_multiview) = this_multiview {
1180                    device.require_features(wgt::Features::MULTIVIEW)?;
1181                    if this_multiview.get() > device.limits.max_multiview_view_count {
1182                        return Err(RenderPassErrorInner::TooManyMultiviewViews);
1183                    }
1184                }
1185
1186                detected_multiview = Some(this_multiview);
1187            }
1188
1189            Ok(())
1190        };
1191        let mut add_view = |view: &TextureView, location| {
1192            let render_extent = view.render_extent.map_err(|reason| {
1193                RenderPassErrorInner::TextureViewIsNotRenderable { location, reason }
1194            })?;
1195            if let Some(ex) = extent {
1196                if ex != render_extent {
1197                    return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
1198                        expected_location: attachment_location,
1199                        expected_extent: ex,
1200                        actual_location: location,
1201                        actual_extent: render_extent,
1202                    });
1203                }
1204            } else {
1205                extent = Some(render_extent);
1206            }
1207            if sample_count == 0 {
1208                sample_count = view.samples;
1209            } else if sample_count != view.samples {
1210                return Err(RenderPassErrorInner::AttachmentSampleCountMismatch {
1211                    expected_location: attachment_location,
1212                    expected_samples: sample_count,
1213                    actual_location: location,
1214                    actual_samples: view.samples,
1215                });
1216            }
1217            attachment_location = location;
1218            Ok(())
1219        };
1220
1221        let mut depth_stencil = None;
1222
1223        if let Some(at) = depth_stencil_attachment.as_ref() {
1224            let view = &at.view;
1225            check_multiview(view)?;
1226            add_view(view, AttachmentErrorLocation::Depth)?;
1227
1228            let ds_aspects = view.desc.aspects();
1229
1230            if !ds_aspects.contains(hal::FormatAspects::STENCIL)
1231                || (at.stencil.load_op().eq_variant(at.depth.load_op())
1232                    && at.stencil.store_op() == at.depth.store_op())
1233            {
1234                Self::add_pass_texture_init_actions(
1235                    at.depth.load_op(),
1236                    at.depth.store_op(),
1237                    texture_memory_actions,
1238                    view,
1239                    pending_discard_init_fixups,
1240                );
1241            } else if !ds_aspects.contains(hal::FormatAspects::DEPTH) {
1242                Self::add_pass_texture_init_actions(
1243                    at.stencil.load_op(),
1244                    at.stencil.store_op(),
1245                    texture_memory_actions,
1246                    view,
1247                    pending_discard_init_fixups,
1248                );
1249            } else {
1250                // This is the only place (anywhere in wgpu) where Stencil &
1251                // Depth init state can diverge.
1252                //
1253                // To safe us the overhead of tracking init state of texture
1254                // aspects everywhere, we're going to cheat a little bit in
1255                // order to keep the init state of both Stencil and Depth
1256                // aspects in sync. The expectation is that we hit this path
1257                // extremely rarely!
1258                //
1259                // Diverging LoadOp, i.e. Load + Clear:
1260                //
1261                // Record MemoryInitKind::NeedsInitializedMemory for the entire
1262                // surface, a bit wasteful on unit but no negative effect!
1263                //
1264                // Rationale: If the loaded channel is uninitialized it needs
1265                // clearing, the cleared channel doesn't care. (If everything is
1266                // already initialized nothing special happens)
1267                //
1268                // (possible minor optimization: Clear caused by
1269                // NeedsInitializedMemory should know that it doesn't need to
1270                // clear the aspect that was set to C)
1271                let need_init_beforehand =
1272                    at.depth.load_op() == LoadOp::Load || at.stencil.load_op() == LoadOp::Load;
1273                if need_init_beforehand {
1274                    pending_discard_init_fixups.extend(
1275                        texture_memory_actions.register_init_action(&TextureInitTrackerAction {
1276                            texture: view.parent.clone(),
1277                            range: TextureInitRange::from(view.selector.clone()),
1278                            kind: MemoryInitKind::NeedsInitializedMemory,
1279                        }),
1280                    );
1281                }
1282
1283                // Diverging Store, i.e. Discard + Store:
1284                //
1285                // Immediately zero out channel that is set to discard after
1286                // we're done with the render pass. This allows us to set the
1287                // entire surface to MemoryInitKind::ImplicitlyInitialized (if
1288                // it isn't already set to NeedsInitializedMemory).
1289                //
1290                // (possible optimization: Delay and potentially drop this zeroing)
1291                if at.depth.store_op() != at.stencil.store_op() {
1292                    if !need_init_beforehand {
1293                        texture_memory_actions.register_implicit_init(
1294                            &view.parent,
1295                            TextureInitRange::from(view.selector.clone()),
1296                        );
1297                    }
1298                    divergent_discarded_depth_stencil_aspect = Some((
1299                        if at.depth.store_op() == StoreOp::Discard {
1300                            wgt::TextureAspect::DepthOnly
1301                        } else {
1302                            wgt::TextureAspect::StencilOnly
1303                        },
1304                        view.clone(),
1305                    ));
1306                } else if at.depth.store_op() == StoreOp::Discard {
1307                    // Both are discarded using the regular path.
1308                    discarded_surfaces.push(TextureSurfaceDiscard {
1309                        texture: view.parent.clone(),
1310                        mip_level: view.selector.mips.start,
1311                        layer: view.selector.layers.start,
1312                    });
1313                }
1314            }
1315
1316            is_depth_read_only = at.depth.is_readonly();
1317            is_stencil_read_only = at.stencil.is_readonly();
1318
1319            let usage = if is_depth_read_only
1320                && is_stencil_read_only
1321                && device
1322                    .downlevel
1323                    .flags
1324                    .contains(wgt::DownlevelFlags::READ_ONLY_DEPTH_STENCIL)
1325            {
1326                // If the texture supports TEXTURE_BINDING, it can be used as a shader
1327                // resource and a read-only depth attachment simultaneously. But if it
1328                // doesn't support TEXTURE_BINDING, don't attempt to transition it to a
1329                // shader resource state, because DX12 will raise an error.
1330                if view.desc.usage.contains(TextureUsages::TEXTURE_BINDING) {
1331                    wgt::TextureUses::DEPTH_STENCIL_READ | wgt::TextureUses::RESOURCE
1332                } else {
1333                    wgt::TextureUses::DEPTH_STENCIL_READ
1334                }
1335            } else {
1336                wgt::TextureUses::DEPTH_STENCIL_WRITE
1337            };
1338            render_attachments.push(view.to_render_attachment(usage));
1339
1340            depth_stencil = Some(hal::DepthStencilAttachment {
1341                target: hal::Attachment {
1342                    view: view.try_raw(snatch_guard)?,
1343                    usage,
1344                },
1345                depth_ops: at.depth.hal_ops(),
1346                stencil_ops: at.stencil.hal_ops(),
1347                clear_value: (at.depth.clear_value(), at.stencil.clear_value()),
1348            });
1349        }
1350
1351        let mut attachment_set = crate::FastHashSet::default();
1352
1353        let mut color_attachments_hal =
1354            ArrayVec::<Option<hal::ColorAttachment<_>>, { hal::MAX_COLOR_ATTACHMENTS }>::new();
1355        for (index, attachment) in color_attachments.iter().enumerate() {
1356            let at = if let Some(attachment) = attachment.as_ref() {
1357                attachment
1358            } else {
1359                color_attachments_hal.push(None);
1360                continue;
1361            };
1362            let color_view: &TextureView = &at.view;
1363            color_view.same_device(device)?;
1364            check_multiview(color_view)?;
1365            add_view(
1366                color_view,
1367                AttachmentErrorLocation::Color {
1368                    index,
1369                    resolve: false,
1370                },
1371            )?;
1372
1373            if !color_view.desc.aspects().intersects(
1374                hal::FormatAspects::COLOR
1375                    | hal::FormatAspects::PLANE_0
1376                    | hal::FormatAspects::PLANE_1
1377                    | hal::FormatAspects::PLANE_2,
1378            ) {
1379                return Err(RenderPassErrorInner::ColorAttachment(
1380                    ColorAttachmentError::InvalidFormat(color_view.desc.format),
1381                ));
1382            }
1383
1384            if color_view.desc.dimension == TextureViewDimension::D3 {
1385                if let Some(depth_slice) = at.depth_slice {
1386                    let mip = color_view.desc.range.base_mip_level;
1387                    let mip_size = color_view
1388                        .parent
1389                        .desc
1390                        .size
1391                        .mip_level_size(mip, color_view.parent.desc.dimension);
1392                    let limit = mip_size.depth_or_array_layers;
1393                    if depth_slice >= limit {
1394                        return Err(RenderPassErrorInner::ColorAttachment(
1395                            ColorAttachmentError::DepthSliceLimit {
1396                                given: depth_slice,
1397                                limit,
1398                            },
1399                        ));
1400                    }
1401                } else {
1402                    return Err(RenderPassErrorInner::ColorAttachment(
1403                        ColorAttachmentError::MissingDepthSlice,
1404                    ));
1405                }
1406            } else if at.depth_slice.is_some() {
1407                return Err(RenderPassErrorInner::ColorAttachment(
1408                    ColorAttachmentError::UnneededDepthSlice,
1409                ));
1410            }
1411
1412            validation::validate_color_attachment_bytes_per_sample(
1413                color_attachments
1414                    .iter()
1415                    .flatten()
1416                    .map(|at| at.view.desc.format),
1417                device.limits.max_color_attachment_bytes_per_sample,
1418            )
1419            .map_err(RenderPassErrorInner::ColorAttachment)?;
1420
1421            fn check_attachment_overlap(
1422                attachment_set: &mut crate::FastHashSet<(crate::track::TrackerIndex, u32, u32)>,
1423                view: &TextureView,
1424                depth_slice: Option<u32>,
1425            ) -> Result<(), ColorAttachmentError> {
1426                let mut insert = |slice| {
1427                    let mip_level = view.desc.range.base_mip_level;
1428                    if attachment_set.insert((
1429                        view.parent.tracking_data.tracker_index(),
1430                        mip_level,
1431                        slice,
1432                    )) {
1433                        Ok(())
1434                    } else {
1435                        Err(ColorAttachmentError::SubresourceOverlap {
1436                            view: view.error_ident(),
1437                            mip_level,
1438                            depth_or_array_layer: slice,
1439                        })
1440                    }
1441                };
1442                match view.desc.dimension {
1443                    TextureViewDimension::D2 => {
1444                        insert(view.desc.range.base_array_layer)?;
1445                    }
1446                    TextureViewDimension::D2Array => {
1447                        for layer in view.selector.layers.clone() {
1448                            insert(layer)?;
1449                        }
1450                    }
1451                    TextureViewDimension::D3 => {
1452                        insert(depth_slice.unwrap())?;
1453                    }
1454                    _ => unreachable!(),
1455                };
1456                Ok(())
1457            }
1458
1459            check_attachment_overlap(&mut attachment_set, color_view, at.depth_slice)?;
1460
1461            Self::add_pass_texture_init_actions(
1462                at.load_op,
1463                at.store_op,
1464                texture_memory_actions,
1465                color_view,
1466                pending_discard_init_fixups,
1467            );
1468            render_attachments
1469                .push(color_view.to_render_attachment(wgt::TextureUses::COLOR_TARGET));
1470
1471            let mut hal_resolve_target = None;
1472            if let Some(resolve_view) = &at.resolve_target {
1473                resolve_view.same_device(device)?;
1474                check_multiview(resolve_view)?;
1475
1476                check_attachment_overlap(&mut attachment_set, resolve_view, None)?;
1477
1478                let resolve_location = AttachmentErrorLocation::Color {
1479                    index,
1480                    resolve: true,
1481                };
1482
1483                let render_extent = resolve_view.render_extent.map_err(|reason| {
1484                    RenderPassErrorInner::TextureViewIsNotRenderable {
1485                        location: resolve_location,
1486                        reason,
1487                    }
1488                })?;
1489                if color_view.render_extent.unwrap() != render_extent {
1490                    return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
1491                        expected_location: attachment_location,
1492                        expected_extent: extent.unwrap_or_default(),
1493                        actual_location: resolve_location,
1494                        actual_extent: render_extent,
1495                    });
1496                }
1497                if color_view.samples == 1 || resolve_view.samples != 1 {
1498                    return Err(RenderPassErrorInner::InvalidResolveSampleCounts {
1499                        location: resolve_location,
1500                        src: color_view.samples,
1501                        dst: resolve_view.samples,
1502                    });
1503                }
1504                if color_view.desc.format != resolve_view.desc.format {
1505                    return Err(RenderPassErrorInner::MismatchedResolveTextureFormat {
1506                        location: resolve_location,
1507                        src: color_view.desc.format,
1508                        dst: resolve_view.desc.format,
1509                    });
1510                }
1511                if !resolve_view
1512                    .format_features
1513                    .flags
1514                    .contains(wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE)
1515                {
1516                    return Err(RenderPassErrorInner::UnsupportedResolveTargetFormat {
1517                        location: resolve_location,
1518                        format: resolve_view.desc.format,
1519                    });
1520                }
1521
1522                texture_memory_actions.register_implicit_init(
1523                    &resolve_view.parent,
1524                    TextureInitRange::from(resolve_view.selector.clone()),
1525                );
1526                render_attachments
1527                    .push(resolve_view.to_render_attachment(wgt::TextureUses::COLOR_TARGET));
1528
1529                hal_resolve_target = Some(hal::Attachment {
1530                    view: resolve_view.try_raw(snatch_guard)?,
1531                    usage: wgt::TextureUses::COLOR_TARGET,
1532                });
1533            }
1534
1535            color_attachments_hal.push(Some(hal::ColorAttachment {
1536                target: hal::Attachment {
1537                    view: color_view.try_raw(snatch_guard)?,
1538                    usage: wgt::TextureUses::COLOR_TARGET,
1539                },
1540                depth_slice: at.depth_slice,
1541                resolve_target: hal_resolve_target,
1542                ops: at.hal_ops(),
1543                clear_value: at.clear_value(),
1544            }));
1545        }
1546
1547        let extent = extent.ok_or(RenderPassErrorInner::MissingAttachments)?;
1548
1549        let detected_multiview =
1550            detected_multiview.expect("Multiview was not detected, no attachments");
1551        if let Some(mask) = multiview_mask {
1552            // 0x01 will have msb 0
1553            let mask_msb = 31 - mask.leading_zeros();
1554            let detected_mv = detected_multiview.map(NonZeroU32::get).unwrap_or(1);
1555            if mask_msb >= detected_mv {
1556                return Err(RenderPassErrorInner::MultiViewMismatch);
1557            }
1558            if mask.get() != (1 << detected_mv) - 1 {
1559                device.require_features(wgt::Features::SELECTIVE_MULTIVIEW)?;
1560            }
1561        }
1562
1563        let attachment_formats = AttachmentData {
1564            colors: color_attachments
1565                .iter()
1566                .map(|at| at.as_ref().map(|at| at.view.desc.format))
1567                .collect(),
1568            resolves: color_attachments
1569                .iter()
1570                .filter_map(|at| {
1571                    at.as_ref().and_then(|at| {
1572                        at.resolve_target
1573                            .as_ref()
1574                            .map(|resolve| resolve.desc.format)
1575                    })
1576                })
1577                .collect(),
1578            depth_stencil: depth_stencil_attachment
1579                .as_ref()
1580                .map(|at| at.view.desc.format),
1581        };
1582
1583        let context = RenderPassContext {
1584            attachments: attachment_formats,
1585            sample_count,
1586            multiview_mask,
1587        };
1588
1589        let timestamp_writes_hal = if let Some(tw) = timestamp_writes.as_ref() {
1590            let query_set = &tw.query_set;
1591            query_set.same_device(device)?;
1592
1593            if let Some(index) = tw.beginning_of_pass_write_index {
1594                pending_query_resets.use_query_set(query_set, index);
1595            }
1596            if let Some(index) = tw.end_of_pass_write_index {
1597                pending_query_resets.use_query_set(query_set, index);
1598            }
1599
1600            Some(hal::PassTimestampWrites {
1601                query_set: query_set.raw(),
1602                beginning_of_pass_write_index: tw.beginning_of_pass_write_index,
1603                end_of_pass_write_index: tw.end_of_pass_write_index,
1604            })
1605        } else {
1606            None
1607        };
1608
1609        let occlusion_query_set_hal = if let Some(query_set) = occlusion_query_set.as_ref() {
1610            query_set.same_device(device)?;
1611            Some(query_set.raw())
1612        } else {
1613            None
1614        };
1615
1616        let hal_desc = hal::RenderPassDescriptor {
1617            label: hal_label,
1618            extent,
1619            sample_count,
1620            color_attachments: &color_attachments_hal,
1621            depth_stencil_attachment: depth_stencil,
1622            multiview_mask,
1623            timestamp_writes: timestamp_writes_hal,
1624            occlusion_query_set: occlusion_query_set_hal,
1625        };
1626        unsafe {
1627            encoder
1628                .begin_render_pass(&hal_desc)
1629                .map_err(|e| device.handle_hal_error(e))?;
1630        };
1631        drop(color_attachments_hal); // Drop, so we can consume `color_attachments` for the tracker.
1632
1633        // Can't borrow the tracker more than once, so have to add to the tracker after the `begin_render_pass` hal call.
1634        if let Some(tw) = timestamp_writes.take() {
1635            trackers.query_sets.insert_single(tw.query_set);
1636        };
1637        if let Some(occlusion_query_set) = occlusion_query_set.take() {
1638            trackers.query_sets.insert_single(occlusion_query_set);
1639        };
1640        if let Some(at) = depth_stencil_attachment.take() {
1641            trackers.views.insert_single(at.view.clone());
1642        }
1643        for at in color_attachments.iter().flatten() {
1644            trackers.views.insert_single(at.view.clone());
1645            if let Some(resolve_target) = at.resolve_target.clone() {
1646                trackers.views.insert_single(resolve_target);
1647            }
1648        }
1649
1650        Ok(Self {
1651            context,
1652            render_attachments,
1653            is_depth_read_only,
1654            is_stencil_read_only,
1655            extent,
1656            divergent_discarded_depth_stencil_aspect,
1657            multiview_mask,
1658        })
1659    }
1660
1661    fn finish(
1662        self,
1663        device: &Device,
1664        raw: &mut dyn hal::DynCommandEncoder,
1665        snatch_guard: &SnatchGuard,
1666        scope: &mut UsageScope<'_>,
1667        instance_flags: wgt::InstanceFlags,
1668    ) -> Result<(), RenderPassErrorInner> {
1669        profiling::scope!("RenderPassInfo::finish");
1670        unsafe {
1671            raw.end_render_pass();
1672        }
1673
1674        for ra in self.render_attachments {
1675            let texture = &ra.texture;
1676            texture.check_usage(TextureUsages::RENDER_ATTACHMENT)?;
1677
1678            // the tracker set of the pass is always in "extend" mode
1679            unsafe {
1680                scope
1681                    .textures
1682                    .merge_single(texture, Some(ra.selector.clone()), ra.usage)?
1683            };
1684        }
1685
1686        // If either only stencil or depth was discarded, we put in a special
1687        // clear pass to keep the init status of the aspects in sync. We do this
1688        // so we don't need to track init state for depth/stencil aspects
1689        // individually.
1690        //
1691        // Note that we don't go the usual route of "brute force" initializing
1692        // the texture when need arises here, since this path is actually
1693        // something a user may genuinely want (where as the other cases are
1694        // more seen along the lines as gracefully handling a user error).
1695        if let Some((aspect, view)) = self.divergent_discarded_depth_stencil_aspect {
1696            let (depth_ops, stencil_ops) = if aspect == wgt::TextureAspect::DepthOnly {
1697                (
1698                    hal::AttachmentOps::LOAD_CLEAR | hal::AttachmentOps::STORE, // clear depth
1699                    hal::AttachmentOps::LOAD | hal::AttachmentOps::STORE,       // unchanged stencil
1700                )
1701            } else {
1702                (
1703                    hal::AttachmentOps::LOAD | hal::AttachmentOps::STORE, // unchanged stencil
1704                    hal::AttachmentOps::LOAD_CLEAR | hal::AttachmentOps::STORE, // clear depth
1705                )
1706            };
1707            let desc = hal::RenderPassDescriptor::<'_, _, dyn hal::DynTextureView> {
1708                label: hal_label(
1709                    Some("(wgpu internal) Zero init discarded depth/stencil aspect"),
1710                    instance_flags,
1711                ),
1712                extent: view.render_extent.unwrap(),
1713                sample_count: view.samples,
1714                color_attachments: &[],
1715                depth_stencil_attachment: Some(hal::DepthStencilAttachment {
1716                    target: hal::Attachment {
1717                        view: view.try_raw(snatch_guard)?,
1718                        usage: wgt::TextureUses::DEPTH_STENCIL_WRITE,
1719                    },
1720                    depth_ops,
1721                    stencil_ops,
1722                    clear_value: (0.0, 0),
1723                }),
1724                multiview_mask: self.multiview_mask,
1725                timestamp_writes: None,
1726                occlusion_query_set: None,
1727            };
1728            unsafe {
1729                raw.begin_render_pass(&desc)
1730                    .map_err(|e| device.handle_hal_error(e))?;
1731                raw.end_render_pass();
1732            }
1733        }
1734
1735        Ok(())
1736    }
1737}
1738
1739impl Global {
1740    /// Creates a render pass.
1741    ///
1742    /// If creation fails, an invalid pass is returned. Attempting to record
1743    /// commands into an invalid pass is permitted, but a validation error will
1744    /// ultimately be generated when the parent encoder is finished, and it is
1745    /// not possible to run any commands from the invalid pass.
1746    ///
1747    /// If successful, puts the encoder into the [`Locked`] state.
1748    ///
1749    /// [`Locked`]: crate::command::CommandEncoderStatus::Locked
1750    pub fn command_encoder_begin_render_pass(
1751        &self,
1752        encoder_id: id::CommandEncoderId,
1753        desc: &RenderPassDescriptor<'_>,
1754    ) -> (RenderPass, Option<CommandEncoderError>) {
1755        use EncoderStateError as SErr;
1756
1757        fn fill_arc_desc(
1758            hub: &crate::hub::Hub,
1759            desc: &RenderPassDescriptor<'_>,
1760            arc_desc: &mut ArcRenderPassDescriptor,
1761            device: &Device,
1762        ) -> Result<(), RenderPassErrorInner> {
1763            device.check_is_valid()?;
1764
1765            let query_sets = hub.query_sets.read();
1766            let texture_views = hub.texture_views.read();
1767
1768            let max_color_attachments = device.limits.max_color_attachments as usize;
1769            if desc.color_attachments.len() > max_color_attachments {
1770                return Err(RenderPassErrorInner::ColorAttachment(
1771                    ColorAttachmentError::TooMany {
1772                        given: desc.color_attachments.len(),
1773                        limit: max_color_attachments,
1774                    },
1775                ));
1776            }
1777
1778            for color_attachment in desc.color_attachments.iter() {
1779                if let Some(RenderPassColorAttachment {
1780                    view: view_id,
1781                    depth_slice,
1782                    resolve_target,
1783                    load_op,
1784                    store_op,
1785                }) = color_attachment
1786                {
1787                    let view = texture_views.get(*view_id).get()?;
1788                    view.same_device(device)?;
1789
1790                    if view.desc.usage.contains(TextureUsages::TRANSIENT)
1791                        && *store_op != StoreOp::Discard
1792                    {
1793                        return Err(RenderPassErrorInner::ColorAttachment(
1794                            ColorAttachmentError::InvalidUsageForStoreOp(
1795                                TextureUsages::TRANSIENT,
1796                                StoreOp::Discard,
1797                                *store_op,
1798                            ),
1799                        ));
1800                    }
1801
1802                    let resolve_target = if let Some(resolve_target_id) = resolve_target {
1803                        let rt_arc = texture_views.get(*resolve_target_id).get()?;
1804                        rt_arc.same_device(device)?;
1805
1806                        Some(rt_arc)
1807                    } else {
1808                        None
1809                    };
1810
1811                    arc_desc
1812                        .color_attachments
1813                        .push(Some(ArcRenderPassColorAttachment {
1814                            view,
1815                            depth_slice: *depth_slice,
1816                            resolve_target,
1817                            load_op: *load_op,
1818                            store_op: *store_op,
1819                        }));
1820                } else {
1821                    arc_desc.color_attachments.push(None);
1822                }
1823            }
1824
1825            arc_desc.depth_stencil_attachment =
1826            // https://gpuweb.github.io/gpuweb/#abstract-opdef-gpurenderpassdepthstencilattachment-gpurenderpassdepthstencilattachment-valid-usage
1827                if let Some(depth_stencil_attachment) = desc.depth_stencil_attachment {
1828                    let view = texture_views.get(depth_stencil_attachment.view).get()?;
1829                    view.same_device(device)?;
1830
1831                    let format = view.desc.format;
1832                    if !format.is_depth_stencil_format() {
1833                        return Err(RenderPassErrorInner::InvalidAttachment(AttachmentError::InvalidDepthStencilAttachmentFormat(
1834                            view.desc.format,
1835                        )));
1836                    }
1837
1838                    Some(ResolvedRenderPassDepthStencilAttachment {
1839                        view,
1840                        depth: if format.has_depth_aspect() {
1841                            depth_stencil_attachment.depth.resolve(|clear| if let Some(clear) = clear {
1842                                // If this.depthLoadOp is "clear", this.depthClearValue must be provided and must be between 0.0 and 1.0, inclusive.
1843                                if !(0.0..=1.0).contains(&clear) {
1844                                    Err(AttachmentError::ClearValueOutOfRange(clear))
1845                                } else {
1846                                    Ok(clear)
1847                                }
1848                            } else {
1849                                Err(AttachmentError::NoClearValue)
1850                            })?
1851                        } else {
1852                            ResolvedPassChannel::ReadOnly
1853                        },
1854                        stencil: if format.has_stencil_aspect() {
1855                            depth_stencil_attachment.stencil.resolve(|clear| Ok(clear.unwrap_or_default()))?
1856                        } else {
1857                            ResolvedPassChannel::ReadOnly
1858                        },
1859                    })
1860                } else {
1861                    None
1862                };
1863
1864            arc_desc.timestamp_writes = desc
1865                .timestamp_writes
1866                .map(|tw| {
1867                    Global::validate_pass_timestamp_writes::<RenderPassErrorInner>(
1868                        device,
1869                        &query_sets,
1870                        tw,
1871                    )
1872                })
1873                .transpose()?;
1874
1875            arc_desc.occlusion_query_set =
1876                if let Some(occlusion_query_set) = desc.occlusion_query_set {
1877                    let query_set = query_sets.get(occlusion_query_set).get()?;
1878                    query_set.same_device(device)?;
1879
1880                    if !matches!(query_set.desc.ty, wgt::QueryType::Occlusion) {
1881                        return Err(QueryUseError::IncompatibleType {
1882                            set_type: query_set.desc.ty.into(),
1883                            query_type: super::SimplifiedQueryType::Occlusion,
1884                        }
1885                        .into());
1886                    }
1887
1888                    Some(query_set)
1889                } else {
1890                    None
1891                };
1892
1893            arc_desc.multiview_mask = desc.multiview_mask;
1894
1895            Ok(())
1896        }
1897
1898        let scope = PassErrorScope::Pass;
1899        let hub = &self.hub;
1900
1901        let cmd_enc = hub.command_encoders.get(encoder_id);
1902        let mut cmd_buf_data = cmd_enc.data.lock();
1903
1904        match cmd_buf_data.lock_encoder() {
1905            Ok(()) => {
1906                drop(cmd_buf_data);
1907                let mut arc_desc = ArcRenderPassDescriptor {
1908                    label: &desc.label,
1909                    timestamp_writes: None,
1910                    color_attachments: ArrayVec::new(),
1911                    depth_stencil_attachment: None,
1912                    occlusion_query_set: None,
1913                    multiview_mask: None,
1914                };
1915                match fill_arc_desc(hub, desc, &mut arc_desc, &cmd_enc.device) {
1916                    Ok(()) => (RenderPass::new(cmd_enc, arc_desc), None),
1917                    Err(err) => (
1918                        RenderPass::new_invalid(cmd_enc, &desc.label, err.map_pass_err(scope)),
1919                        None,
1920                    ),
1921                }
1922            }
1923            Err(err @ SErr::Locked) => {
1924                // Attempting to open a new pass while the encoder is locked
1925                // invalidates the encoder, but does not generate a validation
1926                // error.
1927                cmd_buf_data.invalidate(err.clone());
1928                drop(cmd_buf_data);
1929                (
1930                    RenderPass::new_invalid(cmd_enc, &desc.label, err.map_pass_err(scope)),
1931                    None,
1932                )
1933            }
1934            Err(err @ (SErr::Ended | SErr::Submitted)) => {
1935                // Attempting to open a new pass after the encode has ended
1936                // generates an immediate validation error.
1937                drop(cmd_buf_data);
1938                (
1939                    RenderPass::new_invalid(cmd_enc, &desc.label, err.clone().map_pass_err(scope)),
1940                    Some(err.into()),
1941                )
1942            }
1943            Err(err @ SErr::Invalid) => {
1944                // Passes can be opened even on an invalid encoder. Such passes
1945                // are even valid, but since there's no visible side-effect of
1946                // the pass being valid and there's no point in storing recorded
1947                // commands that will ultimately be discarded, we open an
1948                // invalid pass to save that work.
1949                drop(cmd_buf_data);
1950                (
1951                    RenderPass::new_invalid(cmd_enc, &desc.label, err.map_pass_err(scope)),
1952                    None,
1953                )
1954            }
1955            Err(SErr::Unlocked) => {
1956                unreachable!("lock_encoder cannot fail due to the encoder being unlocked")
1957            }
1958        }
1959    }
1960
1961    pub fn render_pass_end(&self, pass: &mut RenderPass) -> Result<(), EncoderStateError> {
1962        profiling::scope!(
1963            "CommandEncoder::run_render_pass {}",
1964            pass.base.label.as_deref().unwrap_or("")
1965        );
1966
1967        let cmd_enc = pass.parent.take().ok_or(EncoderStateError::Ended)?;
1968        let mut cmd_buf_data = cmd_enc.data.lock();
1969
1970        cmd_buf_data.unlock_encoder()?;
1971
1972        let base = pass.base.take();
1973
1974        if let Err(RenderPassError {
1975            inner:
1976                RenderPassErrorInner::EncoderState(
1977                    err @ (EncoderStateError::Locked | EncoderStateError::Ended),
1978                ),
1979            scope: _,
1980        }) = base
1981        {
1982            // Most encoding errors are detected and raised within `finish()`.
1983            //
1984            // However, we raise a validation error here if the pass was opened
1985            // within another pass, or on a finished encoder. The latter is
1986            // particularly important, because in that case reporting errors via
1987            // `CommandEncoder::finish` is not possible.
1988            return Err(err.clone());
1989        }
1990
1991        cmd_buf_data.push_with(|| -> Result<_, RenderPassError> {
1992            Ok(ArcCommand::RunRenderPass {
1993                pass: base?,
1994                color_attachments: SmallVec::from(pass.color_attachments.as_slice()),
1995                depth_stencil_attachment: pass.depth_stencil_attachment.take(),
1996                timestamp_writes: pass.timestamp_writes.take(),
1997                occlusion_query_set: pass.occlusion_query_set.take(),
1998                multiview_mask: pass.multiview_mask,
1999            })
2000        })
2001    }
2002}
2003
2004pub(super) fn encode_render_pass(
2005    parent_state: &mut EncodingState<InnerCommandEncoder>,
2006    mut base: BasePass<ArcRenderCommand, Infallible>,
2007    color_attachments: ColorAttachments<Arc<TextureView>>,
2008    mut depth_stencil_attachment: Option<
2009        ResolvedRenderPassDepthStencilAttachment<Arc<TextureView>>,
2010    >,
2011    mut timestamp_writes: Option<ArcPassTimestampWrites>,
2012    occlusion_query_set: Option<Arc<QuerySet>>,
2013    multiview_mask: Option<NonZeroU32>,
2014) -> Result<(), RenderPassError> {
2015    let pass_scope = PassErrorScope::Pass;
2016
2017    let device = parent_state.device;
2018
2019    let mut indirect_draw_validation_batcher = crate::indirect_validation::DrawBatcher::new();
2020
2021    // We automatically keep extending command buffers over time, and because
2022    // we want to insert a command buffer _before_ what we're about to record,
2023    // we need to make sure to close the previous one.
2024    parent_state
2025        .raw_encoder
2026        .close_if_open()
2027        .map_pass_err(pass_scope)?;
2028    let raw_encoder = parent_state
2029        .raw_encoder
2030        .open_pass(base.label.as_deref())
2031        .map_pass_err(pass_scope)?;
2032
2033    let (scope, pending_discard_init_fixups, mut pending_query_resets) = {
2034        let mut pending_query_resets = QueryResetMap::new();
2035        let mut pending_discard_init_fixups = SurfacesInDiscardState::new();
2036
2037        let info = RenderPassInfo::start(
2038            device,
2039            hal_label(base.label.as_deref(), device.instance_flags),
2040            &color_attachments,
2041            depth_stencil_attachment.take(),
2042            timestamp_writes.take(),
2043            // Still needed down the line.
2044            // TODO(wumpf): by restructuring the code, we could get rid of some of this Arc clone.
2045            occlusion_query_set.clone(),
2046            raw_encoder,
2047            parent_state.tracker,
2048            parent_state.texture_memory_actions,
2049            &mut pending_query_resets,
2050            &mut pending_discard_init_fixups,
2051            parent_state.snatch_guard,
2052            multiview_mask,
2053        )
2054        .map_pass_err(pass_scope)?;
2055
2056        let indices = &device.tracker_indices;
2057        parent_state
2058            .tracker
2059            .buffers
2060            .set_size(indices.buffers.size());
2061        parent_state
2062            .tracker
2063            .textures
2064            .set_size(indices.textures.size());
2065
2066        let mut debug_scope_depth = 0;
2067
2068        let mut state = State {
2069            pipeline_flags: PipelineFlags::empty(),
2070            blend_constant: OptionalState::Unused,
2071            stencil_reference: 0,
2072            pipeline: None,
2073            index: IndexState::default(),
2074            vertex: VertexState::default(),
2075
2076            info,
2077
2078            pass: pass::PassState {
2079                base: EncodingState {
2080                    device,
2081                    raw_encoder,
2082                    tracker: parent_state.tracker,
2083                    buffer_memory_init_actions: parent_state.buffer_memory_init_actions,
2084                    texture_memory_actions: parent_state.texture_memory_actions,
2085                    as_actions: parent_state.as_actions,
2086                    temp_resources: parent_state.temp_resources,
2087                    indirect_draw_validation_resources: parent_state
2088                        .indirect_draw_validation_resources,
2089                    snatch_guard: parent_state.snatch_guard,
2090                    debug_scope_depth: &mut debug_scope_depth,
2091                },
2092                pending_discard_init_fixups,
2093                scope: device.new_usage_scope(),
2094                binder: Binder::new(),
2095
2096                temp_offsets: Vec::new(),
2097                dynamic_offset_count: 0,
2098
2099                string_offset: 0,
2100            },
2101
2102            immediate_slots_set: Default::default(),
2103
2104            active_occlusion_query: None,
2105            active_pipeline_statistics_query: None,
2106        };
2107
2108        for command in base.commands.drain(..) {
2109            match command {
2110                ArcRenderCommand::SetBindGroup {
2111                    index,
2112                    num_dynamic_offsets,
2113                    bind_group,
2114                } => {
2115                    let scope = PassErrorScope::SetBindGroup;
2116                    pass::set_bind_group::<RenderPassErrorInner>(
2117                        &mut state.pass,
2118                        device,
2119                        &base.dynamic_offsets,
2120                        index,
2121                        num_dynamic_offsets,
2122                        bind_group,
2123                        true,
2124                    )
2125                    .map_pass_err(scope)?;
2126                }
2127                ArcRenderCommand::SetPipeline(pipeline) => {
2128                    let scope = PassErrorScope::SetPipelineRender;
2129                    set_pipeline(&mut state, device, pipeline).map_pass_err(scope)?;
2130                }
2131                ArcRenderCommand::SetIndexBuffer {
2132                    buffer,
2133                    index_format,
2134                    offset,
2135                    size,
2136                } => {
2137                    let scope = PassErrorScope::SetIndexBuffer;
2138                    set_index_buffer(&mut state, device, buffer, index_format, offset, size)
2139                        .map_pass_err(scope)?;
2140                }
2141                ArcRenderCommand::SetVertexBuffer {
2142                    slot,
2143                    buffer,
2144                    offset,
2145                    size,
2146                } => {
2147                    let scope = PassErrorScope::SetVertexBuffer;
2148                    set_vertex_buffer(&mut state, device, slot, buffer, offset, size)
2149                        .map_pass_err(scope)?;
2150                }
2151                ArcRenderCommand::SetBlendConstant(ref color) => {
2152                    set_blend_constant(&mut state, color);
2153                }
2154                ArcRenderCommand::SetStencilReference(value) => {
2155                    set_stencil_reference(&mut state, value);
2156                }
2157                ArcRenderCommand::SetViewport {
2158                    rect,
2159                    depth_min,
2160                    depth_max,
2161                } => {
2162                    let scope = PassErrorScope::SetViewport;
2163                    set_viewport(&mut state, rect, depth_min, depth_max).map_pass_err(scope)?;
2164                }
2165                ArcRenderCommand::SetImmediate {
2166                    offset,
2167                    size_bytes,
2168                    values_offset,
2169                } => {
2170                    let scope = PassErrorScope::SetImmediate;
2171                    pass::set_immediates::<RenderPassErrorInner, _>(
2172                        &mut state.pass,
2173                        &base.immediates_data,
2174                        offset,
2175                        size_bytes,
2176                        values_offset,
2177                        |_| {},
2178                    )
2179                    .map_pass_err(scope)?;
2180                    state.immediate_slots_set |=
2181                        naga::valid::ImmediateSlots::from_range(offset, size_bytes);
2182                }
2183                ArcRenderCommand::SetScissor(rect) => {
2184                    let scope = PassErrorScope::SetScissorRect;
2185                    set_scissor(&mut state, rect).map_pass_err(scope)?;
2186                }
2187                ArcRenderCommand::Draw {
2188                    vertex_count,
2189                    instance_count,
2190                    first_vertex,
2191                    first_instance,
2192                } => {
2193                    let scope = PassErrorScope::Draw {
2194                        kind: DrawKind::Draw,
2195                        family: DrawCommandFamily::Draw,
2196                    };
2197                    draw(
2198                        &mut state,
2199                        vertex_count,
2200                        instance_count,
2201                        first_vertex,
2202                        first_instance,
2203                    )
2204                    .map_pass_err(scope)?;
2205                }
2206                ArcRenderCommand::DrawIndexed {
2207                    index_count,
2208                    instance_count,
2209                    first_index,
2210                    base_vertex,
2211                    first_instance,
2212                } => {
2213                    let scope = PassErrorScope::Draw {
2214                        kind: DrawKind::Draw,
2215                        family: DrawCommandFamily::DrawIndexed,
2216                    };
2217                    draw_indexed(
2218                        &mut state,
2219                        index_count,
2220                        instance_count,
2221                        first_index,
2222                        base_vertex,
2223                        first_instance,
2224                    )
2225                    .map_pass_err(scope)?;
2226                }
2227                ArcRenderCommand::DrawMeshTasks {
2228                    group_count_x,
2229                    group_count_y,
2230                    group_count_z,
2231                } => {
2232                    let scope = PassErrorScope::Draw {
2233                        kind: DrawKind::Draw,
2234                        family: DrawCommandFamily::DrawMeshTasks,
2235                    };
2236                    draw_mesh_tasks(&mut state, group_count_x, group_count_y, group_count_z)
2237                        .map_pass_err(scope)?;
2238                }
2239                ArcRenderCommand::DrawIndirect {
2240                    buffer,
2241                    offset,
2242                    count,
2243                    family,
2244
2245                    vertex_or_index_limit: _,
2246                    instance_limit: _,
2247                } => {
2248                    let scope = PassErrorScope::Draw {
2249                        kind: if count != 1 {
2250                            DrawKind::MultiDrawIndirect
2251                        } else {
2252                            DrawKind::DrawIndirect
2253                        },
2254                        family,
2255                    };
2256                    multi_draw_indirect(
2257                        &mut state,
2258                        &mut indirect_draw_validation_batcher,
2259                        device,
2260                        buffer,
2261                        offset,
2262                        count,
2263                        family,
2264                    )
2265                    .map_pass_err(scope)?;
2266                }
2267                ArcRenderCommand::MultiDrawIndirectCount {
2268                    buffer,
2269                    offset,
2270                    count_buffer,
2271                    count_buffer_offset,
2272                    max_count,
2273                    family,
2274                } => {
2275                    let scope = PassErrorScope::Draw {
2276                        kind: DrawKind::MultiDrawIndirectCount,
2277                        family,
2278                    };
2279                    multi_draw_indirect_count(
2280                        &mut state,
2281                        device,
2282                        buffer,
2283                        offset,
2284                        count_buffer,
2285                        count_buffer_offset,
2286                        max_count,
2287                        family,
2288                    )
2289                    .map_pass_err(scope)?;
2290                }
2291                ArcRenderCommand::PushDebugGroup { color: _, len } => {
2292                    pass::push_debug_group(&mut state.pass, &base.string_data, len);
2293                }
2294                ArcRenderCommand::PopDebugGroup => {
2295                    let scope = PassErrorScope::PopDebugGroup;
2296                    pass::pop_debug_group::<RenderPassErrorInner>(&mut state.pass)
2297                        .map_pass_err(scope)?;
2298                }
2299                ArcRenderCommand::InsertDebugMarker { color: _, len } => {
2300                    pass::insert_debug_marker(&mut state.pass, &base.string_data, len);
2301                }
2302                ArcRenderCommand::WriteTimestamp {
2303                    query_set,
2304                    query_index,
2305                } => {
2306                    let scope = PassErrorScope::WriteTimestamp;
2307                    pass::write_timestamp::<RenderPassErrorInner>(
2308                        &mut state.pass,
2309                        device,
2310                        Some(&mut pending_query_resets),
2311                        query_set,
2312                        query_index,
2313                    )
2314                    .map_pass_err(scope)?;
2315                }
2316                ArcRenderCommand::BeginOcclusionQuery { query_index } => {
2317                    api_log!("RenderPass::begin_occlusion_query {query_index}");
2318                    let scope = PassErrorScope::BeginOcclusionQuery;
2319
2320                    let query_set = occlusion_query_set
2321                        .clone()
2322                        .ok_or(RenderPassErrorInner::MissingOcclusionQuerySet)
2323                        .map_pass_err(scope)?;
2324
2325                    validate_and_begin_occlusion_query(
2326                        query_set,
2327                        state.pass.base.raw_encoder,
2328                        &mut state.pass.base.tracker.query_sets,
2329                        query_index,
2330                        Some(&mut pending_query_resets),
2331                        &mut state.active_occlusion_query,
2332                    )
2333                    .map_pass_err(scope)?;
2334                }
2335                ArcRenderCommand::EndOcclusionQuery => {
2336                    api_log!("RenderPass::end_occlusion_query");
2337                    let scope = PassErrorScope::EndOcclusionQuery;
2338
2339                    end_occlusion_query(
2340                        state.pass.base.raw_encoder,
2341                        &mut state.active_occlusion_query,
2342                    )
2343                    .map_pass_err(scope)?;
2344                }
2345                ArcRenderCommand::BeginPipelineStatisticsQuery {
2346                    query_set,
2347                    query_index,
2348                } => {
2349                    api_log!(
2350                        "RenderPass::begin_pipeline_statistics_query {query_index} {}",
2351                        query_set.error_ident()
2352                    );
2353                    let scope = PassErrorScope::BeginPipelineStatisticsQuery;
2354
2355                    validate_and_begin_pipeline_statistics_query(
2356                        query_set,
2357                        state.pass.base.raw_encoder,
2358                        &mut state.pass.base.tracker.query_sets,
2359                        device,
2360                        query_index,
2361                        Some(&mut pending_query_resets),
2362                        &mut state.active_pipeline_statistics_query,
2363                    )
2364                    .map_pass_err(scope)?;
2365                }
2366                ArcRenderCommand::EndPipelineStatisticsQuery => {
2367                    api_log!("RenderPass::end_pipeline_statistics_query");
2368                    let scope = PassErrorScope::EndPipelineStatisticsQuery;
2369
2370                    end_pipeline_statistics_query(
2371                        state.pass.base.raw_encoder,
2372                        &mut state.active_pipeline_statistics_query,
2373                    )
2374                    .map_pass_err(scope)?;
2375                }
2376                ArcRenderCommand::ExecuteBundle(bundle) => {
2377                    let scope = PassErrorScope::ExecuteBundle;
2378                    execute_bundle(
2379                        &mut state,
2380                        &mut indirect_draw_validation_batcher,
2381                        device,
2382                        bundle,
2383                    )
2384                    .map_pass_err(scope)?;
2385                }
2386            }
2387        }
2388
2389        if *state.pass.base.debug_scope_depth > 0 {
2390            Err(
2391                RenderPassErrorInner::DebugGroupError(DebugGroupError::MissingPop)
2392                    .map_pass_err(pass_scope),
2393            )?;
2394        }
2395        if state.active_occlusion_query.is_some() {
2396            Err(RenderPassErrorInner::QueryUse(QueryUseError::MissingEnd {
2397                query_type: super::SimplifiedQueryType::Occlusion,
2398            })
2399            .map_pass_err(pass_scope))?;
2400        }
2401        if state.active_pipeline_statistics_query.is_some() {
2402            Err(RenderPassErrorInner::QueryUse(QueryUseError::MissingEnd {
2403                query_type: super::SimplifiedQueryType::PipelineStatistics,
2404            })
2405            .map_pass_err(pass_scope))?;
2406        }
2407
2408        state
2409            .info
2410            .finish(
2411                device,
2412                state.pass.base.raw_encoder,
2413                state.pass.base.snatch_guard,
2414                &mut state.pass.scope,
2415                device.instance_flags,
2416            )
2417            .map_pass_err(pass_scope)?;
2418
2419        let trackers = state.pass.scope;
2420
2421        let pending_discard_init_fixups = state.pass.pending_discard_init_fixups;
2422
2423        parent_state.raw_encoder.close().map_pass_err(pass_scope)?;
2424        (trackers, pending_discard_init_fixups, pending_query_resets)
2425    };
2426
2427    let encoder = &mut parent_state.raw_encoder;
2428    let tracker = &mut parent_state.tracker;
2429
2430    {
2431        let transit = encoder
2432            .open_pass(hal_label(
2433                Some("(wgpu internal) Pre Pass"),
2434                device.instance_flags,
2435            ))
2436            .map_pass_err(pass_scope)?;
2437
2438        fixup_discarded_surfaces(
2439            pending_discard_init_fixups.into_iter(),
2440            transit,
2441            &mut tracker.textures,
2442            device,
2443            parent_state.snatch_guard,
2444        );
2445
2446        pending_query_resets.reset_queries(transit);
2447
2448        CommandEncoder::insert_barriers_from_scope(
2449            transit,
2450            tracker,
2451            &scope,
2452            parent_state.snatch_guard,
2453        );
2454
2455        if let Some(ref indirect_validation) = device.indirect_validation {
2456            indirect_validation
2457                .draw
2458                .inject_validation_pass(
2459                    device,
2460                    parent_state.snatch_guard,
2461                    parent_state.indirect_draw_validation_resources,
2462                    parent_state.temp_resources,
2463                    transit,
2464                    indirect_draw_validation_batcher,
2465                )
2466                .map_pass_err(pass_scope)?;
2467        }
2468    }
2469
2470    encoder.close_and_swap().map_pass_err(pass_scope)?;
2471
2472    Ok(())
2473}
2474
2475fn set_pipeline(
2476    state: &mut State,
2477    device: &Arc<Device>,
2478    pipeline: Arc<RenderPipeline>,
2479) -> Result<(), RenderPassErrorInner> {
2480    api_log!("RenderPass::set_pipeline {}", pipeline.error_ident());
2481
2482    state.pipeline = Some(pipeline.clone());
2483
2484    let pipeline = state
2485        .pass
2486        .base
2487        .tracker
2488        .render_pipelines
2489        .insert_single(pipeline)
2490        .clone();
2491
2492    pipeline.same_device(device)?;
2493
2494    state
2495        .info
2496        .context
2497        .check_compatible(&pipeline.pass_context, pipeline.as_ref())
2498        .map_err(RenderCommandError::IncompatiblePipelineTargets)?;
2499
2500    state.pipeline_flags = pipeline.flags;
2501
2502    if pipeline.flags.contains(PipelineFlags::WRITES_DEPTH) && state.info.is_depth_read_only {
2503        return Err(RenderCommandError::IncompatibleDepthAccess(pipeline.error_ident()).into());
2504    }
2505    if pipeline.flags.contains(PipelineFlags::WRITES_STENCIL) && state.info.is_stencil_read_only {
2506        return Err(RenderCommandError::IncompatibleStencilAccess(pipeline.error_ident()).into());
2507    }
2508
2509    state
2510        .blend_constant
2511        .require(pipeline.flags.contains(PipelineFlags::BLEND_CONSTANT));
2512
2513    unsafe {
2514        state
2515            .pass
2516            .base
2517            .raw_encoder
2518            .set_render_pipeline(pipeline.raw());
2519    }
2520
2521    if pipeline.flags.contains(PipelineFlags::STENCIL_REFERENCE) {
2522        unsafe {
2523            state
2524                .pass
2525                .base
2526                .raw_encoder
2527                .set_stencil_reference(state.stencil_reference);
2528        }
2529    }
2530
2531    // Rebind resource
2532    pass::change_pipeline_layout::<RenderPassErrorInner, _>(
2533        &mut state.pass,
2534        &pipeline.layout,
2535        &pipeline.late_sized_buffer_groups,
2536        || {},
2537    )?;
2538
2539    // Update vertex buffer limits.
2540    state.vertex.update_limits(&pipeline.vertex_steps);
2541    Ok(())
2542}
2543
2544// This function is duplicative of `bundle::set_index_buffer`.
2545fn set_index_buffer(
2546    state: &mut State,
2547    device: &Arc<Device>,
2548    buffer: Arc<Buffer>,
2549    index_format: IndexFormat,
2550    offset: u64,
2551    size: Option<BufferSize>,
2552) -> Result<(), RenderPassErrorInner> {
2553    api_log!("RenderPass::set_index_buffer {}", buffer.error_ident());
2554
2555    state
2556        .pass
2557        .scope
2558        .buffers
2559        .merge_single(&buffer, wgt::BufferUses::INDEX)?;
2560
2561    buffer.same_device(device)?;
2562
2563    buffer.check_usage(BufferUsages::INDEX)?;
2564
2565    if !offset.is_multiple_of(u64::try_from(index_format.byte_size()).unwrap()) {
2566        return Err(RenderCommandError::UnalignedIndexBuffer {
2567            offset,
2568            alignment: index_format.byte_size(),
2569        }
2570        .into());
2571    }
2572    let (binding, resolved_size) = buffer
2573        .binding(offset, size, state.pass.base.snatch_guard)
2574        .map_err(RenderCommandError::from)?;
2575    let end = offset + resolved_size;
2576    state.index.update_buffer(offset..end, index_format);
2577
2578    state.pass.base.buffer_memory_init_actions.extend(
2579        buffer.initialization_status.read().create_action(
2580            &buffer,
2581            offset..end,
2582            MemoryInitKind::NeedsInitializedMemory,
2583        ),
2584    );
2585
2586    unsafe {
2587        hal::DynCommandEncoder::set_index_buffer(
2588            state.pass.base.raw_encoder,
2589            binding,
2590            index_format,
2591        );
2592    }
2593    Ok(())
2594}
2595
2596// This function is duplicative of `render::set_vertex_buffer`.
2597fn set_vertex_buffer(
2598    state: &mut State,
2599    device: &Arc<Device>,
2600    slot: u32,
2601    buffer: Option<Arc<Buffer>>,
2602    offset: u64,
2603    size: Option<BufferSize>,
2604) -> Result<(), RenderPassErrorInner> {
2605    if let Some(ref buffer) = buffer {
2606        api_log!(
2607            "RenderPass::set_vertex_buffer {slot} {}",
2608            buffer.error_ident()
2609        );
2610    } else {
2611        api_log!("RenderPass::set_vertex_buffer {slot} None");
2612    }
2613
2614    let max_vertex_buffers = state.pass.base.device.limits.max_vertex_buffers;
2615    if slot >= max_vertex_buffers {
2616        return Err(RenderCommandError::VertexBufferIndexOutOfRange {
2617            index: slot,
2618            max: max_vertex_buffers,
2619        }
2620        .into());
2621    }
2622
2623    if let Some(buffer) = buffer {
2624        buffer.same_device(device)?;
2625        buffer.check_usage(BufferUsages::VERTEX)?;
2626
2627        if !offset.is_multiple_of(wgt::VERTEX_ALIGNMENT) {
2628            return Err(RenderCommandError::UnalignedVertexBuffer { slot, offset }.into());
2629        }
2630        let binding_size = buffer
2631            .resolve_binding_size(offset, size)
2632            .map_err(RenderCommandError::from)?;
2633        let buffer_range = offset..(offset + binding_size);
2634
2635        state
2636            .pass
2637            .scope
2638            .buffers
2639            .merge_single(&buffer, wgt::BufferUses::VERTEX)?;
2640
2641        state.pass.base.buffer_memory_init_actions.extend(
2642            buffer.initialization_status.read().create_action(
2643                &buffer,
2644                buffer_range.clone(),
2645                MemoryInitKind::NeedsInitializedMemory,
2646            ),
2647        );
2648
2649        state
2650            .vertex
2651            .set_buffer(slot as usize, buffer, buffer_range.clone());
2652        if let Some(pipeline) = state.pipeline.as_ref() {
2653            state.vertex.update_limits(&pipeline.vertex_steps);
2654        }
2655    } else {
2656        if offset != 0 {
2657            return Err(RenderCommandError::from(
2658                crate::binding_model::BindingError::UnbindingVertexBufferOffsetNotZero {
2659                    slot,
2660                    offset,
2661                },
2662            )
2663            .into());
2664        }
2665        if let Some(size) = size {
2666            return Err(RenderCommandError::from(
2667                crate::binding_model::BindingError::UnbindingVertexBufferSizeNotZero {
2668                    slot,
2669                    size: size.get(),
2670                },
2671            )
2672            .into());
2673        }
2674
2675        state.vertex.clear_buffer(slot as usize);
2676        if let Some(pipeline) = state.pipeline.as_ref() {
2677            state.vertex.update_limits(&pipeline.vertex_steps);
2678        }
2679    }
2680
2681    Ok(())
2682}
2683
2684fn set_blend_constant(state: &mut State, color: &Color) {
2685    api_log!("RenderPass::set_blend_constant");
2686
2687    state.blend_constant = OptionalState::Set;
2688    let array = [
2689        color.r as f32,
2690        color.g as f32,
2691        color.b as f32,
2692        color.a as f32,
2693    ];
2694    unsafe {
2695        state.pass.base.raw_encoder.set_blend_constants(&array);
2696    }
2697}
2698
2699fn set_stencil_reference(state: &mut State, value: u32) {
2700    api_log!("RenderPass::set_stencil_reference {value}");
2701
2702    state.stencil_reference = value;
2703    if state
2704        .pipeline_flags
2705        .contains(PipelineFlags::STENCIL_REFERENCE)
2706    {
2707        unsafe {
2708            state.pass.base.raw_encoder.set_stencil_reference(value);
2709        }
2710    }
2711}
2712
2713fn set_viewport(
2714    state: &mut State,
2715    rect: Rect<f32>,
2716    depth_min: f32,
2717    depth_max: f32,
2718) -> Result<(), RenderPassErrorInner> {
2719    api_log!("RenderPass::set_viewport {rect:?}");
2720
2721    if rect.w < 0.0
2722        || rect.h < 0.0
2723        || rect.w > state.pass.base.device.limits.max_texture_dimension_2d as f32
2724        || rect.h > state.pass.base.device.limits.max_texture_dimension_2d as f32
2725    {
2726        return Err(RenderCommandError::InvalidViewportRectSize {
2727            w: rect.w,
2728            h: rect.h,
2729            max: state.pass.base.device.limits.max_texture_dimension_2d,
2730        }
2731        .into());
2732    }
2733
2734    let max_viewport_range = state.pass.base.device.limits.max_texture_dimension_2d as f32 * 2.0;
2735
2736    if rect.x < -max_viewport_range
2737        || rect.y < -max_viewport_range
2738        || rect.x + rect.w > max_viewport_range - 1.0
2739        || rect.y + rect.h > max_viewport_range - 1.0
2740    {
2741        return Err(RenderCommandError::InvalidViewportRectPosition {
2742            rect,
2743            min: -max_viewport_range,
2744            max: max_viewport_range - 1.0,
2745        }
2746        .into());
2747    }
2748    if !(0.0..=1.0).contains(&depth_min)
2749        || !(0.0..=1.0).contains(&depth_max)
2750        || depth_min > depth_max
2751    {
2752        return Err(RenderCommandError::InvalidViewportDepth(depth_min, depth_max).into());
2753    }
2754    let r = hal::Rect {
2755        x: rect.x,
2756        y: rect.y,
2757        w: rect.w,
2758        h: rect.h,
2759    };
2760    unsafe {
2761        state
2762            .pass
2763            .base
2764            .raw_encoder
2765            .set_viewport(&r, depth_min..depth_max);
2766    }
2767    Ok(())
2768}
2769
2770fn set_scissor(state: &mut State, rect: Rect<u32>) -> Result<(), RenderPassErrorInner> {
2771    api_log!("RenderPass::set_scissor_rect {rect:?}");
2772
2773    if rect.x.saturating_add(rect.w) > state.info.extent.width
2774        || rect.y.saturating_add(rect.h) > state.info.extent.height
2775    {
2776        return Err(RenderCommandError::InvalidScissorRect(rect, state.info.extent).into());
2777    }
2778    let r = hal::Rect {
2779        x: rect.x,
2780        y: rect.y,
2781        w: rect.w,
2782        h: rect.h,
2783    };
2784    unsafe {
2785        state.pass.base.raw_encoder.set_scissor_rect(&r);
2786    }
2787    Ok(())
2788}
2789
2790fn validate_mesh_draw_multiview(state: &State) -> Result<(), RenderPassErrorInner> {
2791    if let Some(mv) = state.info.multiview_mask {
2792        let highest_bit = 31 - mv.leading_zeros();
2793
2794        let features = state.pass.base.device.features;
2795
2796        if !features.contains(wgt::Features::EXPERIMENTAL_MESH_SHADER_MULTIVIEW)
2797            || highest_bit > state.pass.base.device.limits.max_mesh_multiview_view_count
2798        {
2799            return Err(RenderPassErrorInner::Draw(
2800                DrawError::MeshPipelineMultiviewLimitsViolated {
2801                    highest_view_index: highest_bit,
2802                    max_multiviews: state.pass.base.device.limits.max_mesh_multiview_view_count,
2803                },
2804            ));
2805        }
2806    }
2807    Ok(())
2808}
2809
2810fn draw(
2811    state: &mut State,
2812    vertex_count: u32,
2813    instance_count: u32,
2814    first_vertex: u32,
2815    first_instance: u32,
2816) -> Result<(), RenderPassErrorInner> {
2817    api_log!("RenderPass::draw {vertex_count} {instance_count} {first_vertex} {first_instance}");
2818
2819    state.is_ready(DrawCommandFamily::Draw)?;
2820    state.flush_vertex_buffers()?;
2821    state.flush_bindings()?;
2822
2823    state
2824        .vertex
2825        .limits
2826        .validate_vertex_limit(first_vertex, vertex_count)?;
2827    state
2828        .vertex
2829        .limits
2830        .validate_instance_limit(first_instance, instance_count)?;
2831
2832    unsafe {
2833        if instance_count > 0 && vertex_count > 0 {
2834            state.pass.base.raw_encoder.draw(
2835                first_vertex,
2836                vertex_count,
2837                first_instance,
2838                instance_count,
2839            );
2840        }
2841    }
2842    Ok(())
2843}
2844
2845fn draw_indexed(
2846    state: &mut State,
2847    index_count: u32,
2848    instance_count: u32,
2849    first_index: u32,
2850    base_vertex: i32,
2851    first_instance: u32,
2852) -> Result<(), RenderPassErrorInner> {
2853    api_log!("RenderPass::draw_indexed {index_count} {instance_count} {first_index} {base_vertex} {first_instance}");
2854
2855    state.is_ready(DrawCommandFamily::DrawIndexed)?;
2856    state.flush_vertex_buffers()?;
2857    state.flush_bindings()?;
2858
2859    let last_index = first_index as u64 + index_count as u64;
2860    let index_limit = state.index.limit;
2861    if last_index > index_limit {
2862        return Err(DrawError::IndexBeyondLimit {
2863            last_index,
2864            index_limit,
2865        }
2866        .into());
2867    }
2868    state
2869        .vertex
2870        .limits
2871        .validate_instance_limit(first_instance, instance_count)?;
2872
2873    unsafe {
2874        if instance_count > 0 && index_count > 0 {
2875            state.pass.base.raw_encoder.draw_indexed(
2876                first_index,
2877                index_count,
2878                base_vertex,
2879                first_instance,
2880                instance_count,
2881            );
2882        }
2883    }
2884    Ok(())
2885}
2886
2887fn draw_mesh_tasks(
2888    state: &mut State,
2889    group_count_x: u32,
2890    group_count_y: u32,
2891    group_count_z: u32,
2892) -> Result<(), RenderPassErrorInner> {
2893    api_log!("RenderPass::draw_mesh_tasks {group_count_x} {group_count_y} {group_count_z}");
2894
2895    state.is_ready(DrawCommandFamily::DrawMeshTasks)?;
2896
2897    state.flush_bindings()?;
2898    validate_mesh_draw_multiview(state)?;
2899
2900    let limits = &state.pass.base.device.limits;
2901    let (groups_size_limit, max_groups) = if state.pipeline.as_ref().unwrap().has_task_shader {
2902        (
2903            limits.max_task_workgroups_per_dimension,
2904            limits.max_task_workgroup_total_count,
2905        )
2906    } else {
2907        (
2908            limits.max_mesh_workgroups_per_dimension,
2909            limits.max_mesh_workgroup_total_count,
2910        )
2911    };
2912
2913    let total_count = check_workgroup_sizes(
2914        &[group_count_x, group_count_y, group_count_z],
2915        &[groups_size_limit, groups_size_limit, groups_size_limit],
2916        "max_task_mesh_workgroups_per_dimension",
2917        max_groups,
2918        "max_task_mesh_workgroup_total_count",
2919    )
2920    .map_err(|err| RenderPassErrorInner::Draw(err.into()))?;
2921
2922    unsafe {
2923        if total_count > 0 {
2924            state.pass.base.raw_encoder.draw_mesh_tasks(
2925                group_count_x,
2926                group_count_y,
2927                group_count_z,
2928            );
2929        }
2930    }
2931    Ok(())
2932}
2933
2934fn multi_draw_indirect(
2935    state: &mut State,
2936    indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
2937    device: &Arc<Device>,
2938    indirect_buffer: Arc<Buffer>,
2939    offset: u64,
2940    count: u32,
2941    family: DrawCommandFamily,
2942) -> Result<(), RenderPassErrorInner> {
2943    api_log!(
2944        "RenderPass::draw_indirect (family:{family:?}) {} {offset} {count:?}",
2945        indirect_buffer.error_ident()
2946    );
2947
2948    state.is_ready(family)?;
2949    state.flush_vertex_buffers()?;
2950    state.flush_bindings()?;
2951
2952    if family == DrawCommandFamily::DrawMeshTasks {
2953        validate_mesh_draw_multiview(state)?;
2954    }
2955
2956    state
2957        .pass
2958        .base
2959        .device
2960        .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
2961
2962    indirect_buffer.same_device(device)?;
2963    indirect_buffer.check_usage(BufferUsages::INDIRECT)?;
2964    indirect_buffer.check_destroyed(state.pass.base.snatch_guard)?;
2965
2966    if !offset.is_multiple_of(4) {
2967        return Err(RenderPassErrorInner::UnalignedIndirectBufferOffset(offset));
2968    }
2969
2970    let stride = get_src_stride_of_indirect_args(family);
2971    let args_size = match stride.checked_mul(u64::from(count)) {
2972        Some(sz) if sz <= indirect_buffer.size && indirect_buffer.size - sz >= offset => sz,
2973        args_size => {
2974            return Err(RenderPassErrorInner::IndirectBufferOverrun {
2975                count,
2976                offset,
2977                args_size: args_size.unwrap_or(u64::MAX),
2978                buffer_size: indirect_buffer.size,
2979            });
2980        }
2981    };
2982
2983    state.pass.base.buffer_memory_init_actions.extend(
2984        indirect_buffer.initialization_status.read().create_action(
2985            &indirect_buffer,
2986            offset..offset + args_size,
2987            MemoryInitKind::NeedsInitializedMemory,
2988        ),
2989    );
2990
2991    fn draw(
2992        raw_encoder: &mut dyn hal::DynCommandEncoder,
2993        family: DrawCommandFamily,
2994        indirect_buffer: &dyn hal::DynBuffer,
2995        offset: u64,
2996        count: u32,
2997    ) {
2998        match family {
2999            DrawCommandFamily::Draw => unsafe {
3000                raw_encoder.draw_indirect(indirect_buffer, offset, count);
3001            },
3002            DrawCommandFamily::DrawIndexed => unsafe {
3003                raw_encoder.draw_indexed_indirect(indirect_buffer, offset, count);
3004            },
3005            DrawCommandFamily::DrawMeshTasks => unsafe {
3006                raw_encoder.draw_mesh_tasks_indirect(indirect_buffer, offset, count);
3007            },
3008        }
3009    }
3010
3011    if state.pass.base.device.indirect_validation.is_some() {
3012        state
3013            .pass
3014            .scope
3015            .buffers
3016            .merge_single(&indirect_buffer, wgt::BufferUses::STORAGE_READ_ONLY)?;
3017
3018        struct DrawData {
3019            buffer_index: usize,
3020            offset: u64,
3021            count: u32,
3022        }
3023
3024        struct DrawContext<'a> {
3025            raw_encoder: &'a mut dyn hal::DynCommandEncoder,
3026            device: &'a Device,
3027
3028            indirect_draw_validation_resources: &'a mut crate::indirect_validation::DrawResources,
3029            indirect_draw_validation_batcher: &'a mut crate::indirect_validation::DrawBatcher,
3030
3031            indirect_buffer: Arc<Buffer>,
3032            family: DrawCommandFamily,
3033            vertex_or_index_limit: u64,
3034            instance_limit: u64,
3035        }
3036
3037        impl<'a> DrawContext<'a> {
3038            fn add(&mut self, offset: u64) -> Result<DrawData, DeviceError> {
3039                let (dst_resource_index, dst_offset) = self.indirect_draw_validation_batcher.add(
3040                    self.indirect_draw_validation_resources,
3041                    self.device,
3042                    &self.indirect_buffer,
3043                    offset,
3044                    self.family,
3045                    self.vertex_or_index_limit,
3046                    self.instance_limit,
3047                )?;
3048                Ok(DrawData {
3049                    buffer_index: dst_resource_index,
3050                    offset: dst_offset,
3051                    count: 1,
3052                })
3053            }
3054            fn draw(&mut self, draw_data: DrawData) {
3055                let dst_buffer = self
3056                    .indirect_draw_validation_resources
3057                    .get_dst_buffer(draw_data.buffer_index);
3058                draw(
3059                    self.raw_encoder,
3060                    self.family,
3061                    dst_buffer,
3062                    draw_data.offset,
3063                    draw_data.count,
3064                );
3065            }
3066        }
3067
3068        let mut draw_ctx = DrawContext {
3069            raw_encoder: state.pass.base.raw_encoder,
3070            device: state.pass.base.device,
3071            indirect_draw_validation_resources: state.pass.base.indirect_draw_validation_resources,
3072            indirect_draw_validation_batcher,
3073            indirect_buffer,
3074            family,
3075            vertex_or_index_limit: if family == DrawCommandFamily::DrawIndexed {
3076                state.index.limit
3077            } else {
3078                state.vertex.limits.vertex_limit
3079            },
3080            instance_limit: state.vertex.limits.instance_limit,
3081        };
3082
3083        let mut current_draw_data = draw_ctx.add(offset)?;
3084
3085        for i in 1..count {
3086            let draw_data = draw_ctx.add(offset + stride * i as u64)?;
3087
3088            if draw_data.buffer_index == current_draw_data.buffer_index {
3089                #[cfg(debug_assertions)]
3090                {
3091                    let dst_stride =
3092                        get_dst_stride_of_indirect_args(state.pass.base.device.backend(), family);
3093                    debug_assert_eq!(
3094                        draw_data.offset,
3095                        current_draw_data.offset + dst_stride * current_draw_data.count as u64
3096                    );
3097                }
3098                current_draw_data.count += 1;
3099            } else {
3100                draw_ctx.draw(current_draw_data);
3101                current_draw_data = draw_data;
3102            }
3103        }
3104
3105        draw_ctx.draw(current_draw_data);
3106    } else {
3107        state
3108            .pass
3109            .scope
3110            .buffers
3111            .merge_single(&indirect_buffer, wgt::BufferUses::INDIRECT)?;
3112
3113        draw(
3114            state.pass.base.raw_encoder,
3115            family,
3116            indirect_buffer.try_raw(state.pass.base.snatch_guard)?,
3117            offset,
3118            count,
3119        );
3120    };
3121
3122    Ok(())
3123}
3124
3125fn multi_draw_indirect_count(
3126    state: &mut State,
3127    device: &Arc<Device>,
3128    indirect_buffer: Arc<Buffer>,
3129    offset: u64,
3130    count_buffer: Arc<Buffer>,
3131    count_buffer_offset: u64,
3132    max_count: u32,
3133    family: DrawCommandFamily,
3134) -> Result<(), RenderPassErrorInner> {
3135    api_log!(
3136        "RenderPass::multi_draw_indirect_count (family:{family:?}) {} {offset} {} {count_buffer_offset:?} {max_count:?}",
3137        indirect_buffer.error_ident(),
3138        count_buffer.error_ident()
3139    );
3140
3141    state.is_ready(family)?;
3142    state.flush_vertex_buffers()?;
3143    state.flush_bindings()?;
3144
3145    if family == DrawCommandFamily::DrawMeshTasks {
3146        validate_mesh_draw_multiview(state)?;
3147    }
3148
3149    let stride = get_src_stride_of_indirect_args(family);
3150
3151    state
3152        .pass
3153        .base
3154        .device
3155        .require_features(wgt::Features::MULTI_DRAW_INDIRECT_COUNT)?;
3156    state
3157        .pass
3158        .base
3159        .device
3160        .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
3161
3162    indirect_buffer.same_device(device)?;
3163    count_buffer.same_device(device)?;
3164
3165    state
3166        .pass
3167        .scope
3168        .buffers
3169        .merge_single(&indirect_buffer, wgt::BufferUses::INDIRECT)?;
3170
3171    indirect_buffer.check_usage(BufferUsages::INDIRECT)?;
3172    let indirect_raw = indirect_buffer.try_raw(state.pass.base.snatch_guard)?;
3173
3174    state
3175        .pass
3176        .scope
3177        .buffers
3178        .merge_single(&count_buffer, wgt::BufferUses::INDIRECT)?;
3179
3180    count_buffer.check_usage(BufferUsages::INDIRECT)?;
3181    let count_raw = count_buffer.try_raw(state.pass.base.snatch_guard)?;
3182
3183    if !offset.is_multiple_of(4) {
3184        return Err(RenderPassErrorInner::UnalignedIndirectBufferOffset(offset));
3185    }
3186
3187    let args_size = match stride.checked_mul(u64::from(max_count)) {
3188        Some(sz) if sz <= indirect_buffer.size && indirect_buffer.size - sz >= offset => sz,
3189        args_size => {
3190            return Err(RenderPassErrorInner::IndirectBufferOverrun {
3191                count: 1,
3192                offset,
3193                args_size: args_size.unwrap_or(u64::MAX),
3194                buffer_size: indirect_buffer.size,
3195            });
3196        }
3197    };
3198
3199    state.pass.base.buffer_memory_init_actions.extend(
3200        indirect_buffer.initialization_status.read().create_action(
3201            &indirect_buffer,
3202            offset..offset + args_size,
3203            MemoryInitKind::NeedsInitializedMemory,
3204        ),
3205    );
3206
3207    let begin_count_offset = count_buffer_offset;
3208    let count_bytes = 4;
3209    if count_buffer.size < count_bytes || count_buffer.size - count_bytes < count_buffer_offset {
3210        return Err(RenderPassErrorInner::IndirectCountBufferOverrun {
3211            begin_count_offset,
3212            count_bytes: 4,
3213            count_buffer_size: count_buffer.size,
3214        });
3215    }
3216    state.pass.base.buffer_memory_init_actions.extend(
3217        count_buffer.initialization_status.read().create_action(
3218            &count_buffer,
3219            count_buffer_offset..count_buffer_offset + count_bytes,
3220            MemoryInitKind::NeedsInitializedMemory,
3221        ),
3222    );
3223
3224    match family {
3225        DrawCommandFamily::Draw => unsafe {
3226            state.pass.base.raw_encoder.draw_indirect_count(
3227                indirect_raw,
3228                offset,
3229                count_raw,
3230                count_buffer_offset,
3231                max_count,
3232            );
3233        },
3234        DrawCommandFamily::DrawIndexed => unsafe {
3235            state.pass.base.raw_encoder.draw_indexed_indirect_count(
3236                indirect_raw,
3237                offset,
3238                count_raw,
3239                count_buffer_offset,
3240                max_count,
3241            );
3242        },
3243        DrawCommandFamily::DrawMeshTasks => unsafe {
3244            state.pass.base.raw_encoder.draw_mesh_tasks_indirect_count(
3245                indirect_raw,
3246                offset,
3247                count_raw,
3248                count_buffer_offset,
3249                max_count,
3250            );
3251        },
3252    }
3253    Ok(())
3254}
3255
3256fn execute_bundle(
3257    state: &mut State,
3258    indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
3259    device: &Arc<Device>,
3260    bundle: Arc<super::RenderBundle>,
3261) -> Result<(), RenderPassErrorInner> {
3262    api_log!("RenderPass::execute_bundle {}", bundle.error_ident());
3263
3264    let bundle = state.pass.base.tracker.bundles.insert_single(bundle);
3265
3266    bundle.same_device(device)?;
3267
3268    state
3269        .info
3270        .context
3271        .check_compatible(&bundle.context, bundle.as_ref())
3272        .map_err(RenderPassErrorInner::IncompatibleBundleTargets)?;
3273
3274    if (state.info.is_depth_read_only && !bundle.is_depth_read_only)
3275        || (state.info.is_stencil_read_only && !bundle.is_stencil_read_only)
3276    {
3277        return Err(
3278            RenderPassErrorInner::IncompatibleBundleReadOnlyDepthStencil {
3279                pass_depth: state.info.is_depth_read_only,
3280                pass_stencil: state.info.is_stencil_read_only,
3281                bundle_depth: bundle.is_depth_read_only,
3282                bundle_stencil: bundle.is_stencil_read_only,
3283            },
3284        );
3285    }
3286
3287    state.pass.base.buffer_memory_init_actions.extend(
3288        bundle
3289            .buffer_memory_init_actions
3290            .iter()
3291            .filter_map(|action| {
3292                action
3293                    .buffer
3294                    .initialization_status
3295                    .read()
3296                    .check_action(action)
3297            }),
3298    );
3299    for action in bundle.texture_memory_init_actions.iter() {
3300        state.pass.pending_discard_init_fixups.extend(
3301            state
3302                .pass
3303                .base
3304                .texture_memory_actions
3305                .register_init_action(action),
3306        );
3307    }
3308
3309    unsafe {
3310        bundle.execute(
3311            state.pass.base.raw_encoder,
3312            state.pass.base.indirect_draw_validation_resources,
3313            indirect_draw_validation_batcher,
3314            state.pass.base.snatch_guard,
3315        )
3316    }
3317    .map_err(|e| match e {
3318        ExecutionError::Device(e) => RenderPassErrorInner::Device(e),
3319        ExecutionError::DestroyedResource(e) => {
3320            RenderPassErrorInner::RenderCommand(RenderCommandError::DestroyedResource(e))
3321        }
3322        ExecutionError::Unimplemented(what) => {
3323            RenderPassErrorInner::RenderCommand(RenderCommandError::Unimplemented(what))
3324        }
3325    })?;
3326
3327    unsafe {
3328        state.pass.scope.merge_render_bundle(&bundle.used)?;
3329    };
3330    state.reset_bundle();
3331    Ok(())
3332}
3333
3334// Recording a render pass.
3335//
3336// The only error that should be returned from these methods is
3337// `EncoderStateError::Ended`, when the pass has already ended and an immediate
3338// validation error is raised.
3339//
3340// All other errors should be stored in the pass for later reporting when
3341// `CommandEncoder.finish()` is called.
3342//
3343// The `pass_try!` macro should be used to handle errors appropriately. Note
3344// that the `pass_try!` and `pass_base!` macros may return early from the
3345// function that invokes them, like the `?` operator.
3346impl Global {
3347    pub fn render_pass_set_bind_group(
3348        &self,
3349        pass: &mut RenderPass,
3350        index: u32,
3351        bind_group_id: Option<id::BindGroupId>,
3352        offsets: &[DynamicOffset],
3353    ) -> Result<(), PassStateError> {
3354        let scope = PassErrorScope::SetBindGroup;
3355
3356        // This statement will return an error if the pass is ended. It's
3357        // important the error check comes before the early-out for
3358        // `set_and_check_redundant`.
3359        let base = pass_base!(pass, scope);
3360
3361        if pass.current_bind_groups.set_and_check_redundant(
3362            bind_group_id,
3363            index,
3364            &mut base.dynamic_offsets,
3365            offsets,
3366        ) {
3367            return Ok(());
3368        }
3369
3370        let mut bind_group = None;
3371        if let Some(bind_group_id) = bind_group_id {
3372            let hub = &self.hub;
3373            bind_group = Some(pass_try!(
3374                base,
3375                scope,
3376                hub.bind_groups.get(bind_group_id).get(),
3377            ));
3378        }
3379
3380        base.commands.push(ArcRenderCommand::SetBindGroup {
3381            index,
3382            num_dynamic_offsets: offsets.len(),
3383            bind_group,
3384        });
3385
3386        Ok(())
3387    }
3388
3389    pub fn render_pass_set_pipeline(
3390        &self,
3391        pass: &mut RenderPass,
3392        pipeline_id: id::RenderPipelineId,
3393    ) -> Result<(), PassStateError> {
3394        let scope = PassErrorScope::SetPipelineRender;
3395
3396        let redundant = pass.current_pipeline.set_and_check_redundant(pipeline_id);
3397
3398        // This statement will return an error if the pass is ended.
3399        // Its important the error check comes before the early-out for `redundant`.
3400        let base = pass_base!(pass, scope);
3401
3402        if redundant {
3403            return Ok(());
3404        }
3405
3406        let hub = &self.hub;
3407        let pipeline = pass_try!(base, scope, hub.render_pipelines.get(pipeline_id).get());
3408
3409        base.commands.push(ArcRenderCommand::SetPipeline(pipeline));
3410
3411        Ok(())
3412    }
3413
3414    pub fn render_pass_set_index_buffer(
3415        &self,
3416        pass: &mut RenderPass,
3417        buffer_id: id::BufferId,
3418        index_format: IndexFormat,
3419        offset: BufferAddress,
3420        size: Option<BufferSize>,
3421    ) -> Result<(), PassStateError> {
3422        let scope = PassErrorScope::SetIndexBuffer;
3423        let base = pass_base!(pass, scope);
3424
3425        base.commands.push(ArcRenderCommand::SetIndexBuffer {
3426            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3427            index_format,
3428            offset,
3429            size,
3430        });
3431
3432        Ok(())
3433    }
3434
3435    pub fn render_pass_set_vertex_buffer(
3436        &self,
3437        pass: &mut RenderPass,
3438        slot: u32,
3439        buffer_id: Option<id::BufferId>,
3440        offset: BufferAddress,
3441        size: Option<BufferSize>,
3442    ) -> Result<(), PassStateError> {
3443        let scope = PassErrorScope::SetVertexBuffer;
3444        let base = pass_base!(pass, scope);
3445
3446        let buffer = if let Some(buffer_id) = buffer_id {
3447            Some(pass_try!(base, scope, self.resolve_buffer_id(buffer_id)))
3448        } else {
3449            None
3450        };
3451
3452        base.commands.push(ArcRenderCommand::SetVertexBuffer {
3453            slot,
3454            buffer,
3455            offset,
3456            size,
3457        });
3458
3459        Ok(())
3460    }
3461
3462    pub fn render_pass_set_blend_constant(
3463        &self,
3464        pass: &mut RenderPass,
3465        color: Color,
3466    ) -> Result<(), PassStateError> {
3467        let scope = PassErrorScope::SetBlendConstant;
3468        let base = pass_base!(pass, scope);
3469
3470        base.commands
3471            .push(ArcRenderCommand::SetBlendConstant(color));
3472
3473        Ok(())
3474    }
3475
3476    pub fn render_pass_set_stencil_reference(
3477        &self,
3478        pass: &mut RenderPass,
3479        value: u32,
3480    ) -> Result<(), PassStateError> {
3481        let scope = PassErrorScope::SetStencilReference;
3482        let base = pass_base!(pass, scope);
3483
3484        base.commands
3485            .push(ArcRenderCommand::SetStencilReference(value));
3486
3487        Ok(())
3488    }
3489
3490    pub fn render_pass_set_viewport(
3491        &self,
3492        pass: &mut RenderPass,
3493        x: f32,
3494        y: f32,
3495        w: f32,
3496        h: f32,
3497        depth_min: f32,
3498        depth_max: f32,
3499    ) -> Result<(), PassStateError> {
3500        let scope = PassErrorScope::SetViewport;
3501        let base = pass_base!(pass, scope);
3502
3503        base.commands.push(ArcRenderCommand::SetViewport {
3504            rect: Rect { x, y, w, h },
3505            depth_min,
3506            depth_max,
3507        });
3508
3509        Ok(())
3510    }
3511
3512    pub fn render_pass_set_scissor_rect(
3513        &self,
3514        pass: &mut RenderPass,
3515        x: u32,
3516        y: u32,
3517        w: u32,
3518        h: u32,
3519    ) -> Result<(), PassStateError> {
3520        let scope = PassErrorScope::SetScissorRect;
3521        let base = pass_base!(pass, scope);
3522
3523        base.commands
3524            .push(ArcRenderCommand::SetScissor(Rect { x, y, w, h }));
3525
3526        Ok(())
3527    }
3528
3529    pub fn render_pass_set_immediates(
3530        &self,
3531        pass: &mut RenderPass,
3532        offset: u32,
3533        data: &[u8],
3534    ) -> Result<(), PassStateError> {
3535        let scope = PassErrorScope::SetImmediate;
3536        let base = pass_base!(pass, scope);
3537
3538        if offset & (wgt::IMMEDIATE_DATA_ALIGNMENT - 1) != 0 {
3539            pass_try!(
3540                base,
3541                scope,
3542                Err(RenderPassErrorInner::ImmediateOffsetAlignment)
3543            );
3544        }
3545        if data.len() as u32 & (wgt::IMMEDIATE_DATA_ALIGNMENT - 1) != 0 {
3546            pass_try!(
3547                base,
3548                scope,
3549                Err(RenderPassErrorInner::ImmediateDataizeAlignment)
3550            );
3551        }
3552
3553        let value_offset = pass_try!(
3554            base,
3555            scope,
3556            base.immediates_data
3557                .len()
3558                .try_into()
3559                .map_err(|_| RenderPassErrorInner::ImmediateOutOfMemory),
3560        );
3561
3562        base.immediates_data.extend(
3563            data.chunks_exact(wgt::IMMEDIATE_DATA_ALIGNMENT as usize)
3564                .map(|arr| u32::from_ne_bytes([arr[0], arr[1], arr[2], arr[3]])),
3565        );
3566
3567        base.commands.push(ArcRenderCommand::SetImmediate {
3568            offset,
3569            size_bytes: data.len() as u32,
3570            values_offset: Some(value_offset),
3571        });
3572
3573        Ok(())
3574    }
3575
3576    pub fn render_pass_draw(
3577        &self,
3578        pass: &mut RenderPass,
3579        vertex_count: u32,
3580        instance_count: u32,
3581        first_vertex: u32,
3582        first_instance: u32,
3583    ) -> Result<(), PassStateError> {
3584        let scope = PassErrorScope::Draw {
3585            kind: DrawKind::Draw,
3586            family: DrawCommandFamily::Draw,
3587        };
3588        let base = pass_base!(pass, scope);
3589
3590        base.commands.push(ArcRenderCommand::Draw {
3591            vertex_count,
3592            instance_count,
3593            first_vertex,
3594            first_instance,
3595        });
3596
3597        Ok(())
3598    }
3599
3600    pub fn render_pass_draw_indexed(
3601        &self,
3602        pass: &mut RenderPass,
3603        index_count: u32,
3604        instance_count: u32,
3605        first_index: u32,
3606        base_vertex: i32,
3607        first_instance: u32,
3608    ) -> Result<(), PassStateError> {
3609        let scope = PassErrorScope::Draw {
3610            kind: DrawKind::Draw,
3611            family: DrawCommandFamily::DrawIndexed,
3612        };
3613        let base = pass_base!(pass, scope);
3614
3615        base.commands.push(ArcRenderCommand::DrawIndexed {
3616            index_count,
3617            instance_count,
3618            first_index,
3619            base_vertex,
3620            first_instance,
3621        });
3622
3623        Ok(())
3624    }
3625
3626    pub fn render_pass_draw_mesh_tasks(
3627        &self,
3628        pass: &mut RenderPass,
3629        group_count_x: u32,
3630        group_count_y: u32,
3631        group_count_z: u32,
3632    ) -> Result<(), RenderPassError> {
3633        let scope = PassErrorScope::Draw {
3634            kind: DrawKind::Draw,
3635            family: DrawCommandFamily::DrawMeshTasks,
3636        };
3637        let base = pass_base!(pass, scope);
3638
3639        base.commands.push(ArcRenderCommand::DrawMeshTasks {
3640            group_count_x,
3641            group_count_y,
3642            group_count_z,
3643        });
3644        Ok(())
3645    }
3646
3647    pub fn render_pass_draw_indirect(
3648        &self,
3649        pass: &mut RenderPass,
3650        buffer_id: id::BufferId,
3651        offset: BufferAddress,
3652    ) -> Result<(), PassStateError> {
3653        let scope = PassErrorScope::Draw {
3654            kind: DrawKind::DrawIndirect,
3655            family: DrawCommandFamily::Draw,
3656        };
3657        let base = pass_base!(pass, scope);
3658
3659        base.commands.push(ArcRenderCommand::DrawIndirect {
3660            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3661            offset,
3662            count: 1,
3663            family: DrawCommandFamily::Draw,
3664
3665            vertex_or_index_limit: None,
3666            instance_limit: None,
3667        });
3668
3669        Ok(())
3670    }
3671
3672    pub fn render_pass_draw_indexed_indirect(
3673        &self,
3674        pass: &mut RenderPass,
3675        buffer_id: id::BufferId,
3676        offset: BufferAddress,
3677    ) -> Result<(), PassStateError> {
3678        let scope = PassErrorScope::Draw {
3679            kind: DrawKind::DrawIndirect,
3680            family: DrawCommandFamily::DrawIndexed,
3681        };
3682        let base = pass_base!(pass, scope);
3683
3684        base.commands.push(ArcRenderCommand::DrawIndirect {
3685            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3686            offset,
3687            count: 1,
3688            family: DrawCommandFamily::DrawIndexed,
3689
3690            vertex_or_index_limit: None,
3691            instance_limit: None,
3692        });
3693
3694        Ok(())
3695    }
3696
3697    pub fn render_pass_draw_mesh_tasks_indirect(
3698        &self,
3699        pass: &mut RenderPass,
3700        buffer_id: id::BufferId,
3701        offset: BufferAddress,
3702    ) -> Result<(), RenderPassError> {
3703        let scope = PassErrorScope::Draw {
3704            kind: DrawKind::DrawIndirect,
3705            family: DrawCommandFamily::DrawMeshTasks,
3706        };
3707        let base = pass_base!(pass, scope);
3708
3709        base.commands.push(ArcRenderCommand::DrawIndirect {
3710            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3711            offset,
3712            count: 1,
3713            family: DrawCommandFamily::DrawMeshTasks,
3714
3715            vertex_or_index_limit: None,
3716            instance_limit: None,
3717        });
3718
3719        Ok(())
3720    }
3721
3722    pub fn render_pass_multi_draw_indirect(
3723        &self,
3724        pass: &mut RenderPass,
3725        buffer_id: id::BufferId,
3726        offset: BufferAddress,
3727        count: u32,
3728    ) -> Result<(), PassStateError> {
3729        let scope = PassErrorScope::Draw {
3730            kind: DrawKind::MultiDrawIndirect,
3731            family: DrawCommandFamily::Draw,
3732        };
3733        let base = pass_base!(pass, scope);
3734
3735        base.commands.push(ArcRenderCommand::DrawIndirect {
3736            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3737            offset,
3738            count,
3739            family: DrawCommandFamily::Draw,
3740
3741            vertex_or_index_limit: None,
3742            instance_limit: None,
3743        });
3744
3745        Ok(())
3746    }
3747
3748    pub fn render_pass_multi_draw_indexed_indirect(
3749        &self,
3750        pass: &mut RenderPass,
3751        buffer_id: id::BufferId,
3752        offset: BufferAddress,
3753        count: u32,
3754    ) -> Result<(), PassStateError> {
3755        let scope = PassErrorScope::Draw {
3756            kind: DrawKind::MultiDrawIndirect,
3757            family: DrawCommandFamily::DrawIndexed,
3758        };
3759        let base = pass_base!(pass, scope);
3760
3761        base.commands.push(ArcRenderCommand::DrawIndirect {
3762            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3763            offset,
3764            count,
3765            family: DrawCommandFamily::DrawIndexed,
3766
3767            vertex_or_index_limit: None,
3768            instance_limit: None,
3769        });
3770
3771        Ok(())
3772    }
3773
3774    pub fn render_pass_multi_draw_mesh_tasks_indirect(
3775        &self,
3776        pass: &mut RenderPass,
3777        buffer_id: id::BufferId,
3778        offset: BufferAddress,
3779        count: u32,
3780    ) -> Result<(), RenderPassError> {
3781        let scope = PassErrorScope::Draw {
3782            kind: DrawKind::MultiDrawIndirect,
3783            family: DrawCommandFamily::DrawMeshTasks,
3784        };
3785        let base = pass_base!(pass, scope);
3786
3787        base.commands.push(ArcRenderCommand::DrawIndirect {
3788            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3789            offset,
3790            count,
3791            family: DrawCommandFamily::DrawMeshTasks,
3792
3793            vertex_or_index_limit: None,
3794            instance_limit: None,
3795        });
3796
3797        Ok(())
3798    }
3799
3800    pub fn render_pass_multi_draw_indirect_count(
3801        &self,
3802        pass: &mut RenderPass,
3803        buffer_id: id::BufferId,
3804        offset: BufferAddress,
3805        count_buffer_id: id::BufferId,
3806        count_buffer_offset: BufferAddress,
3807        max_count: u32,
3808    ) -> Result<(), PassStateError> {
3809        let scope = PassErrorScope::Draw {
3810            kind: DrawKind::MultiDrawIndirectCount,
3811            family: DrawCommandFamily::Draw,
3812        };
3813        let base = pass_base!(pass, scope);
3814
3815        base.commands
3816            .push(ArcRenderCommand::MultiDrawIndirectCount {
3817                buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3818                offset,
3819                count_buffer: pass_try!(base, scope, self.resolve_buffer_id(count_buffer_id)),
3820                count_buffer_offset,
3821                max_count,
3822                family: DrawCommandFamily::Draw,
3823            });
3824
3825        Ok(())
3826    }
3827
3828    pub fn render_pass_multi_draw_indexed_indirect_count(
3829        &self,
3830        pass: &mut RenderPass,
3831        buffer_id: id::BufferId,
3832        offset: BufferAddress,
3833        count_buffer_id: id::BufferId,
3834        count_buffer_offset: BufferAddress,
3835        max_count: u32,
3836    ) -> Result<(), PassStateError> {
3837        let scope = PassErrorScope::Draw {
3838            kind: DrawKind::MultiDrawIndirectCount,
3839            family: DrawCommandFamily::DrawIndexed,
3840        };
3841        let base = pass_base!(pass, scope);
3842
3843        base.commands
3844            .push(ArcRenderCommand::MultiDrawIndirectCount {
3845                buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3846                offset,
3847                count_buffer: pass_try!(base, scope, self.resolve_buffer_id(count_buffer_id)),
3848                count_buffer_offset,
3849                max_count,
3850                family: DrawCommandFamily::DrawIndexed,
3851            });
3852
3853        Ok(())
3854    }
3855
3856    pub fn render_pass_multi_draw_mesh_tasks_indirect_count(
3857        &self,
3858        pass: &mut RenderPass,
3859        buffer_id: id::BufferId,
3860        offset: BufferAddress,
3861        count_buffer_id: id::BufferId,
3862        count_buffer_offset: BufferAddress,
3863        max_count: u32,
3864    ) -> Result<(), RenderPassError> {
3865        let scope = PassErrorScope::Draw {
3866            kind: DrawKind::MultiDrawIndirectCount,
3867            family: DrawCommandFamily::DrawMeshTasks,
3868        };
3869        let base = pass_base!(pass, scope);
3870
3871        base.commands
3872            .push(ArcRenderCommand::MultiDrawIndirectCount {
3873                buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3874                offset,
3875                count_buffer: pass_try!(base, scope, self.resolve_buffer_id(count_buffer_id)),
3876                count_buffer_offset,
3877                max_count,
3878                family: DrawCommandFamily::DrawMeshTasks,
3879            });
3880
3881        Ok(())
3882    }
3883
3884    pub fn render_pass_push_debug_group(
3885        &self,
3886        pass: &mut RenderPass,
3887        label: &str,
3888        color: u32,
3889    ) -> Result<(), PassStateError> {
3890        let base = pass_base!(pass, PassErrorScope::PushDebugGroup);
3891
3892        let bytes = label.as_bytes();
3893        base.string_data.extend_from_slice(bytes);
3894
3895        base.commands.push(ArcRenderCommand::PushDebugGroup {
3896            color,
3897            len: bytes.len(),
3898        });
3899
3900        Ok(())
3901    }
3902
3903    pub fn render_pass_pop_debug_group(&self, pass: &mut RenderPass) -> Result<(), PassStateError> {
3904        let base = pass_base!(pass, PassErrorScope::PopDebugGroup);
3905
3906        base.commands.push(ArcRenderCommand::PopDebugGroup);
3907
3908        Ok(())
3909    }
3910
3911    pub fn render_pass_insert_debug_marker(
3912        &self,
3913        pass: &mut RenderPass,
3914        label: &str,
3915        color: u32,
3916    ) -> Result<(), PassStateError> {
3917        let base = pass_base!(pass, PassErrorScope::InsertDebugMarker);
3918
3919        let bytes = label.as_bytes();
3920        base.string_data.extend_from_slice(bytes);
3921
3922        base.commands.push(ArcRenderCommand::InsertDebugMarker {
3923            color,
3924            len: bytes.len(),
3925        });
3926
3927        Ok(())
3928    }
3929
3930    pub fn render_pass_write_timestamp(
3931        &self,
3932        pass: &mut RenderPass,
3933        query_set_id: id::QuerySetId,
3934        query_index: u32,
3935    ) -> Result<(), PassStateError> {
3936        let scope = PassErrorScope::WriteTimestamp;
3937        let base = pass_base!(pass, scope);
3938
3939        base.commands.push(ArcRenderCommand::WriteTimestamp {
3940            query_set: pass_try!(base, scope, self.resolve_query_set(query_set_id)),
3941            query_index,
3942        });
3943
3944        Ok(())
3945    }
3946
3947    pub fn render_pass_begin_occlusion_query(
3948        &self,
3949        pass: &mut RenderPass,
3950        query_index: u32,
3951    ) -> Result<(), PassStateError> {
3952        let scope = PassErrorScope::BeginOcclusionQuery;
3953        let base = pass_base!(pass, scope);
3954
3955        base.commands
3956            .push(ArcRenderCommand::BeginOcclusionQuery { query_index });
3957
3958        Ok(())
3959    }
3960
3961    pub fn render_pass_end_occlusion_query(
3962        &self,
3963        pass: &mut RenderPass,
3964    ) -> Result<(), PassStateError> {
3965        let scope = PassErrorScope::EndOcclusionQuery;
3966        let base = pass_base!(pass, scope);
3967
3968        base.commands.push(ArcRenderCommand::EndOcclusionQuery);
3969
3970        Ok(())
3971    }
3972
3973    pub fn render_pass_begin_pipeline_statistics_query(
3974        &self,
3975        pass: &mut RenderPass,
3976        query_set_id: id::QuerySetId,
3977        query_index: u32,
3978    ) -> Result<(), PassStateError> {
3979        let scope = PassErrorScope::BeginPipelineStatisticsQuery;
3980        let base = pass_base!(pass, scope);
3981
3982        base.commands
3983            .push(ArcRenderCommand::BeginPipelineStatisticsQuery {
3984                query_set: pass_try!(base, scope, self.resolve_query_set(query_set_id)),
3985                query_index,
3986            });
3987
3988        Ok(())
3989    }
3990
3991    pub fn render_pass_end_pipeline_statistics_query(
3992        &self,
3993        pass: &mut RenderPass,
3994    ) -> Result<(), PassStateError> {
3995        let scope = PassErrorScope::EndPipelineStatisticsQuery;
3996        let base = pass_base!(pass, scope);
3997
3998        base.commands
3999            .push(ArcRenderCommand::EndPipelineStatisticsQuery);
4000
4001        Ok(())
4002    }
4003
4004    pub fn render_pass_execute_bundles(
4005        &self,
4006        pass: &mut RenderPass,
4007        render_bundle_ids: &[id::RenderBundleId],
4008    ) -> Result<(), PassStateError> {
4009        let scope = PassErrorScope::ExecuteBundle;
4010        let base = pass_base!(pass, scope);
4011
4012        let hub = &self.hub;
4013        let bundles = hub.render_bundles.read();
4014
4015        for &bundle_id in render_bundle_ids {
4016            let bundle = pass_try!(base, scope, bundles.get(bundle_id).get());
4017
4018            base.commands.push(ArcRenderCommand::ExecuteBundle(bundle));
4019        }
4020        pass.current_pipeline.reset();
4021        pass.current_bind_groups.reset();
4022
4023        Ok(())
4024    }
4025}
4026
4027pub(crate) const fn get_src_stride_of_indirect_args(family: DrawCommandFamily) -> u64 {
4028    match family {
4029        DrawCommandFamily::Draw => size_of::<wgt::DrawIndirectArgs>() as u64,
4030        DrawCommandFamily::DrawIndexed => size_of::<wgt::DrawIndexedIndirectArgs>() as u64,
4031        DrawCommandFamily::DrawMeshTasks => size_of::<wgt::DispatchIndirectArgs>() as u64,
4032    }
4033}
4034
4035pub(crate) const fn get_dst_stride_of_indirect_args(
4036    backend: wgt::Backend,
4037    family: DrawCommandFamily,
4038) -> u64 {
4039    // space for D3D12 special constants
4040    let extra = if matches!(backend, wgt::Backend::Dx12) {
4041        3 * size_of::<u32>() as u64
4042    } else {
4043        0
4044    };
4045    extra + get_src_stride_of_indirect_args(family)
4046}