wgpu_core/command/
render.rs

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