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        let e: &dyn WebGpuError = match inner {
874            RenderPassErrorInner::Device(e) => e,
875            RenderPassErrorInner::ColorAttachment(e) => e,
876            RenderPassErrorInner::EncoderState(e) => e,
877            RenderPassErrorInner::DebugGroupError(e) => e,
878            RenderPassErrorInner::MissingFeatures(e) => e,
879            RenderPassErrorInner::MissingDownlevelFlags(e) => e,
880            RenderPassErrorInner::RenderCommand(e) => e,
881            RenderPassErrorInner::Draw(e) => e,
882            RenderPassErrorInner::Bind(e) => e,
883            RenderPassErrorInner::QueryUse(e) => e,
884            RenderPassErrorInner::DestroyedResource(e) => e,
885            RenderPassErrorInner::InvalidResource(e) => e,
886            RenderPassErrorInner::IncompatibleBundleTargets(e) => e,
887            RenderPassErrorInner::InvalidAttachment(e) => e,
888            RenderPassErrorInner::TimestampWrites(e) => e,
889            RenderPassErrorInner::InvalidValuesOffset(e) => e,
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 => return ErrorType::Validation,
914        };
915        e.webgpu_error_type()
916    }
917}
918
919struct RenderAttachment {
920    texture: Arc<Texture>,
921    selector: TextureSelector,
922    usage: wgt::TextureUses,
923}
924
925impl TextureView {
926    fn to_render_attachment(&self, usage: wgt::TextureUses) -> RenderAttachment {
927        RenderAttachment {
928            texture: self.parent.clone(),
929            selector: self.selector.clone(),
930            usage,
931        }
932    }
933}
934
935const MAX_TOTAL_ATTACHMENTS: usize = hal::MAX_COLOR_ATTACHMENTS + hal::MAX_COLOR_ATTACHMENTS + 1;
936type AttachmentDataVec<T> = ArrayVec<T, MAX_TOTAL_ATTACHMENTS>;
937
938struct RenderPassInfo {
939    context: RenderPassContext,
940    /// All render attachments, including depth/stencil
941    render_attachments: AttachmentDataVec<RenderAttachment>,
942    is_depth_read_only: bool,
943    is_stencil_read_only: bool,
944    extent: wgt::Extent3d,
945
946    divergent_discarded_depth_stencil_aspect: Option<(wgt::TextureAspect, Arc<TextureView>)>,
947    multiview_mask: Option<NonZeroU32>,
948}
949
950impl RenderPassInfo {
951    fn add_pass_texture_init_actions<V>(
952        load_op: LoadOp<V>,
953        store_op: StoreOp,
954        texture_memory_actions: &mut CommandBufferTextureMemoryActions,
955        view: &TextureView,
956        pending_discard_init_fixups: &mut SurfacesInDiscardState,
957    ) {
958        if matches!(load_op, LoadOp::Load) {
959            pending_discard_init_fixups.extend(texture_memory_actions.register_init_action(
960                &TextureInitTrackerAction {
961                    texture: view.parent.clone(),
962                    range: TextureInitRange::from(view.selector.clone()),
963                    // Note that this is needed even if the target is discarded,
964                    kind: MemoryInitKind::NeedsInitializedMemory,
965                },
966            ));
967        } else if store_op == StoreOp::Store {
968            // Clear + Store
969            texture_memory_actions.register_implicit_init(
970                &view.parent,
971                TextureInitRange::from(view.selector.clone()),
972            );
973        }
974        if store_op == StoreOp::Discard {
975            // the discard happens at the *end* of a pass, but recording the
976            // discard right away be alright since the texture can't be used
977            // during the pass anyways
978            texture_memory_actions.discard(TextureSurfaceDiscard {
979                texture: view.parent.clone(),
980                mip_level: view.selector.mips.start,
981                layer: view.selector.layers.start,
982            });
983        }
984    }
985
986    fn start(
987        device: &Arc<Device>,
988        hal_label: Option<&str>,
989        color_attachments: &[Option<ArcRenderPassColorAttachment>],
990        mut depth_stencil_attachment: Option<
991            ResolvedRenderPassDepthStencilAttachment<Arc<TextureView>>,
992        >,
993        mut timestamp_writes: Option<ArcPassTimestampWrites>,
994        mut occlusion_query_set: Option<Arc<QuerySet>>,
995        encoder: &mut dyn hal::DynCommandEncoder,
996        trackers: &mut Tracker,
997        texture_memory_actions: &mut CommandBufferTextureMemoryActions,
998        pending_query_resets: &mut QueryResetMap,
999        pending_discard_init_fixups: &mut SurfacesInDiscardState,
1000        snatch_guard: &SnatchGuard<'_>,
1001        multiview_mask: Option<NonZeroU32>,
1002    ) -> Result<Self, RenderPassErrorInner> {
1003        profiling::scope!("RenderPassInfo::start");
1004
1005        // We default to false intentionally, even if depth-stencil isn't used at all.
1006        // This allows us to use the primary raw pipeline in `RenderPipeline`,
1007        // instead of the special read-only one, which would be `None`.
1008        let mut is_depth_read_only = false;
1009        let mut is_stencil_read_only = false;
1010
1011        let mut render_attachments = AttachmentDataVec::<RenderAttachment>::new();
1012        let mut discarded_surfaces = AttachmentDataVec::new();
1013        let mut divergent_discarded_depth_stencil_aspect = None;
1014
1015        let mut attachment_location = AttachmentErrorLocation::Color {
1016            index: usize::MAX,
1017            resolve: false,
1018        };
1019        let mut extent = None;
1020        let mut sample_count = 0;
1021
1022        let mut detected_multiview: Option<Option<NonZeroU32>> = None;
1023
1024        let mut check_multiview = |view: &TextureView| {
1025            // Get the multiview configuration for this texture view
1026            let layers = view.selector.layers.end - view.selector.layers.start;
1027            let this_multiview = if layers >= 2 {
1028                // Trivially proven by the if above
1029                Some(unsafe { NonZeroU32::new_unchecked(layers) })
1030            } else {
1031                None
1032            };
1033
1034            // Make sure that if this view is a multiview, it is set to be an array
1035            if this_multiview.is_some() && view.desc.dimension != TextureViewDimension::D2Array {
1036                return Err(RenderPassErrorInner::MultiViewDimensionMismatch);
1037            }
1038
1039            // Validate matching first, or store the first one
1040            if let Some(multiview) = detected_multiview {
1041                if multiview != this_multiview {
1042                    return Err(RenderPassErrorInner::MultiViewMismatch);
1043                }
1044            } else {
1045                // Multiview is only supported if the feature is enabled
1046                if let Some(this_multiview) = this_multiview {
1047                    device.require_features(wgt::Features::MULTIVIEW)?;
1048                    if this_multiview.get() > device.limits.max_multiview_view_count {
1049                        return Err(RenderPassErrorInner::TooManyMultiviewViews);
1050                    }
1051                }
1052
1053                detected_multiview = Some(this_multiview);
1054            }
1055
1056            Ok(())
1057        };
1058        let mut add_view = |view: &TextureView, location| {
1059            let render_extent = view.render_extent.map_err(|reason| {
1060                RenderPassErrorInner::TextureViewIsNotRenderable { location, reason }
1061            })?;
1062            if let Some(ex) = extent {
1063                if ex != render_extent {
1064                    return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
1065                        expected_location: attachment_location,
1066                        expected_extent: ex,
1067                        actual_location: location,
1068                        actual_extent: render_extent,
1069                    });
1070                }
1071            } else {
1072                extent = Some(render_extent);
1073            }
1074            if sample_count == 0 {
1075                sample_count = view.samples;
1076            } else if sample_count != view.samples {
1077                return Err(RenderPassErrorInner::AttachmentSampleCountMismatch {
1078                    expected_location: attachment_location,
1079                    expected_samples: sample_count,
1080                    actual_location: location,
1081                    actual_samples: view.samples,
1082                });
1083            }
1084            attachment_location = location;
1085            Ok(())
1086        };
1087
1088        let mut depth_stencil = None;
1089
1090        if let Some(at) = depth_stencil_attachment.as_ref() {
1091            let view = &at.view;
1092            check_multiview(view)?;
1093            add_view(view, AttachmentErrorLocation::Depth)?;
1094
1095            let ds_aspects = view.desc.aspects();
1096
1097            if !ds_aspects.contains(hal::FormatAspects::STENCIL)
1098                || (at.stencil.load_op().eq_variant(at.depth.load_op())
1099                    && at.stencil.store_op() == at.depth.store_op())
1100            {
1101                Self::add_pass_texture_init_actions(
1102                    at.depth.load_op(),
1103                    at.depth.store_op(),
1104                    texture_memory_actions,
1105                    view,
1106                    pending_discard_init_fixups,
1107                );
1108            } else if !ds_aspects.contains(hal::FormatAspects::DEPTH) {
1109                Self::add_pass_texture_init_actions(
1110                    at.stencil.load_op(),
1111                    at.stencil.store_op(),
1112                    texture_memory_actions,
1113                    view,
1114                    pending_discard_init_fixups,
1115                );
1116            } else {
1117                // This is the only place (anywhere in wgpu) where Stencil &
1118                // Depth init state can diverge.
1119                //
1120                // To safe us the overhead of tracking init state of texture
1121                // aspects everywhere, we're going to cheat a little bit in
1122                // order to keep the init state of both Stencil and Depth
1123                // aspects in sync. The expectation is that we hit this path
1124                // extremely rarely!
1125                //
1126                // Diverging LoadOp, i.e. Load + Clear:
1127                //
1128                // Record MemoryInitKind::NeedsInitializedMemory for the entire
1129                // surface, a bit wasteful on unit but no negative effect!
1130                //
1131                // Rationale: If the loaded channel is uninitialized it needs
1132                // clearing, the cleared channel doesn't care. (If everything is
1133                // already initialized nothing special happens)
1134                //
1135                // (possible minor optimization: Clear caused by
1136                // NeedsInitializedMemory should know that it doesn't need to
1137                // clear the aspect that was set to C)
1138                let need_init_beforehand =
1139                    at.depth.load_op() == LoadOp::Load || at.stencil.load_op() == LoadOp::Load;
1140                if need_init_beforehand {
1141                    pending_discard_init_fixups.extend(
1142                        texture_memory_actions.register_init_action(&TextureInitTrackerAction {
1143                            texture: view.parent.clone(),
1144                            range: TextureInitRange::from(view.selector.clone()),
1145                            kind: MemoryInitKind::NeedsInitializedMemory,
1146                        }),
1147                    );
1148                }
1149
1150                // Diverging Store, i.e. Discard + Store:
1151                //
1152                // Immediately zero out channel that is set to discard after
1153                // we're done with the render pass. This allows us to set the
1154                // entire surface to MemoryInitKind::ImplicitlyInitialized (if
1155                // it isn't already set to NeedsInitializedMemory).
1156                //
1157                // (possible optimization: Delay and potentially drop this zeroing)
1158                if at.depth.store_op() != at.stencil.store_op() {
1159                    if !need_init_beforehand {
1160                        texture_memory_actions.register_implicit_init(
1161                            &view.parent,
1162                            TextureInitRange::from(view.selector.clone()),
1163                        );
1164                    }
1165                    divergent_discarded_depth_stencil_aspect = Some((
1166                        if at.depth.store_op() == StoreOp::Discard {
1167                            wgt::TextureAspect::DepthOnly
1168                        } else {
1169                            wgt::TextureAspect::StencilOnly
1170                        },
1171                        view.clone(),
1172                    ));
1173                } else if at.depth.store_op() == StoreOp::Discard {
1174                    // Both are discarded using the regular path.
1175                    discarded_surfaces.push(TextureSurfaceDiscard {
1176                        texture: view.parent.clone(),
1177                        mip_level: view.selector.mips.start,
1178                        layer: view.selector.layers.start,
1179                    });
1180                }
1181            }
1182
1183            is_depth_read_only = at.depth.is_readonly();
1184            is_stencil_read_only = at.stencil.is_readonly();
1185
1186            let usage = if is_depth_read_only
1187                && is_stencil_read_only
1188                && device
1189                    .downlevel
1190                    .flags
1191                    .contains(wgt::DownlevelFlags::READ_ONLY_DEPTH_STENCIL)
1192            {
1193                wgt::TextureUses::DEPTH_STENCIL_READ | wgt::TextureUses::RESOURCE
1194            } else {
1195                wgt::TextureUses::DEPTH_STENCIL_WRITE
1196            };
1197            render_attachments.push(view.to_render_attachment(usage));
1198
1199            depth_stencil = Some(hal::DepthStencilAttachment {
1200                target: hal::Attachment {
1201                    view: view.try_raw(snatch_guard)?,
1202                    usage,
1203                },
1204                depth_ops: at.depth.hal_ops(),
1205                stencil_ops: at.stencil.hal_ops(),
1206                clear_value: (at.depth.clear_value(), at.stencil.clear_value()),
1207            });
1208        }
1209
1210        let mut attachment_set = crate::FastHashSet::default();
1211
1212        let mut color_attachments_hal =
1213            ArrayVec::<Option<hal::ColorAttachment<_>>, { hal::MAX_COLOR_ATTACHMENTS }>::new();
1214        for (index, attachment) in color_attachments.iter().enumerate() {
1215            let at = if let Some(attachment) = attachment.as_ref() {
1216                attachment
1217            } else {
1218                color_attachments_hal.push(None);
1219                continue;
1220            };
1221            let color_view: &TextureView = &at.view;
1222            color_view.same_device(device)?;
1223            check_multiview(color_view)?;
1224            add_view(
1225                color_view,
1226                AttachmentErrorLocation::Color {
1227                    index,
1228                    resolve: false,
1229                },
1230            )?;
1231
1232            if !color_view.desc.aspects().intersects(
1233                hal::FormatAspects::COLOR
1234                    | hal::FormatAspects::PLANE_0
1235                    | hal::FormatAspects::PLANE_1
1236                    | hal::FormatAspects::PLANE_2,
1237            ) {
1238                return Err(RenderPassErrorInner::ColorAttachment(
1239                    ColorAttachmentError::InvalidFormat(color_view.desc.format),
1240                ));
1241            }
1242
1243            if color_view.desc.dimension == TextureViewDimension::D3 {
1244                if let Some(depth_slice) = at.depth_slice {
1245                    let mip = color_view.desc.range.base_mip_level;
1246                    let mip_size = color_view
1247                        .parent
1248                        .desc
1249                        .size
1250                        .mip_level_size(mip, color_view.parent.desc.dimension);
1251                    let limit = mip_size.depth_or_array_layers;
1252                    if depth_slice >= limit {
1253                        return Err(RenderPassErrorInner::ColorAttachment(
1254                            ColorAttachmentError::DepthSliceLimit {
1255                                given: depth_slice,
1256                                limit,
1257                            },
1258                        ));
1259                    }
1260                } else {
1261                    return Err(RenderPassErrorInner::ColorAttachment(
1262                        ColorAttachmentError::MissingDepthSlice,
1263                    ));
1264                }
1265            } else if at.depth_slice.is_some() {
1266                return Err(RenderPassErrorInner::ColorAttachment(
1267                    ColorAttachmentError::UnneededDepthSlice,
1268                ));
1269            }
1270
1271            validation::validate_color_attachment_bytes_per_sample(
1272                color_attachments
1273                    .iter()
1274                    .flatten()
1275                    .map(|at| at.view.desc.format),
1276                device.limits.max_color_attachment_bytes_per_sample,
1277            )
1278            .map_err(RenderPassErrorInner::ColorAttachment)?;
1279
1280            fn check_attachment_overlap(
1281                attachment_set: &mut crate::FastHashSet<(crate::track::TrackerIndex, u32, u32)>,
1282                view: &TextureView,
1283                depth_slice: Option<u32>,
1284            ) -> Result<(), ColorAttachmentError> {
1285                let mut insert = |slice| {
1286                    let mip_level = view.desc.range.base_mip_level;
1287                    if attachment_set.insert((
1288                        view.parent.tracking_data.tracker_index(),
1289                        mip_level,
1290                        slice,
1291                    )) {
1292                        Ok(())
1293                    } else {
1294                        Err(ColorAttachmentError::SubresourceOverlap {
1295                            view: view.error_ident(),
1296                            mip_level,
1297                            depth_or_array_layer: slice,
1298                        })
1299                    }
1300                };
1301                match view.desc.dimension {
1302                    TextureViewDimension::D2 => {
1303                        insert(view.desc.range.base_array_layer)?;
1304                    }
1305                    TextureViewDimension::D2Array => {
1306                        for layer in view.selector.layers.clone() {
1307                            insert(layer)?;
1308                        }
1309                    }
1310                    TextureViewDimension::D3 => {
1311                        insert(depth_slice.unwrap())?;
1312                    }
1313                    _ => unreachable!(),
1314                };
1315                Ok(())
1316            }
1317
1318            check_attachment_overlap(&mut attachment_set, color_view, at.depth_slice)?;
1319
1320            Self::add_pass_texture_init_actions(
1321                at.load_op,
1322                at.store_op,
1323                texture_memory_actions,
1324                color_view,
1325                pending_discard_init_fixups,
1326            );
1327            render_attachments
1328                .push(color_view.to_render_attachment(wgt::TextureUses::COLOR_TARGET));
1329
1330            let mut hal_resolve_target = None;
1331            if let Some(resolve_view) = &at.resolve_target {
1332                resolve_view.same_device(device)?;
1333                check_multiview(resolve_view)?;
1334
1335                check_attachment_overlap(&mut attachment_set, resolve_view, None)?;
1336
1337                let resolve_location = AttachmentErrorLocation::Color {
1338                    index,
1339                    resolve: true,
1340                };
1341
1342                let render_extent = resolve_view.render_extent.map_err(|reason| {
1343                    RenderPassErrorInner::TextureViewIsNotRenderable {
1344                        location: resolve_location,
1345                        reason,
1346                    }
1347                })?;
1348                if color_view.render_extent.unwrap() != render_extent {
1349                    return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
1350                        expected_location: attachment_location,
1351                        expected_extent: extent.unwrap_or_default(),
1352                        actual_location: resolve_location,
1353                        actual_extent: render_extent,
1354                    });
1355                }
1356                if color_view.samples == 1 || resolve_view.samples != 1 {
1357                    return Err(RenderPassErrorInner::InvalidResolveSampleCounts {
1358                        location: resolve_location,
1359                        src: color_view.samples,
1360                        dst: resolve_view.samples,
1361                    });
1362                }
1363                if color_view.desc.format != resolve_view.desc.format {
1364                    return Err(RenderPassErrorInner::MismatchedResolveTextureFormat {
1365                        location: resolve_location,
1366                        src: color_view.desc.format,
1367                        dst: resolve_view.desc.format,
1368                    });
1369                }
1370                if !resolve_view
1371                    .format_features
1372                    .flags
1373                    .contains(wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE)
1374                {
1375                    return Err(RenderPassErrorInner::UnsupportedResolveTargetFormat {
1376                        location: resolve_location,
1377                        format: resolve_view.desc.format,
1378                    });
1379                }
1380
1381                texture_memory_actions.register_implicit_init(
1382                    &resolve_view.parent,
1383                    TextureInitRange::from(resolve_view.selector.clone()),
1384                );
1385                render_attachments
1386                    .push(resolve_view.to_render_attachment(wgt::TextureUses::COLOR_TARGET));
1387
1388                hal_resolve_target = Some(hal::Attachment {
1389                    view: resolve_view.try_raw(snatch_guard)?,
1390                    usage: wgt::TextureUses::COLOR_TARGET,
1391                });
1392            }
1393
1394            color_attachments_hal.push(Some(hal::ColorAttachment {
1395                target: hal::Attachment {
1396                    view: color_view.try_raw(snatch_guard)?,
1397                    usage: wgt::TextureUses::COLOR_TARGET,
1398                },
1399                depth_slice: at.depth_slice,
1400                resolve_target: hal_resolve_target,
1401                ops: at.hal_ops(),
1402                clear_value: at.clear_value(),
1403            }));
1404        }
1405
1406        let extent = extent.ok_or(RenderPassErrorInner::MissingAttachments)?;
1407
1408        let detected_multiview =
1409            detected_multiview.expect("Multiview was not detected, no attachments");
1410        if let Some(mask) = multiview_mask {
1411            // 0x01 will have msb 0
1412            let mask_msb = 31 - mask.leading_zeros();
1413            let detected_mv = detected_multiview.map(NonZeroU32::get).unwrap_or(1);
1414            if mask_msb >= detected_mv {
1415                return Err(RenderPassErrorInner::MultiViewMismatch);
1416            }
1417            if mask.get() != (1 << detected_mv) - 1 {
1418                device.require_features(wgt::Features::SELECTIVE_MULTIVIEW)?;
1419            }
1420        }
1421
1422        let attachment_formats = AttachmentData {
1423            colors: color_attachments
1424                .iter()
1425                .map(|at| at.as_ref().map(|at| at.view.desc.format))
1426                .collect(),
1427            resolves: color_attachments
1428                .iter()
1429                .filter_map(|at| {
1430                    at.as_ref().and_then(|at| {
1431                        at.resolve_target
1432                            .as_ref()
1433                            .map(|resolve| resolve.desc.format)
1434                    })
1435                })
1436                .collect(),
1437            depth_stencil: depth_stencil_attachment
1438                .as_ref()
1439                .map(|at| at.view.desc.format),
1440        };
1441
1442        let context = RenderPassContext {
1443            attachments: attachment_formats,
1444            sample_count,
1445            multiview_mask,
1446        };
1447
1448        let timestamp_writes_hal = if let Some(tw) = timestamp_writes.as_ref() {
1449            let query_set = &tw.query_set;
1450            query_set.same_device(device)?;
1451
1452            if let Some(index) = tw.beginning_of_pass_write_index {
1453                pending_query_resets.use_query_set(query_set, index);
1454            }
1455            if let Some(index) = tw.end_of_pass_write_index {
1456                pending_query_resets.use_query_set(query_set, index);
1457            }
1458
1459            Some(hal::PassTimestampWrites {
1460                query_set: query_set.raw(),
1461                beginning_of_pass_write_index: tw.beginning_of_pass_write_index,
1462                end_of_pass_write_index: tw.end_of_pass_write_index,
1463            })
1464        } else {
1465            None
1466        };
1467
1468        let occlusion_query_set_hal = if let Some(query_set) = occlusion_query_set.as_ref() {
1469            query_set.same_device(device)?;
1470            Some(query_set.raw())
1471        } else {
1472            None
1473        };
1474
1475        let hal_desc = hal::RenderPassDescriptor {
1476            label: hal_label,
1477            extent,
1478            sample_count,
1479            color_attachments: &color_attachments_hal,
1480            depth_stencil_attachment: depth_stencil,
1481            multiview_mask,
1482            timestamp_writes: timestamp_writes_hal,
1483            occlusion_query_set: occlusion_query_set_hal,
1484        };
1485        unsafe {
1486            encoder
1487                .begin_render_pass(&hal_desc)
1488                .map_err(|e| device.handle_hal_error(e))?;
1489        };
1490        drop(color_attachments_hal); // Drop, so we can consume `color_attachments` for the tracker.
1491
1492        // Can't borrow the tracker more than once, so have to add to the tracker after the `begin_render_pass` hal call.
1493        if let Some(tw) = timestamp_writes.take() {
1494            trackers.query_sets.insert_single(tw.query_set);
1495        };
1496        if let Some(occlusion_query_set) = occlusion_query_set.take() {
1497            trackers.query_sets.insert_single(occlusion_query_set);
1498        };
1499        if let Some(at) = depth_stencil_attachment.take() {
1500            trackers.views.insert_single(at.view.clone());
1501        }
1502        for at in color_attachments.iter().flatten() {
1503            trackers.views.insert_single(at.view.clone());
1504            if let Some(resolve_target) = at.resolve_target.clone() {
1505                trackers.views.insert_single(resolve_target);
1506            }
1507        }
1508
1509        Ok(Self {
1510            context,
1511            render_attachments,
1512            is_depth_read_only,
1513            is_stencil_read_only,
1514            extent,
1515            divergent_discarded_depth_stencil_aspect,
1516            multiview_mask,
1517        })
1518    }
1519
1520    fn finish(
1521        self,
1522        device: &Device,
1523        raw: &mut dyn hal::DynCommandEncoder,
1524        snatch_guard: &SnatchGuard,
1525        scope: &mut UsageScope<'_>,
1526        instance_flags: wgt::InstanceFlags,
1527    ) -> Result<(), RenderPassErrorInner> {
1528        profiling::scope!("RenderPassInfo::finish");
1529        unsafe {
1530            raw.end_render_pass();
1531        }
1532
1533        for ra in self.render_attachments {
1534            let texture = &ra.texture;
1535            texture.check_usage(TextureUsages::RENDER_ATTACHMENT)?;
1536
1537            // the tracker set of the pass is always in "extend" mode
1538            unsafe {
1539                scope
1540                    .textures
1541                    .merge_single(texture, Some(ra.selector.clone()), ra.usage)?
1542            };
1543        }
1544
1545        // If either only stencil or depth was discarded, we put in a special
1546        // clear pass to keep the init status of the aspects in sync. We do this
1547        // so we don't need to track init state for depth/stencil aspects
1548        // individually.
1549        //
1550        // Note that we don't go the usual route of "brute force" initializing
1551        // the texture when need arises here, since this path is actually
1552        // something a user may genuinely want (where as the other cases are
1553        // more seen along the lines as gracefully handling a user error).
1554        if let Some((aspect, view)) = self.divergent_discarded_depth_stencil_aspect {
1555            let (depth_ops, stencil_ops) = if aspect == wgt::TextureAspect::DepthOnly {
1556                (
1557                    hal::AttachmentOps::LOAD_CLEAR | hal::AttachmentOps::STORE, // clear depth
1558                    hal::AttachmentOps::LOAD | hal::AttachmentOps::STORE,       // unchanged stencil
1559                )
1560            } else {
1561                (
1562                    hal::AttachmentOps::LOAD | hal::AttachmentOps::STORE, // unchanged stencil
1563                    hal::AttachmentOps::LOAD_CLEAR | hal::AttachmentOps::STORE, // clear depth
1564                )
1565            };
1566            let desc = hal::RenderPassDescriptor::<'_, _, dyn hal::DynTextureView> {
1567                label: hal_label(
1568                    Some("(wgpu internal) Zero init discarded depth/stencil aspect"),
1569                    instance_flags,
1570                ),
1571                extent: view.render_extent.unwrap(),
1572                sample_count: view.samples,
1573                color_attachments: &[],
1574                depth_stencil_attachment: Some(hal::DepthStencilAttachment {
1575                    target: hal::Attachment {
1576                        view: view.try_raw(snatch_guard)?,
1577                        usage: wgt::TextureUses::DEPTH_STENCIL_WRITE,
1578                    },
1579                    depth_ops,
1580                    stencil_ops,
1581                    clear_value: (0.0, 0),
1582                }),
1583                multiview_mask: self.multiview_mask,
1584                timestamp_writes: None,
1585                occlusion_query_set: None,
1586            };
1587            unsafe {
1588                raw.begin_render_pass(&desc)
1589                    .map_err(|e| device.handle_hal_error(e))?;
1590                raw.end_render_pass();
1591            }
1592        }
1593
1594        Ok(())
1595    }
1596}
1597
1598impl Global {
1599    /// Creates a render pass.
1600    ///
1601    /// If creation fails, an invalid pass is returned. Attempting to record
1602    /// commands into an invalid pass is permitted, but a validation error will
1603    /// ultimately be generated when the parent encoder is finished, and it is
1604    /// not possible to run any commands from the invalid pass.
1605    ///
1606    /// If successful, puts the encoder into the [`Locked`] state.
1607    ///
1608    /// [`Locked`]: crate::command::CommandEncoderStatus::Locked
1609    pub fn command_encoder_begin_render_pass(
1610        &self,
1611        encoder_id: id::CommandEncoderId,
1612        desc: &RenderPassDescriptor<'_>,
1613    ) -> (RenderPass, Option<CommandEncoderError>) {
1614        use EncoderStateError as SErr;
1615
1616        fn fill_arc_desc(
1617            hub: &crate::hub::Hub,
1618            desc: &RenderPassDescriptor<'_>,
1619            arc_desc: &mut ArcRenderPassDescriptor,
1620            device: &Device,
1621        ) -> Result<(), RenderPassErrorInner> {
1622            device.check_is_valid()?;
1623
1624            let query_sets = hub.query_sets.read();
1625            let texture_views = hub.texture_views.read();
1626
1627            let max_color_attachments = device.limits.max_color_attachments as usize;
1628            if desc.color_attachments.len() > max_color_attachments {
1629                return Err(RenderPassErrorInner::ColorAttachment(
1630                    ColorAttachmentError::TooMany {
1631                        given: desc.color_attachments.len(),
1632                        limit: max_color_attachments,
1633                    },
1634                ));
1635            }
1636
1637            for color_attachment in desc.color_attachments.iter() {
1638                if let Some(RenderPassColorAttachment {
1639                    view: view_id,
1640                    depth_slice,
1641                    resolve_target,
1642                    load_op,
1643                    store_op,
1644                }) = color_attachment
1645                {
1646                    let view = texture_views.get(*view_id).get()?;
1647                    view.same_device(device)?;
1648
1649                    if view.desc.usage.contains(TextureUsages::TRANSIENT)
1650                        && *store_op != StoreOp::Discard
1651                    {
1652                        return Err(RenderPassErrorInner::ColorAttachment(
1653                            ColorAttachmentError::InvalidUsageForStoreOp(
1654                                TextureUsages::TRANSIENT,
1655                                StoreOp::Discard,
1656                                *store_op,
1657                            ),
1658                        ));
1659                    }
1660
1661                    let resolve_target = if let Some(resolve_target_id) = resolve_target {
1662                        let rt_arc = texture_views.get(*resolve_target_id).get()?;
1663                        rt_arc.same_device(device)?;
1664
1665                        Some(rt_arc)
1666                    } else {
1667                        None
1668                    };
1669
1670                    arc_desc
1671                        .color_attachments
1672                        .push(Some(ArcRenderPassColorAttachment {
1673                            view,
1674                            depth_slice: *depth_slice,
1675                            resolve_target,
1676                            load_op: *load_op,
1677                            store_op: *store_op,
1678                        }));
1679                } else {
1680                    arc_desc.color_attachments.push(None);
1681                }
1682            }
1683
1684            arc_desc.depth_stencil_attachment =
1685            // https://gpuweb.github.io/gpuweb/#abstract-opdef-gpurenderpassdepthstencilattachment-gpurenderpassdepthstencilattachment-valid-usage
1686                if let Some(depth_stencil_attachment) = desc.depth_stencil_attachment {
1687                    let view = texture_views.get(depth_stencil_attachment.view).get()?;
1688                    view.same_device(device)?;
1689
1690                    let format = view.desc.format;
1691                    if !format.is_depth_stencil_format() {
1692                        return Err(RenderPassErrorInner::InvalidAttachment(AttachmentError::InvalidDepthStencilAttachmentFormat(
1693                            view.desc.format,
1694                        )));
1695                    }
1696
1697                    Some(ResolvedRenderPassDepthStencilAttachment {
1698                        view,
1699                        depth: if format.has_depth_aspect() {
1700                            depth_stencil_attachment.depth.resolve(|clear| if let Some(clear) = clear {
1701                                // If this.depthLoadOp is "clear", this.depthClearValue must be provided and must be between 0.0 and 1.0, inclusive.
1702                                if !(0.0..=1.0).contains(&clear) {
1703                                    Err(AttachmentError::ClearValueOutOfRange(clear))
1704                                } else {
1705                                    Ok(clear)
1706                                }
1707                            } else {
1708                                Err(AttachmentError::NoClearValue)
1709                            })?
1710                        } else {
1711                            ResolvedPassChannel::ReadOnly
1712                        },
1713                        stencil: if format.has_stencil_aspect() {
1714                            depth_stencil_attachment.stencil.resolve(|clear| Ok(clear.unwrap_or_default()))?
1715                        } else {
1716                            ResolvedPassChannel::ReadOnly
1717                        },
1718                    })
1719                } else {
1720                    None
1721                };
1722
1723            arc_desc.timestamp_writes = desc
1724                .timestamp_writes
1725                .map(|tw| {
1726                    Global::validate_pass_timestamp_writes::<RenderPassErrorInner>(
1727                        device,
1728                        &query_sets,
1729                        tw,
1730                    )
1731                })
1732                .transpose()?;
1733
1734            arc_desc.occlusion_query_set =
1735                if let Some(occlusion_query_set) = desc.occlusion_query_set {
1736                    let query_set = query_sets.get(occlusion_query_set).get()?;
1737                    query_set.same_device(device)?;
1738
1739                    if !matches!(query_set.desc.ty, wgt::QueryType::Occlusion) {
1740                        return Err(QueryUseError::IncompatibleType {
1741                            set_type: query_set.desc.ty.into(),
1742                            query_type: super::SimplifiedQueryType::Occlusion,
1743                        }
1744                        .into());
1745                    }
1746
1747                    Some(query_set)
1748                } else {
1749                    None
1750                };
1751
1752            arc_desc.multiview_mask = desc.multiview_mask;
1753
1754            Ok(())
1755        }
1756
1757        let scope = PassErrorScope::Pass;
1758        let hub = &self.hub;
1759
1760        let cmd_enc = hub.command_encoders.get(encoder_id);
1761        let mut cmd_buf_data = cmd_enc.data.lock();
1762
1763        match cmd_buf_data.lock_encoder() {
1764            Ok(()) => {
1765                drop(cmd_buf_data);
1766                let mut arc_desc = ArcRenderPassDescriptor {
1767                    label: &desc.label,
1768                    timestamp_writes: None,
1769                    color_attachments: ArrayVec::new(),
1770                    depth_stencil_attachment: None,
1771                    occlusion_query_set: None,
1772                    multiview_mask: None,
1773                };
1774                match fill_arc_desc(hub, desc, &mut arc_desc, &cmd_enc.device) {
1775                    Ok(()) => (RenderPass::new(cmd_enc, arc_desc), None),
1776                    Err(err) => (
1777                        RenderPass::new_invalid(cmd_enc, &desc.label, err.map_pass_err(scope)),
1778                        None,
1779                    ),
1780                }
1781            }
1782            Err(err @ SErr::Locked) => {
1783                // Attempting to open a new pass while the encoder is locked
1784                // invalidates the encoder, but does not generate a validation
1785                // error.
1786                cmd_buf_data.invalidate(err.clone());
1787                drop(cmd_buf_data);
1788                (
1789                    RenderPass::new_invalid(cmd_enc, &desc.label, err.map_pass_err(scope)),
1790                    None,
1791                )
1792            }
1793            Err(err @ (SErr::Ended | SErr::Submitted)) => {
1794                // Attempting to open a new pass after the encode has ended
1795                // generates an immediate validation error.
1796                drop(cmd_buf_data);
1797                (
1798                    RenderPass::new_invalid(cmd_enc, &desc.label, err.clone().map_pass_err(scope)),
1799                    Some(err.into()),
1800                )
1801            }
1802            Err(err @ SErr::Invalid) => {
1803                // Passes can be opened even on an invalid encoder. Such passes
1804                // are even valid, but since there's no visible side-effect of
1805                // the pass being valid and there's no point in storing recorded
1806                // commands that will ultimately be discarded, we open an
1807                // invalid pass to save that work.
1808                drop(cmd_buf_data);
1809                (
1810                    RenderPass::new_invalid(cmd_enc, &desc.label, err.map_pass_err(scope)),
1811                    None,
1812                )
1813            }
1814            Err(SErr::Unlocked) => {
1815                unreachable!("lock_encoder cannot fail due to the encoder being unlocked")
1816            }
1817        }
1818    }
1819
1820    pub fn render_pass_end(&self, pass: &mut RenderPass) -> Result<(), EncoderStateError> {
1821        profiling::scope!(
1822            "CommandEncoder::run_render_pass {}",
1823            pass.base.label.as_deref().unwrap_or("")
1824        );
1825
1826        let cmd_enc = pass.parent.take().ok_or(EncoderStateError::Ended)?;
1827        let mut cmd_buf_data = cmd_enc.data.lock();
1828
1829        cmd_buf_data.unlock_encoder()?;
1830
1831        let base = pass.base.take();
1832
1833        if let Err(RenderPassError {
1834            inner:
1835                RenderPassErrorInner::EncoderState(
1836                    err @ (EncoderStateError::Locked | EncoderStateError::Ended),
1837                ),
1838            scope: _,
1839        }) = base
1840        {
1841            // Most encoding errors are detected and raised within `finish()`.
1842            //
1843            // However, we raise a validation error here if the pass was opened
1844            // within another pass, or on a finished encoder. The latter is
1845            // particularly important, because in that case reporting errors via
1846            // `CommandEncoder::finish` is not possible.
1847            return Err(err.clone());
1848        }
1849
1850        cmd_buf_data.push_with(|| -> Result<_, RenderPassError> {
1851            Ok(ArcCommand::RunRenderPass {
1852                pass: base?,
1853                color_attachments: SmallVec::from(pass.color_attachments.as_slice()),
1854                depth_stencil_attachment: pass.depth_stencil_attachment.take(),
1855                timestamp_writes: pass.timestamp_writes.take(),
1856                occlusion_query_set: pass.occlusion_query_set.take(),
1857                multiview_mask: pass.multiview_mask,
1858            })
1859        })
1860    }
1861}
1862
1863pub(super) fn encode_render_pass(
1864    parent_state: &mut EncodingState<InnerCommandEncoder>,
1865    mut base: BasePass<ArcRenderCommand, Infallible>,
1866    color_attachments: ColorAttachments<Arc<TextureView>>,
1867    mut depth_stencil_attachment: Option<
1868        ResolvedRenderPassDepthStencilAttachment<Arc<TextureView>>,
1869    >,
1870    mut timestamp_writes: Option<ArcPassTimestampWrites>,
1871    occlusion_query_set: Option<Arc<QuerySet>>,
1872    multiview_mask: Option<NonZeroU32>,
1873) -> Result<(), RenderPassError> {
1874    let pass_scope = PassErrorScope::Pass;
1875
1876    let device = parent_state.device;
1877
1878    let mut indirect_draw_validation_batcher = crate::indirect_validation::DrawBatcher::new();
1879
1880    // We automatically keep extending command buffers over time, and because
1881    // we want to insert a command buffer _before_ what we're about to record,
1882    // we need to make sure to close the previous one.
1883    parent_state
1884        .raw_encoder
1885        .close_if_open()
1886        .map_pass_err(pass_scope)?;
1887    let raw_encoder = parent_state
1888        .raw_encoder
1889        .open_pass(base.label.as_deref())
1890        .map_pass_err(pass_scope)?;
1891
1892    let (scope, pending_discard_init_fixups, mut pending_query_resets) = {
1893        let mut pending_query_resets = QueryResetMap::new();
1894        let mut pending_discard_init_fixups = SurfacesInDiscardState::new();
1895
1896        let info = RenderPassInfo::start(
1897            device,
1898            hal_label(base.label.as_deref(), device.instance_flags),
1899            &color_attachments,
1900            depth_stencil_attachment.take(),
1901            timestamp_writes.take(),
1902            // Still needed down the line.
1903            // TODO(wumpf): by restructuring the code, we could get rid of some of this Arc clone.
1904            occlusion_query_set.clone(),
1905            raw_encoder,
1906            parent_state.tracker,
1907            parent_state.texture_memory_actions,
1908            &mut pending_query_resets,
1909            &mut pending_discard_init_fixups,
1910            parent_state.snatch_guard,
1911            multiview_mask,
1912        )
1913        .map_pass_err(pass_scope)?;
1914
1915        let indices = &device.tracker_indices;
1916        parent_state
1917            .tracker
1918            .buffers
1919            .set_size(indices.buffers.size());
1920        parent_state
1921            .tracker
1922            .textures
1923            .set_size(indices.textures.size());
1924
1925        let mut debug_scope_depth = 0;
1926
1927        let mut state = State {
1928            pipeline_flags: PipelineFlags::empty(),
1929            blend_constant: OptionalState::Unused,
1930            stencil_reference: 0,
1931            pipeline: None,
1932            index: IndexState::default(),
1933            vertex: VertexState::default(),
1934
1935            info,
1936
1937            pass: pass::PassState {
1938                base: EncodingState {
1939                    device,
1940                    raw_encoder,
1941                    tracker: parent_state.tracker,
1942                    buffer_memory_init_actions: parent_state.buffer_memory_init_actions,
1943                    texture_memory_actions: parent_state.texture_memory_actions,
1944                    as_actions: parent_state.as_actions,
1945                    temp_resources: parent_state.temp_resources,
1946                    indirect_draw_validation_resources: parent_state
1947                        .indirect_draw_validation_resources,
1948                    snatch_guard: parent_state.snatch_guard,
1949                    debug_scope_depth: &mut debug_scope_depth,
1950                },
1951                pending_discard_init_fixups,
1952                scope: device.new_usage_scope(),
1953                binder: Binder::new(),
1954
1955                temp_offsets: Vec::new(),
1956                dynamic_offset_count: 0,
1957
1958                string_offset: 0,
1959            },
1960
1961            active_occlusion_query: None,
1962            active_pipeline_statistics_query: None,
1963        };
1964
1965        for command in base.commands.drain(..) {
1966            match command {
1967                ArcRenderCommand::SetBindGroup {
1968                    index,
1969                    num_dynamic_offsets,
1970                    bind_group,
1971                } => {
1972                    let scope = PassErrorScope::SetBindGroup;
1973                    pass::set_bind_group::<RenderPassErrorInner>(
1974                        &mut state.pass,
1975                        device,
1976                        &base.dynamic_offsets,
1977                        index,
1978                        num_dynamic_offsets,
1979                        bind_group,
1980                        true,
1981                    )
1982                    .map_pass_err(scope)?;
1983                }
1984                ArcRenderCommand::SetPipeline(pipeline) => {
1985                    let scope = PassErrorScope::SetPipelineRender;
1986                    set_pipeline(&mut state, device, pipeline).map_pass_err(scope)?;
1987                }
1988                ArcRenderCommand::SetIndexBuffer {
1989                    buffer,
1990                    index_format,
1991                    offset,
1992                    size,
1993                } => {
1994                    let scope = PassErrorScope::SetIndexBuffer;
1995                    set_index_buffer(&mut state, device, buffer, index_format, offset, size)
1996                        .map_pass_err(scope)?;
1997                }
1998                ArcRenderCommand::SetVertexBuffer {
1999                    slot,
2000                    buffer,
2001                    offset,
2002                    size,
2003                } => {
2004                    let scope = PassErrorScope::SetVertexBuffer;
2005                    set_vertex_buffer(&mut state, device, slot, buffer, offset, size)
2006                        .map_pass_err(scope)?;
2007                }
2008                ArcRenderCommand::SetBlendConstant(ref color) => {
2009                    set_blend_constant(&mut state, color);
2010                }
2011                ArcRenderCommand::SetStencilReference(value) => {
2012                    set_stencil_reference(&mut state, value);
2013                }
2014                ArcRenderCommand::SetViewport {
2015                    rect,
2016                    depth_min,
2017                    depth_max,
2018                } => {
2019                    let scope = PassErrorScope::SetViewport;
2020                    set_viewport(&mut state, rect, depth_min, depth_max).map_pass_err(scope)?;
2021                }
2022                ArcRenderCommand::SetImmediate {
2023                    offset,
2024                    size_bytes,
2025                    values_offset,
2026                } => {
2027                    let scope = PassErrorScope::SetImmediate;
2028                    pass::set_immediates::<RenderPassErrorInner, _>(
2029                        &mut state.pass,
2030                        &base.immediates_data,
2031                        offset,
2032                        size_bytes,
2033                        values_offset,
2034                        |_| {},
2035                    )
2036                    .map_pass_err(scope)?;
2037                }
2038                ArcRenderCommand::SetScissor(rect) => {
2039                    let scope = PassErrorScope::SetScissorRect;
2040                    set_scissor(&mut state, rect).map_pass_err(scope)?;
2041                }
2042                ArcRenderCommand::Draw {
2043                    vertex_count,
2044                    instance_count,
2045                    first_vertex,
2046                    first_instance,
2047                } => {
2048                    let scope = PassErrorScope::Draw {
2049                        kind: DrawKind::Draw,
2050                        family: DrawCommandFamily::Draw,
2051                    };
2052                    draw(
2053                        &mut state,
2054                        vertex_count,
2055                        instance_count,
2056                        first_vertex,
2057                        first_instance,
2058                    )
2059                    .map_pass_err(scope)?;
2060                }
2061                ArcRenderCommand::DrawIndexed {
2062                    index_count,
2063                    instance_count,
2064                    first_index,
2065                    base_vertex,
2066                    first_instance,
2067                } => {
2068                    let scope = PassErrorScope::Draw {
2069                        kind: DrawKind::Draw,
2070                        family: DrawCommandFamily::DrawIndexed,
2071                    };
2072                    draw_indexed(
2073                        &mut state,
2074                        index_count,
2075                        instance_count,
2076                        first_index,
2077                        base_vertex,
2078                        first_instance,
2079                    )
2080                    .map_pass_err(scope)?;
2081                }
2082                ArcRenderCommand::DrawMeshTasks {
2083                    group_count_x,
2084                    group_count_y,
2085                    group_count_z,
2086                } => {
2087                    let scope = PassErrorScope::Draw {
2088                        kind: DrawKind::Draw,
2089                        family: DrawCommandFamily::DrawMeshTasks,
2090                    };
2091                    draw_mesh_tasks(&mut state, group_count_x, group_count_y, group_count_z)
2092                        .map_pass_err(scope)?;
2093                }
2094                ArcRenderCommand::DrawIndirect {
2095                    buffer,
2096                    offset,
2097                    count,
2098                    family,
2099
2100                    vertex_or_index_limit: _,
2101                    instance_limit: _,
2102                } => {
2103                    let scope = PassErrorScope::Draw {
2104                        kind: if count != 1 {
2105                            DrawKind::MultiDrawIndirect
2106                        } else {
2107                            DrawKind::DrawIndirect
2108                        },
2109                        family,
2110                    };
2111                    multi_draw_indirect(
2112                        &mut state,
2113                        &mut indirect_draw_validation_batcher,
2114                        device,
2115                        buffer,
2116                        offset,
2117                        count,
2118                        family,
2119                    )
2120                    .map_pass_err(scope)?;
2121                }
2122                ArcRenderCommand::MultiDrawIndirectCount {
2123                    buffer,
2124                    offset,
2125                    count_buffer,
2126                    count_buffer_offset,
2127                    max_count,
2128                    family,
2129                } => {
2130                    let scope = PassErrorScope::Draw {
2131                        kind: DrawKind::MultiDrawIndirectCount,
2132                        family,
2133                    };
2134                    multi_draw_indirect_count(
2135                        &mut state,
2136                        device,
2137                        buffer,
2138                        offset,
2139                        count_buffer,
2140                        count_buffer_offset,
2141                        max_count,
2142                        family,
2143                    )
2144                    .map_pass_err(scope)?;
2145                }
2146                ArcRenderCommand::PushDebugGroup { color: _, len } => {
2147                    pass::push_debug_group(&mut state.pass, &base.string_data, len);
2148                }
2149                ArcRenderCommand::PopDebugGroup => {
2150                    let scope = PassErrorScope::PopDebugGroup;
2151                    pass::pop_debug_group::<RenderPassErrorInner>(&mut state.pass)
2152                        .map_pass_err(scope)?;
2153                }
2154                ArcRenderCommand::InsertDebugMarker { color: _, len } => {
2155                    pass::insert_debug_marker(&mut state.pass, &base.string_data, len);
2156                }
2157                ArcRenderCommand::WriteTimestamp {
2158                    query_set,
2159                    query_index,
2160                } => {
2161                    let scope = PassErrorScope::WriteTimestamp;
2162                    pass::write_timestamp::<RenderPassErrorInner>(
2163                        &mut state.pass,
2164                        device,
2165                        Some(&mut pending_query_resets),
2166                        query_set,
2167                        query_index,
2168                    )
2169                    .map_pass_err(scope)?;
2170                }
2171                ArcRenderCommand::BeginOcclusionQuery { query_index } => {
2172                    api_log!("RenderPass::begin_occlusion_query {query_index}");
2173                    let scope = PassErrorScope::BeginOcclusionQuery;
2174
2175                    let query_set = occlusion_query_set
2176                        .clone()
2177                        .ok_or(RenderPassErrorInner::MissingOcclusionQuerySet)
2178                        .map_pass_err(scope)?;
2179
2180                    validate_and_begin_occlusion_query(
2181                        query_set,
2182                        state.pass.base.raw_encoder,
2183                        &mut state.pass.base.tracker.query_sets,
2184                        query_index,
2185                        Some(&mut pending_query_resets),
2186                        &mut state.active_occlusion_query,
2187                    )
2188                    .map_pass_err(scope)?;
2189                }
2190                ArcRenderCommand::EndOcclusionQuery => {
2191                    api_log!("RenderPass::end_occlusion_query");
2192                    let scope = PassErrorScope::EndOcclusionQuery;
2193
2194                    end_occlusion_query(
2195                        state.pass.base.raw_encoder,
2196                        &mut state.active_occlusion_query,
2197                    )
2198                    .map_pass_err(scope)?;
2199                }
2200                ArcRenderCommand::BeginPipelineStatisticsQuery {
2201                    query_set,
2202                    query_index,
2203                } => {
2204                    api_log!(
2205                        "RenderPass::begin_pipeline_statistics_query {query_index} {}",
2206                        query_set.error_ident()
2207                    );
2208                    let scope = PassErrorScope::BeginPipelineStatisticsQuery;
2209
2210                    validate_and_begin_pipeline_statistics_query(
2211                        query_set,
2212                        state.pass.base.raw_encoder,
2213                        &mut state.pass.base.tracker.query_sets,
2214                        device,
2215                        query_index,
2216                        Some(&mut pending_query_resets),
2217                        &mut state.active_pipeline_statistics_query,
2218                    )
2219                    .map_pass_err(scope)?;
2220                }
2221                ArcRenderCommand::EndPipelineStatisticsQuery => {
2222                    api_log!("RenderPass::end_pipeline_statistics_query");
2223                    let scope = PassErrorScope::EndPipelineStatisticsQuery;
2224
2225                    end_pipeline_statistics_query(
2226                        state.pass.base.raw_encoder,
2227                        &mut state.active_pipeline_statistics_query,
2228                    )
2229                    .map_pass_err(scope)?;
2230                }
2231                ArcRenderCommand::ExecuteBundle(bundle) => {
2232                    let scope = PassErrorScope::ExecuteBundle;
2233                    execute_bundle(
2234                        &mut state,
2235                        &mut indirect_draw_validation_batcher,
2236                        device,
2237                        bundle,
2238                    )
2239                    .map_pass_err(scope)?;
2240                }
2241            }
2242        }
2243
2244        if *state.pass.base.debug_scope_depth > 0 {
2245            Err(
2246                RenderPassErrorInner::DebugGroupError(DebugGroupError::MissingPop)
2247                    .map_pass_err(pass_scope),
2248            )?;
2249        }
2250        if state.active_occlusion_query.is_some() {
2251            Err(RenderPassErrorInner::QueryUse(QueryUseError::MissingEnd {
2252                query_type: super::SimplifiedQueryType::Occlusion,
2253            })
2254            .map_pass_err(pass_scope))?;
2255        }
2256        if state.active_pipeline_statistics_query.is_some() {
2257            Err(RenderPassErrorInner::QueryUse(QueryUseError::MissingEnd {
2258                query_type: super::SimplifiedQueryType::PipelineStatistics,
2259            })
2260            .map_pass_err(pass_scope))?;
2261        }
2262
2263        state
2264            .info
2265            .finish(
2266                device,
2267                state.pass.base.raw_encoder,
2268                state.pass.base.snatch_guard,
2269                &mut state.pass.scope,
2270                device.instance_flags,
2271            )
2272            .map_pass_err(pass_scope)?;
2273
2274        let trackers = state.pass.scope;
2275
2276        let pending_discard_init_fixups = state.pass.pending_discard_init_fixups;
2277
2278        parent_state.raw_encoder.close().map_pass_err(pass_scope)?;
2279        (trackers, pending_discard_init_fixups, pending_query_resets)
2280    };
2281
2282    let encoder = &mut parent_state.raw_encoder;
2283    let tracker = &mut parent_state.tracker;
2284
2285    {
2286        let transit = encoder
2287            .open_pass(hal_label(
2288                Some("(wgpu internal) Pre Pass"),
2289                device.instance_flags,
2290            ))
2291            .map_pass_err(pass_scope)?;
2292
2293        fixup_discarded_surfaces(
2294            pending_discard_init_fixups.into_iter(),
2295            transit,
2296            &mut tracker.textures,
2297            device,
2298            parent_state.snatch_guard,
2299        );
2300
2301        pending_query_resets.reset_queries(transit);
2302
2303        CommandEncoder::insert_barriers_from_scope(
2304            transit,
2305            tracker,
2306            &scope,
2307            parent_state.snatch_guard,
2308        );
2309
2310        if let Some(ref indirect_validation) = device.indirect_validation {
2311            indirect_validation
2312                .draw
2313                .inject_validation_pass(
2314                    device,
2315                    parent_state.snatch_guard,
2316                    parent_state.indirect_draw_validation_resources,
2317                    parent_state.temp_resources,
2318                    transit,
2319                    indirect_draw_validation_batcher,
2320                )
2321                .map_pass_err(pass_scope)?;
2322        }
2323    }
2324
2325    encoder.close_and_swap().map_pass_err(pass_scope)?;
2326
2327    Ok(())
2328}
2329
2330fn set_pipeline(
2331    state: &mut State,
2332    device: &Arc<Device>,
2333    pipeline: Arc<RenderPipeline>,
2334) -> Result<(), RenderPassErrorInner> {
2335    api_log!("RenderPass::set_pipeline {}", pipeline.error_ident());
2336
2337    state.pipeline = Some(pipeline.clone());
2338
2339    let pipeline = state
2340        .pass
2341        .base
2342        .tracker
2343        .render_pipelines
2344        .insert_single(pipeline)
2345        .clone();
2346
2347    pipeline.same_device(device)?;
2348
2349    state
2350        .info
2351        .context
2352        .check_compatible(&pipeline.pass_context, pipeline.as_ref())
2353        .map_err(RenderCommandError::IncompatiblePipelineTargets)?;
2354
2355    state.pipeline_flags = pipeline.flags;
2356
2357    if pipeline.flags.contains(PipelineFlags::WRITES_DEPTH) && state.info.is_depth_read_only {
2358        return Err(RenderCommandError::IncompatibleDepthAccess(pipeline.error_ident()).into());
2359    }
2360    if pipeline.flags.contains(PipelineFlags::WRITES_STENCIL) && state.info.is_stencil_read_only {
2361        return Err(RenderCommandError::IncompatibleStencilAccess(pipeline.error_ident()).into());
2362    }
2363
2364    state
2365        .blend_constant
2366        .require(pipeline.flags.contains(PipelineFlags::BLEND_CONSTANT));
2367
2368    unsafe {
2369        state
2370            .pass
2371            .base
2372            .raw_encoder
2373            .set_render_pipeline(pipeline.raw());
2374    }
2375
2376    if pipeline.flags.contains(PipelineFlags::STENCIL_REFERENCE) {
2377        unsafe {
2378            state
2379                .pass
2380                .base
2381                .raw_encoder
2382                .set_stencil_reference(state.stencil_reference);
2383        }
2384    }
2385
2386    // Rebind resource
2387    pass::change_pipeline_layout::<RenderPassErrorInner, _>(
2388        &mut state.pass,
2389        &pipeline.layout,
2390        &pipeline.late_sized_buffer_groups,
2391        || {},
2392    )?;
2393
2394    // Update vertex buffer limits.
2395    state.vertex.update_limits(&pipeline.vertex_steps);
2396    Ok(())
2397}
2398
2399// This function is duplicative of `bundle::set_index_buffer`.
2400fn set_index_buffer(
2401    state: &mut State,
2402    device: &Arc<Device>,
2403    buffer: Arc<crate::resource::Buffer>,
2404    index_format: IndexFormat,
2405    offset: u64,
2406    size: Option<BufferSize>,
2407) -> Result<(), RenderPassErrorInner> {
2408    api_log!("RenderPass::set_index_buffer {}", buffer.error_ident());
2409
2410    state
2411        .pass
2412        .scope
2413        .buffers
2414        .merge_single(&buffer, wgt::BufferUses::INDEX)?;
2415
2416    buffer.same_device(device)?;
2417
2418    buffer.check_usage(BufferUsages::INDEX)?;
2419
2420    if !offset.is_multiple_of(u64::try_from(index_format.byte_size()).unwrap()) {
2421        return Err(RenderCommandError::UnalignedIndexBuffer {
2422            offset,
2423            alignment: index_format.byte_size(),
2424        }
2425        .into());
2426    }
2427    let (binding, resolved_size) = buffer
2428        .binding(offset, size, state.pass.base.snatch_guard)
2429        .map_err(RenderCommandError::from)?;
2430    let end = offset + resolved_size;
2431    state.index.update_buffer(offset..end, index_format);
2432
2433    state.pass.base.buffer_memory_init_actions.extend(
2434        buffer.initialization_status.read().create_action(
2435            &buffer,
2436            offset..end,
2437            MemoryInitKind::NeedsInitializedMemory,
2438        ),
2439    );
2440
2441    unsafe {
2442        hal::DynCommandEncoder::set_index_buffer(
2443            state.pass.base.raw_encoder,
2444            binding,
2445            index_format,
2446        );
2447    }
2448    Ok(())
2449}
2450
2451// This function is duplicative of `render::set_vertex_buffer`.
2452fn set_vertex_buffer(
2453    state: &mut State,
2454    device: &Arc<Device>,
2455    slot: u32,
2456    buffer: Arc<crate::resource::Buffer>,
2457    offset: u64,
2458    size: Option<BufferSize>,
2459) -> Result<(), RenderPassErrorInner> {
2460    api_log!(
2461        "RenderPass::set_vertex_buffer {slot} {}",
2462        buffer.error_ident()
2463    );
2464
2465    state
2466        .pass
2467        .scope
2468        .buffers
2469        .merge_single(&buffer, wgt::BufferUses::VERTEX)?;
2470
2471    buffer.same_device(device)?;
2472
2473    let max_vertex_buffers = state.pass.base.device.limits.max_vertex_buffers;
2474    if slot >= max_vertex_buffers {
2475        return Err(RenderCommandError::VertexBufferIndexOutOfRange {
2476            index: slot,
2477            max: max_vertex_buffers,
2478        }
2479        .into());
2480    }
2481
2482    buffer.check_usage(BufferUsages::VERTEX)?;
2483
2484    if !offset.is_multiple_of(wgt::VERTEX_ALIGNMENT) {
2485        return Err(RenderCommandError::UnalignedVertexBuffer { slot, offset }.into());
2486    }
2487    let (binding, buffer_size) = buffer
2488        .binding(offset, size, state.pass.base.snatch_guard)
2489        .map_err(RenderCommandError::from)?;
2490    state.vertex.buffer_sizes[slot as usize] = Some(buffer_size);
2491
2492    state.pass.base.buffer_memory_init_actions.extend(
2493        buffer.initialization_status.read().create_action(
2494            &buffer,
2495            offset..(offset + buffer_size),
2496            MemoryInitKind::NeedsInitializedMemory,
2497        ),
2498    );
2499
2500    unsafe {
2501        hal::DynCommandEncoder::set_vertex_buffer(state.pass.base.raw_encoder, slot, binding);
2502    }
2503    if let Some(pipeline) = state.pipeline.as_ref() {
2504        state.vertex.update_limits(&pipeline.vertex_steps);
2505    }
2506    Ok(())
2507}
2508
2509fn set_blend_constant(state: &mut State, color: &Color) {
2510    api_log!("RenderPass::set_blend_constant");
2511
2512    state.blend_constant = OptionalState::Set;
2513    let array = [
2514        color.r as f32,
2515        color.g as f32,
2516        color.b as f32,
2517        color.a as f32,
2518    ];
2519    unsafe {
2520        state.pass.base.raw_encoder.set_blend_constants(&array);
2521    }
2522}
2523
2524fn set_stencil_reference(state: &mut State, value: u32) {
2525    api_log!("RenderPass::set_stencil_reference {value}");
2526
2527    state.stencil_reference = value;
2528    if state
2529        .pipeline_flags
2530        .contains(PipelineFlags::STENCIL_REFERENCE)
2531    {
2532        unsafe {
2533            state.pass.base.raw_encoder.set_stencil_reference(value);
2534        }
2535    }
2536}
2537
2538fn set_viewport(
2539    state: &mut State,
2540    rect: Rect<f32>,
2541    depth_min: f32,
2542    depth_max: f32,
2543) -> Result<(), RenderPassErrorInner> {
2544    api_log!("RenderPass::set_viewport {rect:?}");
2545
2546    if rect.w < 0.0
2547        || rect.h < 0.0
2548        || rect.w > state.pass.base.device.limits.max_texture_dimension_2d as f32
2549        || rect.h > state.pass.base.device.limits.max_texture_dimension_2d as f32
2550    {
2551        return Err(RenderCommandError::InvalidViewportRectSize {
2552            w: rect.w,
2553            h: rect.h,
2554            max: state.pass.base.device.limits.max_texture_dimension_2d,
2555        }
2556        .into());
2557    }
2558
2559    let max_viewport_range = state.pass.base.device.limits.max_texture_dimension_2d as f32 * 2.0;
2560
2561    if rect.x < -max_viewport_range
2562        || rect.y < -max_viewport_range
2563        || rect.x + rect.w > max_viewport_range - 1.0
2564        || rect.y + rect.h > max_viewport_range - 1.0
2565    {
2566        return Err(RenderCommandError::InvalidViewportRectPosition {
2567            rect,
2568            min: -max_viewport_range,
2569            max: max_viewport_range - 1.0,
2570        }
2571        .into());
2572    }
2573    if !(0.0..=1.0).contains(&depth_min)
2574        || !(0.0..=1.0).contains(&depth_max)
2575        || depth_min > depth_max
2576    {
2577        return Err(RenderCommandError::InvalidViewportDepth(depth_min, depth_max).into());
2578    }
2579    let r = hal::Rect {
2580        x: rect.x,
2581        y: rect.y,
2582        w: rect.w,
2583        h: rect.h,
2584    };
2585    unsafe {
2586        state
2587            .pass
2588            .base
2589            .raw_encoder
2590            .set_viewport(&r, depth_min..depth_max);
2591    }
2592    Ok(())
2593}
2594
2595fn set_scissor(state: &mut State, rect: Rect<u32>) -> Result<(), RenderPassErrorInner> {
2596    api_log!("RenderPass::set_scissor_rect {rect:?}");
2597
2598    if rect.x.saturating_add(rect.w) > state.info.extent.width
2599        || rect.y.saturating_add(rect.h) > state.info.extent.height
2600    {
2601        return Err(RenderCommandError::InvalidScissorRect(rect, state.info.extent).into());
2602    }
2603    let r = hal::Rect {
2604        x: rect.x,
2605        y: rect.y,
2606        w: rect.w,
2607        h: rect.h,
2608    };
2609    unsafe {
2610        state.pass.base.raw_encoder.set_scissor_rect(&r);
2611    }
2612    Ok(())
2613}
2614
2615fn validate_mesh_draw_multiview(state: &State) -> Result<(), RenderPassErrorInner> {
2616    if let Some(mv) = state.info.multiview_mask {
2617        let highest_bit = 31 - mv.leading_zeros();
2618
2619        let features = state.pass.base.device.features;
2620
2621        if !features.contains(wgt::Features::EXPERIMENTAL_MESH_SHADER_MULTIVIEW)
2622            || highest_bit > state.pass.base.device.limits.max_mesh_multiview_view_count
2623        {
2624            return Err(RenderPassErrorInner::Draw(
2625                DrawError::MeshPipelineMultiviewLimitsViolated {
2626                    highest_view_index: highest_bit,
2627                    max_multiviews: state.pass.base.device.limits.max_mesh_multiview_view_count,
2628                },
2629            ));
2630        }
2631    }
2632    Ok(())
2633}
2634
2635fn draw(
2636    state: &mut State,
2637    vertex_count: u32,
2638    instance_count: u32,
2639    first_vertex: u32,
2640    first_instance: u32,
2641) -> Result<(), RenderPassErrorInner> {
2642    api_log!("RenderPass::draw {vertex_count} {instance_count} {first_vertex} {first_instance}");
2643
2644    state.is_ready(DrawCommandFamily::Draw)?;
2645    state.flush_bindings()?;
2646
2647    state
2648        .vertex
2649        .limits
2650        .validate_vertex_limit(first_vertex, vertex_count)?;
2651    state
2652        .vertex
2653        .limits
2654        .validate_instance_limit(first_instance, instance_count)?;
2655
2656    unsafe {
2657        if instance_count > 0 && vertex_count > 0 {
2658            state.pass.base.raw_encoder.draw(
2659                first_vertex,
2660                vertex_count,
2661                first_instance,
2662                instance_count,
2663            );
2664        }
2665    }
2666    Ok(())
2667}
2668
2669fn draw_indexed(
2670    state: &mut State,
2671    index_count: u32,
2672    instance_count: u32,
2673    first_index: u32,
2674    base_vertex: i32,
2675    first_instance: u32,
2676) -> Result<(), RenderPassErrorInner> {
2677    api_log!("RenderPass::draw_indexed {index_count} {instance_count} {first_index} {base_vertex} {first_instance}");
2678
2679    state.is_ready(DrawCommandFamily::DrawIndexed)?;
2680    state.flush_bindings()?;
2681
2682    let last_index = first_index as u64 + index_count as u64;
2683    let index_limit = state.index.limit;
2684    if last_index > index_limit {
2685        return Err(DrawError::IndexBeyondLimit {
2686            last_index,
2687            index_limit,
2688        }
2689        .into());
2690    }
2691    state
2692        .vertex
2693        .limits
2694        .validate_instance_limit(first_instance, instance_count)?;
2695
2696    unsafe {
2697        if instance_count > 0 && index_count > 0 {
2698            state.pass.base.raw_encoder.draw_indexed(
2699                first_index,
2700                index_count,
2701                base_vertex,
2702                first_instance,
2703                instance_count,
2704            );
2705        }
2706    }
2707    Ok(())
2708}
2709
2710fn draw_mesh_tasks(
2711    state: &mut State,
2712    group_count_x: u32,
2713    group_count_y: u32,
2714    group_count_z: u32,
2715) -> Result<(), RenderPassErrorInner> {
2716    api_log!("RenderPass::draw_mesh_tasks {group_count_x} {group_count_y} {group_count_z}");
2717
2718    state.is_ready(DrawCommandFamily::DrawMeshTasks)?;
2719
2720    state.flush_bindings()?;
2721    validate_mesh_draw_multiview(state)?;
2722
2723    let groups_size_limit = state
2724        .pass
2725        .base
2726        .device
2727        .limits
2728        .max_task_mesh_workgroups_per_dimension;
2729    let max_groups = state
2730        .pass
2731        .base
2732        .device
2733        .limits
2734        .max_task_mesh_workgroup_total_count;
2735    if group_count_x > groups_size_limit
2736        || group_count_y > groups_size_limit
2737        || group_count_z > groups_size_limit
2738        || group_count_x * group_count_y * group_count_z > max_groups
2739    {
2740        return Err(DrawError::InvalidGroupSize {
2741            current: [group_count_x, group_count_y, group_count_z],
2742            limit: groups_size_limit,
2743            max_total: max_groups,
2744        }
2745        .into());
2746    }
2747
2748    unsafe {
2749        if group_count_x > 0 && group_count_y > 0 && group_count_z > 0 {
2750            state.pass.base.raw_encoder.draw_mesh_tasks(
2751                group_count_x,
2752                group_count_y,
2753                group_count_z,
2754            );
2755        }
2756    }
2757    Ok(())
2758}
2759
2760fn multi_draw_indirect(
2761    state: &mut State,
2762    indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
2763    device: &Arc<Device>,
2764    indirect_buffer: Arc<crate::resource::Buffer>,
2765    offset: u64,
2766    count: u32,
2767    family: DrawCommandFamily,
2768) -> Result<(), RenderPassErrorInner> {
2769    api_log!(
2770        "RenderPass::draw_indirect (family:{family:?}) {} {offset} {count:?}",
2771        indirect_buffer.error_ident()
2772    );
2773
2774    state.is_ready(family)?;
2775    state.flush_bindings()?;
2776
2777    if family == DrawCommandFamily::DrawMeshTasks {
2778        validate_mesh_draw_multiview(state)?;
2779    }
2780
2781    state
2782        .pass
2783        .base
2784        .device
2785        .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
2786
2787    indirect_buffer.same_device(device)?;
2788    indirect_buffer.check_usage(BufferUsages::INDIRECT)?;
2789    indirect_buffer.check_destroyed(state.pass.base.snatch_guard)?;
2790
2791    if !offset.is_multiple_of(4) {
2792        return Err(RenderPassErrorInner::UnalignedIndirectBufferOffset(offset));
2793    }
2794
2795    let stride = get_stride_of_indirect_args(family);
2796
2797    let end_offset = offset + stride * count as u64;
2798    if end_offset > indirect_buffer.size {
2799        return Err(RenderPassErrorInner::IndirectBufferOverrun {
2800            count,
2801            offset,
2802            end_offset,
2803            buffer_size: indirect_buffer.size,
2804        });
2805    }
2806
2807    state.pass.base.buffer_memory_init_actions.extend(
2808        indirect_buffer.initialization_status.read().create_action(
2809            &indirect_buffer,
2810            offset..end_offset,
2811            MemoryInitKind::NeedsInitializedMemory,
2812        ),
2813    );
2814
2815    fn draw(
2816        raw_encoder: &mut dyn hal::DynCommandEncoder,
2817        family: DrawCommandFamily,
2818        indirect_buffer: &dyn hal::DynBuffer,
2819        offset: u64,
2820        count: u32,
2821    ) {
2822        match family {
2823            DrawCommandFamily::Draw => unsafe {
2824                raw_encoder.draw_indirect(indirect_buffer, offset, count);
2825            },
2826            DrawCommandFamily::DrawIndexed => unsafe {
2827                raw_encoder.draw_indexed_indirect(indirect_buffer, offset, count);
2828            },
2829            DrawCommandFamily::DrawMeshTasks => unsafe {
2830                raw_encoder.draw_mesh_tasks_indirect(indirect_buffer, offset, count);
2831            },
2832        }
2833    }
2834
2835    if state.pass.base.device.indirect_validation.is_some() {
2836        state
2837            .pass
2838            .scope
2839            .buffers
2840            .merge_single(&indirect_buffer, wgt::BufferUses::STORAGE_READ_ONLY)?;
2841
2842        struct DrawData {
2843            buffer_index: usize,
2844            offset: u64,
2845            count: u32,
2846        }
2847
2848        struct DrawContext<'a> {
2849            raw_encoder: &'a mut dyn hal::DynCommandEncoder,
2850            device: &'a Device,
2851
2852            indirect_draw_validation_resources: &'a mut crate::indirect_validation::DrawResources,
2853            indirect_draw_validation_batcher: &'a mut crate::indirect_validation::DrawBatcher,
2854
2855            indirect_buffer: Arc<crate::resource::Buffer>,
2856            family: DrawCommandFamily,
2857            vertex_or_index_limit: u64,
2858            instance_limit: u64,
2859        }
2860
2861        impl<'a> DrawContext<'a> {
2862            fn add(&mut self, offset: u64) -> Result<DrawData, DeviceError> {
2863                let (dst_resource_index, dst_offset) = self.indirect_draw_validation_batcher.add(
2864                    self.indirect_draw_validation_resources,
2865                    self.device,
2866                    &self.indirect_buffer,
2867                    offset,
2868                    self.family,
2869                    self.vertex_or_index_limit,
2870                    self.instance_limit,
2871                )?;
2872                Ok(DrawData {
2873                    buffer_index: dst_resource_index,
2874                    offset: dst_offset,
2875                    count: 1,
2876                })
2877            }
2878            fn draw(&mut self, draw_data: DrawData) {
2879                let dst_buffer = self
2880                    .indirect_draw_validation_resources
2881                    .get_dst_buffer(draw_data.buffer_index);
2882                draw(
2883                    self.raw_encoder,
2884                    self.family,
2885                    dst_buffer,
2886                    draw_data.offset,
2887                    draw_data.count,
2888                );
2889            }
2890        }
2891
2892        let mut draw_ctx = DrawContext {
2893            raw_encoder: state.pass.base.raw_encoder,
2894            device: state.pass.base.device,
2895            indirect_draw_validation_resources: state.pass.base.indirect_draw_validation_resources,
2896            indirect_draw_validation_batcher,
2897            indirect_buffer,
2898            family,
2899            vertex_or_index_limit: if family == DrawCommandFamily::DrawIndexed {
2900                state.index.limit
2901            } else {
2902                state.vertex.limits.vertex_limit
2903            },
2904            instance_limit: state.vertex.limits.instance_limit,
2905        };
2906
2907        let mut current_draw_data = draw_ctx.add(offset)?;
2908
2909        for i in 1..count {
2910            let draw_data = draw_ctx.add(offset + stride * i as u64)?;
2911
2912            if draw_data.buffer_index == current_draw_data.buffer_index {
2913                debug_assert_eq!(
2914                    draw_data.offset,
2915                    current_draw_data.offset + stride * current_draw_data.count as u64
2916                );
2917                current_draw_data.count += 1;
2918            } else {
2919                draw_ctx.draw(current_draw_data);
2920                current_draw_data = draw_data;
2921            }
2922        }
2923
2924        draw_ctx.draw(current_draw_data);
2925    } else {
2926        state
2927            .pass
2928            .scope
2929            .buffers
2930            .merge_single(&indirect_buffer, wgt::BufferUses::INDIRECT)?;
2931
2932        draw(
2933            state.pass.base.raw_encoder,
2934            family,
2935            indirect_buffer.try_raw(state.pass.base.snatch_guard)?,
2936            offset,
2937            count,
2938        );
2939    };
2940
2941    Ok(())
2942}
2943
2944fn multi_draw_indirect_count(
2945    state: &mut State,
2946    device: &Arc<Device>,
2947    indirect_buffer: Arc<crate::resource::Buffer>,
2948    offset: u64,
2949    count_buffer: Arc<crate::resource::Buffer>,
2950    count_buffer_offset: u64,
2951    max_count: u32,
2952    family: DrawCommandFamily,
2953) -> Result<(), RenderPassErrorInner> {
2954    api_log!(
2955        "RenderPass::multi_draw_indirect_count (family:{family:?}) {} {offset} {} {count_buffer_offset:?} {max_count:?}",
2956        indirect_buffer.error_ident(),
2957        count_buffer.error_ident()
2958    );
2959
2960    state.is_ready(family)?;
2961    state.flush_bindings()?;
2962
2963    if family == DrawCommandFamily::DrawMeshTasks {
2964        validate_mesh_draw_multiview(state)?;
2965    }
2966
2967    let stride = get_stride_of_indirect_args(family);
2968
2969    state
2970        .pass
2971        .base
2972        .device
2973        .require_features(wgt::Features::MULTI_DRAW_INDIRECT_COUNT)?;
2974    state
2975        .pass
2976        .base
2977        .device
2978        .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
2979
2980    indirect_buffer.same_device(device)?;
2981    count_buffer.same_device(device)?;
2982
2983    state
2984        .pass
2985        .scope
2986        .buffers
2987        .merge_single(&indirect_buffer, wgt::BufferUses::INDIRECT)?;
2988
2989    indirect_buffer.check_usage(BufferUsages::INDIRECT)?;
2990    let indirect_raw = indirect_buffer.try_raw(state.pass.base.snatch_guard)?;
2991
2992    state
2993        .pass
2994        .scope
2995        .buffers
2996        .merge_single(&count_buffer, wgt::BufferUses::INDIRECT)?;
2997
2998    count_buffer.check_usage(BufferUsages::INDIRECT)?;
2999    let count_raw = count_buffer.try_raw(state.pass.base.snatch_guard)?;
3000
3001    if !offset.is_multiple_of(4) {
3002        return Err(RenderPassErrorInner::UnalignedIndirectBufferOffset(offset));
3003    }
3004
3005    let end_offset = offset + stride * max_count as u64;
3006    if end_offset > indirect_buffer.size {
3007        return Err(RenderPassErrorInner::IndirectBufferOverrun {
3008            count: 1,
3009            offset,
3010            end_offset,
3011            buffer_size: indirect_buffer.size,
3012        });
3013    }
3014    state.pass.base.buffer_memory_init_actions.extend(
3015        indirect_buffer.initialization_status.read().create_action(
3016            &indirect_buffer,
3017            offset..end_offset,
3018            MemoryInitKind::NeedsInitializedMemory,
3019        ),
3020    );
3021
3022    let begin_count_offset = count_buffer_offset;
3023    let end_count_offset = count_buffer_offset + 4;
3024    if end_count_offset > count_buffer.size {
3025        return Err(RenderPassErrorInner::IndirectCountBufferOverrun {
3026            begin_count_offset,
3027            end_count_offset,
3028            count_buffer_size: count_buffer.size,
3029        });
3030    }
3031    state.pass.base.buffer_memory_init_actions.extend(
3032        count_buffer.initialization_status.read().create_action(
3033            &count_buffer,
3034            count_buffer_offset..end_count_offset,
3035            MemoryInitKind::NeedsInitializedMemory,
3036        ),
3037    );
3038
3039    match family {
3040        DrawCommandFamily::Draw => unsafe {
3041            state.pass.base.raw_encoder.draw_indirect_count(
3042                indirect_raw,
3043                offset,
3044                count_raw,
3045                count_buffer_offset,
3046                max_count,
3047            );
3048        },
3049        DrawCommandFamily::DrawIndexed => unsafe {
3050            state.pass.base.raw_encoder.draw_indexed_indirect_count(
3051                indirect_raw,
3052                offset,
3053                count_raw,
3054                count_buffer_offset,
3055                max_count,
3056            );
3057        },
3058        DrawCommandFamily::DrawMeshTasks => unsafe {
3059            state.pass.base.raw_encoder.draw_mesh_tasks_indirect_count(
3060                indirect_raw,
3061                offset,
3062                count_raw,
3063                count_buffer_offset,
3064                max_count,
3065            );
3066        },
3067    }
3068    Ok(())
3069}
3070
3071fn execute_bundle(
3072    state: &mut State,
3073    indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
3074    device: &Arc<Device>,
3075    bundle: Arc<super::RenderBundle>,
3076) -> Result<(), RenderPassErrorInner> {
3077    api_log!("RenderPass::execute_bundle {}", bundle.error_ident());
3078
3079    let bundle = state.pass.base.tracker.bundles.insert_single(bundle);
3080
3081    bundle.same_device(device)?;
3082
3083    state
3084        .info
3085        .context
3086        .check_compatible(&bundle.context, bundle.as_ref())
3087        .map_err(RenderPassErrorInner::IncompatibleBundleTargets)?;
3088
3089    if (state.info.is_depth_read_only && !bundle.is_depth_read_only)
3090        || (state.info.is_stencil_read_only && !bundle.is_stencil_read_only)
3091    {
3092        return Err(
3093            RenderPassErrorInner::IncompatibleBundleReadOnlyDepthStencil {
3094                pass_depth: state.info.is_depth_read_only,
3095                pass_stencil: state.info.is_stencil_read_only,
3096                bundle_depth: bundle.is_depth_read_only,
3097                bundle_stencil: bundle.is_stencil_read_only,
3098            },
3099        );
3100    }
3101
3102    state.pass.base.buffer_memory_init_actions.extend(
3103        bundle
3104            .buffer_memory_init_actions
3105            .iter()
3106            .filter_map(|action| {
3107                action
3108                    .buffer
3109                    .initialization_status
3110                    .read()
3111                    .check_action(action)
3112            }),
3113    );
3114    for action in bundle.texture_memory_init_actions.iter() {
3115        state.pass.pending_discard_init_fixups.extend(
3116            state
3117                .pass
3118                .base
3119                .texture_memory_actions
3120                .register_init_action(action),
3121        );
3122    }
3123
3124    unsafe {
3125        bundle.execute(
3126            state.pass.base.raw_encoder,
3127            state.pass.base.indirect_draw_validation_resources,
3128            indirect_draw_validation_batcher,
3129            state.pass.base.snatch_guard,
3130        )
3131    }
3132    .map_err(|e| match e {
3133        ExecutionError::Device(e) => RenderPassErrorInner::Device(e),
3134        ExecutionError::DestroyedResource(e) => {
3135            RenderPassErrorInner::RenderCommand(RenderCommandError::DestroyedResource(e))
3136        }
3137        ExecutionError::Unimplemented(what) => {
3138            RenderPassErrorInner::RenderCommand(RenderCommandError::Unimplemented(what))
3139        }
3140    })?;
3141
3142    unsafe {
3143        state.pass.scope.merge_render_bundle(&bundle.used)?;
3144    };
3145    state.reset_bundle();
3146    Ok(())
3147}
3148
3149// Recording a render pass.
3150//
3151// The only error that should be returned from these methods is
3152// `EncoderStateError::Ended`, when the pass has already ended and an immediate
3153// validation error is raised.
3154//
3155// All other errors should be stored in the pass for later reporting when
3156// `CommandEncoder.finish()` is called.
3157//
3158// The `pass_try!` macro should be used to handle errors appropriately. Note
3159// that the `pass_try!` and `pass_base!` macros may return early from the
3160// function that invokes them, like the `?` operator.
3161impl Global {
3162    pub fn render_pass_set_bind_group(
3163        &self,
3164        pass: &mut RenderPass,
3165        index: u32,
3166        bind_group_id: Option<id::BindGroupId>,
3167        offsets: &[DynamicOffset],
3168    ) -> Result<(), PassStateError> {
3169        let scope = PassErrorScope::SetBindGroup;
3170
3171        // This statement will return an error if the pass is ended. It's
3172        // important the error check comes before the early-out for
3173        // `set_and_check_redundant`.
3174        let base = pass_base!(pass, scope);
3175
3176        if pass.current_bind_groups.set_and_check_redundant(
3177            bind_group_id,
3178            index,
3179            &mut base.dynamic_offsets,
3180            offsets,
3181        ) {
3182            return Ok(());
3183        }
3184
3185        let mut bind_group = None;
3186        if let Some(bind_group_id) = bind_group_id {
3187            let hub = &self.hub;
3188            bind_group = Some(pass_try!(
3189                base,
3190                scope,
3191                hub.bind_groups.get(bind_group_id).get(),
3192            ));
3193        }
3194
3195        base.commands.push(ArcRenderCommand::SetBindGroup {
3196            index,
3197            num_dynamic_offsets: offsets.len(),
3198            bind_group,
3199        });
3200
3201        Ok(())
3202    }
3203
3204    pub fn render_pass_set_pipeline(
3205        &self,
3206        pass: &mut RenderPass,
3207        pipeline_id: id::RenderPipelineId,
3208    ) -> Result<(), PassStateError> {
3209        let scope = PassErrorScope::SetPipelineRender;
3210
3211        let redundant = pass.current_pipeline.set_and_check_redundant(pipeline_id);
3212
3213        // This statement will return an error if the pass is ended.
3214        // Its important the error check comes before the early-out for `redundant`.
3215        let base = pass_base!(pass, scope);
3216
3217        if redundant {
3218            return Ok(());
3219        }
3220
3221        let hub = &self.hub;
3222        let pipeline = pass_try!(base, scope, hub.render_pipelines.get(pipeline_id).get());
3223
3224        base.commands.push(ArcRenderCommand::SetPipeline(pipeline));
3225
3226        Ok(())
3227    }
3228
3229    pub fn render_pass_set_index_buffer(
3230        &self,
3231        pass: &mut RenderPass,
3232        buffer_id: id::BufferId,
3233        index_format: IndexFormat,
3234        offset: BufferAddress,
3235        size: Option<BufferSize>,
3236    ) -> Result<(), PassStateError> {
3237        let scope = PassErrorScope::SetIndexBuffer;
3238        let base = pass_base!(pass, scope);
3239
3240        base.commands.push(ArcRenderCommand::SetIndexBuffer {
3241            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3242            index_format,
3243            offset,
3244            size,
3245        });
3246
3247        Ok(())
3248    }
3249
3250    pub fn render_pass_set_vertex_buffer(
3251        &self,
3252        pass: &mut RenderPass,
3253        slot: u32,
3254        buffer_id: id::BufferId,
3255        offset: BufferAddress,
3256        size: Option<BufferSize>,
3257    ) -> Result<(), PassStateError> {
3258        let scope = PassErrorScope::SetVertexBuffer;
3259        let base = pass_base!(pass, scope);
3260
3261        base.commands.push(ArcRenderCommand::SetVertexBuffer {
3262            slot,
3263            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3264            offset,
3265            size,
3266        });
3267
3268        Ok(())
3269    }
3270
3271    pub fn render_pass_set_blend_constant(
3272        &self,
3273        pass: &mut RenderPass,
3274        color: Color,
3275    ) -> Result<(), PassStateError> {
3276        let scope = PassErrorScope::SetBlendConstant;
3277        let base = pass_base!(pass, scope);
3278
3279        base.commands
3280            .push(ArcRenderCommand::SetBlendConstant(color));
3281
3282        Ok(())
3283    }
3284
3285    pub fn render_pass_set_stencil_reference(
3286        &self,
3287        pass: &mut RenderPass,
3288        value: u32,
3289    ) -> Result<(), PassStateError> {
3290        let scope = PassErrorScope::SetStencilReference;
3291        let base = pass_base!(pass, scope);
3292
3293        base.commands
3294            .push(ArcRenderCommand::SetStencilReference(value));
3295
3296        Ok(())
3297    }
3298
3299    pub fn render_pass_set_viewport(
3300        &self,
3301        pass: &mut RenderPass,
3302        x: f32,
3303        y: f32,
3304        w: f32,
3305        h: f32,
3306        depth_min: f32,
3307        depth_max: f32,
3308    ) -> Result<(), PassStateError> {
3309        let scope = PassErrorScope::SetViewport;
3310        let base = pass_base!(pass, scope);
3311
3312        base.commands.push(ArcRenderCommand::SetViewport {
3313            rect: Rect { x, y, w, h },
3314            depth_min,
3315            depth_max,
3316        });
3317
3318        Ok(())
3319    }
3320
3321    pub fn render_pass_set_scissor_rect(
3322        &self,
3323        pass: &mut RenderPass,
3324        x: u32,
3325        y: u32,
3326        w: u32,
3327        h: u32,
3328    ) -> Result<(), PassStateError> {
3329        let scope = PassErrorScope::SetScissorRect;
3330        let base = pass_base!(pass, scope);
3331
3332        base.commands
3333            .push(ArcRenderCommand::SetScissor(Rect { x, y, w, h }));
3334
3335        Ok(())
3336    }
3337
3338    pub fn render_pass_set_immediates(
3339        &self,
3340        pass: &mut RenderPass,
3341        offset: u32,
3342        data: &[u8],
3343    ) -> Result<(), PassStateError> {
3344        let scope = PassErrorScope::SetImmediate;
3345        let base = pass_base!(pass, scope);
3346
3347        if offset & (wgt::IMMEDIATE_DATA_ALIGNMENT - 1) != 0 {
3348            pass_try!(
3349                base,
3350                scope,
3351                Err(RenderPassErrorInner::ImmediateOffsetAlignment)
3352            );
3353        }
3354        if data.len() as u32 & (wgt::IMMEDIATE_DATA_ALIGNMENT - 1) != 0 {
3355            pass_try!(
3356                base,
3357                scope,
3358                Err(RenderPassErrorInner::ImmediateDataizeAlignment)
3359            );
3360        }
3361
3362        let value_offset = pass_try!(
3363            base,
3364            scope,
3365            base.immediates_data
3366                .len()
3367                .try_into()
3368                .map_err(|_| RenderPassErrorInner::ImmediateOutOfMemory),
3369        );
3370
3371        base.immediates_data.extend(
3372            data.chunks_exact(wgt::IMMEDIATE_DATA_ALIGNMENT as usize)
3373                .map(|arr| u32::from_ne_bytes([arr[0], arr[1], arr[2], arr[3]])),
3374        );
3375
3376        base.commands.push(ArcRenderCommand::SetImmediate {
3377            offset,
3378            size_bytes: data.len() as u32,
3379            values_offset: Some(value_offset),
3380        });
3381
3382        Ok(())
3383    }
3384
3385    pub fn render_pass_draw(
3386        &self,
3387        pass: &mut RenderPass,
3388        vertex_count: u32,
3389        instance_count: u32,
3390        first_vertex: u32,
3391        first_instance: u32,
3392    ) -> Result<(), PassStateError> {
3393        let scope = PassErrorScope::Draw {
3394            kind: DrawKind::Draw,
3395            family: DrawCommandFamily::Draw,
3396        };
3397        let base = pass_base!(pass, scope);
3398
3399        base.commands.push(ArcRenderCommand::Draw {
3400            vertex_count,
3401            instance_count,
3402            first_vertex,
3403            first_instance,
3404        });
3405
3406        Ok(())
3407    }
3408
3409    pub fn render_pass_draw_indexed(
3410        &self,
3411        pass: &mut RenderPass,
3412        index_count: u32,
3413        instance_count: u32,
3414        first_index: u32,
3415        base_vertex: i32,
3416        first_instance: u32,
3417    ) -> Result<(), PassStateError> {
3418        let scope = PassErrorScope::Draw {
3419            kind: DrawKind::Draw,
3420            family: DrawCommandFamily::DrawIndexed,
3421        };
3422        let base = pass_base!(pass, scope);
3423
3424        base.commands.push(ArcRenderCommand::DrawIndexed {
3425            index_count,
3426            instance_count,
3427            first_index,
3428            base_vertex,
3429            first_instance,
3430        });
3431
3432        Ok(())
3433    }
3434
3435    pub fn render_pass_draw_mesh_tasks(
3436        &self,
3437        pass: &mut RenderPass,
3438        group_count_x: u32,
3439        group_count_y: u32,
3440        group_count_z: u32,
3441    ) -> Result<(), RenderPassError> {
3442        let scope = PassErrorScope::Draw {
3443            kind: DrawKind::Draw,
3444            family: DrawCommandFamily::DrawMeshTasks,
3445        };
3446        let base = pass_base!(pass, scope);
3447
3448        base.commands.push(ArcRenderCommand::DrawMeshTasks {
3449            group_count_x,
3450            group_count_y,
3451            group_count_z,
3452        });
3453        Ok(())
3454    }
3455
3456    pub fn render_pass_draw_indirect(
3457        &self,
3458        pass: &mut RenderPass,
3459        buffer_id: id::BufferId,
3460        offset: BufferAddress,
3461    ) -> Result<(), PassStateError> {
3462        let scope = PassErrorScope::Draw {
3463            kind: DrawKind::DrawIndirect,
3464            family: DrawCommandFamily::Draw,
3465        };
3466        let base = pass_base!(pass, scope);
3467
3468        base.commands.push(ArcRenderCommand::DrawIndirect {
3469            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3470            offset,
3471            count: 1,
3472            family: DrawCommandFamily::Draw,
3473
3474            vertex_or_index_limit: None,
3475            instance_limit: None,
3476        });
3477
3478        Ok(())
3479    }
3480
3481    pub fn render_pass_draw_indexed_indirect(
3482        &self,
3483        pass: &mut RenderPass,
3484        buffer_id: id::BufferId,
3485        offset: BufferAddress,
3486    ) -> Result<(), PassStateError> {
3487        let scope = PassErrorScope::Draw {
3488            kind: DrawKind::DrawIndirect,
3489            family: DrawCommandFamily::DrawIndexed,
3490        };
3491        let base = pass_base!(pass, scope);
3492
3493        base.commands.push(ArcRenderCommand::DrawIndirect {
3494            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3495            offset,
3496            count: 1,
3497            family: DrawCommandFamily::DrawIndexed,
3498
3499            vertex_or_index_limit: None,
3500            instance_limit: None,
3501        });
3502
3503        Ok(())
3504    }
3505
3506    pub fn render_pass_draw_mesh_tasks_indirect(
3507        &self,
3508        pass: &mut RenderPass,
3509        buffer_id: id::BufferId,
3510        offset: BufferAddress,
3511    ) -> Result<(), RenderPassError> {
3512        let scope = PassErrorScope::Draw {
3513            kind: DrawKind::DrawIndirect,
3514            family: DrawCommandFamily::DrawMeshTasks,
3515        };
3516        let base = pass_base!(pass, scope);
3517
3518        base.commands.push(ArcRenderCommand::DrawIndirect {
3519            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3520            offset,
3521            count: 1,
3522            family: DrawCommandFamily::DrawMeshTasks,
3523
3524            vertex_or_index_limit: None,
3525            instance_limit: None,
3526        });
3527
3528        Ok(())
3529    }
3530
3531    pub fn render_pass_multi_draw_indirect(
3532        &self,
3533        pass: &mut RenderPass,
3534        buffer_id: id::BufferId,
3535        offset: BufferAddress,
3536        count: u32,
3537    ) -> Result<(), PassStateError> {
3538        let scope = PassErrorScope::Draw {
3539            kind: DrawKind::MultiDrawIndirect,
3540            family: DrawCommandFamily::Draw,
3541        };
3542        let base = pass_base!(pass, scope);
3543
3544        base.commands.push(ArcRenderCommand::DrawIndirect {
3545            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3546            offset,
3547            count,
3548            family: DrawCommandFamily::Draw,
3549
3550            vertex_or_index_limit: None,
3551            instance_limit: None,
3552        });
3553
3554        Ok(())
3555    }
3556
3557    pub fn render_pass_multi_draw_indexed_indirect(
3558        &self,
3559        pass: &mut RenderPass,
3560        buffer_id: id::BufferId,
3561        offset: BufferAddress,
3562        count: u32,
3563    ) -> Result<(), PassStateError> {
3564        let scope = PassErrorScope::Draw {
3565            kind: DrawKind::MultiDrawIndirect,
3566            family: DrawCommandFamily::DrawIndexed,
3567        };
3568        let base = pass_base!(pass, scope);
3569
3570        base.commands.push(ArcRenderCommand::DrawIndirect {
3571            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3572            offset,
3573            count,
3574            family: DrawCommandFamily::DrawIndexed,
3575
3576            vertex_or_index_limit: None,
3577            instance_limit: None,
3578        });
3579
3580        Ok(())
3581    }
3582
3583    pub fn render_pass_multi_draw_mesh_tasks_indirect(
3584        &self,
3585        pass: &mut RenderPass,
3586        buffer_id: id::BufferId,
3587        offset: BufferAddress,
3588        count: u32,
3589    ) -> Result<(), RenderPassError> {
3590        let scope = PassErrorScope::Draw {
3591            kind: DrawKind::MultiDrawIndirect,
3592            family: DrawCommandFamily::DrawMeshTasks,
3593        };
3594        let base = pass_base!(pass, scope);
3595
3596        base.commands.push(ArcRenderCommand::DrawIndirect {
3597            buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3598            offset,
3599            count,
3600            family: DrawCommandFamily::DrawMeshTasks,
3601
3602            vertex_or_index_limit: None,
3603            instance_limit: None,
3604        });
3605
3606        Ok(())
3607    }
3608
3609    pub fn render_pass_multi_draw_indirect_count(
3610        &self,
3611        pass: &mut RenderPass,
3612        buffer_id: id::BufferId,
3613        offset: BufferAddress,
3614        count_buffer_id: id::BufferId,
3615        count_buffer_offset: BufferAddress,
3616        max_count: u32,
3617    ) -> Result<(), PassStateError> {
3618        let scope = PassErrorScope::Draw {
3619            kind: DrawKind::MultiDrawIndirectCount,
3620            family: DrawCommandFamily::Draw,
3621        };
3622        let base = pass_base!(pass, scope);
3623
3624        base.commands
3625            .push(ArcRenderCommand::MultiDrawIndirectCount {
3626                buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3627                offset,
3628                count_buffer: pass_try!(base, scope, self.resolve_buffer_id(count_buffer_id)),
3629                count_buffer_offset,
3630                max_count,
3631                family: DrawCommandFamily::Draw,
3632            });
3633
3634        Ok(())
3635    }
3636
3637    pub fn render_pass_multi_draw_indexed_indirect_count(
3638        &self,
3639        pass: &mut RenderPass,
3640        buffer_id: id::BufferId,
3641        offset: BufferAddress,
3642        count_buffer_id: id::BufferId,
3643        count_buffer_offset: BufferAddress,
3644        max_count: u32,
3645    ) -> Result<(), PassStateError> {
3646        let scope = PassErrorScope::Draw {
3647            kind: DrawKind::MultiDrawIndirectCount,
3648            family: DrawCommandFamily::DrawIndexed,
3649        };
3650        let base = pass_base!(pass, scope);
3651
3652        base.commands
3653            .push(ArcRenderCommand::MultiDrawIndirectCount {
3654                buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3655                offset,
3656                count_buffer: pass_try!(base, scope, self.resolve_buffer_id(count_buffer_id)),
3657                count_buffer_offset,
3658                max_count,
3659                family: DrawCommandFamily::DrawIndexed,
3660            });
3661
3662        Ok(())
3663    }
3664
3665    pub fn render_pass_multi_draw_mesh_tasks_indirect_count(
3666        &self,
3667        pass: &mut RenderPass,
3668        buffer_id: id::BufferId,
3669        offset: BufferAddress,
3670        count_buffer_id: id::BufferId,
3671        count_buffer_offset: BufferAddress,
3672        max_count: u32,
3673    ) -> Result<(), RenderPassError> {
3674        let scope = PassErrorScope::Draw {
3675            kind: DrawKind::MultiDrawIndirectCount,
3676            family: DrawCommandFamily::DrawMeshTasks,
3677        };
3678        let base = pass_base!(pass, scope);
3679
3680        base.commands
3681            .push(ArcRenderCommand::MultiDrawIndirectCount {
3682                buffer: pass_try!(base, scope, self.resolve_buffer_id(buffer_id)),
3683                offset,
3684                count_buffer: pass_try!(base, scope, self.resolve_buffer_id(count_buffer_id)),
3685                count_buffer_offset,
3686                max_count,
3687                family: DrawCommandFamily::DrawMeshTasks,
3688            });
3689
3690        Ok(())
3691    }
3692
3693    pub fn render_pass_push_debug_group(
3694        &self,
3695        pass: &mut RenderPass,
3696        label: &str,
3697        color: u32,
3698    ) -> Result<(), PassStateError> {
3699        let base = pass_base!(pass, PassErrorScope::PushDebugGroup);
3700
3701        let bytes = label.as_bytes();
3702        base.string_data.extend_from_slice(bytes);
3703
3704        base.commands.push(ArcRenderCommand::PushDebugGroup {
3705            color,
3706            len: bytes.len(),
3707        });
3708
3709        Ok(())
3710    }
3711
3712    pub fn render_pass_pop_debug_group(&self, pass: &mut RenderPass) -> Result<(), PassStateError> {
3713        let base = pass_base!(pass, PassErrorScope::PopDebugGroup);
3714
3715        base.commands.push(ArcRenderCommand::PopDebugGroup);
3716
3717        Ok(())
3718    }
3719
3720    pub fn render_pass_insert_debug_marker(
3721        &self,
3722        pass: &mut RenderPass,
3723        label: &str,
3724        color: u32,
3725    ) -> Result<(), PassStateError> {
3726        let base = pass_base!(pass, PassErrorScope::InsertDebugMarker);
3727
3728        let bytes = label.as_bytes();
3729        base.string_data.extend_from_slice(bytes);
3730
3731        base.commands.push(ArcRenderCommand::InsertDebugMarker {
3732            color,
3733            len: bytes.len(),
3734        });
3735
3736        Ok(())
3737    }
3738
3739    pub fn render_pass_write_timestamp(
3740        &self,
3741        pass: &mut RenderPass,
3742        query_set_id: id::QuerySetId,
3743        query_index: u32,
3744    ) -> Result<(), PassStateError> {
3745        let scope = PassErrorScope::WriteTimestamp;
3746        let base = pass_base!(pass, scope);
3747
3748        base.commands.push(ArcRenderCommand::WriteTimestamp {
3749            query_set: pass_try!(base, scope, self.resolve_query_set(query_set_id)),
3750            query_index,
3751        });
3752
3753        Ok(())
3754    }
3755
3756    pub fn render_pass_begin_occlusion_query(
3757        &self,
3758        pass: &mut RenderPass,
3759        query_index: u32,
3760    ) -> Result<(), PassStateError> {
3761        let scope = PassErrorScope::BeginOcclusionQuery;
3762        let base = pass_base!(pass, scope);
3763
3764        base.commands
3765            .push(ArcRenderCommand::BeginOcclusionQuery { query_index });
3766
3767        Ok(())
3768    }
3769
3770    pub fn render_pass_end_occlusion_query(
3771        &self,
3772        pass: &mut RenderPass,
3773    ) -> Result<(), PassStateError> {
3774        let scope = PassErrorScope::EndOcclusionQuery;
3775        let base = pass_base!(pass, scope);
3776
3777        base.commands.push(ArcRenderCommand::EndOcclusionQuery);
3778
3779        Ok(())
3780    }
3781
3782    pub fn render_pass_begin_pipeline_statistics_query(
3783        &self,
3784        pass: &mut RenderPass,
3785        query_set_id: id::QuerySetId,
3786        query_index: u32,
3787    ) -> Result<(), PassStateError> {
3788        let scope = PassErrorScope::BeginPipelineStatisticsQuery;
3789        let base = pass_base!(pass, scope);
3790
3791        base.commands
3792            .push(ArcRenderCommand::BeginPipelineStatisticsQuery {
3793                query_set: pass_try!(base, scope, self.resolve_query_set(query_set_id)),
3794                query_index,
3795            });
3796
3797        Ok(())
3798    }
3799
3800    pub fn render_pass_end_pipeline_statistics_query(
3801        &self,
3802        pass: &mut RenderPass,
3803    ) -> Result<(), PassStateError> {
3804        let scope = PassErrorScope::EndPipelineStatisticsQuery;
3805        let base = pass_base!(pass, scope);
3806
3807        base.commands
3808            .push(ArcRenderCommand::EndPipelineStatisticsQuery);
3809
3810        Ok(())
3811    }
3812
3813    pub fn render_pass_execute_bundles(
3814        &self,
3815        pass: &mut RenderPass,
3816        render_bundle_ids: &[id::RenderBundleId],
3817    ) -> Result<(), PassStateError> {
3818        let scope = PassErrorScope::ExecuteBundle;
3819        let base = pass_base!(pass, scope);
3820
3821        let hub = &self.hub;
3822        let bundles = hub.render_bundles.read();
3823
3824        for &bundle_id in render_bundle_ids {
3825            let bundle = pass_try!(base, scope, bundles.get(bundle_id).get());
3826
3827            base.commands.push(ArcRenderCommand::ExecuteBundle(bundle));
3828        }
3829        pass.current_pipeline.reset();
3830        pass.current_bind_groups.reset();
3831
3832        Ok(())
3833    }
3834}
3835
3836pub(crate) const fn get_stride_of_indirect_args(family: DrawCommandFamily) -> u64 {
3837    match family {
3838        DrawCommandFamily::Draw => size_of::<wgt::DrawIndirectArgs>() as u64,
3839        DrawCommandFamily::DrawIndexed => size_of::<wgt::DrawIndexedIndirectArgs>() as u64,
3840        DrawCommandFamily::DrawMeshTasks => size_of::<wgt::DispatchIndirectArgs>() as u64,
3841    }
3842}