wgpu_core/command/
render.rs

1use crate::resource::Resource;
2use crate::snatch::SnatchGuard;
3use crate::{
4    api_log,
5    binding_model::BindError,
6    command::{
7        self,
8        bind::Binder,
9        end_occlusion_query, end_pipeline_statistics_query,
10        memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState},
11        BasePass, BasePassRef, BindGroupStateChange, CommandBuffer, CommandEncoderError,
12        CommandEncoderStatus, DrawError, ExecutionError, MapPassErr, PassErrorScope, QueryUseError,
13        RenderCommand, RenderCommandError, StateChange,
14    },
15    device::{
16        AttachmentData, Device, DeviceError, MissingDownlevelFlags, MissingFeatures,
17        RenderPassCompatibilityCheckType, RenderPassCompatibilityError, RenderPassContext,
18    },
19    error::{ErrorFormatter, PrettyError},
20    global::Global,
21    hal_api::HalApi,
22    hal_label, id,
23    init_tracker::{MemoryInitKind, TextureInitRange, TextureInitTrackerAction},
24    pipeline::{self, PipelineFlags},
25    resource::{QuerySet, Texture, TextureView, TextureViewNotRenderableReason},
26    storage::Storage,
27    track::{TextureSelector, Tracker, UsageConflict, UsageScope},
28    validation::{
29        check_buffer_usage, check_texture_usage, MissingBufferUsageError, MissingTextureUsageError,
30    },
31    Label,
32};
33
34use arrayvec::ArrayVec;
35use hal::CommandEncoder as _;
36use thiserror::Error;
37use wgt::{
38    BufferAddress, BufferSize, BufferUsages, Color, IndexFormat, TextureUsages,
39    TextureViewDimension, VertexStepMode,
40};
41
42#[cfg(feature = "serde")]
43use serde::Deserialize;
44#[cfg(feature = "serde")]
45use serde::Serialize;
46
47use std::sync::Arc;
48use std::{borrow::Cow, fmt, iter, marker::PhantomData, mem, num::NonZeroU32, ops::Range, str};
49
50use super::{
51    memory_init::TextureSurfaceDiscard, CommandBufferTextureMemoryActions, CommandEncoder,
52    QueryResetMap,
53};
54
55/// Operation to perform to the output attachment at the start of a renderpass.
56#[repr(C)]
57#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
58#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
59#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
60pub enum LoadOp {
61    /// Clear the output attachment with the clear color. Clearing is faster than loading.
62    Clear = 0,
63    /// Do not clear output attachment.
64    Load = 1,
65}
66
67/// Operation to perform to the output attachment at the end of a renderpass.
68#[repr(C)]
69#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
70#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
71#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
72pub enum StoreOp {
73    /// Discards the content of the render target.
74    ///
75    /// If you don't care about the contents of the target, this can be faster.
76    Discard = 0,
77    /// Store the result of the renderpass.
78    Store = 1,
79}
80
81/// Describes an individual channel within a render pass, such as color, depth, or stencil.
82#[repr(C)]
83#[derive(Clone, Debug, Eq, PartialEq)]
84#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
85pub struct PassChannel<V> {
86    /// Operation to perform to the output attachment at the start of a
87    /// renderpass.
88    ///
89    /// This must be clear if it is the first renderpass rendering to a swap
90    /// chain image.
91    pub load_op: LoadOp,
92    /// Operation to perform to the output attachment at the end of a renderpass.
93    pub store_op: StoreOp,
94    /// If load_op is [`LoadOp::Clear`], the attachment will be cleared to this
95    /// color.
96    pub clear_value: V,
97    /// If true, the relevant channel is not changed by a renderpass, and the
98    /// corresponding attachment can be used inside the pass by other read-only
99    /// usages.
100    pub read_only: bool,
101}
102
103impl<V> PassChannel<V> {
104    fn hal_ops(&self) -> hal::AttachmentOps {
105        let mut ops = hal::AttachmentOps::empty();
106        match self.load_op {
107            LoadOp::Load => ops |= hal::AttachmentOps::LOAD,
108            LoadOp::Clear => (),
109        };
110        match self.store_op {
111            StoreOp::Store => ops |= hal::AttachmentOps::STORE,
112            StoreOp::Discard => (),
113        };
114        ops
115    }
116}
117
118/// Describes a color attachment to a render pass.
119#[repr(C)]
120#[derive(Clone, Debug, PartialEq)]
121#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
122pub struct RenderPassColorAttachment {
123    /// The view to use as an attachment.
124    pub view: id::TextureViewId,
125    /// The view that will receive the resolved output if multisampling is used.
126    pub resolve_target: Option<id::TextureViewId>,
127    /// What operations will be performed on this color attachment.
128    pub channel: PassChannel<Color>,
129}
130
131/// Describes a depth/stencil attachment to a render pass.
132#[repr(C)]
133#[derive(Clone, Debug, PartialEq)]
134#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
135pub struct RenderPassDepthStencilAttachment {
136    /// The view to use as an attachment.
137    pub view: id::TextureViewId,
138    /// What operations will be performed on the depth part of the attachment.
139    pub depth: PassChannel<f32>,
140    /// What operations will be performed on the stencil part of the attachment.
141    pub stencil: PassChannel<u32>,
142}
143
144impl RenderPassDepthStencilAttachment {
145    /// Validate the given aspects' read-only flags against their load
146    /// and store ops.
147    ///
148    /// When an aspect is read-only, its load and store ops must be
149    /// `LoadOp::Load` and `StoreOp::Store`.
150    ///
151    /// On success, return a pair `(depth, stencil)` indicating
152    /// whether the depth and stencil passes are read-only.
153    fn depth_stencil_read_only(
154        &self,
155        aspects: hal::FormatAspects,
156    ) -> Result<(bool, bool), RenderPassErrorInner> {
157        let mut depth_read_only = true;
158        let mut stencil_read_only = true;
159
160        if aspects.contains(hal::FormatAspects::DEPTH) {
161            if self.depth.read_only
162                && (self.depth.load_op, self.depth.store_op) != (LoadOp::Load, StoreOp::Store)
163            {
164                return Err(RenderPassErrorInner::InvalidDepthOps);
165            }
166            depth_read_only = self.depth.read_only;
167        }
168
169        if aspects.contains(hal::FormatAspects::STENCIL) {
170            if self.stencil.read_only
171                && (self.stencil.load_op, self.stencil.store_op) != (LoadOp::Load, StoreOp::Store)
172            {
173                return Err(RenderPassErrorInner::InvalidStencilOps);
174            }
175            stencil_read_only = self.stencil.read_only;
176        }
177
178        Ok((depth_read_only, stencil_read_only))
179    }
180}
181
182/// Location to write a timestamp to (beginning or end of the pass).
183#[repr(C)]
184#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
185#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
186#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
187pub enum RenderPassTimestampLocation {
188    Beginning = 0,
189    End = 1,
190}
191
192/// Describes the writing of timestamp values in a render pass.
193#[repr(C)]
194#[derive(Clone, Debug, PartialEq, Eq)]
195#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
196pub struct RenderPassTimestampWrites {
197    /// The query set to write the timestamp to.
198    pub query_set: id::QuerySetId,
199    /// The index of the query set at which a start timestamp of this pass is written, if any.
200    pub beginning_of_pass_write_index: Option<u32>,
201    /// The index of the query set at which an end timestamp of this pass is written, if any.
202    pub end_of_pass_write_index: Option<u32>,
203}
204
205/// Describes the attachments of a render pass.
206#[derive(Clone, Debug, Default, PartialEq)]
207pub struct RenderPassDescriptor<'a> {
208    pub label: Label<'a>,
209    /// The color attachments of the render pass.
210    pub color_attachments: Cow<'a, [Option<RenderPassColorAttachment>]>,
211    /// The depth and stencil attachment of the render pass, if any.
212    pub depth_stencil_attachment: Option<&'a RenderPassDepthStencilAttachment>,
213    /// Defines where and when timestamp values will be written for this pass.
214    pub timestamp_writes: Option<&'a RenderPassTimestampWrites>,
215    /// Defines where the occlusion query results will be stored for this pass.
216    pub occlusion_query_set: Option<id::QuerySetId>,
217}
218
219#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
220pub struct RenderPass {
221    base: BasePass<RenderCommand>,
222    parent_id: id::CommandEncoderId,
223    color_targets: ArrayVec<Option<RenderPassColorAttachment>, { hal::MAX_COLOR_ATTACHMENTS }>,
224    depth_stencil_target: Option<RenderPassDepthStencilAttachment>,
225    timestamp_writes: Option<RenderPassTimestampWrites>,
226    occlusion_query_set_id: Option<id::QuerySetId>,
227
228    // Resource binding dedupe state.
229    #[cfg_attr(feature = "serde", serde(skip))]
230    current_bind_groups: BindGroupStateChange,
231    #[cfg_attr(feature = "serde", serde(skip))]
232    current_pipeline: StateChange<id::RenderPipelineId>,
233}
234
235impl RenderPass {
236    pub fn new(parent_id: id::CommandEncoderId, desc: &RenderPassDescriptor) -> Self {
237        Self {
238            base: BasePass::new(&desc.label),
239            parent_id,
240            color_targets: desc.color_attachments.iter().cloned().collect(),
241            depth_stencil_target: desc.depth_stencil_attachment.cloned(),
242            timestamp_writes: desc.timestamp_writes.cloned(),
243            occlusion_query_set_id: desc.occlusion_query_set,
244
245            current_bind_groups: BindGroupStateChange::new(),
246            current_pipeline: StateChange::new(),
247        }
248    }
249
250    pub fn parent_id(&self) -> id::CommandEncoderId {
251        self.parent_id
252    }
253
254    #[cfg(feature = "trace")]
255    pub fn into_command(self) -> crate::device::trace::Command {
256        crate::device::trace::Command::RunRenderPass {
257            base: self.base,
258            target_colors: self.color_targets.into_iter().collect(),
259            target_depth_stencil: self.depth_stencil_target,
260            timestamp_writes: self.timestamp_writes,
261            occlusion_query_set_id: self.occlusion_query_set_id,
262        }
263    }
264
265    pub fn set_index_buffer(
266        &mut self,
267        buffer_id: id::BufferId,
268        index_format: IndexFormat,
269        offset: BufferAddress,
270        size: Option<BufferSize>,
271    ) {
272        self.base.commands.push(RenderCommand::SetIndexBuffer {
273            buffer_id,
274            index_format,
275            offset,
276            size,
277        });
278    }
279}
280
281impl fmt::Debug for RenderPass {
282    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
283        f.debug_struct("RenderPass")
284            .field("encoder_id", &self.parent_id)
285            .field("color_targets", &self.color_targets)
286            .field("depth_stencil_target", &self.depth_stencil_target)
287            .field("command count", &self.base.commands.len())
288            .field("dynamic offset count", &self.base.dynamic_offsets.len())
289            .field(
290                "push constant u32 count",
291                &self.base.push_constant_data.len(),
292            )
293            .finish()
294    }
295}
296
297#[derive(Debug, PartialEq)]
298enum OptionalState {
299    Unused,
300    Required,
301    Set,
302}
303
304impl OptionalState {
305    fn require(&mut self, require: bool) {
306        if require && *self == Self::Unused {
307            *self = Self::Required;
308        }
309    }
310}
311
312#[derive(Debug, Default)]
313struct IndexState {
314    bound_buffer_view: Option<(id::BufferId, Range<BufferAddress>)>,
315    format: Option<IndexFormat>,
316    pipeline_format: Option<IndexFormat>,
317    limit: u64,
318}
319
320impl IndexState {
321    fn update_limit(&mut self) {
322        self.limit = match self.bound_buffer_view {
323            Some((_, ref range)) => {
324                let format = self
325                    .format
326                    .expect("IndexState::update_limit must be called after a index buffer is set");
327                let shift = match format {
328                    IndexFormat::Uint16 => 1,
329                    IndexFormat::Uint32 => 2,
330                };
331
332                (range.end - range.start) >> shift
333            }
334            None => 0,
335        }
336    }
337
338    fn reset(&mut self) {
339        self.bound_buffer_view = None;
340        self.limit = 0;
341    }
342}
343
344#[derive(Clone, Copy, Debug)]
345struct VertexBufferState {
346    total_size: BufferAddress,
347    step: pipeline::VertexStep,
348    bound: bool,
349}
350
351impl VertexBufferState {
352    const EMPTY: Self = Self {
353        total_size: 0,
354        step: pipeline::VertexStep {
355            stride: 0,
356            last_stride: 0,
357            mode: VertexStepMode::Vertex,
358        },
359        bound: false,
360    };
361}
362
363#[derive(Debug, Default)]
364struct VertexState {
365    inputs: ArrayVec<VertexBufferState, { hal::MAX_VERTEX_BUFFERS }>,
366    /// Length of the shortest vertex rate vertex buffer
367    vertex_limit: u64,
368    /// Buffer slot which the shortest vertex rate vertex buffer is bound to
369    vertex_limit_slot: u32,
370    /// Length of the shortest instance rate vertex buffer
371    instance_limit: u64,
372    /// Buffer slot which the shortest instance rate vertex buffer is bound to
373    instance_limit_slot: u32,
374    /// Total amount of buffers required by the pipeline.
375    buffers_required: u32,
376}
377
378impl VertexState {
379    fn update_limits(&mut self) {
380        // Implements the validation from https://gpuweb.github.io/gpuweb/#dom-gpurendercommandsmixin-draw
381        // Except that the formula is shuffled to extract the number of vertices in order
382        // to carry the bulk of the computation when changing states instead of when producing
383        // draws. Draw calls tend to happen at a higher frequency. Here we determine vertex
384        // limits that can be cheaply checked for each draw call.
385        self.vertex_limit = u32::MAX as u64;
386        self.instance_limit = u32::MAX as u64;
387        for (idx, vbs) in self.inputs.iter().enumerate() {
388            if !vbs.bound {
389                continue;
390            }
391
392            let limit = if vbs.total_size < vbs.step.last_stride {
393                // The buffer cannot fit the last vertex.
394                0
395            } else {
396                if vbs.step.stride == 0 {
397                    // We already checked that the last stride fits, the same
398                    // vertex will be repeated so this slot can accommodate any number of
399                    // vertices.
400                    continue;
401                }
402
403                // The general case.
404                (vbs.total_size - vbs.step.last_stride) / vbs.step.stride + 1
405            };
406
407            match vbs.step.mode {
408                VertexStepMode::Vertex => {
409                    if limit < self.vertex_limit {
410                        self.vertex_limit = limit;
411                        self.vertex_limit_slot = idx as _;
412                    }
413                }
414                VertexStepMode::Instance => {
415                    if limit < self.instance_limit {
416                        self.instance_limit = limit;
417                        self.instance_limit_slot = idx as _;
418                    }
419                }
420            }
421        }
422    }
423
424    fn reset(&mut self) {
425        self.inputs.clear();
426        self.vertex_limit = 0;
427        self.instance_limit = 0;
428    }
429}
430
431#[derive(Debug)]
432struct State<A: HalApi> {
433    pipeline_flags: PipelineFlags,
434    binder: Binder<A>,
435    blend_constant: OptionalState,
436    stencil_reference: u32,
437    pipeline: Option<id::RenderPipelineId>,
438    index: IndexState,
439    vertex: VertexState,
440    debug_scope_depth: u32,
441}
442
443impl<A: HalApi> State<A> {
444    fn is_ready(&self, indexed: bool) -> Result<(), DrawError> {
445        // Determine how many vertex buffers have already been bound
446        let vertex_buffer_count = self.vertex.inputs.iter().take_while(|v| v.bound).count() as u32;
447        // Compare with the needed quantity
448        if vertex_buffer_count < self.vertex.buffers_required {
449            return Err(DrawError::MissingVertexBuffer {
450                index: vertex_buffer_count,
451            });
452        }
453
454        let bind_mask = self.binder.invalid_mask();
455        if bind_mask != 0 {
456            //let (expected, provided) = self.binder.entries[index as usize].info();
457            return Err(DrawError::IncompatibleBindGroup {
458                index: bind_mask.trailing_zeros(),
459                diff: self.binder.bgl_diff(),
460            });
461        }
462        if self.pipeline.is_none() {
463            return Err(DrawError::MissingPipeline);
464        }
465        if self.blend_constant == OptionalState::Required {
466            return Err(DrawError::MissingBlendConstant);
467        }
468
469        if indexed {
470            // Pipeline expects an index buffer
471            if let Some(pipeline_index_format) = self.index.pipeline_format {
472                // We have a buffer bound
473                let buffer_index_format = self.index.format.ok_or(DrawError::MissingIndexBuffer)?;
474
475                // The buffers are different formats
476                if pipeline_index_format != buffer_index_format {
477                    return Err(DrawError::UnmatchedIndexFormats {
478                        pipeline: pipeline_index_format,
479                        buffer: buffer_index_format,
480                    });
481                }
482            }
483        }
484
485        self.binder.check_late_buffer_bindings()?;
486
487        Ok(())
488    }
489
490    /// Reset the `RenderBundle`-related states.
491    fn reset_bundle(&mut self) {
492        self.binder.reset();
493        self.pipeline = None;
494        self.index.reset();
495        self.vertex.reset();
496    }
497}
498
499/// Describes an attachment location in words.
500///
501/// Can be used as "the {loc} has..." or "{loc} has..."
502#[derive(Debug, Copy, Clone)]
503pub enum AttachmentErrorLocation {
504    Color { index: usize, resolve: bool },
505    Depth,
506}
507
508impl fmt::Display for AttachmentErrorLocation {
509    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
510        match *self {
511            AttachmentErrorLocation::Color {
512                index,
513                resolve: false,
514            } => write!(f, "color attachment at index {index}'s texture view"),
515            AttachmentErrorLocation::Color {
516                index,
517                resolve: true,
518            } => write!(
519                f,
520                "color attachment at index {index}'s resolve texture view"
521            ),
522            AttachmentErrorLocation::Depth => write!(f, "depth attachment's texture view"),
523        }
524    }
525}
526
527#[derive(Clone, Debug, Error)]
528#[non_exhaustive]
529pub enum ColorAttachmentError {
530    #[error("Attachment format {0:?} is not a color format")]
531    InvalidFormat(wgt::TextureFormat),
532    #[error("The number of color attachments {given} exceeds the limit {limit}")]
533    TooMany { given: usize, limit: usize },
534    #[error("The total number of bytes per sample in color attachments {total} exceeds the limit {limit}")]
535    TooManyBytesPerSample { total: u32, limit: u32 },
536}
537
538/// Error encountered when performing a render pass.
539#[derive(Clone, Debug, Error)]
540pub enum RenderPassErrorInner {
541    #[error(transparent)]
542    Device(DeviceError),
543    #[error(transparent)]
544    ColorAttachment(#[from] ColorAttachmentError),
545    #[error(transparent)]
546    Encoder(#[from] CommandEncoderError),
547    #[error("Attachment texture view {0:?} is invalid")]
548    InvalidAttachment(id::TextureViewId),
549    #[error("Attachment texture view {0:?} is invalid")]
550    InvalidResolveTarget(id::TextureViewId),
551    #[error("The format of the depth-stencil attachment ({0:?}) is not a depth-stencil format")]
552    InvalidDepthStencilAttachmentFormat(wgt::TextureFormat),
553    #[error("The format of the {location} ({format:?}) is not resolvable")]
554    UnsupportedResolveTargetFormat {
555        location: AttachmentErrorLocation,
556        format: wgt::TextureFormat,
557    },
558    #[error("No color attachments or depth attachments were provided, at least one attachment of any kind must be provided")]
559    MissingAttachments,
560    #[error("The {location} is not renderable:")]
561    TextureViewIsNotRenderable {
562        location: AttachmentErrorLocation,
563        #[source]
564        reason: TextureViewNotRenderableReason,
565    },
566    #[error("Attachments have differing sizes: the {expected_location} has extent {expected_extent:?} but is followed by the {actual_location} which has {actual_extent:?}")]
567    AttachmentsDimensionMismatch {
568        expected_location: AttachmentErrorLocation,
569        expected_extent: wgt::Extent3d,
570        actual_location: AttachmentErrorLocation,
571        actual_extent: wgt::Extent3d,
572    },
573    #[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:?}")]
574    AttachmentSampleCountMismatch {
575        expected_location: AttachmentErrorLocation,
576        expected_samples: u32,
577        actual_location: AttachmentErrorLocation,
578        actual_samples: u32,
579    },
580    #[error("The resolve source, {location}, must be multi-sampled (has {src} samples) while the resolve destination must not be multisampled (has {dst} samples)")]
581    InvalidResolveSampleCounts {
582        location: AttachmentErrorLocation,
583        src: u32,
584        dst: u32,
585    },
586    #[error(
587        "Resource source, {location}, format ({src:?}) must match the resolve destination format ({dst:?})"
588    )]
589    MismatchedResolveTextureFormat {
590        location: AttachmentErrorLocation,
591        src: wgt::TextureFormat,
592        dst: wgt::TextureFormat,
593    },
594    #[error("Surface texture is dropped before the render pass is finished")]
595    SurfaceTextureDropped,
596    #[error("Not enough memory left for render pass")]
597    OutOfMemory,
598    #[error("The bind group at index {0:?} is invalid")]
599    InvalidBindGroup(usize),
600    #[error("Unable to clear non-present/read-only depth")]
601    InvalidDepthOps,
602    #[error("Unable to clear non-present/read-only stencil")]
603    InvalidStencilOps,
604    #[error("Setting `values_offset` to be `None` is only for internal use in render bundles")]
605    InvalidValuesOffset,
606    #[error(transparent)]
607    MissingFeatures(#[from] MissingFeatures),
608    #[error(transparent)]
609    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
610    #[error("Indirect draw uses bytes {offset}..{end_offset} {} which overruns indirect buffer of size {buffer_size}",
611        count.map_or_else(String::new, |v| format!("(using count {v})")))]
612    IndirectBufferOverrun {
613        count: Option<NonZeroU32>,
614        offset: u64,
615        end_offset: u64,
616        buffer_size: u64,
617    },
618    #[error("Indirect draw uses bytes {begin_count_offset}..{end_count_offset} which overruns indirect buffer of size {count_buffer_size}")]
619    IndirectCountBufferOverrun {
620        begin_count_offset: u64,
621        end_count_offset: u64,
622        count_buffer_size: u64,
623    },
624    #[error("Cannot pop debug group, because number of pushed debug groups is zero")]
625    InvalidPopDebugGroup,
626    #[error(transparent)]
627    ResourceUsageConflict(#[from] UsageConflict),
628    #[error("Render bundle has incompatible targets, {0}")]
629    IncompatibleBundleTargets(#[from] RenderPassCompatibilityError),
630    #[error(
631        "Render bundle has incompatible read-only flags: \
632             bundle has flags depth = {bundle_depth} and stencil = {bundle_stencil}, \
633             while the pass has flags depth = {pass_depth} and stencil = {pass_stencil}. \
634             Read-only renderpasses are only compatible with read-only bundles for that aspect."
635    )]
636    IncompatibleBundleReadOnlyDepthStencil {
637        pass_depth: bool,
638        pass_stencil: bool,
639        bundle_depth: bool,
640        bundle_stencil: bool,
641    },
642    #[error(transparent)]
643    RenderCommand(#[from] RenderCommandError),
644    #[error(transparent)]
645    Draw(#[from] DrawError),
646    #[error(transparent)]
647    Bind(#[from] BindError),
648    #[error(transparent)]
649    QueryUse(#[from] QueryUseError),
650    #[error("Multiview layer count must match")]
651    MultiViewMismatch,
652    #[error(
653        "Multiview pass texture views with more than one array layer must have D2Array dimension"
654    )]
655    MultiViewDimensionMismatch,
656    #[error("QuerySet {0:?} is invalid")]
657    InvalidQuerySet(id::QuerySetId),
658    #[error("missing occlusion query set")]
659    MissingOcclusionQuerySet,
660}
661
662impl PrettyError for RenderPassErrorInner {
663    fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
664        fmt.error(self);
665        if let Self::InvalidAttachment(id) = *self {
666            fmt.texture_view_label_with_key(&id, "attachment");
667        };
668        if let Self::Draw(DrawError::IncompatibleBindGroup { diff, .. }) = self {
669            for d in diff {
670                fmt.note(&d);
671            }
672        };
673    }
674}
675
676impl From<MissingBufferUsageError> for RenderPassErrorInner {
677    fn from(error: MissingBufferUsageError) -> Self {
678        Self::RenderCommand(error.into())
679    }
680}
681
682impl From<MissingTextureUsageError> for RenderPassErrorInner {
683    fn from(error: MissingTextureUsageError) -> Self {
684        Self::RenderCommand(error.into())
685    }
686}
687
688impl From<DeviceError> for RenderPassErrorInner {
689    fn from(error: DeviceError) -> Self {
690        Self::Device(error)
691    }
692}
693
694/// Error encountered when performing a render pass.
695#[derive(Clone, Debug, Error)]
696#[error("{scope}")]
697pub struct RenderPassError {
698    pub scope: PassErrorScope,
699    #[source]
700    inner: RenderPassErrorInner,
701}
702impl PrettyError for RenderPassError {
703    fn fmt_pretty(&self, fmt: &mut ErrorFormatter) {
704        // This error is wrapper for the inner error,
705        // but the scope has useful labels
706        fmt.error(self);
707        self.scope.fmt_pretty(fmt);
708    }
709}
710
711impl<T, E> MapPassErr<T, RenderPassError> for Result<T, E>
712where
713    E: Into<RenderPassErrorInner>,
714{
715    fn map_pass_err(self, scope: PassErrorScope) -> Result<T, RenderPassError> {
716        self.map_err(|inner| RenderPassError {
717            scope,
718            inner: inner.into(),
719        })
720    }
721}
722
723struct RenderAttachment<'a, A: HalApi> {
724    texture: Arc<Texture<A>>,
725    selector: &'a TextureSelector,
726    usage: hal::TextureUses,
727}
728
729impl<A: HalApi> TextureView<A> {
730    fn to_render_attachment(&self, usage: hal::TextureUses) -> RenderAttachment<A> {
731        RenderAttachment {
732            texture: self.parent.clone(),
733            selector: &self.selector,
734            usage,
735        }
736    }
737}
738
739const MAX_TOTAL_ATTACHMENTS: usize = hal::MAX_COLOR_ATTACHMENTS + hal::MAX_COLOR_ATTACHMENTS + 1;
740type AttachmentDataVec<T> = ArrayVec<T, MAX_TOTAL_ATTACHMENTS>;
741
742struct RenderPassInfo<'a, 'd, A: HalApi> {
743    context: RenderPassContext,
744    usage_scope: UsageScope<'d, A>,
745    /// All render attachments, including depth/stencil
746    render_attachments: AttachmentDataVec<RenderAttachment<'a, A>>,
747    is_depth_read_only: bool,
748    is_stencil_read_only: bool,
749    extent: wgt::Extent3d,
750    _phantom: PhantomData<A>,
751
752    pending_discard_init_fixups: SurfacesInDiscardState<A>,
753    divergent_discarded_depth_stencil_aspect: Option<(wgt::TextureAspect, &'a TextureView<A>)>,
754    multiview: Option<NonZeroU32>,
755}
756
757impl<'a, 'd, A: HalApi> RenderPassInfo<'a, 'd, A> {
758    fn add_pass_texture_init_actions<V>(
759        channel: &PassChannel<V>,
760        texture_memory_actions: &mut CommandBufferTextureMemoryActions<A>,
761        view: &TextureView<A>,
762        pending_discard_init_fixups: &mut SurfacesInDiscardState<A>,
763    ) {
764        if channel.load_op == LoadOp::Load {
765            pending_discard_init_fixups.extend(texture_memory_actions.register_init_action(
766                &TextureInitTrackerAction {
767                    texture: view.parent.clone(),
768                    range: TextureInitRange::from(view.selector.clone()),
769                    // Note that this is needed even if the target is discarded,
770                    kind: MemoryInitKind::NeedsInitializedMemory,
771                },
772            ));
773        } else if channel.store_op == StoreOp::Store {
774            // Clear + Store
775            texture_memory_actions.register_implicit_init(
776                &view.parent,
777                TextureInitRange::from(view.selector.clone()),
778            );
779        }
780        if channel.store_op == StoreOp::Discard {
781            // the discard happens at the *end* of a pass, but recording the
782            // discard right away be alright since the texture can't be used
783            // during the pass anyways
784            texture_memory_actions.discard(TextureSurfaceDiscard {
785                texture: view.parent.clone(),
786                mip_level: view.selector.mips.start,
787                layer: view.selector.layers.start,
788            });
789        }
790    }
791
792    fn start(
793        device: &'d Device<A>,
794        label: Option<&str>,
795        color_attachments: &[Option<RenderPassColorAttachment>],
796        depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>,
797        timestamp_writes: Option<&RenderPassTimestampWrites>,
798        occlusion_query_set: Option<id::QuerySetId>,
799        encoder: &mut CommandEncoder<A>,
800        trackers: &mut Tracker<A>,
801        texture_memory_actions: &mut CommandBufferTextureMemoryActions<A>,
802        pending_query_resets: &mut QueryResetMap<A>,
803        view_guard: &'a Storage<TextureView<A>>,
804        query_set_guard: &'a Storage<QuerySet<A>>,
805        snatch_guard: &SnatchGuard<'a>,
806    ) -> Result<Self, RenderPassErrorInner> {
807        profiling::scope!("RenderPassInfo::start");
808
809        // We default to false intentionally, even if depth-stencil isn't used at all.
810        // This allows us to use the primary raw pipeline in `RenderPipeline`,
811        // instead of the special read-only one, which would be `None`.
812        let mut is_depth_read_only = false;
813        let mut is_stencil_read_only = false;
814
815        let mut render_attachments = AttachmentDataVec::<RenderAttachment<A>>::new();
816        let mut discarded_surfaces = AttachmentDataVec::new();
817        let mut pending_discard_init_fixups = SurfacesInDiscardState::new();
818        let mut divergent_discarded_depth_stencil_aspect = None;
819
820        let mut attachment_location = AttachmentErrorLocation::Color {
821            index: usize::MAX,
822            resolve: false,
823        };
824        let mut extent = None;
825        let mut sample_count = 0;
826
827        let mut detected_multiview: Option<Option<NonZeroU32>> = None;
828
829        let mut check_multiview = |view: &TextureView<A>| {
830            // Get the multiview configuration for this texture view
831            let layers = view.selector.layers.end - view.selector.layers.start;
832            let this_multiview = if layers >= 2 {
833                // Trivially proven by the if above
834                Some(unsafe { NonZeroU32::new_unchecked(layers) })
835            } else {
836                None
837            };
838
839            // Make sure that if this view is a multiview, it is set to be an array
840            if this_multiview.is_some() && view.desc.dimension != TextureViewDimension::D2Array {
841                return Err(RenderPassErrorInner::MultiViewDimensionMismatch);
842            }
843
844            // Validate matching first, or store the first one
845            if let Some(multiview) = detected_multiview {
846                if multiview != this_multiview {
847                    return Err(RenderPassErrorInner::MultiViewMismatch);
848                }
849            } else {
850                // Multiview is only supported if the feature is enabled
851                if this_multiview.is_some() {
852                    device.require_features(wgt::Features::MULTIVIEW)?;
853                }
854
855                detected_multiview = Some(this_multiview);
856            }
857
858            Ok(())
859        };
860        let mut add_view = |view: &TextureView<A>, location| {
861            let render_extent = view.render_extent.map_err(|reason| {
862                RenderPassErrorInner::TextureViewIsNotRenderable { location, reason }
863            })?;
864            if let Some(ex) = extent {
865                if ex != render_extent {
866                    return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
867                        expected_location: attachment_location,
868                        expected_extent: ex,
869                        actual_location: location,
870                        actual_extent: render_extent,
871                    });
872                }
873            } else {
874                extent = Some(render_extent);
875            }
876            if sample_count == 0 {
877                sample_count = view.samples;
878            } else if sample_count != view.samples {
879                return Err(RenderPassErrorInner::AttachmentSampleCountMismatch {
880                    expected_location: attachment_location,
881                    expected_samples: sample_count,
882                    actual_location: location,
883                    actual_samples: view.samples,
884                });
885            }
886            attachment_location = location;
887            Ok(())
888        };
889
890        let mut colors =
891            ArrayVec::<Option<hal::ColorAttachment<A>>, { hal::MAX_COLOR_ATTACHMENTS }>::new();
892        let mut depth_stencil = None;
893
894        if let Some(at) = depth_stencil_attachment {
895            let view: &TextureView<A> = trackers
896                .views
897                .add_single(view_guard, at.view)
898                .ok_or(RenderPassErrorInner::InvalidAttachment(at.view))?;
899            check_multiview(view)?;
900            add_view(view, AttachmentErrorLocation::Depth)?;
901
902            let ds_aspects = view.desc.aspects();
903            if ds_aspects.contains(hal::FormatAspects::COLOR) {
904                return Err(RenderPassErrorInner::InvalidDepthStencilAttachmentFormat(
905                    view.desc.format,
906                ));
907            }
908
909            if !ds_aspects.contains(hal::FormatAspects::STENCIL)
910                || (at.stencil.load_op == at.depth.load_op
911                    && at.stencil.store_op == at.depth.store_op)
912            {
913                Self::add_pass_texture_init_actions(
914                    &at.depth,
915                    texture_memory_actions,
916                    view,
917                    &mut pending_discard_init_fixups,
918                );
919            } else if !ds_aspects.contains(hal::FormatAspects::DEPTH) {
920                Self::add_pass_texture_init_actions(
921                    &at.stencil,
922                    texture_memory_actions,
923                    view,
924                    &mut pending_discard_init_fixups,
925                );
926            } else {
927                // This is the only place (anywhere in wgpu) where Stencil &
928                // Depth init state can diverge.
929                //
930                // To safe us the overhead of tracking init state of texture
931                // aspects everywhere, we're going to cheat a little bit in
932                // order to keep the init state of both Stencil and Depth
933                // aspects in sync. The expectation is that we hit this path
934                // extremely rarely!
935                //
936                // Diverging LoadOp, i.e. Load + Clear:
937                //
938                // Record MemoryInitKind::NeedsInitializedMemory for the entire
939                // surface, a bit wasteful on unit but no negative effect!
940                //
941                // Rationale: If the loaded channel is uninitialized it needs
942                // clearing, the cleared channel doesn't care. (If everything is
943                // already initialized nothing special happens)
944                //
945                // (possible minor optimization: Clear caused by
946                // NeedsInitializedMemory should know that it doesn't need to
947                // clear the aspect that was set to C)
948                let need_init_beforehand =
949                    at.depth.load_op == LoadOp::Load || at.stencil.load_op == LoadOp::Load;
950                if need_init_beforehand {
951                    pending_discard_init_fixups.extend(
952                        texture_memory_actions.register_init_action(&TextureInitTrackerAction {
953                            texture: view.parent.clone(),
954                            range: TextureInitRange::from(view.selector.clone()),
955                            kind: MemoryInitKind::NeedsInitializedMemory,
956                        }),
957                    );
958                }
959
960                // Diverging Store, i.e. Discard + Store:
961                //
962                // Immediately zero out channel that is set to discard after
963                // we're done with the render pass. This allows us to set the
964                // entire surface to MemoryInitKind::ImplicitlyInitialized (if
965                // it isn't already set to NeedsInitializedMemory).
966                //
967                // (possible optimization: Delay and potentially drop this zeroing)
968                if at.depth.store_op != at.stencil.store_op {
969                    if !need_init_beforehand {
970                        texture_memory_actions.register_implicit_init(
971                            &view.parent,
972                            TextureInitRange::from(view.selector.clone()),
973                        );
974                    }
975                    divergent_discarded_depth_stencil_aspect = Some((
976                        if at.depth.store_op == StoreOp::Discard {
977                            wgt::TextureAspect::DepthOnly
978                        } else {
979                            wgt::TextureAspect::StencilOnly
980                        },
981                        view,
982                    ));
983                } else if at.depth.store_op == StoreOp::Discard {
984                    // Both are discarded using the regular path.
985                    discarded_surfaces.push(TextureSurfaceDiscard {
986                        texture: view.parent.clone(),
987                        mip_level: view.selector.mips.start,
988                        layer: view.selector.layers.start,
989                    });
990                }
991            }
992
993            (is_depth_read_only, is_stencil_read_only) = at.depth_stencil_read_only(ds_aspects)?;
994
995            let usage = if is_depth_read_only
996                && is_stencil_read_only
997                && device
998                    .downlevel
999                    .flags
1000                    .contains(wgt::DownlevelFlags::READ_ONLY_DEPTH_STENCIL)
1001            {
1002                hal::TextureUses::DEPTH_STENCIL_READ | hal::TextureUses::RESOURCE
1003            } else {
1004                hal::TextureUses::DEPTH_STENCIL_WRITE
1005            };
1006            render_attachments.push(view.to_render_attachment(usage));
1007
1008            depth_stencil = Some(hal::DepthStencilAttachment {
1009                target: hal::Attachment {
1010                    view: view
1011                        .raw(snatch_guard)
1012                        .ok_or_else(|| RenderPassErrorInner::InvalidAttachment(view.info.id()))?,
1013                    usage,
1014                },
1015                depth_ops: at.depth.hal_ops(),
1016                stencil_ops: at.stencil.hal_ops(),
1017                clear_value: (at.depth.clear_value, at.stencil.clear_value),
1018            });
1019        }
1020
1021        for (index, attachment) in color_attachments.iter().enumerate() {
1022            let at = if let Some(attachment) = attachment.as_ref() {
1023                attachment
1024            } else {
1025                colors.push(None);
1026                continue;
1027            };
1028            let color_view: &TextureView<A> = trackers
1029                .views
1030                .add_single(view_guard, at.view)
1031                .ok_or(RenderPassErrorInner::InvalidAttachment(at.view))?;
1032            check_multiview(color_view)?;
1033            add_view(
1034                color_view,
1035                AttachmentErrorLocation::Color {
1036                    index,
1037                    resolve: false,
1038                },
1039            )?;
1040
1041            if !color_view
1042                .desc
1043                .aspects()
1044                .contains(hal::FormatAspects::COLOR)
1045            {
1046                return Err(RenderPassErrorInner::ColorAttachment(
1047                    ColorAttachmentError::InvalidFormat(color_view.desc.format),
1048                ));
1049            }
1050
1051            Self::add_pass_texture_init_actions(
1052                &at.channel,
1053                texture_memory_actions,
1054                color_view,
1055                &mut pending_discard_init_fixups,
1056            );
1057            render_attachments
1058                .push(color_view.to_render_attachment(hal::TextureUses::COLOR_TARGET));
1059
1060            let mut hal_resolve_target = None;
1061            if let Some(resolve_target) = at.resolve_target {
1062                let resolve_view: &TextureView<A> = trackers
1063                    .views
1064                    .add_single(view_guard, resolve_target)
1065                    .ok_or(RenderPassErrorInner::InvalidAttachment(resolve_target))?;
1066
1067                check_multiview(resolve_view)?;
1068
1069                let resolve_location = AttachmentErrorLocation::Color {
1070                    index,
1071                    resolve: true,
1072                };
1073
1074                let render_extent = resolve_view.render_extent.map_err(|reason| {
1075                    RenderPassErrorInner::TextureViewIsNotRenderable {
1076                        location: resolve_location,
1077                        reason,
1078                    }
1079                })?;
1080                if color_view.render_extent.unwrap() != render_extent {
1081                    return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
1082                        expected_location: attachment_location,
1083                        expected_extent: extent.unwrap_or_default(),
1084                        actual_location: resolve_location,
1085                        actual_extent: render_extent,
1086                    });
1087                }
1088                if color_view.samples == 1 || resolve_view.samples != 1 {
1089                    return Err(RenderPassErrorInner::InvalidResolveSampleCounts {
1090                        location: resolve_location,
1091                        src: color_view.samples,
1092                        dst: resolve_view.samples,
1093                    });
1094                }
1095                if color_view.desc.format != resolve_view.desc.format {
1096                    return Err(RenderPassErrorInner::MismatchedResolveTextureFormat {
1097                        location: resolve_location,
1098                        src: color_view.desc.format,
1099                        dst: resolve_view.desc.format,
1100                    });
1101                }
1102                if !resolve_view
1103                    .format_features
1104                    .flags
1105                    .contains(wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE)
1106                {
1107                    return Err(RenderPassErrorInner::UnsupportedResolveTargetFormat {
1108                        location: resolve_location,
1109                        format: resolve_view.desc.format,
1110                    });
1111                }
1112
1113                texture_memory_actions.register_implicit_init(
1114                    &resolve_view.parent,
1115                    TextureInitRange::from(resolve_view.selector.clone()),
1116                );
1117                render_attachments
1118                    .push(resolve_view.to_render_attachment(hal::TextureUses::COLOR_TARGET));
1119
1120                hal_resolve_target = Some(hal::Attachment {
1121                    view: resolve_view.raw(snatch_guard).ok_or_else(|| {
1122                        RenderPassErrorInner::InvalidResolveTarget(resolve_view.info.id())
1123                    })?,
1124                    usage: hal::TextureUses::COLOR_TARGET,
1125                });
1126            }
1127
1128            colors.push(Some(hal::ColorAttachment {
1129                target: hal::Attachment {
1130                    view: color_view.raw(snatch_guard).ok_or_else(|| {
1131                        RenderPassErrorInner::InvalidAttachment(color_view.info.id())
1132                    })?,
1133                    usage: hal::TextureUses::COLOR_TARGET,
1134                },
1135                resolve_target: hal_resolve_target,
1136                ops: at.channel.hal_ops(),
1137                clear_value: at.channel.clear_value,
1138            }));
1139        }
1140
1141        let extent = extent.ok_or(RenderPassErrorInner::MissingAttachments)?;
1142        let multiview = detected_multiview.expect("Multiview was not detected, no attachments");
1143
1144        let view_data = AttachmentData {
1145            colors: color_attachments
1146                .iter()
1147                .map(|at| at.as_ref().map(|at| view_guard.get(at.view).unwrap()))
1148                .collect(),
1149            resolves: color_attachments
1150                .iter()
1151                .filter_map(|at| match *at {
1152                    Some(RenderPassColorAttachment {
1153                        resolve_target: Some(resolve),
1154                        ..
1155                    }) => Some(view_guard.get(resolve).unwrap()),
1156                    _ => None,
1157                })
1158                .collect(),
1159            depth_stencil: depth_stencil_attachment.map(|at| view_guard.get(at.view).unwrap()),
1160        };
1161
1162        let context = RenderPassContext {
1163            attachments: view_data.map(|view| view.desc.format),
1164            sample_count,
1165            multiview,
1166        };
1167
1168        let timestamp_writes = if let Some(tw) = timestamp_writes {
1169            let query_set = trackers
1170                .query_sets
1171                .add_single(query_set_guard, tw.query_set)
1172                .ok_or(RenderPassErrorInner::InvalidQuerySet(tw.query_set))?;
1173
1174            if let Some(index) = tw.beginning_of_pass_write_index {
1175                pending_query_resets.use_query_set(tw.query_set, query_set, index);
1176            }
1177            if let Some(index) = tw.end_of_pass_write_index {
1178                pending_query_resets.use_query_set(tw.query_set, query_set, index);
1179            }
1180
1181            Some(hal::RenderPassTimestampWrites {
1182                query_set: query_set.raw.as_ref().unwrap(),
1183                beginning_of_pass_write_index: tw.beginning_of_pass_write_index,
1184                end_of_pass_write_index: tw.end_of_pass_write_index,
1185            })
1186        } else {
1187            None
1188        };
1189
1190        let occlusion_query_set = if let Some(occlusion_query_set) = occlusion_query_set {
1191            let query_set = trackers
1192                .query_sets
1193                .add_single(query_set_guard, occlusion_query_set)
1194                .ok_or(RenderPassErrorInner::InvalidQuerySet(occlusion_query_set))?;
1195
1196            Some(query_set.raw.as_ref().unwrap())
1197        } else {
1198            None
1199        };
1200
1201        let hal_desc = hal::RenderPassDescriptor {
1202            label: hal_label(label, device.instance_flags),
1203            extent,
1204            sample_count,
1205            color_attachments: &colors,
1206            depth_stencil_attachment: depth_stencil,
1207            multiview,
1208            timestamp_writes,
1209            occlusion_query_set,
1210        };
1211        unsafe {
1212            encoder.raw.begin_render_pass(&hal_desc);
1213        };
1214
1215        Ok(Self {
1216            context,
1217            usage_scope: device.new_usage_scope(),
1218            render_attachments,
1219            is_depth_read_only,
1220            is_stencil_read_only,
1221            extent,
1222            _phantom: PhantomData,
1223            pending_discard_init_fixups,
1224            divergent_discarded_depth_stencil_aspect,
1225            multiview,
1226        })
1227    }
1228
1229    fn finish(
1230        mut self,
1231        raw: &mut A::CommandEncoder,
1232        snatch_guard: &SnatchGuard,
1233    ) -> Result<(UsageScope<'d, A>, SurfacesInDiscardState<A>), RenderPassErrorInner> {
1234        profiling::scope!("RenderPassInfo::finish");
1235        unsafe {
1236            raw.end_render_pass();
1237        }
1238
1239        for ra in self.render_attachments {
1240            let texture = &ra.texture;
1241            check_texture_usage(texture.desc.usage, TextureUsages::RENDER_ATTACHMENT)?;
1242
1243            // the tracker set of the pass is always in "extend" mode
1244            unsafe {
1245                self.usage_scope
1246                    .textures
1247                    .merge_single(texture, Some(ra.selector.clone()), ra.usage)
1248                    .map_err(UsageConflict::from)?
1249            };
1250        }
1251
1252        // If either only stencil or depth was discarded, we put in a special
1253        // clear pass to keep the init status of the aspects in sync. We do this
1254        // so we don't need to track init state for depth/stencil aspects
1255        // individually.
1256        //
1257        // Note that we don't go the usual route of "brute force" initializing
1258        // the texture when need arises here, since this path is actually
1259        // something a user may genuinely want (where as the other cases are
1260        // more seen along the lines as gracefully handling a user error).
1261        if let Some((aspect, view)) = self.divergent_discarded_depth_stencil_aspect {
1262            let (depth_ops, stencil_ops) = if aspect == wgt::TextureAspect::DepthOnly {
1263                (
1264                    hal::AttachmentOps::STORE,                            // clear depth
1265                    hal::AttachmentOps::LOAD | hal::AttachmentOps::STORE, // unchanged stencil
1266                )
1267            } else {
1268                (
1269                    hal::AttachmentOps::LOAD | hal::AttachmentOps::STORE, // unchanged stencil
1270                    hal::AttachmentOps::STORE,                            // clear depth
1271                )
1272            };
1273            let desc = hal::RenderPassDescriptor {
1274                label: Some("(wgpu internal) Zero init discarded depth/stencil aspect"),
1275                extent: view.render_extent.unwrap(),
1276                sample_count: view.samples,
1277                color_attachments: &[],
1278                depth_stencil_attachment: Some(hal::DepthStencilAttachment {
1279                    target: hal::Attachment {
1280                        view: view.raw(snatch_guard).ok_or_else(|| {
1281                            RenderPassErrorInner::InvalidAttachment(view.info.id())
1282                        })?,
1283                        usage: hal::TextureUses::DEPTH_STENCIL_WRITE,
1284                    },
1285                    depth_ops,
1286                    stencil_ops,
1287                    clear_value: (0.0, 0),
1288                }),
1289                multiview: self.multiview,
1290                timestamp_writes: None,
1291                occlusion_query_set: None,
1292            };
1293            unsafe {
1294                raw.begin_render_pass(&desc);
1295                raw.end_render_pass();
1296            }
1297        }
1298
1299        Ok((self.usage_scope, self.pending_discard_init_fixups))
1300    }
1301}
1302
1303// Common routines between render/compute
1304
1305impl Global {
1306    pub fn command_encoder_run_render_pass<A: HalApi>(
1307        &self,
1308        encoder_id: id::CommandEncoderId,
1309        pass: &RenderPass,
1310    ) -> Result<(), RenderPassError> {
1311        self.command_encoder_run_render_pass_impl::<A>(
1312            encoder_id,
1313            pass.base.as_ref(),
1314            &pass.color_targets,
1315            pass.depth_stencil_target.as_ref(),
1316            pass.timestamp_writes.as_ref(),
1317            pass.occlusion_query_set_id,
1318        )
1319    }
1320
1321    #[doc(hidden)]
1322    pub fn command_encoder_run_render_pass_impl<A: HalApi>(
1323        &self,
1324        encoder_id: id::CommandEncoderId,
1325        base: BasePassRef<RenderCommand>,
1326        color_attachments: &[Option<RenderPassColorAttachment>],
1327        depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>,
1328        timestamp_writes: Option<&RenderPassTimestampWrites>,
1329        occlusion_query_set_id: Option<id::QuerySetId>,
1330    ) -> Result<(), RenderPassError> {
1331        profiling::scope!(
1332            "CommandEncoder::run_render_pass {}",
1333            base.label.unwrap_or("")
1334        );
1335
1336        let discard_hal_labels = self
1337            .instance
1338            .flags
1339            .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS);
1340        let label = hal_label(base.label, self.instance.flags);
1341
1342        let pass_scope = PassErrorScope::Pass(encoder_id);
1343
1344        let hub = A::hub(self);
1345
1346        let cmd_buf: Arc<CommandBuffer<A>> =
1347            CommandBuffer::get_encoder(hub, encoder_id).map_pass_err(pass_scope)?;
1348        let device = &cmd_buf.device;
1349        let snatch_guard = device.snatchable_lock.read();
1350
1351        let (scope, pending_discard_init_fixups) = {
1352            let mut cmd_buf_data = cmd_buf.data.lock();
1353            let cmd_buf_data = cmd_buf_data.as_mut().unwrap();
1354
1355            #[cfg(feature = "trace")]
1356            if let Some(ref mut list) = cmd_buf_data.commands {
1357                list.push(crate::device::trace::Command::RunRenderPass {
1358                    base: BasePass::from_ref(base),
1359                    target_colors: color_attachments.to_vec(),
1360                    target_depth_stencil: depth_stencil_attachment.cloned(),
1361                    timestamp_writes: timestamp_writes.cloned(),
1362                    occlusion_query_set_id,
1363                });
1364            }
1365
1366            if !device.is_valid() {
1367                return Err(DeviceError::Lost).map_pass_err(pass_scope);
1368            }
1369
1370            let encoder = &mut cmd_buf_data.encoder;
1371            let status = &mut cmd_buf_data.status;
1372            let tracker = &mut cmd_buf_data.trackers;
1373            let buffer_memory_init_actions = &mut cmd_buf_data.buffer_memory_init_actions;
1374            let texture_memory_actions = &mut cmd_buf_data.texture_memory_actions;
1375            let pending_query_resets = &mut cmd_buf_data.pending_query_resets;
1376
1377            // We automatically keep extending command buffers over time, and because
1378            // we want to insert a command buffer _before_ what we're about to record,
1379            // we need to make sure to close the previous one.
1380            encoder.close().map_pass_err(pass_scope)?;
1381            // We will reset this to `Recording` if we succeed, acts as a fail-safe.
1382            *status = CommandEncoderStatus::Error;
1383            encoder.open_pass(label).map_pass_err(pass_scope)?;
1384
1385            let bundle_guard = hub.render_bundles.read();
1386            let bind_group_guard = hub.bind_groups.read();
1387            let render_pipeline_guard = hub.render_pipelines.read();
1388            let query_set_guard = hub.query_sets.read();
1389            let buffer_guard = hub.buffers.read();
1390            let view_guard = hub.texture_views.read();
1391
1392            log::trace!(
1393                "Encoding render pass begin in command buffer {:?}",
1394                encoder_id
1395            );
1396
1397            let mut info = RenderPassInfo::start(
1398                device,
1399                label,
1400                color_attachments,
1401                depth_stencil_attachment,
1402                timestamp_writes,
1403                occlusion_query_set_id,
1404                encoder,
1405                tracker,
1406                texture_memory_actions,
1407                pending_query_resets,
1408                &*view_guard,
1409                &*query_set_guard,
1410                &snatch_guard,
1411            )
1412            .map_pass_err(pass_scope)?;
1413
1414            let indices = &device.tracker_indices;
1415            tracker.buffers.set_size(indices.buffers.size());
1416            tracker.textures.set_size(indices.textures.size());
1417            tracker.views.set_size(indices.texture_views.size());
1418            tracker.bind_groups.set_size(indices.bind_groups.size());
1419            tracker
1420                .render_pipelines
1421                .set_size(indices.render_pipelines.size());
1422            tracker.bundles.set_size(indices.bundles.size());
1423            tracker.query_sets.set_size(indices.query_sets.size());
1424
1425            let raw = &mut encoder.raw;
1426
1427            let mut state = State {
1428                pipeline_flags: PipelineFlags::empty(),
1429                binder: Binder::new(),
1430                blend_constant: OptionalState::Unused,
1431                stencil_reference: 0,
1432                pipeline: None,
1433                index: IndexState::default(),
1434                vertex: VertexState::default(),
1435                debug_scope_depth: 0,
1436            };
1437            let mut temp_offsets = Vec::new();
1438            let mut dynamic_offset_count = 0;
1439            let mut string_offset = 0;
1440            let mut active_query = None;
1441
1442            for command in base.commands {
1443                match *command {
1444                    RenderCommand::SetBindGroup {
1445                        index,
1446                        num_dynamic_offsets,
1447                        bind_group_id,
1448                    } => {
1449                        api_log!("RenderPass::set_bind_group {index} {bind_group_id:?}");
1450
1451                        let scope = PassErrorScope::SetBindGroup(bind_group_id);
1452                        let max_bind_groups = device.limits.max_bind_groups;
1453                        if index >= max_bind_groups {
1454                            return Err(RenderCommandError::BindGroupIndexOutOfRange {
1455                                index,
1456                                max: max_bind_groups,
1457                            })
1458                            .map_pass_err(scope);
1459                        }
1460
1461                        temp_offsets.clear();
1462                        temp_offsets.extend_from_slice(
1463                            &base.dynamic_offsets
1464                                [dynamic_offset_count..dynamic_offset_count + num_dynamic_offsets],
1465                        );
1466                        dynamic_offset_count += num_dynamic_offsets;
1467
1468                        let bind_group = tracker
1469                            .bind_groups
1470                            .add_single(&*bind_group_guard, bind_group_id)
1471                            .ok_or(RenderCommandError::InvalidBindGroup(bind_group_id))
1472                            .map_pass_err(scope)?;
1473
1474                        if bind_group.device.as_info().id() != device.as_info().id() {
1475                            return Err(DeviceError::WrongDevice).map_pass_err(scope);
1476                        }
1477
1478                        bind_group
1479                            .validate_dynamic_bindings(index, &temp_offsets, &cmd_buf.limits)
1480                            .map_pass_err(scope)?;
1481
1482                        // merge the resource tracker in
1483                        unsafe {
1484                            info.usage_scope
1485                                .merge_bind_group(&bind_group.used)
1486                                .map_pass_err(scope)?;
1487                        }
1488                        //Note: stateless trackers are not merged: the lifetime reference
1489                        // is held to the bind group itself.
1490
1491                        buffer_memory_init_actions.extend(
1492                            bind_group.used_buffer_ranges.iter().filter_map(|action| {
1493                                action
1494                                    .buffer
1495                                    .initialization_status
1496                                    .read()
1497                                    .check_action(action)
1498                            }),
1499                        );
1500                        for action in bind_group.used_texture_ranges.iter() {
1501                            info.pending_discard_init_fixups
1502                                .extend(texture_memory_actions.register_init_action(action));
1503                        }
1504
1505                        let pipeline_layout = state.binder.pipeline_layout.clone();
1506                        let entries =
1507                            state
1508                                .binder
1509                                .assign_group(index as usize, bind_group, &temp_offsets);
1510                        if !entries.is_empty() && pipeline_layout.is_some() {
1511                            let pipeline_layout = pipeline_layout.as_ref().unwrap().raw();
1512                            for (i, e) in entries.iter().enumerate() {
1513                                if let Some(group) = e.group.as_ref() {
1514                                    let raw_bg = group
1515                                        .raw(&snatch_guard)
1516                                        .ok_or(RenderPassErrorInner::InvalidBindGroup(i))
1517                                        .map_pass_err(scope)?;
1518                                    unsafe {
1519                                        raw.set_bind_group(
1520                                            pipeline_layout,
1521                                            index + i as u32,
1522                                            raw_bg,
1523                                            &e.dynamic_offsets,
1524                                        );
1525                                    }
1526                                }
1527                            }
1528                        }
1529                    }
1530                    RenderCommand::SetPipeline(pipeline_id) => {
1531                        api_log!("RenderPass::set_pipeline {pipeline_id:?}");
1532
1533                        let scope = PassErrorScope::SetPipelineRender(pipeline_id);
1534                        state.pipeline = Some(pipeline_id);
1535
1536                        let pipeline: &pipeline::RenderPipeline<A> = tracker
1537                            .render_pipelines
1538                            .add_single(&*render_pipeline_guard, pipeline_id)
1539                            .ok_or(RenderCommandError::InvalidPipeline(pipeline_id))
1540                            .map_pass_err(scope)?;
1541
1542                        if pipeline.device.as_info().id() != device.as_info().id() {
1543                            return Err(DeviceError::WrongDevice).map_pass_err(scope);
1544                        }
1545
1546                        info.context
1547                            .check_compatible(
1548                                &pipeline.pass_context,
1549                                RenderPassCompatibilityCheckType::RenderPipeline,
1550                            )
1551                            .map_err(RenderCommandError::IncompatiblePipelineTargets)
1552                            .map_pass_err(scope)?;
1553
1554                        state.pipeline_flags = pipeline.flags;
1555
1556                        if (pipeline.flags.contains(PipelineFlags::WRITES_DEPTH)
1557                            && info.is_depth_read_only)
1558                            || (pipeline.flags.contains(PipelineFlags::WRITES_STENCIL)
1559                                && info.is_stencil_read_only)
1560                        {
1561                            return Err(RenderCommandError::IncompatiblePipelineRods)
1562                                .map_pass_err(scope);
1563                        }
1564
1565                        state
1566                            .blend_constant
1567                            .require(pipeline.flags.contains(PipelineFlags::BLEND_CONSTANT));
1568
1569                        unsafe {
1570                            raw.set_render_pipeline(pipeline.raw());
1571                        }
1572
1573                        if pipeline.flags.contains(PipelineFlags::STENCIL_REFERENCE) {
1574                            unsafe {
1575                                raw.set_stencil_reference(state.stencil_reference);
1576                            }
1577                        }
1578
1579                        // Rebind resource
1580                        if state.binder.pipeline_layout.is_none()
1581                            || !state
1582                                .binder
1583                                .pipeline_layout
1584                                .as_ref()
1585                                .unwrap()
1586                                .is_equal(&pipeline.layout)
1587                        {
1588                            let (start_index, entries) = state.binder.change_pipeline_layout(
1589                                &pipeline.layout,
1590                                &pipeline.late_sized_buffer_groups,
1591                            );
1592                            if !entries.is_empty() {
1593                                for (i, e) in entries.iter().enumerate() {
1594                                    if let Some(group) = e.group.as_ref() {
1595                                        let raw_bg = group
1596                                            .raw(&snatch_guard)
1597                                            .ok_or(RenderPassErrorInner::InvalidBindGroup(i))
1598                                            .map_pass_err(scope)?;
1599                                        unsafe {
1600                                            raw.set_bind_group(
1601                                                pipeline.layout.raw(),
1602                                                start_index as u32 + i as u32,
1603                                                raw_bg,
1604                                                &e.dynamic_offsets,
1605                                            );
1606                                        }
1607                                    }
1608                                }
1609                            }
1610
1611                            // Clear push constant ranges
1612                            let non_overlapping = super::bind::compute_nonoverlapping_ranges(
1613                                &pipeline.layout.push_constant_ranges,
1614                            );
1615                            for range in non_overlapping {
1616                                let offset = range.range.start;
1617                                let size_bytes = range.range.end - offset;
1618                                super::push_constant_clear(
1619                                    offset,
1620                                    size_bytes,
1621                                    |clear_offset, clear_data| unsafe {
1622                                        raw.set_push_constants(
1623                                            pipeline.layout.raw(),
1624                                            range.stages,
1625                                            clear_offset,
1626                                            clear_data,
1627                                        );
1628                                    },
1629                                );
1630                            }
1631                        }
1632
1633                        state.index.pipeline_format = pipeline.strip_index_format;
1634
1635                        let vertex_steps_len = pipeline.vertex_steps.len();
1636                        state.vertex.buffers_required = vertex_steps_len as u32;
1637
1638                        // Initialize each `vertex.inputs[i].step` from
1639                        // `pipeline.vertex_steps[i]`.  Enlarge `vertex.inputs`
1640                        // as necessary to accommodate all slots in the
1641                        // pipeline. If `vertex.inputs` is longer, fill the
1642                        // extra entries with default `VertexStep`s.
1643                        while state.vertex.inputs.len() < vertex_steps_len {
1644                            state.vertex.inputs.push(VertexBufferState::EMPTY);
1645                        }
1646
1647                        // This is worse as a `zip`, but it's close.
1648                        let mut steps = pipeline.vertex_steps.iter();
1649                        for input in state.vertex.inputs.iter_mut() {
1650                            input.step = steps.next().cloned().unwrap_or_default();
1651                        }
1652
1653                        // Update vertex buffer limits.
1654                        state.vertex.update_limits();
1655                    }
1656                    RenderCommand::SetIndexBuffer {
1657                        buffer_id,
1658                        index_format,
1659                        offset,
1660                        size,
1661                    } => {
1662                        api_log!("RenderPass::set_index_buffer {buffer_id:?}");
1663
1664                        let scope = PassErrorScope::SetIndexBuffer(buffer_id);
1665                        let buffer = info
1666                            .usage_scope
1667                            .buffers
1668                            .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDEX)
1669                            .map_pass_err(scope)?;
1670
1671                        if buffer.device.as_info().id() != device.as_info().id() {
1672                            return Err(DeviceError::WrongDevice).map_pass_err(scope);
1673                        }
1674
1675                        check_buffer_usage(buffer_id, buffer.usage, BufferUsages::INDEX)
1676                            .map_pass_err(scope)?;
1677                        let buf_raw = buffer
1678                            .raw
1679                            .get(&snatch_guard)
1680                            .ok_or(RenderCommandError::DestroyedBuffer(buffer_id))
1681                            .map_pass_err(scope)?;
1682
1683                        let end = match size {
1684                            Some(s) => offset + s.get(),
1685                            None => buffer.size,
1686                        };
1687                        state.index.bound_buffer_view = Some((buffer_id, offset..end));
1688
1689                        state.index.format = Some(index_format);
1690                        state.index.update_limit();
1691
1692                        buffer_memory_init_actions.extend(
1693                            buffer.initialization_status.read().create_action(
1694                                buffer,
1695                                offset..end,
1696                                MemoryInitKind::NeedsInitializedMemory,
1697                            ),
1698                        );
1699
1700                        let bb = hal::BufferBinding {
1701                            buffer: buf_raw,
1702                            offset,
1703                            size,
1704                        };
1705                        unsafe {
1706                            raw.set_index_buffer(bb, index_format);
1707                        }
1708                    }
1709                    RenderCommand::SetVertexBuffer {
1710                        slot,
1711                        buffer_id,
1712                        offset,
1713                        size,
1714                    } => {
1715                        api_log!("RenderPass::set_vertex_buffer {slot} {buffer_id:?}");
1716
1717                        let scope = PassErrorScope::SetVertexBuffer(buffer_id);
1718                        let buffer = info
1719                            .usage_scope
1720                            .buffers
1721                            .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::VERTEX)
1722                            .map_pass_err(scope)?;
1723
1724                        if buffer.device.as_info().id() != device.as_info().id() {
1725                            return Err(DeviceError::WrongDevice).map_pass_err(scope);
1726                        }
1727
1728                        let max_vertex_buffers = device.limits.max_vertex_buffers;
1729                        if slot >= max_vertex_buffers {
1730                            return Err(RenderCommandError::VertexBufferIndexOutOfRange {
1731                                index: slot,
1732                                max: max_vertex_buffers,
1733                            })
1734                            .map_pass_err(scope);
1735                        }
1736
1737                        check_buffer_usage(buffer_id, buffer.usage, BufferUsages::VERTEX)
1738                            .map_pass_err(scope)?;
1739                        let buf_raw = buffer
1740                            .raw
1741                            .get(&snatch_guard)
1742                            .ok_or(RenderCommandError::DestroyedBuffer(buffer_id))
1743                            .map_pass_err(scope)?;
1744
1745                        let empty_slots =
1746                            (1 + slot as usize).saturating_sub(state.vertex.inputs.len());
1747                        state
1748                            .vertex
1749                            .inputs
1750                            .extend(iter::repeat(VertexBufferState::EMPTY).take(empty_slots));
1751                        let vertex_state = &mut state.vertex.inputs[slot as usize];
1752                        //TODO: where are we checking that the offset is in bound?
1753                        vertex_state.total_size = match size {
1754                            Some(s) => s.get(),
1755                            None => buffer.size - offset,
1756                        };
1757                        vertex_state.bound = true;
1758
1759                        buffer_memory_init_actions.extend(
1760                            buffer.initialization_status.read().create_action(
1761                                buffer,
1762                                offset..(offset + vertex_state.total_size),
1763                                MemoryInitKind::NeedsInitializedMemory,
1764                            ),
1765                        );
1766
1767                        let bb = hal::BufferBinding {
1768                            buffer: buf_raw,
1769                            offset,
1770                            size,
1771                        };
1772                        unsafe {
1773                            raw.set_vertex_buffer(slot, bb);
1774                        }
1775                        state.vertex.update_limits();
1776                    }
1777                    RenderCommand::SetBlendConstant(ref color) => {
1778                        api_log!("RenderPass::set_blend_constant");
1779
1780                        state.blend_constant = OptionalState::Set;
1781                        let array = [
1782                            color.r as f32,
1783                            color.g as f32,
1784                            color.b as f32,
1785                            color.a as f32,
1786                        ];
1787                        unsafe {
1788                            raw.set_blend_constants(&array);
1789                        }
1790                    }
1791                    RenderCommand::SetStencilReference(value) => {
1792                        api_log!("RenderPass::set_stencil_reference {value}");
1793
1794                        state.stencil_reference = value;
1795                        if state
1796                            .pipeline_flags
1797                            .contains(PipelineFlags::STENCIL_REFERENCE)
1798                        {
1799                            unsafe {
1800                                raw.set_stencil_reference(value);
1801                            }
1802                        }
1803                    }
1804                    RenderCommand::SetViewport {
1805                        ref rect,
1806                        depth_min,
1807                        depth_max,
1808                    } => {
1809                        api_log!("RenderPass::set_viewport {rect:?}");
1810
1811                        let scope = PassErrorScope::SetViewport;
1812                        if rect.x < 0.0
1813                            || rect.y < 0.0
1814                            || rect.w <= 0.0
1815                            || rect.h <= 0.0
1816                            || rect.x + rect.w > info.extent.width as f32
1817                            || rect.y + rect.h > info.extent.height as f32
1818                        {
1819                            return Err(RenderCommandError::InvalidViewportRect(
1820                                *rect,
1821                                info.extent,
1822                            ))
1823                            .map_pass_err(scope);
1824                        }
1825                        if !(0.0..=1.0).contains(&depth_min) || !(0.0..=1.0).contains(&depth_max) {
1826                            return Err(RenderCommandError::InvalidViewportDepth(
1827                                depth_min, depth_max,
1828                            ))
1829                            .map_pass_err(scope);
1830                        }
1831                        let r = hal::Rect {
1832                            x: rect.x,
1833                            y: rect.y,
1834                            w: rect.w,
1835                            h: rect.h,
1836                        };
1837                        unsafe {
1838                            raw.set_viewport(&r, depth_min..depth_max);
1839                        }
1840                    }
1841                    RenderCommand::SetPushConstant {
1842                        stages,
1843                        offset,
1844                        size_bytes,
1845                        values_offset,
1846                    } => {
1847                        api_log!("RenderPass::set_push_constants");
1848
1849                        let scope = PassErrorScope::SetPushConstant;
1850                        let values_offset = values_offset
1851                            .ok_or(RenderPassErrorInner::InvalidValuesOffset)
1852                            .map_pass_err(scope)?;
1853
1854                        let end_offset_bytes = offset + size_bytes;
1855                        let values_end_offset =
1856                            (values_offset + size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT) as usize;
1857                        let data_slice =
1858                            &base.push_constant_data[(values_offset as usize)..values_end_offset];
1859
1860                        let pipeline_layout = state
1861                            .binder
1862                            .pipeline_layout
1863                            .as_ref()
1864                            .ok_or(DrawError::MissingPipeline)
1865                            .map_pass_err(scope)?;
1866
1867                        pipeline_layout
1868                            .validate_push_constant_ranges(stages, offset, end_offset_bytes)
1869                            .map_err(RenderCommandError::from)
1870                            .map_pass_err(scope)?;
1871
1872                        unsafe {
1873                            raw.set_push_constants(
1874                                pipeline_layout.raw(),
1875                                stages,
1876                                offset,
1877                                data_slice,
1878                            )
1879                        }
1880                    }
1881                    RenderCommand::SetScissor(ref rect) => {
1882                        api_log!("RenderPass::set_scissor_rect {rect:?}");
1883
1884                        let scope = PassErrorScope::SetScissorRect;
1885                        if rect.x + rect.w > info.extent.width
1886                            || rect.y + rect.h > info.extent.height
1887                        {
1888                            return Err(RenderCommandError::InvalidScissorRect(*rect, info.extent))
1889                                .map_pass_err(scope);
1890                        }
1891                        let r = hal::Rect {
1892                            x: rect.x,
1893                            y: rect.y,
1894                            w: rect.w,
1895                            h: rect.h,
1896                        };
1897                        unsafe {
1898                            raw.set_scissor_rect(&r);
1899                        }
1900                    }
1901                    RenderCommand::Draw {
1902                        vertex_count,
1903                        instance_count,
1904                        first_vertex,
1905                        first_instance,
1906                    } => {
1907                        api_log!(
1908                            "RenderPass::draw {vertex_count} {instance_count} {first_vertex} {first_instance}"
1909                        );
1910
1911                        let indexed = false;
1912                        let scope = PassErrorScope::Draw {
1913                            indexed,
1914                            indirect: false,
1915                            pipeline: state.pipeline,
1916                        };
1917                        state.is_ready(indexed).map_pass_err(scope)?;
1918
1919                        let last_vertex = first_vertex as u64 + vertex_count as u64;
1920                        let vertex_limit = state.vertex.vertex_limit;
1921                        if last_vertex > vertex_limit {
1922                            return Err(DrawError::VertexBeyondLimit {
1923                                last_vertex,
1924                                vertex_limit,
1925                                slot: state.vertex.vertex_limit_slot,
1926                            })
1927                            .map_pass_err(scope);
1928                        }
1929                        let last_instance = first_instance as u64 + instance_count as u64;
1930                        let instance_limit = state.vertex.instance_limit;
1931                        if last_instance > instance_limit {
1932                            return Err(DrawError::InstanceBeyondLimit {
1933                                last_instance,
1934                                instance_limit,
1935                                slot: state.vertex.instance_limit_slot,
1936                            })
1937                            .map_pass_err(scope);
1938                        }
1939
1940                        unsafe {
1941                            if instance_count > 0 && vertex_count > 0 {
1942                                raw.draw(
1943                                    first_vertex,
1944                                    vertex_count,
1945                                    first_instance,
1946                                    instance_count,
1947                                );
1948                            }
1949                        }
1950                    }
1951                    RenderCommand::DrawIndexed {
1952                        index_count,
1953                        instance_count,
1954                        first_index,
1955                        base_vertex,
1956                        first_instance,
1957                    } => {
1958                        api_log!("RenderPass::draw_indexed {index_count} {instance_count} {first_index} {base_vertex} {first_instance}");
1959
1960                        let indexed = true;
1961                        let scope = PassErrorScope::Draw {
1962                            indexed,
1963                            indirect: false,
1964                            pipeline: state.pipeline,
1965                        };
1966                        state.is_ready(indexed).map_pass_err(scope)?;
1967
1968                        let last_index = first_index as u64 + index_count as u64;
1969                        let index_limit = state.index.limit;
1970                        if last_index > index_limit {
1971                            return Err(DrawError::IndexBeyondLimit {
1972                                last_index,
1973                                index_limit,
1974                            })
1975                            .map_pass_err(scope);
1976                        }
1977                        let last_instance = first_instance as u64 + instance_count as u64;
1978                        let instance_limit = state.vertex.instance_limit;
1979                        if last_instance > instance_limit {
1980                            return Err(DrawError::InstanceBeyondLimit {
1981                                last_instance,
1982                                instance_limit,
1983                                slot: state.vertex.instance_limit_slot,
1984                            })
1985                            .map_pass_err(scope);
1986                        }
1987
1988                        unsafe {
1989                            if instance_count > 0 && index_count > 0 {
1990                                raw.draw_indexed(
1991                                    first_index,
1992                                    index_count,
1993                                    base_vertex,
1994                                    first_instance,
1995                                    instance_count,
1996                                );
1997                            }
1998                        }
1999                    }
2000                    RenderCommand::MultiDrawIndirect {
2001                        buffer_id,
2002                        offset,
2003                        count,
2004                        indexed,
2005                    } => {
2006                        api_log!("RenderPass::draw_indirect (indexed:{indexed}) {buffer_id:?} {offset} {count:?}");
2007
2008                        let scope = PassErrorScope::Draw {
2009                            indexed,
2010                            indirect: true,
2011                            pipeline: state.pipeline,
2012                        };
2013                        state.is_ready(indexed).map_pass_err(scope)?;
2014
2015                        let stride = match indexed {
2016                            false => mem::size_of::<wgt::DrawIndirectArgs>(),
2017                            true => mem::size_of::<wgt::DrawIndexedIndirectArgs>(),
2018                        };
2019
2020                        if count.is_some() {
2021                            device
2022                                .require_features(wgt::Features::MULTI_DRAW_INDIRECT)
2023                                .map_pass_err(scope)?;
2024                        }
2025                        device
2026                            .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
2027                            .map_pass_err(scope)?;
2028
2029                        let indirect_buffer = info
2030                            .usage_scope
2031                            .buffers
2032                            .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
2033                            .map_pass_err(scope)?;
2034                        check_buffer_usage(
2035                            buffer_id,
2036                            indirect_buffer.usage,
2037                            BufferUsages::INDIRECT,
2038                        )
2039                        .map_pass_err(scope)?;
2040                        let indirect_raw = indirect_buffer
2041                            .raw
2042                            .get(&snatch_guard)
2043                            .ok_or(RenderCommandError::DestroyedBuffer(buffer_id))
2044                            .map_pass_err(scope)?;
2045
2046                        let actual_count = count.map_or(1, |c| c.get());
2047
2048                        let end_offset = offset + stride as u64 * actual_count as u64;
2049                        if end_offset > indirect_buffer.size {
2050                            return Err(RenderPassErrorInner::IndirectBufferOverrun {
2051                                count,
2052                                offset,
2053                                end_offset,
2054                                buffer_size: indirect_buffer.size,
2055                            })
2056                            .map_pass_err(scope);
2057                        }
2058
2059                        buffer_memory_init_actions.extend(
2060                            indirect_buffer.initialization_status.read().create_action(
2061                                indirect_buffer,
2062                                offset..end_offset,
2063                                MemoryInitKind::NeedsInitializedMemory,
2064                            ),
2065                        );
2066
2067                        match indexed {
2068                            false => unsafe {
2069                                raw.draw_indirect(indirect_raw, offset, actual_count);
2070                            },
2071                            true => unsafe {
2072                                raw.draw_indexed_indirect(indirect_raw, offset, actual_count);
2073                            },
2074                        }
2075                    }
2076                    RenderCommand::MultiDrawIndirectCount {
2077                        buffer_id,
2078                        offset,
2079                        count_buffer_id,
2080                        count_buffer_offset,
2081                        max_count,
2082                        indexed,
2083                    } => {
2084                        api_log!("RenderPass::multi_draw_indirect_count (indexed:{indexed}) {buffer_id:?} {offset} {count_buffer_id:?} {count_buffer_offset:?} {max_count:?}");
2085
2086                        let scope = PassErrorScope::Draw {
2087                            indexed,
2088                            indirect: true,
2089                            pipeline: state.pipeline,
2090                        };
2091                        state.is_ready(indexed).map_pass_err(scope)?;
2092
2093                        let stride = match indexed {
2094                            false => mem::size_of::<wgt::DrawIndirectArgs>(),
2095                            true => mem::size_of::<wgt::DrawIndexedIndirectArgs>(),
2096                        } as u64;
2097
2098                        device
2099                            .require_features(wgt::Features::MULTI_DRAW_INDIRECT_COUNT)
2100                            .map_pass_err(scope)?;
2101                        device
2102                            .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
2103                            .map_pass_err(scope)?;
2104
2105                        let indirect_buffer = info
2106                            .usage_scope
2107                            .buffers
2108                            .merge_single(&*buffer_guard, buffer_id, hal::BufferUses::INDIRECT)
2109                            .map_pass_err(scope)?;
2110                        check_buffer_usage(
2111                            buffer_id,
2112                            indirect_buffer.usage,
2113                            BufferUsages::INDIRECT,
2114                        )
2115                        .map_pass_err(scope)?;
2116                        let indirect_raw = indirect_buffer
2117                            .raw
2118                            .get(&snatch_guard)
2119                            .ok_or(RenderCommandError::DestroyedBuffer(buffer_id))
2120                            .map_pass_err(scope)?;
2121
2122                        let count_buffer = info
2123                            .usage_scope
2124                            .buffers
2125                            .merge_single(
2126                                &*buffer_guard,
2127                                count_buffer_id,
2128                                hal::BufferUses::INDIRECT,
2129                            )
2130                            .map_pass_err(scope)?;
2131                        check_buffer_usage(buffer_id, count_buffer.usage, BufferUsages::INDIRECT)
2132                            .map_pass_err(scope)?;
2133                        let count_raw = count_buffer
2134                            .raw
2135                            .get(&snatch_guard)
2136                            .ok_or(RenderCommandError::DestroyedBuffer(count_buffer_id))
2137                            .map_pass_err(scope)?;
2138
2139                        let end_offset = offset + stride * max_count as u64;
2140                        if end_offset > indirect_buffer.size {
2141                            return Err(RenderPassErrorInner::IndirectBufferOverrun {
2142                                count: None,
2143                                offset,
2144                                end_offset,
2145                                buffer_size: indirect_buffer.size,
2146                            })
2147                            .map_pass_err(scope);
2148                        }
2149                        buffer_memory_init_actions.extend(
2150                            indirect_buffer.initialization_status.read().create_action(
2151                                indirect_buffer,
2152                                offset..end_offset,
2153                                MemoryInitKind::NeedsInitializedMemory,
2154                            ),
2155                        );
2156
2157                        let begin_count_offset = count_buffer_offset;
2158                        let end_count_offset = count_buffer_offset + 4;
2159                        if end_count_offset > count_buffer.size {
2160                            return Err(RenderPassErrorInner::IndirectCountBufferOverrun {
2161                                begin_count_offset,
2162                                end_count_offset,
2163                                count_buffer_size: count_buffer.size,
2164                            })
2165                            .map_pass_err(scope);
2166                        }
2167                        buffer_memory_init_actions.extend(
2168                            count_buffer.initialization_status.read().create_action(
2169                                count_buffer,
2170                                count_buffer_offset..end_count_offset,
2171                                MemoryInitKind::NeedsInitializedMemory,
2172                            ),
2173                        );
2174
2175                        match indexed {
2176                            false => unsafe {
2177                                raw.draw_indirect_count(
2178                                    indirect_raw,
2179                                    offset,
2180                                    count_raw,
2181                                    count_buffer_offset,
2182                                    max_count,
2183                                );
2184                            },
2185                            true => unsafe {
2186                                raw.draw_indexed_indirect_count(
2187                                    indirect_raw,
2188                                    offset,
2189                                    count_raw,
2190                                    count_buffer_offset,
2191                                    max_count,
2192                                );
2193                            },
2194                        }
2195                    }
2196                    RenderCommand::PushDebugGroup { color: _, len } => {
2197                        state.debug_scope_depth += 1;
2198                        if !discard_hal_labels {
2199                            let label = str::from_utf8(
2200                                &base.string_data[string_offset..string_offset + len],
2201                            )
2202                            .unwrap();
2203
2204                            api_log!("RenderPass::push_debug_group {label:?}");
2205                            unsafe {
2206                                raw.begin_debug_marker(label);
2207                            }
2208                        }
2209                        string_offset += len;
2210                    }
2211                    RenderCommand::PopDebugGroup => {
2212                        api_log!("RenderPass::pop_debug_group");
2213
2214                        let scope = PassErrorScope::PopDebugGroup;
2215                        if state.debug_scope_depth == 0 {
2216                            return Err(RenderPassErrorInner::InvalidPopDebugGroup)
2217                                .map_pass_err(scope);
2218                        }
2219                        state.debug_scope_depth -= 1;
2220                        if !discard_hal_labels {
2221                            unsafe {
2222                                raw.end_debug_marker();
2223                            }
2224                        }
2225                    }
2226                    RenderCommand::InsertDebugMarker { color: _, len } => {
2227                        if !discard_hal_labels {
2228                            let label = str::from_utf8(
2229                                &base.string_data[string_offset..string_offset + len],
2230                            )
2231                            .unwrap();
2232                            api_log!("RenderPass::insert_debug_marker {label:?}");
2233                            unsafe {
2234                                raw.insert_debug_marker(label);
2235                            }
2236                        }
2237                        string_offset += len;
2238                    }
2239                    RenderCommand::WriteTimestamp {
2240                        query_set_id,
2241                        query_index,
2242                    } => {
2243                        api_log!("RenderPass::write_timestamps {query_set_id:?} {query_index}");
2244                        let scope = PassErrorScope::WriteTimestamp;
2245
2246                        device
2247                            .require_features(wgt::Features::TIMESTAMP_QUERY_INSIDE_PASSES)
2248                            .map_pass_err(scope)?;
2249
2250                        let query_set = tracker
2251                            .query_sets
2252                            .add_single(&*query_set_guard, query_set_id)
2253                            .ok_or(RenderCommandError::InvalidQuerySet(query_set_id))
2254                            .map_pass_err(scope)?;
2255
2256                        query_set
2257                            .validate_and_write_timestamp(
2258                                raw,
2259                                query_set_id,
2260                                query_index,
2261                                Some(&mut cmd_buf_data.pending_query_resets),
2262                            )
2263                            .map_pass_err(scope)?;
2264                    }
2265                    RenderCommand::BeginOcclusionQuery { query_index } => {
2266                        api_log!("RenderPass::begin_occlusion_query {query_index}");
2267                        let scope = PassErrorScope::BeginOcclusionQuery;
2268
2269                        let query_set_id = occlusion_query_set_id
2270                            .ok_or(RenderPassErrorInner::MissingOcclusionQuerySet)
2271                            .map_pass_err(scope)?;
2272
2273                        let query_set = tracker
2274                            .query_sets
2275                            .add_single(&*query_set_guard, query_set_id)
2276                            .ok_or(RenderCommandError::InvalidQuerySet(query_set_id))
2277                            .map_pass_err(scope)?;
2278
2279                        query_set
2280                            .validate_and_begin_occlusion_query(
2281                                raw,
2282                                query_set_id,
2283                                query_index,
2284                                Some(&mut cmd_buf_data.pending_query_resets),
2285                                &mut active_query,
2286                            )
2287                            .map_pass_err(scope)?;
2288                    }
2289                    RenderCommand::EndOcclusionQuery => {
2290                        api_log!("RenderPass::end_occlusion_query");
2291                        let scope = PassErrorScope::EndOcclusionQuery;
2292
2293                        end_occlusion_query(raw, &*query_set_guard, &mut active_query)
2294                            .map_pass_err(scope)?;
2295                    }
2296                    RenderCommand::BeginPipelineStatisticsQuery {
2297                        query_set_id,
2298                        query_index,
2299                    } => {
2300                        api_log!("RenderPass::begin_pipeline_statistics_query {query_set_id:?} {query_index}");
2301                        let scope = PassErrorScope::BeginPipelineStatisticsQuery;
2302
2303                        let query_set = tracker
2304                            .query_sets
2305                            .add_single(&*query_set_guard, query_set_id)
2306                            .ok_or(RenderCommandError::InvalidQuerySet(query_set_id))
2307                            .map_pass_err(scope)?;
2308
2309                        query_set
2310                            .validate_and_begin_pipeline_statistics_query(
2311                                raw,
2312                                query_set_id,
2313                                query_index,
2314                                Some(&mut cmd_buf_data.pending_query_resets),
2315                                &mut active_query,
2316                            )
2317                            .map_pass_err(scope)?;
2318                    }
2319                    RenderCommand::EndPipelineStatisticsQuery => {
2320                        api_log!("RenderPass::end_pipeline_statistics_query");
2321                        let scope = PassErrorScope::EndPipelineStatisticsQuery;
2322
2323                        end_pipeline_statistics_query(raw, &*query_set_guard, &mut active_query)
2324                            .map_pass_err(scope)?;
2325                    }
2326                    RenderCommand::ExecuteBundle(bundle_id) => {
2327                        api_log!("RenderPass::execute_bundle {bundle_id:?}");
2328                        let scope = PassErrorScope::ExecuteBundle;
2329                        let bundle: &command::RenderBundle<A> = tracker
2330                            .bundles
2331                            .add_single(&*bundle_guard, bundle_id)
2332                            .ok_or(RenderCommandError::InvalidRenderBundle(bundle_id))
2333                            .map_pass_err(scope)?;
2334
2335                        if bundle.device.as_info().id() != device.as_info().id() {
2336                            return Err(DeviceError::WrongDevice).map_pass_err(scope);
2337                        }
2338
2339                        info.context
2340                            .check_compatible(
2341                                &bundle.context,
2342                                RenderPassCompatibilityCheckType::RenderBundle,
2343                            )
2344                            .map_err(RenderPassErrorInner::IncompatibleBundleTargets)
2345                            .map_pass_err(scope)?;
2346
2347                        if (info.is_depth_read_only && !bundle.is_depth_read_only)
2348                            || (info.is_stencil_read_only && !bundle.is_stencil_read_only)
2349                        {
2350                            return Err(
2351                                RenderPassErrorInner::IncompatibleBundleReadOnlyDepthStencil {
2352                                    pass_depth: info.is_depth_read_only,
2353                                    pass_stencil: info.is_stencil_read_only,
2354                                    bundle_depth: bundle.is_depth_read_only,
2355                                    bundle_stencil: bundle.is_stencil_read_only,
2356                                },
2357                            )
2358                            .map_pass_err(scope);
2359                        }
2360
2361                        buffer_memory_init_actions.extend(
2362                            bundle
2363                                .buffer_memory_init_actions
2364                                .iter()
2365                                .filter_map(|action| {
2366                                    action
2367                                        .buffer
2368                                        .initialization_status
2369                                        .read()
2370                                        .check_action(action)
2371                                }),
2372                        );
2373                        for action in bundle.texture_memory_init_actions.iter() {
2374                            info.pending_discard_init_fixups
2375                                .extend(texture_memory_actions.register_init_action(action));
2376                        }
2377
2378                        unsafe { bundle.execute(raw, &snatch_guard) }
2379                            .map_err(|e| match e {
2380                                ExecutionError::DestroyedBuffer(id) => {
2381                                    RenderCommandError::DestroyedBuffer(id)
2382                                }
2383                                ExecutionError::InvalidBindGroup(id) => {
2384                                    RenderCommandError::InvalidBindGroup(id)
2385                                }
2386                                ExecutionError::Unimplemented(what) => {
2387                                    RenderCommandError::Unimplemented(what)
2388                                }
2389                            })
2390                            .map_pass_err(scope)?;
2391
2392                        unsafe {
2393                            info.usage_scope
2394                                .merge_render_bundle(&bundle.used)
2395                                .map_pass_err(scope)?;
2396                            tracker
2397                                .add_from_render_bundle(&bundle.used)
2398                                .map_pass_err(scope)?;
2399                        };
2400                        state.reset_bundle();
2401                    }
2402                }
2403            }
2404
2405            log::trace!("Merging renderpass into cmd_buf {:?}", encoder_id);
2406            let (trackers, pending_discard_init_fixups) =
2407                info.finish(raw, &snatch_guard).map_pass_err(pass_scope)?;
2408
2409            encoder.close().map_pass_err(pass_scope)?;
2410            (trackers, pending_discard_init_fixups)
2411        };
2412
2413        let cmd_buf = hub
2414            .command_buffers
2415            .get(encoder_id.into_command_buffer_id())
2416            .unwrap();
2417        let mut cmd_buf_data = cmd_buf.data.lock();
2418        let cmd_buf_data = cmd_buf_data.as_mut().unwrap();
2419
2420        let query_set_guard = hub.query_sets.read();
2421
2422        let encoder = &mut cmd_buf_data.encoder;
2423        let status = &mut cmd_buf_data.status;
2424        let tracker = &mut cmd_buf_data.trackers;
2425
2426        {
2427            let transit = encoder.open().map_pass_err(pass_scope)?;
2428
2429            fixup_discarded_surfaces(
2430                pending_discard_init_fixups.into_iter(),
2431                transit,
2432                &mut tracker.textures,
2433                &cmd_buf.device,
2434                &snatch_guard,
2435            );
2436
2437            cmd_buf_data
2438                .pending_query_resets
2439                .reset_queries(
2440                    transit,
2441                    &query_set_guard,
2442                    cmd_buf.device.info.id().backend(),
2443                )
2444                .map_err(RenderCommandError::InvalidQuerySet)
2445                .map_pass_err(PassErrorScope::QueryReset)?;
2446
2447            super::CommandBuffer::insert_barriers_from_scope(
2448                transit,
2449                tracker,
2450                &scope,
2451                &snatch_guard,
2452            );
2453        }
2454
2455        *status = CommandEncoderStatus::Recording;
2456        encoder.close_and_swap().map_pass_err(pass_scope)?;
2457
2458        Ok(())
2459    }
2460}
2461
2462pub mod render_commands {
2463    use super::{
2464        super::{Rect, RenderCommand},
2465        RenderPass,
2466    };
2467    use crate::id;
2468    use std::{convert::TryInto, num::NonZeroU32};
2469    use wgt::{BufferAddress, BufferSize, Color, DynamicOffset, IndexFormat};
2470
2471    /// # Safety
2472    ///
2473    /// This function is unsafe as there is no guarantee that the given pointer is
2474    /// valid for `offset_length` elements.
2475    pub fn wgpu_render_pass_set_bind_group(
2476        pass: &mut RenderPass,
2477        index: u32,
2478        bind_group_id: id::BindGroupId,
2479        offsets: &[DynamicOffset],
2480    ) {
2481        let redundant = pass.current_bind_groups.set_and_check_redundant(
2482            bind_group_id,
2483            index,
2484            &mut pass.base.dynamic_offsets,
2485            offsets,
2486        );
2487
2488        if redundant {
2489            return;
2490        }
2491
2492        pass.base.commands.push(RenderCommand::SetBindGroup {
2493            index,
2494            num_dynamic_offsets: offsets.len(),
2495            bind_group_id,
2496        });
2497    }
2498
2499    pub fn wgpu_render_pass_set_pipeline(pass: &mut RenderPass, pipeline_id: id::RenderPipelineId) {
2500        if pass.current_pipeline.set_and_check_redundant(pipeline_id) {
2501            return;
2502        }
2503
2504        pass.base
2505            .commands
2506            .push(RenderCommand::SetPipeline(pipeline_id));
2507    }
2508
2509    pub fn wgpu_render_pass_set_vertex_buffer(
2510        pass: &mut RenderPass,
2511        slot: u32,
2512        buffer_id: id::BufferId,
2513        offset: BufferAddress,
2514        size: Option<BufferSize>,
2515    ) {
2516        pass.base.commands.push(RenderCommand::SetVertexBuffer {
2517            slot,
2518            buffer_id,
2519            offset,
2520            size,
2521        });
2522    }
2523
2524    pub fn wgpu_render_pass_set_index_buffer(
2525        pass: &mut RenderPass,
2526        buffer: id::BufferId,
2527        index_format: IndexFormat,
2528        offset: BufferAddress,
2529        size: Option<BufferSize>,
2530    ) {
2531        pass.set_index_buffer(buffer, index_format, offset, size);
2532    }
2533
2534    pub fn wgpu_render_pass_set_blend_constant(pass: &mut RenderPass, color: &Color) {
2535        pass.base
2536            .commands
2537            .push(RenderCommand::SetBlendConstant(*color));
2538    }
2539
2540    pub fn wgpu_render_pass_set_stencil_reference(pass: &mut RenderPass, value: u32) {
2541        pass.base
2542            .commands
2543            .push(RenderCommand::SetStencilReference(value));
2544    }
2545
2546    pub fn wgpu_render_pass_set_viewport(
2547        pass: &mut RenderPass,
2548        x: f32,
2549        y: f32,
2550        w: f32,
2551        h: f32,
2552        depth_min: f32,
2553        depth_max: f32,
2554    ) {
2555        pass.base.commands.push(RenderCommand::SetViewport {
2556            rect: Rect { x, y, w, h },
2557            depth_min,
2558            depth_max,
2559        });
2560    }
2561
2562    pub fn wgpu_render_pass_set_scissor_rect(
2563        pass: &mut RenderPass,
2564        x: u32,
2565        y: u32,
2566        w: u32,
2567        h: u32,
2568    ) {
2569        pass.base
2570            .commands
2571            .push(RenderCommand::SetScissor(Rect { x, y, w, h }));
2572    }
2573
2574    /// # Safety
2575    ///
2576    /// This function is unsafe as there is no guarantee that the given pointer is
2577    /// valid for `size_bytes` bytes.
2578    pub fn wgpu_render_pass_set_push_constants(
2579        pass: &mut RenderPass,
2580        stages: wgt::ShaderStages,
2581        offset: u32,
2582        data: &[u8],
2583    ) {
2584        assert_eq!(
2585            offset & (wgt::PUSH_CONSTANT_ALIGNMENT - 1),
2586            0,
2587            "Push constant offset must be aligned to 4 bytes."
2588        );
2589        assert_eq!(
2590            data.len() as u32 & (wgt::PUSH_CONSTANT_ALIGNMENT - 1),
2591            0,
2592            "Push constant size must be aligned to 4 bytes."
2593        );
2594        let value_offset = pass.base.push_constant_data.len().try_into().expect(
2595            "Ran out of push constant space. Don't set 4gb of push constants per RenderPass.",
2596        );
2597
2598        pass.base.push_constant_data.extend(
2599            data.chunks_exact(wgt::PUSH_CONSTANT_ALIGNMENT as usize)
2600                .map(|arr| u32::from_ne_bytes([arr[0], arr[1], arr[2], arr[3]])),
2601        );
2602
2603        pass.base.commands.push(RenderCommand::SetPushConstant {
2604            stages,
2605            offset,
2606            size_bytes: data.len() as u32,
2607            values_offset: Some(value_offset),
2608        });
2609    }
2610
2611    pub fn wgpu_render_pass_draw(
2612        pass: &mut RenderPass,
2613        vertex_count: u32,
2614        instance_count: u32,
2615        first_vertex: u32,
2616        first_instance: u32,
2617    ) {
2618        pass.base.commands.push(RenderCommand::Draw {
2619            vertex_count,
2620            instance_count,
2621            first_vertex,
2622            first_instance,
2623        });
2624    }
2625
2626    pub fn wgpu_render_pass_draw_indexed(
2627        pass: &mut RenderPass,
2628        index_count: u32,
2629        instance_count: u32,
2630        first_index: u32,
2631        base_vertex: i32,
2632        first_instance: u32,
2633    ) {
2634        pass.base.commands.push(RenderCommand::DrawIndexed {
2635            index_count,
2636            instance_count,
2637            first_index,
2638            base_vertex,
2639            first_instance,
2640        });
2641    }
2642
2643    pub fn wgpu_render_pass_draw_indirect(
2644        pass: &mut RenderPass,
2645        buffer_id: id::BufferId,
2646        offset: BufferAddress,
2647    ) {
2648        pass.base.commands.push(RenderCommand::MultiDrawIndirect {
2649            buffer_id,
2650            offset,
2651            count: None,
2652            indexed: false,
2653        });
2654    }
2655
2656    pub fn wgpu_render_pass_draw_indexed_indirect(
2657        pass: &mut RenderPass,
2658        buffer_id: id::BufferId,
2659        offset: BufferAddress,
2660    ) {
2661        pass.base.commands.push(RenderCommand::MultiDrawIndirect {
2662            buffer_id,
2663            offset,
2664            count: None,
2665            indexed: true,
2666        });
2667    }
2668
2669    pub fn wgpu_render_pass_multi_draw_indirect(
2670        pass: &mut RenderPass,
2671        buffer_id: id::BufferId,
2672        offset: BufferAddress,
2673        count: u32,
2674    ) {
2675        pass.base.commands.push(RenderCommand::MultiDrawIndirect {
2676            buffer_id,
2677            offset,
2678            count: NonZeroU32::new(count),
2679            indexed: false,
2680        });
2681    }
2682
2683    pub fn wgpu_render_pass_multi_draw_indexed_indirect(
2684        pass: &mut RenderPass,
2685        buffer_id: id::BufferId,
2686        offset: BufferAddress,
2687        count: u32,
2688    ) {
2689        pass.base.commands.push(RenderCommand::MultiDrawIndirect {
2690            buffer_id,
2691            offset,
2692            count: NonZeroU32::new(count),
2693            indexed: true,
2694        });
2695    }
2696
2697    pub fn wgpu_render_pass_multi_draw_indirect_count(
2698        pass: &mut RenderPass,
2699        buffer_id: id::BufferId,
2700        offset: BufferAddress,
2701        count_buffer_id: id::BufferId,
2702        count_buffer_offset: BufferAddress,
2703        max_count: u32,
2704    ) {
2705        pass.base
2706            .commands
2707            .push(RenderCommand::MultiDrawIndirectCount {
2708                buffer_id,
2709                offset,
2710                count_buffer_id,
2711                count_buffer_offset,
2712                max_count,
2713                indexed: false,
2714            });
2715    }
2716
2717    pub fn wgpu_render_pass_multi_draw_indexed_indirect_count(
2718        pass: &mut RenderPass,
2719        buffer_id: id::BufferId,
2720        offset: BufferAddress,
2721        count_buffer_id: id::BufferId,
2722        count_buffer_offset: BufferAddress,
2723        max_count: u32,
2724    ) {
2725        pass.base
2726            .commands
2727            .push(RenderCommand::MultiDrawIndirectCount {
2728                buffer_id,
2729                offset,
2730                count_buffer_id,
2731                count_buffer_offset,
2732                max_count,
2733                indexed: true,
2734            });
2735    }
2736
2737    pub fn wgpu_render_pass_push_debug_group(pass: &mut RenderPass, label: &str, color: u32) {
2738        let bytes = label.as_bytes();
2739        pass.base.string_data.extend_from_slice(bytes);
2740
2741        pass.base.commands.push(RenderCommand::PushDebugGroup {
2742            color,
2743            len: bytes.len(),
2744        });
2745    }
2746
2747    pub fn wgpu_render_pass_pop_debug_group(pass: &mut RenderPass) {
2748        pass.base.commands.push(RenderCommand::PopDebugGroup);
2749    }
2750
2751    pub fn wgpu_render_pass_insert_debug_marker(pass: &mut RenderPass, label: &str, color: u32) {
2752        let bytes = label.as_bytes();
2753        pass.base.string_data.extend_from_slice(bytes);
2754
2755        pass.base.commands.push(RenderCommand::InsertDebugMarker {
2756            color,
2757            len: bytes.len(),
2758        });
2759    }
2760
2761    pub fn wgpu_render_pass_write_timestamp(
2762        pass: &mut RenderPass,
2763        query_set_id: id::QuerySetId,
2764        query_index: u32,
2765    ) {
2766        pass.base.commands.push(RenderCommand::WriteTimestamp {
2767            query_set_id,
2768            query_index,
2769        });
2770    }
2771
2772    pub fn wgpu_render_pass_begin_occlusion_query(pass: &mut RenderPass, query_index: u32) {
2773        pass.base
2774            .commands
2775            .push(RenderCommand::BeginOcclusionQuery { query_index });
2776    }
2777
2778    pub fn wgpu_render_pass_end_occlusion_query(pass: &mut RenderPass) {
2779        pass.base.commands.push(RenderCommand::EndOcclusionQuery);
2780    }
2781
2782    pub fn wgpu_render_pass_begin_pipeline_statistics_query(
2783        pass: &mut RenderPass,
2784        query_set_id: id::QuerySetId,
2785        query_index: u32,
2786    ) {
2787        pass.base
2788            .commands
2789            .push(RenderCommand::BeginPipelineStatisticsQuery {
2790                query_set_id,
2791                query_index,
2792            });
2793    }
2794
2795    pub fn wgpu_render_pass_end_pipeline_statistics_query(pass: &mut RenderPass) {
2796        pass.base
2797            .commands
2798            .push(RenderCommand::EndPipelineStatisticsQuery);
2799    }
2800
2801    pub fn wgpu_render_pass_execute_bundles(
2802        pass: &mut RenderPass,
2803        render_bundle_ids: &[id::RenderBundleId],
2804    ) {
2805        for &bundle_id in render_bundle_ids {
2806            pass.base
2807                .commands
2808                .push(RenderCommand::ExecuteBundle(bundle_id));
2809        }
2810        pass.current_pipeline.reset();
2811        pass.current_bind_groups.reset();
2812    }
2813}