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