li_wgpu_core/command/
render.rs

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