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