Skip to main content

wgpu_core/command/
render.rs

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