wgpu_core/command/
render.rs

1use alloc::{borrow::Cow, boxed::Box, sync::Arc, vec::Vec};
2use core::{convert::Infallible, fmt, num::NonZeroU32, ops::Range, str};
3use parking_lot::Mutex;
4use smallvec::SmallVec;
5
6use arrayvec::ArrayVec;
7use thiserror::Error;
8use wgt::{
9    error::{ErrorType, WebGpuError},
10    BufferAddress, BufferSize, BufferUsages, Color, DynamicOffset, IndexFormat, InstanceFlags,
11    TextureSelector, TextureUsages, TextureViewDimension, VertexStepMode,
12};
13
14use crate::{
15    api_log,
16    binding_model::{BindError, BindGroup, ImmediateUploadError},
17    command::{
18        bind::Binder,
19        memory_init::{fixup_discarded_surfaces, SurfacesInDiscardState, TextureSurfaceDiscard},
20        pass::{self, flush_bindings_helper, ImmediateState},
21        pass_base, pass_try,
22        query::{
23            end_occlusion_query, end_pipeline_statistics_query, record_pass_timestamp_writes,
24            validate_and_begin_occlusion_query, validate_and_begin_pipeline_statistics_query,
25            QueryResetMap, QuerySetWrites,
26        },
27        render_command::ArcRenderCommand,
28        ArcCommand, BasePass, BindGroupStateChange, CommandBufferTextureMemoryActions,
29        CommandEncoder, CommandEncoderError, DebugGroupError, DrawCommandFamily, DrawError,
30        DrawKind, EncoderStateError, EncodingState, ExecutionError, InnerCommandEncoder,
31        MapPassErr, PassErrorScope, PassStateError, PassTimestampWrites, QueryUseError, Rect,
32        RenderBundle, RenderCommandError, StateChange, TimestampWritesError,
33    },
34    device::{
35        AttachmentData, Device, DeviceError, MissingDownlevelFlags, MissingFeatures,
36        RenderPassCompatibilityError, RenderPassContext,
37    },
38    global::Global,
39    hal_label, id, impl_resource_type,
40    init_tracker::{MemoryInitKind, TextureInitRange, TextureInitTrackerAction},
41    pipeline::{PipelineFlags, RenderPipeline, VertexStep},
42    resource::{
43        Buffer, DestroyedResourceError, InvalidOrDestroyedResourceError, InvalidResourceError,
44        Labeled, MissingBufferUsageError, MissingTextureUsageError, ParentDevice, QuerySet,
45        RawResourceAccess, ResourceErrorIdent, Texture, TextureView,
46        TextureViewNotRenderableReason,
47    },
48    snatch::SnatchGuard,
49    track::{ResourceUsageCompatibilityError, Tracker, UsageScope},
50    validation::{self, WorkgroupSizeCheck},
51    Label,
52};
53
54#[cfg(feature = "serde")]
55use serde::Deserialize;
56#[cfg(feature = "serde")]
57use serde::Serialize;
58
59pub use wgt::{LoadOp, StoreOp};
60
61fn load_hal_ops<V>(load: LoadOp<V>) -> hal::AttachmentOps {
62    match load {
63        LoadOp::Load => hal::AttachmentOps::LOAD,
64        LoadOp::Clear(_) => hal::AttachmentOps::LOAD_CLEAR,
65        LoadOp::DontCare(_) => hal::AttachmentOps::LOAD_DONT_CARE,
66    }
67}
68
69fn store_hal_ops(store: StoreOp) -> hal::AttachmentOps {
70    match store {
71        StoreOp::Store => hal::AttachmentOps::STORE,
72        StoreOp::Discard => hal::AttachmentOps::STORE_DISCARD,
73    }
74}
75
76// Stencil clear and reference value should take the LSBs.
77fn convert_stencil_value(value: u32, format: Option<wgt::TextureFormat>) -> u32 {
78    let Some(format) = format else {
79        return value;
80    };
81    let Some(stencil_format) = format.aspect_specific_format(wgt::TextureAspect::StencilOnly)
82    else {
83        return value;
84    };
85    // Currently only 8-bit stencil formats are supported
86    assert_eq!(stencil_format, wgt::TextureFormat::Stencil8);
87    value & 255
88}
89
90/// Describes an individual channel within a render pass, such as color, depth, or stencil.
91///
92/// A channel must either be read-only, or it must specify both load and store
93/// operations. See [`ResolvedPassChannel`] for a validated version.
94#[repr(C)]
95#[derive(Clone, Debug, Eq, PartialEq)]
96#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
97pub struct PassChannel<V> {
98    /// Operation to perform to the output attachment at the start of a
99    /// renderpass.
100    ///
101    /// This must be clear if it is the first renderpass rendering to a swap
102    /// chain image.
103    pub load_op: Option<LoadOp<V>>,
104    /// Operation to perform to the output attachment at the end of a renderpass.
105    pub store_op: Option<StoreOp>,
106    /// If true, the relevant channel is not changed by a renderpass, and the
107    /// corresponding attachment can be used inside the pass by other read-only
108    /// usages.
109    pub read_only: bool,
110}
111
112impl<V: Copy + Default> PassChannel<Option<V>> {
113    fn resolve(
114        &self,
115        instance_flags: InstanceFlags,
116        handle_clear: impl Fn(Option<V>) -> Result<V, AttachmentError>,
117    ) -> Result<ResolvedPassChannel<V>, AttachmentError> {
118        if self.read_only {
119            if self.load_op.is_some() {
120                return Err(AttachmentError::ReadOnlyWithLoad);
121            }
122            if self.store_op.is_some() {
123                return Err(AttachmentError::ReadOnlyWithStore);
124            }
125            Ok(ResolvedPassChannel::ReadOnly)
126        } else {
127            Ok(ResolvedPassChannel::Operational(wgt::Operations {
128                load: match self.load_op.ok_or(AttachmentError::NoLoad)? {
129                    LoadOp::Clear(clear_value) => LoadOp::Clear(handle_clear(clear_value)?),
130                    LoadOp::DontCare(token) => {
131                        if instance_flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) {
132                            return Err(AttachmentError::LoadOpDontCareUnderStrictWebgpuCompliance);
133                        }
134                        LoadOp::DontCare(token)
135                    }
136                    LoadOp::Load => LoadOp::Load,
137                },
138                store: self.store_op.ok_or(AttachmentError::NoStore)?,
139            }))
140        }
141    }
142}
143
144/// Describes an individual channel within a render pass, such as color, depth, or stencil.
145///
146/// Unlike [`PassChannel`], this version uses the Rust type system to guarantee
147/// a valid specification.
148#[derive(Clone, Debug)]
149#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
150pub enum ResolvedPassChannel<V> {
151    ReadOnly,
152    Operational(wgt::Operations<V>),
153}
154
155impl<V: Copy + Default> ResolvedPassChannel<V> {
156    fn load_op(&self) -> LoadOp<V> {
157        match self {
158            ResolvedPassChannel::ReadOnly => LoadOp::Load,
159            ResolvedPassChannel::Operational(wgt::Operations { load, .. }) => *load,
160        }
161    }
162
163    fn store_op(&self) -> StoreOp {
164        match self {
165            ResolvedPassChannel::ReadOnly => StoreOp::Store,
166            ResolvedPassChannel::Operational(wgt::Operations { store, .. }) => *store,
167        }
168    }
169
170    fn clear_value(&self) -> V {
171        match self {
172            Self::Operational(wgt::Operations {
173                load: LoadOp::Clear(clear_value),
174                ..
175            }) => *clear_value,
176            _ => Default::default(),
177        }
178    }
179
180    fn is_readonly(&self) -> bool {
181        matches!(self, Self::ReadOnly)
182    }
183
184    fn hal_ops(&self) -> hal::AttachmentOps {
185        load_hal_ops(self.load_op()) | store_hal_ops(self.store_op())
186    }
187}
188
189/// Describes a color attachment to a render pass.
190#[repr(C)]
191#[derive(Clone, Debug, PartialEq)]
192#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
193pub struct RenderPassColorAttachment<TV = Arc<TextureView>> {
194    /// The view to use as an attachment.
195    pub view: TV,
196    /// The depth slice index of a 3D view. It must not be provided if the view is not 3D.
197    pub depth_slice: Option<u32>,
198    /// The view that will receive the resolved output if multisampling is used.
199    pub resolve_target: Option<TV>,
200    /// Operation to perform to the output attachment at the start of a
201    /// renderpass.
202    ///
203    /// This must be clear if it is the first renderpass rendering to a swap
204    /// chain image.
205    pub load_op: LoadOp<Color>,
206    /// Operation to perform to the output attachment at the end of a renderpass.
207    pub store_op: StoreOp,
208}
209
210// Avoid allocation in the common case that there is only one color attachment,
211// but don't bloat `ArcCommand::RunRenderPass` excessively.
212pub type ColorAttachments<TV = Arc<TextureView>> =
213    SmallVec<[Option<RenderPassColorAttachment<TV>>; 1]>;
214
215impl RenderPassColorAttachment {
216    fn hal_ops(&self) -> hal::AttachmentOps {
217        load_hal_ops(self.load_op) | store_hal_ops(self.store_op)
218    }
219
220    fn clear_value(&self) -> Color {
221        match self.load_op {
222            LoadOp::Clear(clear_value) => clear_value,
223            LoadOp::DontCare(_) | LoadOp::Load => Color::default(),
224        }
225    }
226}
227
228/// Describes a depth/stencil attachment to a render pass.
229///
230/// This version uses the unvalidated [`PassChannel`].
231#[repr(C)]
232#[derive(Clone, Debug, PartialEq)]
233#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
234pub struct RenderPassDepthStencilAttachment<TV> {
235    /// The view to use as an attachment.
236    pub view: TV,
237    /// What operations will be performed on the depth part of the attachment.
238    pub depth: PassChannel<Option<f32>>,
239    /// What operations will be performed on the stencil part of the attachment.
240    pub stencil: PassChannel<Option<u32>>,
241}
242
243/// Describes a depth/stencil attachment to a render pass.
244///
245/// This version uses the validated [`ResolvedPassChannel`].
246#[derive(Clone, Debug)]
247#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
248pub struct ResolvedRenderPassDepthStencilAttachment<TV> {
249    /// The view to use as an attachment.
250    pub view: TV,
251    /// What operations will be performed on the depth part of the attachment.
252    pub depth: ResolvedPassChannel<f32>,
253    /// What operations will be performed on the stencil part of the attachment.
254    pub stencil: ResolvedPassChannel<u32>,
255}
256
257/// Describes the attachments of a render pass.
258#[derive(Clone, Debug, Default, PartialEq)]
259#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
260pub struct RenderPassDescriptor<'a> {
261    pub label: Label<'a>,
262    /// The color attachments of the render pass.
263    pub color_attachments: Cow<'a, [Option<RenderPassColorAttachment<id::TextureViewId>>]>,
264    /// The depth and stencil attachment of the render pass, if any.
265    pub depth_stencil_attachment: Option<RenderPassDepthStencilAttachment<id::TextureViewId>>,
266    /// Defines where and when timestamp values will be written for this pass.
267    pub timestamp_writes: Option<PassTimestampWrites<id::QuerySetId>>,
268    /// Defines where the occlusion query results will be stored for this pass.
269    pub occlusion_query_set: Option<id::QuerySetId>,
270    /// The multiview array layers that will be used
271    pub multiview_mask: Option<NonZeroU32>,
272}
273
274/// Describes the attachments of a render pass.
275#[derive(Clone, Default)]
276pub struct ResolvedRenderPassDescriptor<'a> {
277    pub label: Label<'a>,
278    /// The color attachments of the render pass.
279    pub color_attachments: Cow<'a, [Option<RenderPassColorAttachment<Arc<TextureView>>>]>,
280    /// The depth and stencil attachment of the render pass, if any.
281    pub depth_stencil_attachment: Option<RenderPassDepthStencilAttachment<Arc<TextureView>>>,
282    /// Defines where and when timestamp values will be written for this pass.
283    pub timestamp_writes: Option<PassTimestampWrites<Arc<QuerySet>>>,
284    /// Defines where the occlusion query results will be stored for this pass.
285    pub occlusion_query_set: Option<Arc<QuerySet>>,
286    /// The multiview array layers that will be used
287    pub multiview_mask: Option<NonZeroU32>,
288}
289
290/// Describes the attachments of a render pass.
291struct ArcRenderPassDescriptor<'a> {
292    pub label: &'a Label<'a>,
293    /// The color attachments of the render pass.
294    pub color_attachments:
295        ArrayVec<Option<RenderPassColorAttachment>, { hal::MAX_COLOR_ATTACHMENTS }>,
296    /// The depth and stencil attachment of the render pass, if any.
297    pub depth_stencil_attachment:
298        Option<ResolvedRenderPassDepthStencilAttachment<Arc<TextureView>>>,
299    /// Defines where and when timestamp values will be written for this pass.
300    pub timestamp_writes: Option<PassTimestampWrites>,
301    /// Defines where the occlusion query results will be stored for this pass.
302    pub occlusion_query_set: Option<Arc<QuerySet>>,
303    /// The multiview array layers that will be used
304    pub multiview_mask: Option<NonZeroU32>,
305}
306
307pub type RenderBasePass = BasePass<ArcRenderCommand, RenderPassError>;
308
309/// A pass's [encoder state](https://www.w3.org/TR/webgpu/#encoder-state) and
310/// its validity are two distinct conditions, i.e., the full matrix of
311/// (open, ended) x (valid, invalid) is possible.
312///
313/// The presence or absence of the `parent` `Option` indicates the pass's state.
314/// The presence or absence of an error in `base.error` indicates the pass's
315/// validity.
316pub struct RenderPass {
317    /// All pass data & records is stored here.
318    base: BasePass<ArcRenderCommand, RenderPassError>,
319
320    /// Parent command encoder that this pass records commands into.
321    ///
322    /// If this is `Some`, then the pass is in WebGPU's "open" state. If it is
323    /// `None`, then the pass is in the "ended" state.
324    /// See <https://www.w3.org/TR/webgpu/#encoder-state>
325    parent: Option<Arc<CommandEncoder>>,
326
327    color_attachments: ArrayVec<Option<RenderPassColorAttachment>, { hal::MAX_COLOR_ATTACHMENTS }>,
328    depth_stencil_attachment: Option<ResolvedRenderPassDepthStencilAttachment<Arc<TextureView>>>,
329    timestamp_writes: Option<PassTimestampWrites>,
330    occlusion_query_set: Option<Arc<QuerySet>>,
331    multiview_mask: Option<NonZeroU32>,
332
333    // Resource binding dedupe state.
334    current_bind_groups: BindGroupStateChange,
335    current_pipeline: StateChange<Arc<RenderPipeline>>,
336}
337
338impl_resource_type!(RenderPass);
339
340impl crate::storage::StorageItem for RenderPass {
341    type Marker = id::markers::RenderPassEncoder;
342}
343
344impl RenderPass {
345    /// If the parent command encoder is invalid, the returned pass will be invalid.
346    fn new(parent: Arc<CommandEncoder>, desc: ArcRenderPassDescriptor) -> Self {
347        let ArcRenderPassDescriptor {
348            label,
349            timestamp_writes,
350            color_attachments,
351            depth_stencil_attachment,
352            occlusion_query_set,
353            multiview_mask,
354        } = desc;
355
356        Self {
357            base: BasePass::new(label),
358            parent: Some(parent),
359            color_attachments,
360            depth_stencil_attachment,
361            timestamp_writes,
362            occlusion_query_set,
363            multiview_mask,
364
365            current_bind_groups: BindGroupStateChange::new(),
366            current_pipeline: StateChange::new(),
367        }
368    }
369
370    fn new_invalid(parent: Arc<CommandEncoder>, label: &Label, err: RenderPassError) -> Self {
371        Self {
372            base: BasePass::new_invalid(label, err),
373            parent: Some(parent),
374            color_attachments: ArrayVec::new(),
375            depth_stencil_attachment: None,
376            timestamp_writes: None,
377            occlusion_query_set: None,
378            multiview_mask: None,
379            current_bind_groups: BindGroupStateChange::new(),
380            current_pipeline: StateChange::new(),
381        }
382    }
383
384    #[inline]
385    pub fn label(&self) -> Option<&str> {
386        self.base.label.as_deref()
387    }
388}
389
390impl fmt::Debug for RenderPass {
391    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
392        f.debug_struct("RenderPass")
393            .field("label", &self.label())
394            .field("color_attachments", &self.color_attachments)
395            .field("depth_stencil_target", &self.depth_stencil_attachment)
396            .field("command count", &self.base.commands.len())
397            .field("dynamic offset count", &self.base.dynamic_offsets.len())
398            .field("multiview mask", &self.multiview_mask)
399            .finish()
400    }
401}
402
403#[derive(Debug, PartialEq)]
404enum OptionalState {
405    Unused,
406    Required,
407    Set,
408}
409
410impl OptionalState {
411    fn require(&mut self, require: bool) {
412        if require && *self == Self::Unused {
413            *self = Self::Required;
414        }
415    }
416}
417
418#[derive(Debug, Default)]
419struct IndexState {
420    buffer_format: Option<IndexFormat>,
421    limit: u64,
422}
423
424impl IndexState {
425    fn update_buffer(&mut self, range: Range<BufferAddress>, format: IndexFormat) {
426        self.buffer_format = Some(format);
427        let shift = match format {
428            IndexFormat::Uint16 => 1,
429            IndexFormat::Uint32 => 2,
430        };
431        self.limit = (range.end - range.start) >> shift;
432    }
433
434    fn reset(&mut self) {
435        self.buffer_format = None;
436        self.limit = 0;
437    }
438}
439
440#[derive(Debug, Default)]
441pub(crate) struct VertexLimits {
442    /// Length of the shortest vertex rate vertex buffer
443    pub(crate) vertex_limit: u64,
444    /// Buffer slot which the shortest vertex rate vertex buffer is bound to
445    vertex_limit_slot: u32,
446    /// Length of the shortest instance rate vertex buffer
447    pub(crate) instance_limit: u64,
448    /// Buffer slot which the shortest instance rate vertex buffer is bound to
449    instance_limit_slot: u32,
450}
451
452impl VertexLimits {
453    pub(crate) fn new(
454        buffer_sizes: impl ExactSizeIterator<Item = Option<BufferAddress>>,
455        pipeline_steps: &[Option<VertexStep>],
456    ) -> Self {
457        // Implements the validation from https://gpuweb.github.io/gpuweb/#dom-gpurendercommandsmixin-draw
458        // Except that the formula is shuffled to extract the number of vertices in order
459        // to carry the bulk of the computation when changing states instead of when producing
460        // draws. Draw calls tend to happen at a higher frequency. Here we determine vertex
461        // limits that can be cheaply checked for each draw call.
462
463        let mut vertex_limit = u64::MAX;
464        let mut vertex_limit_slot = 0;
465        let mut instance_limit = u64::MAX;
466        let mut instance_limit_slot = 0;
467
468        for (idx, (buffer_size, step)) in buffer_sizes.zip(pipeline_steps).enumerate() {
469            let Some(step) = step else {
470                continue;
471            };
472
473            let Some(buffer_size) = buffer_size else {
474                // Missing required vertex buffer
475                return Self::default();
476            };
477
478            let limit = if buffer_size < step.last_stride {
479                // The buffer cannot fit the last vertex.
480                0
481            } else {
482                if step.stride == 0 {
483                    // We already checked that the last stride fits, the same
484                    // vertex will be repeated so this slot can accommodate any number of
485                    // vertices.
486                    continue;
487                }
488
489                // The general case.
490                (buffer_size - step.last_stride) / step.stride + 1
491            };
492
493            match step.mode {
494                VertexStepMode::Vertex => {
495                    if limit < vertex_limit {
496                        vertex_limit = limit;
497                        vertex_limit_slot = idx as _;
498                    }
499                }
500                VertexStepMode::Instance => {
501                    if limit < instance_limit {
502                        instance_limit = limit;
503                        instance_limit_slot = idx as _;
504                    }
505                }
506            }
507        }
508
509        Self {
510            vertex_limit,
511            vertex_limit_slot,
512            instance_limit,
513            instance_limit_slot,
514        }
515    }
516
517    pub(crate) fn validate_vertex_limit(
518        &self,
519        first_vertex: u32,
520        vertex_count: u32,
521    ) -> Result<(), DrawError> {
522        let last_vertex = first_vertex as u64 + vertex_count as u64;
523        let vertex_limit = self.vertex_limit;
524        if last_vertex > vertex_limit {
525            return Err(DrawError::VertexBeyondLimit {
526                last_vertex,
527                vertex_limit,
528                slot: self.vertex_limit_slot,
529            });
530        }
531
532        Ok(())
533    }
534
535    pub(crate) fn validate_instance_limit(
536        &self,
537        first_instance: u32,
538        instance_count: u32,
539    ) -> Result<(), DrawError> {
540        let last_instance = first_instance as u64 + instance_count as u64;
541        let instance_limit = self.instance_limit;
542        if last_instance > instance_limit {
543            return Err(DrawError::InstanceBeyondLimit {
544                last_instance,
545                instance_limit,
546                slot: self.instance_limit_slot,
547            });
548        }
549
550        Ok(())
551    }
552}
553
554/// State of a single vertex buffer slot.
555#[derive(Debug)]
556pub(crate) struct VertexSlot {
557    pub(crate) buffer: Arc<Buffer>,
558    pub(crate) range: Range<BufferAddress>,
559    pub(crate) is_dirty: bool,
560}
561
562/// Vertex buffer tracking state, shared between render passes and render bundles.
563///
564/// Tracks which vertex buffer slots are set, and caches the vertex and instance limits
565/// derived from those buffers and the current pipeline, avoiding recomputation on each draw.
566#[derive(Debug, Default)]
567pub(crate) struct VertexState {
568    slots: [Option<VertexSlot>; hal::MAX_VERTEX_BUFFERS],
569    pub(crate) limits: VertexLimits,
570}
571
572impl VertexState {
573    /// Set a vertex buffer slot, marking it dirty.
574    pub(crate) fn set_buffer(
575        &mut self,
576        slot: usize,
577        buffer: Arc<Buffer>,
578        range: Range<BufferAddress>,
579    ) {
580        self.slots[slot] = Some(VertexSlot {
581            buffer,
582            range,
583            is_dirty: true,
584        });
585    }
586
587    /// Clear a vertex buffer slot.
588    pub(crate) fn clear_buffer(&mut self, slot: usize) {
589        self.slots[slot] = None;
590    }
591
592    /// Recompute the cached vertex and instance limits based on the current slots and pipeline.
593    pub(crate) fn update_limits(&mut self, pipeline_steps: &[Option<VertexStep>]) {
594        self.limits = VertexLimits::new(
595            self.slots
596                .iter()
597                .map(|s| s.as_ref().map(|s| s.range.end - s.range.start)),
598            pipeline_steps,
599        );
600    }
601
602    fn last_assigned_index(&self) -> Option<usize> {
603        self.slots
604            .iter()
605            .enumerate()
606            .filter_map(|(i, s)| s.as_ref().map(|_| i))
607            .next_back()
608    }
609
610    pub(super) fn validate(
611        &self,
612        pipeline: &RenderPipeline,
613        binder: &Binder,
614    ) -> Result<(), DrawError> {
615        // Check all needed vertex buffers have been bound
616        for index in pipeline
617            .vertex_steps
618            .iter()
619            .enumerate()
620            .filter_map(|(index, step)| step.map(|_| index))
621        {
622            if self.slots[index].is_none() {
623                return Err(DrawError::MissingVertexBuffer {
624                    pipeline: pipeline.error_ident(),
625                    index,
626                });
627            }
628        }
629
630        let bind_group_space_used = binder.last_assigned_index().map_or(0, |i| i + 1);
631        let vertex_buffer_space_used = self.last_assigned_index().map_or(0, |i| i + 1);
632
633        let bind_groups_plus_vertex_buffers =
634            u32::try_from(bind_group_space_used + vertex_buffer_space_used).unwrap();
635        if bind_groups_plus_vertex_buffers
636            > pipeline.device.limits.max_bind_groups_plus_vertex_buffers
637        {
638            return Err(DrawError::TooManyBindGroupsPlusVertexBuffers {
639                given: bind_groups_plus_vertex_buffers,
640                limit: pipeline.device.limits.max_bind_groups_plus_vertex_buffers,
641            });
642        }
643
644        Ok(())
645    }
646
647    /// Call `f` for each dirty slot with `(slot_index, buffer, offset, size)` and mark them clean.
648    pub(crate) fn flush<F>(&mut self, mut f: F)
649    where
650        F: FnMut(u32, &Arc<Buffer>, BufferAddress, Option<BufferSize>),
651    {
652        for (i, slot) in self.slots.iter_mut().enumerate() {
653            let Some(slot) = slot.as_mut() else { continue };
654            if !slot.is_dirty {
655                continue;
656            }
657            slot.is_dirty = false;
658            let size = slot.range.end - slot.range.start;
659            f(
660                i as u32,
661                &slot.buffer,
662                slot.range.start,
663                BufferSize::new(size),
664            );
665        }
666    }
667}
668
669struct State<'scope, 'snatch_guard, 'cmd_enc> {
670    pipeline_flags: PipelineFlags,
671    blend_constant: OptionalState,
672    stencil_reference: u32,
673    pipeline: Option<Arc<RenderPipeline>>,
674    index: IndexState,
675    vertex: VertexState,
676
677    info: RenderPassInfo,
678
679    pass: pass::PassState<'scope, 'snatch_guard, 'cmd_enc>,
680
681    active_occlusion_query: Option<(Arc<QuerySet>, u32)>,
682    active_pipeline_statistics_query: Option<(Arc<QuerySet>, u32)>,
683}
684
685impl<'scope, 'snatch_guard, 'cmd_enc> State<'scope, 'snatch_guard, 'cmd_enc> {
686    fn is_ready(&self, family: DrawCommandFamily) -> Result<(), DrawError> {
687        if let Some(pipeline) = self.pipeline.as_ref() {
688            self.pass.binder.check_compatibility(pipeline.as_ref())?;
689            self.pass.binder.check_late_buffer_bindings()?;
690
691            if self.blend_constant == OptionalState::Required {
692                return Err(DrawError::MissingBlendConstant);
693            }
694
695            self.vertex.validate(pipeline.as_ref(), &self.pass.binder)?;
696
697            if family == DrawCommandFamily::DrawIndexed {
698                // Pipeline expects an index buffer
699                // We have a buffer bound
700                let buffer_index_format = self
701                    .index
702                    .buffer_format
703                    .ok_or(DrawError::MissingIndexBuffer)?;
704
705                if pipeline.topology.is_strip()
706                    && pipeline.strip_index_format != Some(buffer_index_format)
707                {
708                    return Err(DrawError::UnmatchedStripIndexFormat {
709                        pipeline: pipeline.error_ident(),
710                        strip_index_format: pipeline.strip_index_format,
711                        buffer_format: buffer_index_format,
712                    });
713                }
714            }
715            if (family == DrawCommandFamily::DrawMeshTasks) != pipeline.is_mesh {
716                return Err(DrawError::WrongPipelineType {
717                    wanted_mesh_pipeline: !pipeline.is_mesh,
718                });
719            }
720            if !self
721                .pass
722                .immediate_state
723                .immediate_slots_set
724                .contains(pipeline.immediate_slots_required)
725            {
726                return Err(DrawError::MissingImmediateData {
727                    missing: pipeline
728                        .immediate_slots_required
729                        .difference(self.pass.immediate_state.immediate_slots_set),
730                });
731            }
732            Ok(())
733        } else {
734            Err(DrawError::MissingPipeline(pass::MissingPipeline))
735        }
736    }
737
738    fn flush_immediates(&mut self) {
739        let pipeline = self.pipeline.as_ref().unwrap();
740        let layout = pipeline.layout().unwrap();
741        self.pass
742            .immediate_state
743            .flush_immediates(layout, self.pass.base.raw_encoder);
744    }
745
746    /// Flush binding state in preparation for a draw call.
747    ///
748    /// See the compute pass version for an explanation of some ways that
749    /// `flush_bindings` differs between the two types of passes.
750    fn flush_bindings(&mut self) -> Result<(), RenderPassErrorInner> {
751        flush_bindings_helper(&mut self.pass)?;
752        Ok(())
753    }
754
755    /// Reset the `RenderBundle`-related states.
756    fn reset_bundle(&mut self) {
757        self.pass.binder.reset();
758        self.pipeline = None;
759        self.index.reset();
760        self.vertex = Default::default();
761        self.pass.immediate_state.immediate_slots_set = Default::default();
762    }
763
764    /// Flush dirty vertex buffer slots to the HAL encoder in preparation for a draw call.
765    fn flush_vertex_buffers(&mut self) -> Result<(), RenderPassErrorInner> {
766        let vertex = &mut self.vertex;
767        let raw_encoder: &mut dyn hal::DynCommandEncoder = self.pass.base.raw_encoder;
768        let snatch_guard = self.pass.base.snatch_guard;
769        let mut result = Ok(());
770        vertex.flush(|slot, buffer, offset, size| {
771            if result.is_err() {
772                return;
773            }
774            match buffer.try_raw(snatch_guard) {
775                Ok(raw) => unsafe {
776                    // SAFETY: The offset and size were validated in set_vertex_buffer.
777                    raw_encoder.set_vertex_buffer(
778                        slot,
779                        hal::BufferBinding::new_unchecked(raw, offset, size),
780                    );
781                },
782                Err(e) => result = Err(e.into()),
783            }
784        });
785        result
786    }
787}
788
789/// Describes an attachment location in words.
790///
791/// Can be used as "the {loc} has..." or "{loc} has..."
792#[derive(Debug, Copy, Clone)]
793pub enum AttachmentErrorLocation {
794    Color { index: usize, resolve: bool },
795    Depth,
796}
797
798impl fmt::Display for AttachmentErrorLocation {
799    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
800        match *self {
801            AttachmentErrorLocation::Color {
802                index,
803                resolve: false,
804            } => write!(f, "color attachment at index {index}'s texture view"),
805            AttachmentErrorLocation::Color {
806                index,
807                resolve: true,
808            } => write!(
809                f,
810                "color attachment at index {index}'s resolve texture view"
811            ),
812            AttachmentErrorLocation::Depth => write!(f, "depth attachment's texture view"),
813        }
814    }
815}
816
817#[derive(Clone, Debug, Error)]
818#[non_exhaustive]
819pub enum ColorAttachmentError {
820    #[error("Attachment format {0:?} is not a color format")]
821    InvalidFormat(wgt::TextureFormat),
822    #[error("The number of color attachments {given} exceeds the limit {limit}")]
823    TooMany { given: usize, limit: usize },
824    #[error("The total number of bytes per sample in color attachments {total} exceeds the limit {limit}")]
825    TooManyBytesPerSample { total: u32, limit: u32 },
826    #[error("Depth slice must be less than {limit} but is {given}")]
827    DepthSliceLimit { given: u32, limit: u32 },
828    #[error("Color attachment's view is 3D and requires depth slice to be provided")]
829    MissingDepthSlice,
830    #[error("Depth slice was provided but the color attachment's view is not 3D")]
831    UnneededDepthSlice,
832    #[error("{view}'s subresource at mip {mip_level} and depth/array layer {depth_or_array_layer} is already attached to this render pass")]
833    SubresourceOverlap {
834        view: ResourceErrorIdent,
835        mip_level: u32,
836        depth_or_array_layer: u32,
837    },
838    #[error(
839        "Color attachment with `TRANSIENT_ATTACHMENT` usage can only be used with \
840        `LoadOp::Clear` or `LoadOp::DontCare` (if it is available) and  `StoreOp::Discard`. Operations `{0:?}` were provided"
841    )]
842    InvalidTransientAttachmentOp((LoadOp<Color>, StoreOp)),
843    #[error("Color attachment's load op is `LoadOp::DontCare` but `InstanceFlags::STRICT_WEBGPU_COMPLIANCE` is set")]
844    LoadOpDontCareUnderStrictWebgpuCompliance,
845}
846
847impl WebGpuError for ColorAttachmentError {
848    fn webgpu_error_type(&self) -> ErrorType {
849        ErrorType::Validation
850    }
851}
852
853#[derive(Clone, Debug, Error)]
854#[non_exhaustive]
855pub enum AttachmentError {
856    #[error("The format of the depth-stencil attachment ({0:?}) is not a depth-or-stencil format")]
857    InvalidDepthStencilAttachmentFormat(wgt::TextureFormat),
858    #[error(
859        "Depth attachment with `TRANSIENT_ATTACHMENT` usage can only be used with \
860        `LoadOp::Clear` or `LoadOp::DontCare` (if it is available) and  `StoreOp::Discard`. Operations `{0:?}` were provided"
861    )]
862    InvalidTransientDepthAttachmentOps((LoadOp<Option<f32>>, StoreOp)),
863    #[error("Depth attachment with `TRANSIENT_ATTACHMENT` usage cannot be read-only")]
864    ReadOnlyTransientDepthAttachment,
865    #[error(
866        "Stencil attachment with `TRANSIENT_ATTACHMENT` usage can only be used with \
867        `LoadOp::Clear` or `LoadOp::DontCare` (if it is available) and  `StoreOp::Discard`. Operations `{0:?}` were provided"
868    )]
869    InvalidTransientStencilAttachmentOps((LoadOp<Option<u32>>, StoreOp)),
870    #[error("Stencil attachment with `TRANSIENT_ATTACHMENT` usage cannot be read-only")]
871    ReadOnlyTransientStencilAttachment,
872    #[error("LoadOp must be None for read-only attachments")]
873    ReadOnlyWithLoad,
874    #[error("StoreOp must be None for read-only attachments")]
875    ReadOnlyWithStore,
876    #[error("Depth `LoadOp` and `StoreOp` (`{ops:?}`) must be `None` for attachments (`{format:?}`) without depth aspect")]
877    DepthOpsWithoutAspect {
878        format: wgt::TextureFormat,
879        ops: (Option<LoadOp<Option<f32>>>, Option<StoreOp>),
880    },
881    #[error("Stencil `LoadOp` and `StoreOp` (`{ops:?}`) must be `None` for attachments (`{format:?}`) without stencil aspect")]
882    StencilOpsWithoutAspect {
883        format: wgt::TextureFormat,
884        ops: (Option<LoadOp<Option<u32>>>, Option<StoreOp>),
885    },
886    #[error("Attachment without load")]
887    NoLoad,
888    #[error("Attachment without store")]
889    NoStore,
890    #[error("LoadOp is `Clear` but no clear value was provided")]
891    NoClearValue,
892    #[error("Clear value ({0}) must be between 0.0 and 1.0, inclusive")]
893    ClearValueOutOfRange(f32),
894    #[error("Load op is `DontCare` but `InstanceFlags::STRICT_WEBGPU_COMPLIANCE` is set")]
895    LoadOpDontCareUnderStrictWebgpuCompliance,
896}
897
898impl WebGpuError for AttachmentError {
899    fn webgpu_error_type(&self) -> ErrorType {
900        ErrorType::Validation
901    }
902}
903
904/// Error encountered when performing a render pass.
905#[derive(Clone, Debug, Error)]
906pub enum RenderPassErrorInner {
907    #[error(transparent)]
908    Device(#[from] DeviceError),
909    #[error(transparent)]
910    ColorAttachment(#[from] ColorAttachmentError),
911    #[error(transparent)]
912    InvalidAttachment(#[from] AttachmentError),
913    #[error(transparent)]
914    EncoderState(#[from] EncoderStateError),
915    #[error("Parent encoder is invalid")]
916    InvalidParentEncoder,
917    #[error(transparent)]
918    DebugGroupError(#[from] DebugGroupError),
919    #[error("The format of the {location} ({format:?}) is not resolvable")]
920    UnsupportedResolveTargetFormat {
921        location: AttachmentErrorLocation,
922        format: wgt::TextureFormat,
923    },
924    #[error("The {location} is not valid, because the texture has `TRANSIENT_ATTACHMENT` usage")]
925    InvalidTransientResolveTarget { location: AttachmentErrorLocation },
926    #[error("No color attachments or depth attachments were provided, at least one attachment of any kind must be provided")]
927    MissingAttachments,
928    #[error("The {location} is not renderable:")]
929    TextureViewIsNotRenderable {
930        location: AttachmentErrorLocation,
931        #[source]
932        reason: TextureViewNotRenderableReason,
933    },
934    #[error("Attachments have differing sizes: the {expected_location} has extent {expected_extent:?} but is followed by the {actual_location} which has {actual_extent:?}")]
935    AttachmentsDimensionMismatch {
936        expected_location: AttachmentErrorLocation,
937        expected_extent: wgt::Extent3d,
938        actual_location: AttachmentErrorLocation,
939        actual_extent: wgt::Extent3d,
940    },
941    #[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:?}")]
942    AttachmentSampleCountMismatch {
943        expected_location: AttachmentErrorLocation,
944        expected_samples: u32,
945        actual_location: AttachmentErrorLocation,
946        actual_samples: u32,
947    },
948    #[error("The resolve source, {location}, must be multi-sampled (has {src} samples) while the resolve destination must not be multisampled (has {dst} samples)")]
949    InvalidResolveSampleCounts {
950        location: AttachmentErrorLocation,
951        src: u32,
952        dst: u32,
953    },
954    #[error(
955        "Resource source, {location}, format ({src:?}) must match the resolve destination format ({dst:?})"
956    )]
957    MismatchedResolveTextureFormat {
958        location: AttachmentErrorLocation,
959        src: wgt::TextureFormat,
960        dst: wgt::TextureFormat,
961    },
962    #[error("Unable to clear non-present/read-only depth")]
963    InvalidDepthOps,
964    #[error("Unable to clear non-present/read-only stencil")]
965    InvalidStencilOps,
966    #[error(transparent)]
967    MissingFeatures(#[from] MissingFeatures),
968    #[error(transparent)]
969    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
970    #[error("Indirect draw count of {count_bytes} bytes starting at {begin_count_offset} would overrun buffer of size {count_buffer_size}")]
971    IndirectCountBufferOverrun {
972        count_bytes: u64,
973        begin_count_offset: u64,
974        count_buffer_size: u64,
975    },
976    #[error(transparent)]
977    ResourceUsageCompatibility(#[from] ResourceUsageCompatibilityError),
978    #[error("Render bundle has incompatible targets, {0}")]
979    IncompatibleBundleTargets(#[from] RenderPassCompatibilityError),
980    #[error(
981        "Render bundle has incompatible read-only flags: \
982             bundle has flags depth = {bundle_depth} and stencil = {bundle_stencil}, \
983             while the pass has flags depth = {pass_depth} and stencil = {pass_stencil}. \
984             Read-only renderpasses are only compatible with read-only bundles for that aspect."
985    )]
986    IncompatibleBundleReadOnlyDepthStencil {
987        pass_depth: bool,
988        pass_stencil: bool,
989        bundle_depth: bool,
990        bundle_stencil: bool,
991    },
992    #[error(transparent)]
993    RenderCommand(#[from] RenderCommandError),
994    #[error(transparent)]
995    Draw(#[from] DrawError),
996    #[error(transparent)]
997    Bind(#[from] BindError),
998    #[error(transparent)]
999    QueryUse(#[from] QueryUseError),
1000    #[error("Multiview layer count must match")]
1001    MultiViewMismatch,
1002    #[error(
1003        "Multiview pass texture views with more than one array layer must have D2Array dimension"
1004    )]
1005    MultiViewDimensionMismatch,
1006    #[error("Multiview view count limit violated")]
1007    TooManyMultiviewViews,
1008    #[error("missing occlusion query set")]
1009    MissingOcclusionQuerySet,
1010    #[error(transparent)]
1011    DestroyedResource(#[from] DestroyedResourceError),
1012    #[error("The compute pass has already been ended and no further commands can be recorded")]
1013    PassEnded,
1014    #[error(transparent)]
1015    InvalidResource(#[from] InvalidResourceError),
1016    #[error(transparent)]
1017    TimestampWrites(#[from] TimestampWritesError),
1018}
1019
1020impl From<InvalidOrDestroyedResourceError> for RenderPassErrorInner {
1021    fn from(error: InvalidOrDestroyedResourceError) -> Self {
1022        match error {
1023            InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
1024            InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
1025        }
1026    }
1027}
1028
1029impl From<MissingBufferUsageError> for RenderPassErrorInner {
1030    fn from(error: MissingBufferUsageError) -> Self {
1031        Self::RenderCommand(error.into())
1032    }
1033}
1034
1035impl From<MissingTextureUsageError> for RenderPassErrorInner {
1036    fn from(error: MissingTextureUsageError) -> Self {
1037        Self::RenderCommand(error.into())
1038    }
1039}
1040
1041impl From<pass::BindGroupIndexOutOfRange> for RenderPassErrorInner {
1042    fn from(error: pass::BindGroupIndexOutOfRange) -> Self {
1043        Self::RenderCommand(RenderCommandError::BindGroupIndexOutOfRange(error))
1044    }
1045}
1046
1047impl From<pass::MissingPipeline> for RenderPassErrorInner {
1048    fn from(error: pass::MissingPipeline) -> Self {
1049        Self::Draw(DrawError::MissingPipeline(error))
1050    }
1051}
1052
1053impl From<ImmediateUploadError> for RenderPassErrorInner {
1054    fn from(error: ImmediateUploadError) -> Self {
1055        Self::RenderCommand(error.into())
1056    }
1057}
1058
1059/// Error encountered when performing a render pass.
1060#[derive(Clone, Debug, Error)]
1061#[error("{scope}")]
1062pub struct RenderPassError {
1063    pub scope: PassErrorScope,
1064    #[source]
1065    pub(super) inner: Box<RenderPassErrorInner>,
1066}
1067
1068impl<E: Into<RenderPassErrorInner>> MapPassErr<RenderPassError> for E {
1069    fn map_pass_err(self, scope: PassErrorScope) -> RenderPassError {
1070        RenderPassError {
1071            scope,
1072            inner: Box::new(self.into()),
1073        }
1074    }
1075}
1076
1077impl WebGpuError for RenderPassError {
1078    fn webgpu_error_type(&self) -> ErrorType {
1079        match self.inner.as_ref() {
1080            RenderPassErrorInner::Device(e) => e.webgpu_error_type(),
1081            RenderPassErrorInner::ColorAttachment(e) => e.webgpu_error_type(),
1082            RenderPassErrorInner::EncoderState(e) => e.webgpu_error_type(),
1083            RenderPassErrorInner::DebugGroupError(e) => e.webgpu_error_type(),
1084            RenderPassErrorInner::MissingFeatures(e) => e.webgpu_error_type(),
1085            RenderPassErrorInner::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1086            RenderPassErrorInner::RenderCommand(e) => e.webgpu_error_type(),
1087            RenderPassErrorInner::Draw(e) => e.webgpu_error_type(),
1088            RenderPassErrorInner::Bind(e) => e.webgpu_error_type(),
1089            RenderPassErrorInner::QueryUse(e) => e.webgpu_error_type(),
1090            RenderPassErrorInner::DestroyedResource(e) => e.webgpu_error_type(),
1091            RenderPassErrorInner::InvalidResource(e) => e.webgpu_error_type(),
1092            RenderPassErrorInner::IncompatibleBundleTargets(e) => e.webgpu_error_type(),
1093            RenderPassErrorInner::InvalidAttachment(e) => e.webgpu_error_type(),
1094            RenderPassErrorInner::TimestampWrites(e) => e.webgpu_error_type(),
1095
1096            RenderPassErrorInner::InvalidParentEncoder
1097            | RenderPassErrorInner::UnsupportedResolveTargetFormat { .. }
1098            | RenderPassErrorInner::InvalidTransientResolveTarget { .. }
1099            | RenderPassErrorInner::MissingAttachments
1100            | RenderPassErrorInner::TextureViewIsNotRenderable { .. }
1101            | RenderPassErrorInner::AttachmentsDimensionMismatch { .. }
1102            | RenderPassErrorInner::AttachmentSampleCountMismatch { .. }
1103            | RenderPassErrorInner::InvalidResolveSampleCounts { .. }
1104            | RenderPassErrorInner::MismatchedResolveTextureFormat { .. }
1105            | RenderPassErrorInner::InvalidDepthOps
1106            | RenderPassErrorInner::InvalidStencilOps
1107            | RenderPassErrorInner::IndirectCountBufferOverrun { .. }
1108            | RenderPassErrorInner::ResourceUsageCompatibility(..)
1109            | RenderPassErrorInner::IncompatibleBundleReadOnlyDepthStencil { .. }
1110            | RenderPassErrorInner::MultiViewMismatch
1111            | RenderPassErrorInner::MultiViewDimensionMismatch
1112            | RenderPassErrorInner::TooManyMultiviewViews
1113            | RenderPassErrorInner::MissingOcclusionQuerySet
1114            | RenderPassErrorInner::PassEnded => ErrorType::Validation,
1115        }
1116    }
1117}
1118
1119struct RenderAttachment {
1120    texture: Arc<Texture>,
1121    selector: TextureSelector,
1122    usage: wgt::TextureUses,
1123}
1124
1125impl TextureView {
1126    fn to_render_attachment(&self, usage: wgt::TextureUses) -> RenderAttachment {
1127        RenderAttachment {
1128            texture: self.parent.clone(),
1129            selector: self.selector.clone(),
1130            usage,
1131        }
1132    }
1133}
1134
1135const MAX_TOTAL_ATTACHMENTS: usize = hal::MAX_COLOR_ATTACHMENTS + hal::MAX_COLOR_ATTACHMENTS + 1;
1136type AttachmentDataVec<T> = ArrayVec<T, MAX_TOTAL_ATTACHMENTS>;
1137
1138struct RenderPassInfo {
1139    context: RenderPassContext,
1140    /// All render attachments, including depth/stencil
1141    render_attachments: AttachmentDataVec<RenderAttachment>,
1142    is_depth_read_only: bool,
1143    is_stencil_read_only: bool,
1144    extent: wgt::Extent3d,
1145
1146    divergent_discarded_depth_stencil_aspect: Option<(wgt::TextureAspect, Arc<TextureView>)>,
1147    multiview_mask: Option<NonZeroU32>,
1148}
1149
1150impl RenderPassInfo {
1151    fn add_pass_texture_init_actions<V>(
1152        load_op: LoadOp<V>,
1153        store_op: StoreOp,
1154        texture_memory_actions: &mut CommandBufferTextureMemoryActions,
1155        view: &TextureView,
1156        pending_discard_init_fixups: &mut SurfacesInDiscardState,
1157    ) {
1158        if matches!(load_op, LoadOp::Load) {
1159            pending_discard_init_fixups.extend(texture_memory_actions.register_init_action(
1160                &TextureInitTrackerAction {
1161                    texture: view.parent.clone(),
1162                    range: TextureInitRange::from(view.selector.clone()),
1163                    // Note that this is needed even if the target is discarded,
1164                    kind: MemoryInitKind::NeedsInitializedMemory,
1165                },
1166            ));
1167        } else if store_op == StoreOp::Store {
1168            // Clear + Store
1169            texture_memory_actions.register_implicit_init(
1170                &view.parent,
1171                TextureInitRange::from(view.selector.clone()),
1172            );
1173        }
1174        if store_op == StoreOp::Discard {
1175            // the discard happens at the *end* of a pass, but recording the
1176            // discard right away be alright since the texture can't be used
1177            // during the pass anyways
1178            texture_memory_actions.discard(TextureSurfaceDiscard {
1179                texture: view.parent.clone(),
1180                mip_level: view.selector.mips.start,
1181                layer: view.selector.layers.start,
1182            });
1183        }
1184    }
1185
1186    fn start(
1187        device: &Arc<Device>,
1188        hal_label: Option<&str>,
1189        color_attachments: &[Option<RenderPassColorAttachment>],
1190        mut depth_stencil_attachment: Option<
1191            ResolvedRenderPassDepthStencilAttachment<Arc<TextureView>>,
1192        >,
1193        mut timestamp_writes: Option<PassTimestampWrites>,
1194        mut occlusion_query_set: Option<Arc<QuerySet>>,
1195        encoder: &mut dyn hal::DynCommandEncoder,
1196        trackers: &mut Tracker,
1197        texture_memory_actions: &mut CommandBufferTextureMemoryActions,
1198        pending_query_resets: &mut QueryResetMap,
1199        pending_discard_init_fixups: &mut SurfacesInDiscardState,
1200        snatch_guard: &SnatchGuard<'_>,
1201        query_set_writes: &mut QuerySetWrites,
1202        multiview_mask: Option<NonZeroU32>,
1203    ) -> Result<Self, RenderPassErrorInner> {
1204        profiling::scope!("RenderPassInfo::start");
1205
1206        // We default to false intentionally, even if depth-stencil isn't used at all.
1207        // This allows us to use the primary raw pipeline in `RenderPipeline`,
1208        // instead of the special read-only one, which would be `None`.
1209        let mut is_depth_read_only = false;
1210        let mut is_stencil_read_only = false;
1211
1212        let mut render_attachments = AttachmentDataVec::<RenderAttachment>::new();
1213        let mut discarded_surfaces = AttachmentDataVec::new();
1214        let mut divergent_discarded_depth_stencil_aspect = None;
1215
1216        let mut attachment_location = AttachmentErrorLocation::Color {
1217            index: usize::MAX,
1218            resolve: false,
1219        };
1220        let mut extent = None;
1221        let mut sample_count = 0;
1222
1223        let mut detected_multiview: Option<Option<NonZeroU32>> = None;
1224
1225        let mut check_multiview = |view: &TextureView| {
1226            // Get the multiview configuration for this texture view
1227            let layers = view.selector.layers.end - view.selector.layers.start;
1228            let this_multiview = if layers >= 2 {
1229                // Trivially proven by the if above
1230                Some(unsafe { NonZeroU32::new_unchecked(layers) })
1231            } else {
1232                None
1233            };
1234
1235            // Make sure that if this view is a multiview, it is set to be an array
1236            if this_multiview.is_some() && view.desc.dimension != TextureViewDimension::D2Array {
1237                return Err(RenderPassErrorInner::MultiViewDimensionMismatch);
1238            }
1239
1240            // Validate matching first, or store the first one
1241            if let Some(multiview) = detected_multiview {
1242                if multiview != this_multiview {
1243                    return Err(RenderPassErrorInner::MultiViewMismatch);
1244                }
1245            } else {
1246                // Multiview is only supported if the feature is enabled
1247                if let Some(this_multiview) = this_multiview {
1248                    device.require_features(wgt::Features::MULTIVIEW)?;
1249                    if this_multiview.get() > device.limits.max_multiview_view_count {
1250                        return Err(RenderPassErrorInner::TooManyMultiviewViews);
1251                    }
1252                }
1253
1254                detected_multiview = Some(this_multiview);
1255            }
1256
1257            Ok(())
1258        };
1259        let mut add_view = |view: &TextureView, location| {
1260            let render_extent = view.state()?.render_extent.map_err(|reason| {
1261                RenderPassErrorInner::TextureViewIsNotRenderable { location, reason }
1262            })?;
1263            if let Some(ex) = extent {
1264                if ex != render_extent {
1265                    return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
1266                        expected_location: attachment_location,
1267                        expected_extent: ex,
1268                        actual_location: location,
1269                        actual_extent: render_extent,
1270                    });
1271                }
1272            } else {
1273                extent = Some(render_extent);
1274            }
1275            if sample_count == 0 {
1276                sample_count = view.samples;
1277            } else if sample_count != view.samples {
1278                return Err(RenderPassErrorInner::AttachmentSampleCountMismatch {
1279                    expected_location: attachment_location,
1280                    expected_samples: sample_count,
1281                    actual_location: location,
1282                    actual_samples: view.samples,
1283                });
1284            }
1285            attachment_location = location;
1286            Ok(())
1287        };
1288
1289        let mut depth_stencil = None;
1290
1291        if let Some(at) = depth_stencil_attachment.as_ref() {
1292            let view = &at.view;
1293            check_multiview(view)?;
1294            add_view(view, AttachmentErrorLocation::Depth)?;
1295
1296            let ds_aspects = view.desc.aspects();
1297
1298            if !ds_aspects.contains(hal::FormatAspects::STENCIL)
1299                || (at.stencil.load_op().eq_variant(at.depth.load_op())
1300                    && at.stencil.store_op() == at.depth.store_op())
1301            {
1302                Self::add_pass_texture_init_actions(
1303                    at.depth.load_op(),
1304                    at.depth.store_op(),
1305                    texture_memory_actions,
1306                    view,
1307                    pending_discard_init_fixups,
1308                );
1309            } else if !ds_aspects.contains(hal::FormatAspects::DEPTH) {
1310                Self::add_pass_texture_init_actions(
1311                    at.stencil.load_op(),
1312                    at.stencil.store_op(),
1313                    texture_memory_actions,
1314                    view,
1315                    pending_discard_init_fixups,
1316                );
1317            } else {
1318                // This is the only place (anywhere in wgpu) where Stencil &
1319                // Depth init state can diverge.
1320                //
1321                // To safe us the overhead of tracking init state of texture
1322                // aspects everywhere, we're going to cheat a little bit in
1323                // order to keep the init state of both Stencil and Depth
1324                // aspects in sync. The expectation is that we hit this path
1325                // extremely rarely!
1326                //
1327                // Diverging LoadOp, i.e. Load + Clear:
1328                //
1329                // Record MemoryInitKind::NeedsInitializedMemory for the entire
1330                // surface, a bit wasteful on unit but no negative effect!
1331                //
1332                // Rationale: If the loaded channel is uninitialized it needs
1333                // clearing, the cleared channel doesn't care. (If everything is
1334                // already initialized nothing special happens)
1335                //
1336                // (possible minor optimization: Clear caused by
1337                // NeedsInitializedMemory should know that it doesn't need to
1338                // clear the aspect that was set to C)
1339                let need_init_beforehand =
1340                    at.depth.load_op() == LoadOp::Load || at.stencil.load_op() == LoadOp::Load;
1341                if need_init_beforehand {
1342                    pending_discard_init_fixups.extend(
1343                        texture_memory_actions.register_init_action(&TextureInitTrackerAction {
1344                            texture: view.parent.clone(),
1345                            range: TextureInitRange::from(view.selector.clone()),
1346                            kind: MemoryInitKind::NeedsInitializedMemory,
1347                        }),
1348                    );
1349                }
1350
1351                // Diverging Store, i.e. Discard + Store:
1352                //
1353                // Immediately zero out channel that is set to discard after
1354                // we're done with the render pass. This allows us to set the
1355                // entire surface to MemoryInitKind::ImplicitlyInitialized (if
1356                // it isn't already set to NeedsInitializedMemory).
1357                //
1358                // (possible optimization: Delay and potentially drop this zeroing)
1359                if at.depth.store_op() != at.stencil.store_op() {
1360                    if !need_init_beforehand {
1361                        texture_memory_actions.register_implicit_init(
1362                            &view.parent,
1363                            TextureInitRange::from(view.selector.clone()),
1364                        );
1365                    }
1366                    divergent_discarded_depth_stencil_aspect = Some((
1367                        if at.depth.store_op() == StoreOp::Discard {
1368                            wgt::TextureAspect::DepthOnly
1369                        } else {
1370                            wgt::TextureAspect::StencilOnly
1371                        },
1372                        view.clone(),
1373                    ));
1374                } else if at.depth.store_op() == StoreOp::Discard {
1375                    // Both are discarded using the regular path.
1376                    discarded_surfaces.push(TextureSurfaceDiscard {
1377                        texture: view.parent.clone(),
1378                        mip_level: view.selector.mips.start,
1379                        layer: view.selector.layers.start,
1380                    });
1381                }
1382            }
1383
1384            is_depth_read_only = at.depth.is_readonly();
1385            is_stencil_read_only = at.stencil.is_readonly();
1386
1387            let usage = if is_depth_read_only
1388                && is_stencil_read_only
1389                && device
1390                    .downlevel
1391                    .flags
1392                    .contains(wgt::DownlevelFlags::READ_ONLY_DEPTH_STENCIL)
1393            {
1394                // If the texture supports TEXTURE_BINDING, it can be used as a shader
1395                // resource and a read-only depth attachment simultaneously. But if it
1396                // doesn't support TEXTURE_BINDING, don't attempt to transition it to a
1397                // shader resource state, because DX12 will raise an error.
1398                if view.desc.usage.contains(TextureUsages::TEXTURE_BINDING) {
1399                    wgt::TextureUses::DEPTH_STENCIL_READ | wgt::TextureUses::RESOURCE
1400                } else {
1401                    wgt::TextureUses::DEPTH_STENCIL_READ
1402                }
1403            } else {
1404                wgt::TextureUses::DEPTH_STENCIL_WRITE
1405            };
1406            render_attachments.push(view.to_render_attachment(usage));
1407
1408            depth_stencil = Some(hal::DepthStencilAttachment {
1409                target: hal::Attachment {
1410                    view: view.try_raw(snatch_guard)?,
1411                    usage,
1412                },
1413                depth_ops: at.depth.hal_ops(),
1414                stencil_ops: at.stencil.hal_ops(),
1415                clear_value: (at.depth.clear_value(), at.stencil.clear_value()),
1416            });
1417        }
1418
1419        let mut attachment_set = crate::FastHashSet::default();
1420
1421        let mut color_attachments_hal =
1422            ArrayVec::<Option<hal::ColorAttachment<_>>, { hal::MAX_COLOR_ATTACHMENTS }>::new();
1423        for (index, attachment) in color_attachments.iter().enumerate() {
1424            let at = if let Some(attachment) = attachment.as_ref() {
1425                attachment
1426            } else {
1427                color_attachments_hal.push(None);
1428                continue;
1429            };
1430            let color_view: &TextureView = &at.view;
1431            color_view.same_device(device)?;
1432            check_multiview(color_view)?;
1433            add_view(
1434                color_view,
1435                AttachmentErrorLocation::Color {
1436                    index,
1437                    resolve: false,
1438                },
1439            )?;
1440
1441            if !color_view.desc.aspects().intersects(
1442                hal::FormatAspects::COLOR
1443                    | hal::FormatAspects::PLANE_0
1444                    | hal::FormatAspects::PLANE_1
1445                    | hal::FormatAspects::PLANE_2,
1446            ) {
1447                return Err(RenderPassErrorInner::ColorAttachment(
1448                    ColorAttachmentError::InvalidFormat(color_view.desc.format),
1449                ));
1450            }
1451
1452            if color_view.desc.dimension == TextureViewDimension::D3 {
1453                if let Some(depth_slice) = at.depth_slice {
1454                    let mip = color_view.desc.range.base_mip_level;
1455                    let mip_size = color_view
1456                        .parent
1457                        .desc
1458                        .size
1459                        .mip_level_size(mip, color_view.parent.desc.dimension);
1460                    let limit = mip_size.depth_or_array_layers;
1461                    if depth_slice >= limit {
1462                        return Err(RenderPassErrorInner::ColorAttachment(
1463                            ColorAttachmentError::DepthSliceLimit {
1464                                given: depth_slice,
1465                                limit,
1466                            },
1467                        ));
1468                    }
1469                } else {
1470                    return Err(RenderPassErrorInner::ColorAttachment(
1471                        ColorAttachmentError::MissingDepthSlice,
1472                    ));
1473                }
1474            } else if at.depth_slice.is_some() {
1475                return Err(RenderPassErrorInner::ColorAttachment(
1476                    ColorAttachmentError::UnneededDepthSlice,
1477                ));
1478            }
1479
1480            validation::validate_color_attachment_bytes_per_sample(
1481                color_attachments
1482                    .iter()
1483                    .flatten()
1484                    .map(|at| at.view.desc.format),
1485                device.limits.max_color_attachment_bytes_per_sample,
1486            )
1487            .map_err(RenderPassErrorInner::ColorAttachment)?;
1488
1489            fn check_attachment_overlap(
1490                attachment_set: &mut crate::FastHashSet<(crate::track::TrackerIndex, u32, u32)>,
1491                view: &TextureView,
1492                depth_slice: Option<u32>,
1493            ) -> Result<(), ColorAttachmentError> {
1494                let mut insert = |slice| {
1495                    let mip_level = view.desc.range.base_mip_level;
1496                    if attachment_set.insert((
1497                        view.parent.tracking_data.tracker_index(),
1498                        mip_level,
1499                        slice,
1500                    )) {
1501                        Ok(())
1502                    } else {
1503                        Err(ColorAttachmentError::SubresourceOverlap {
1504                            view: view.error_ident(),
1505                            mip_level,
1506                            depth_or_array_layer: slice,
1507                        })
1508                    }
1509                };
1510                match view.desc.dimension {
1511                    TextureViewDimension::D2 => {
1512                        insert(view.desc.range.base_array_layer)?;
1513                    }
1514                    TextureViewDimension::D2Array => {
1515                        for layer in view.selector.layers.clone() {
1516                            insert(layer)?;
1517                        }
1518                    }
1519                    TextureViewDimension::D3 => {
1520                        insert(depth_slice.unwrap())?;
1521                    }
1522                    _ => unreachable!(),
1523                };
1524                Ok(())
1525            }
1526
1527            check_attachment_overlap(&mut attachment_set, color_view, at.depth_slice)?;
1528
1529            Self::add_pass_texture_init_actions(
1530                at.load_op,
1531                at.store_op,
1532                texture_memory_actions,
1533                color_view,
1534                pending_discard_init_fixups,
1535            );
1536            render_attachments
1537                .push(color_view.to_render_attachment(wgt::TextureUses::COLOR_TARGET));
1538
1539            let mut hal_resolve_target = None;
1540            if let Some(resolve_view) = &at.resolve_target {
1541                resolve_view.same_device(device)?;
1542                check_multiview(resolve_view)?;
1543
1544                check_attachment_overlap(&mut attachment_set, resolve_view, None)?;
1545
1546                let resolve_location = AttachmentErrorLocation::Color {
1547                    index,
1548                    resolve: true,
1549                };
1550
1551                let render_extent = resolve_view.state()?.render_extent.map_err(|reason| {
1552                    RenderPassErrorInner::TextureViewIsNotRenderable {
1553                        location: resolve_location,
1554                        reason,
1555                    }
1556                })?;
1557                if color_view.state()?.render_extent.unwrap() != render_extent {
1558                    return Err(RenderPassErrorInner::AttachmentsDimensionMismatch {
1559                        expected_location: attachment_location,
1560                        expected_extent: extent.unwrap_or_default(),
1561                        actual_location: resolve_location,
1562                        actual_extent: render_extent,
1563                    });
1564                }
1565                if color_view.samples == 1 || resolve_view.samples != 1 {
1566                    return Err(RenderPassErrorInner::InvalidResolveSampleCounts {
1567                        location: resolve_location,
1568                        src: color_view.samples,
1569                        dst: resolve_view.samples,
1570                    });
1571                }
1572                if color_view.desc.format != resolve_view.desc.format {
1573                    return Err(RenderPassErrorInner::MismatchedResolveTextureFormat {
1574                        location: resolve_location,
1575                        src: color_view.desc.format,
1576                        dst: resolve_view.desc.format,
1577                    });
1578                }
1579                if !resolve_view
1580                    .format_features
1581                    .flags
1582                    .contains(wgt::TextureFormatFeatureFlags::MULTISAMPLE_RESOLVE)
1583                {
1584                    return Err(RenderPassErrorInner::UnsupportedResolveTargetFormat {
1585                        location: resolve_location,
1586                        format: resolve_view.desc.format,
1587                    });
1588                }
1589                if resolve_view
1590                    .desc
1591                    .usage
1592                    .contains(TextureUsages::TRANSIENT_ATTACHMENT)
1593                {
1594                    return Err(RenderPassErrorInner::InvalidTransientResolveTarget {
1595                        location: resolve_location,
1596                    });
1597                }
1598
1599                texture_memory_actions.register_implicit_init(
1600                    &resolve_view.parent,
1601                    TextureInitRange::from(resolve_view.selector.clone()),
1602                );
1603                render_attachments
1604                    .push(resolve_view.to_render_attachment(wgt::TextureUses::COLOR_TARGET));
1605
1606                hal_resolve_target = Some(hal::Attachment {
1607                    view: resolve_view.try_raw(snatch_guard)?,
1608                    usage: wgt::TextureUses::COLOR_TARGET,
1609                });
1610            }
1611
1612            color_attachments_hal.push(Some(hal::ColorAttachment {
1613                target: hal::Attachment {
1614                    view: color_view.try_raw(snatch_guard)?,
1615                    usage: wgt::TextureUses::COLOR_TARGET,
1616                },
1617                depth_slice: at.depth_slice,
1618                resolve_target: hal_resolve_target,
1619                ops: at.hal_ops(),
1620                clear_value: at.clear_value(),
1621            }));
1622        }
1623
1624        let extent = extent.ok_or(RenderPassErrorInner::MissingAttachments)?;
1625
1626        let detected_multiview =
1627            detected_multiview.expect("Multiview was not detected, no attachments");
1628        if let Some(mask) = multiview_mask {
1629            // 0x01 will have msb 0
1630            let mask_msb = 31 - mask.leading_zeros();
1631            let detected_mv = detected_multiview.map(NonZeroU32::get).unwrap_or(1);
1632            if mask_msb >= detected_mv {
1633                return Err(RenderPassErrorInner::MultiViewMismatch);
1634            }
1635            if mask.get() != (1 << detected_mv) - 1 {
1636                device.require_features(wgt::Features::SELECTIVE_MULTIVIEW)?;
1637            }
1638        }
1639
1640        let attachment_formats = AttachmentData {
1641            colors: color_attachments
1642                .iter()
1643                .map(|at| at.as_ref().map(|at| at.view.desc.format))
1644                .collect(),
1645            resolves: color_attachments
1646                .iter()
1647                .filter_map(|at| {
1648                    at.as_ref().and_then(|at| {
1649                        at.resolve_target
1650                            .as_ref()
1651                            .map(|resolve| resolve.desc.format)
1652                    })
1653                })
1654                .collect(),
1655            depth_stencil: depth_stencil_attachment
1656                .as_ref()
1657                .map(|at| at.view.desc.format),
1658        };
1659
1660        let context = RenderPassContext {
1661            attachments: attachment_formats,
1662            sample_count,
1663            multiview_mask,
1664        };
1665
1666        let timestamp_writes_hal = if let Some(tw) = timestamp_writes.as_ref() {
1667            let query_set = &tw.query_set;
1668            query_set.same_device(device)?;
1669
1670            if let Some(index) = tw.beginning_of_pass_write_index {
1671                pending_query_resets.use_query_set(query_set, index);
1672            }
1673            if let Some(index) = tw.end_of_pass_write_index {
1674                pending_query_resets.use_query_set(query_set, index);
1675            }
1676
1677            record_pass_timestamp_writes(tw, query_set_writes);
1678
1679            Some(hal::PassTimestampWrites {
1680                query_set: query_set.try_raw(snatch_guard)?,
1681                beginning_of_pass_write_index: tw.beginning_of_pass_write_index,
1682                end_of_pass_write_index: tw.end_of_pass_write_index,
1683            })
1684        } else {
1685            None
1686        };
1687
1688        let occlusion_query_set_hal = if let Some(query_set) = occlusion_query_set.as_ref() {
1689            query_set.same_device(device)?;
1690            Some(query_set.try_raw(snatch_guard)?)
1691        } else {
1692            None
1693        };
1694
1695        let hal_desc = hal::RenderPassDescriptor {
1696            label: hal_label,
1697            extent,
1698            sample_count,
1699            color_attachments: &color_attachments_hal,
1700            depth_stencil_attachment: depth_stencil,
1701            multiview_mask,
1702            timestamp_writes: timestamp_writes_hal,
1703            occlusion_query_set: occlusion_query_set_hal,
1704        };
1705        unsafe {
1706            encoder
1707                .begin_render_pass(&hal_desc)
1708                .map_err(|e| device.handle_hal_error(e))?;
1709        };
1710        drop(color_attachments_hal); // Drop, so we can consume `color_attachments` for the tracker.
1711
1712        // Can't borrow the tracker more than once, so have to add to the tracker after the `begin_render_pass` hal call.
1713        if let Some(tw) = timestamp_writes.take() {
1714            trackers.query_sets.insert_single(tw.query_set);
1715        };
1716        if let Some(occlusion_query_set) = occlusion_query_set.take() {
1717            trackers.query_sets.insert_single(occlusion_query_set);
1718        };
1719        if let Some(at) = depth_stencil_attachment.take() {
1720            trackers.views.insert_single(at.view.clone());
1721        }
1722        for at in color_attachments.iter().flatten() {
1723            trackers.views.insert_single(at.view.clone());
1724            if let Some(resolve_target) = at.resolve_target.clone() {
1725                trackers.views.insert_single(resolve_target);
1726            }
1727        }
1728
1729        Ok(Self {
1730            context,
1731            render_attachments,
1732            is_depth_read_only,
1733            is_stencil_read_only,
1734            extent,
1735            divergent_discarded_depth_stencil_aspect,
1736            multiview_mask,
1737        })
1738    }
1739
1740    fn finish(
1741        self,
1742        device: &Device,
1743        raw: &mut dyn hal::DynCommandEncoder,
1744        snatch_guard: &SnatchGuard,
1745        scope: &mut UsageScope<'_>,
1746        instance_flags: InstanceFlags,
1747    ) -> Result<(), RenderPassErrorInner> {
1748        profiling::scope!("RenderPassInfo::finish");
1749        unsafe {
1750            raw.end_render_pass();
1751        }
1752
1753        for ra in self.render_attachments {
1754            let texture = &ra.texture;
1755            texture.check_usage(TextureUsages::RENDER_ATTACHMENT)?;
1756
1757            // the tracker set of the pass is always in "extend" mode
1758            unsafe {
1759                scope
1760                    .textures
1761                    .merge_single(texture, Some(ra.selector.clone()), ra.usage)?
1762            };
1763        }
1764
1765        // If either only stencil or depth was discarded, we put in a special
1766        // clear pass to keep the init status of the aspects in sync. We do this
1767        // so we don't need to track init state for depth/stencil aspects
1768        // individually.
1769        //
1770        // Note that we don't go the usual route of "brute force" initializing
1771        // the texture when need arises here, since this path is actually
1772        // something a user may genuinely want (where as the other cases are
1773        // more seen along the lines as gracefully handling a user error).
1774        if let Some((aspect, view)) = self.divergent_discarded_depth_stencil_aspect {
1775            let (depth_ops, stencil_ops) = if aspect == wgt::TextureAspect::DepthOnly {
1776                (
1777                    hal::AttachmentOps::LOAD_CLEAR | hal::AttachmentOps::STORE, // clear depth
1778                    hal::AttachmentOps::LOAD | hal::AttachmentOps::STORE,       // unchanged stencil
1779                )
1780            } else {
1781                (
1782                    hal::AttachmentOps::LOAD | hal::AttachmentOps::STORE, // unchanged stencil
1783                    hal::AttachmentOps::LOAD_CLEAR | hal::AttachmentOps::STORE, // clear depth
1784                )
1785            };
1786            let desc = hal::RenderPassDescriptor::<'_, _, dyn hal::DynTextureView> {
1787                label: hal_label(
1788                    Some("(wgpu internal) Zero init discarded depth/stencil aspect"),
1789                    instance_flags,
1790                ),
1791                extent: view.state()?.render_extent.unwrap(),
1792                sample_count: view.samples,
1793                color_attachments: &[],
1794                depth_stencil_attachment: Some(hal::DepthStencilAttachment {
1795                    target: hal::Attachment {
1796                        view: view.try_raw(snatch_guard)?,
1797                        usage: wgt::TextureUses::DEPTH_STENCIL_WRITE,
1798                    },
1799                    depth_ops,
1800                    stencil_ops,
1801                    clear_value: (0.0, 0),
1802                }),
1803                multiview_mask: self.multiview_mask,
1804                timestamp_writes: None,
1805                occlusion_query_set: None,
1806            };
1807            unsafe {
1808                raw.begin_render_pass(&desc)
1809                    .map_err(|e| device.handle_hal_error(e))?;
1810                raw.end_render_pass();
1811            }
1812        }
1813
1814        Ok(())
1815    }
1816}
1817
1818fn check_transient_attachment_ops<V>(load_op: LoadOp<V>, store_op: StoreOp) -> bool {
1819    matches!(
1820        (load_op, store_op),
1821        (LoadOp::Clear(_) | LoadOp::DontCare(_), StoreOp::Discard)
1822    )
1823}
1824
1825impl CommandEncoder {
1826    pub fn begin_render_pass(
1827        self: &Arc<Self>,
1828        desc: ResolvedRenderPassDescriptor<'_>,
1829    ) -> (RenderPass, Option<CommandEncoderError>) {
1830        use EncoderStateError as SErr;
1831
1832        fn fill_arc_desc(
1833            desc: ResolvedRenderPassDescriptor<'_>,
1834            arc_desc: &mut ArcRenderPassDescriptor,
1835            device: &Device,
1836        ) -> Result<(), RenderPassErrorInner> {
1837            device.check_is_valid()?;
1838
1839            let max_color_attachments = device.limits.max_color_attachments as usize;
1840            if desc.color_attachments.len() > max_color_attachments {
1841                return Err(RenderPassErrorInner::ColorAttachment(
1842                    ColorAttachmentError::TooMany {
1843                        given: desc.color_attachments.len(),
1844                        limit: max_color_attachments,
1845                    },
1846                ));
1847            }
1848
1849            for color_attachment in desc.color_attachments.iter() {
1850                if let Some(RenderPassColorAttachment {
1851                    view,
1852                    depth_slice,
1853                    resolve_target,
1854                    load_op,
1855                    store_op,
1856                }) = color_attachment
1857                {
1858                    view.check_valid()?;
1859                    view.same_device(device)?;
1860                    if matches!(*load_op, LoadOp::DontCare(..))
1861                        && device
1862                            .instance_flags
1863                            .contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE)
1864                    {
1865                        return Err(RenderPassErrorInner::ColorAttachment(
1866                            ColorAttachmentError::LoadOpDontCareUnderStrictWebgpuCompliance,
1867                        ));
1868                    }
1869
1870                    if view
1871                        .desc
1872                        .usage
1873                        .contains(TextureUsages::TRANSIENT_ATTACHMENT)
1874                        && !check_transient_attachment_ops(*load_op, *store_op)
1875                    {
1876                        return Err(RenderPassErrorInner::ColorAttachment(
1877                            ColorAttachmentError::InvalidTransientAttachmentOp((
1878                                *load_op, *store_op,
1879                            )),
1880                        ));
1881                    }
1882
1883                    let resolve_target = if let Some(resolve_target) = resolve_target {
1884                        resolve_target.check_valid()?;
1885                        resolve_target.same_device(device)?;
1886
1887                        Some(resolve_target)
1888                    } else {
1889                        None
1890                    };
1891
1892                    arc_desc
1893                        .color_attachments
1894                        .push(Some(RenderPassColorAttachment {
1895                            view: Arc::clone(view),
1896                            depth_slice: *depth_slice,
1897                            resolve_target: resolve_target.map(Arc::clone),
1898                            load_op: *load_op,
1899                            store_op: *store_op,
1900                        }));
1901                } else {
1902                    arc_desc.color_attachments.push(None);
1903                }
1904            }
1905
1906            // https://gpuweb.github.io/gpuweb/#abstract-opdef-gpurenderpassdepthstencilattachment-gpurenderpassdepthstencilattachment-valid-usage
1907            arc_desc.depth_stencil_attachment = if let Some(depth_stencil_attachment) =
1908                desc.depth_stencil_attachment
1909            {
1910                let view = depth_stencil_attachment.view;
1911                view.check_valid()?;
1912                view.same_device(device)?;
1913
1914                let format = view.desc.format;
1915                if !format.is_depth_stencil_format() {
1916                    return Err(RenderPassErrorInner::InvalidAttachment(
1917                        AttachmentError::InvalidDepthStencilAttachmentFormat(view.desc.format),
1918                    ));
1919                }
1920
1921                if view
1922                    .desc
1923                    .usage
1924                    .contains(TextureUsages::TRANSIENT_ATTACHMENT)
1925                {
1926                    // We raise errors here for transient depth/stencil attachments if they
1927                    // are read only, or use unsupported ops. `resolve` (called below)
1928                    // validates that operations are specified iff the attachment is not
1929                    // read-only.
1930                    if format.has_depth_aspect() {
1931                        match depth_stencil_attachment.depth {
1932                            PassChannel {
1933                                load_op: Some(load_op),
1934                                store_op: Some(store_op),
1935                                read_only: _,
1936                            } => {
1937                                if !check_transient_attachment_ops(load_op, store_op) {
1938                                    return Err(RenderPassErrorInner::InvalidAttachment(
1939                                        AttachmentError::InvalidTransientDepthAttachmentOps((
1940                                            load_op, store_op,
1941                                        )),
1942                                    ));
1943                                }
1944                            }
1945                            PassChannel {
1946                                read_only: true, ..
1947                            } => {
1948                                return Err(RenderPassErrorInner::InvalidAttachment(
1949                                    AttachmentError::ReadOnlyTransientDepthAttachment,
1950                                ))
1951                            }
1952                            _ => {}
1953                        }
1954                    }
1955
1956                    if format.has_stencil_aspect() {
1957                        match depth_stencil_attachment.stencil {
1958                            PassChannel {
1959                                load_op: Some(load_op),
1960                                store_op: Some(store_op),
1961                                read_only: _,
1962                            } => {
1963                                if !check_transient_attachment_ops(load_op, store_op) {
1964                                    return Err(RenderPassErrorInner::InvalidAttachment(
1965                                        AttachmentError::InvalidTransientStencilAttachmentOps((
1966                                            load_op, store_op,
1967                                        )),
1968                                    ));
1969                                }
1970                            }
1971                            PassChannel {
1972                                read_only: true, ..
1973                            } => {
1974                                return Err(RenderPassErrorInner::InvalidAttachment(
1975                                    AttachmentError::ReadOnlyTransientStencilAttachment,
1976                                ))
1977                            }
1978                            _ => {}
1979                        }
1980                    }
1981                }
1982
1983                Some(ResolvedRenderPassDepthStencilAttachment {
1984                    view,
1985                    depth: if format.has_depth_aspect() {
1986                        depth_stencil_attachment
1987                            .depth
1988                            .resolve(device.instance_flags, |clear| {
1989                                if let Some(clear) = clear {
1990                                    // If this.depthLoadOp is "clear", this.depthClearValue must be provided and must be between 0.0 and 1.0, inclusive.
1991                                    if !(0.0..=1.0).contains(&clear) {
1992                                        Err(AttachmentError::ClearValueOutOfRange(clear))
1993                                    } else {
1994                                        Ok(clear)
1995                                    }
1996                                } else {
1997                                    Err(AttachmentError::NoClearValue)
1998                                }
1999                            })?
2000                    } else {
2001                        if depth_stencil_attachment.depth.load_op.is_some()
2002                            || depth_stencil_attachment.depth.store_op.is_some()
2003                        {
2004                            return Err(RenderPassErrorInner::InvalidAttachment(
2005                                AttachmentError::DepthOpsWithoutAspect {
2006                                    format,
2007                                    ops: (
2008                                        depth_stencil_attachment.depth.load_op,
2009                                        depth_stencil_attachment.depth.store_op,
2010                                    ),
2011                                },
2012                            ));
2013                        }
2014                        ResolvedPassChannel::ReadOnly
2015                    },
2016                    stencil: if format.has_stencil_aspect() {
2017                        depth_stencil_attachment.stencil.resolve(
2018                            device.instance_flags,
2019                            |clear| {
2020                                Ok(convert_stencil_value(
2021                                    clear.unwrap_or_default(),
2022                                    Some(format),
2023                                ))
2024                            },
2025                        )?
2026                    } else {
2027                        if depth_stencil_attachment.stencil.load_op.is_some()
2028                            || depth_stencil_attachment.stencil.store_op.is_some()
2029                        {
2030                            return Err(RenderPassErrorInner::InvalidAttachment(
2031                                AttachmentError::StencilOpsWithoutAspect {
2032                                    format,
2033                                    ops: (
2034                                        depth_stencil_attachment.stencil.load_op,
2035                                        depth_stencil_attachment.stencil.store_op,
2036                                    ),
2037                                },
2038                            ));
2039                        }
2040                        ResolvedPassChannel::ReadOnly
2041                    },
2042                })
2043            } else {
2044                None
2045            };
2046
2047            arc_desc.timestamp_writes = desc
2048                .timestamp_writes
2049                .map(|tw| {
2050                    CommandEncoder::validate_pass_timestamp_writes::<RenderPassErrorInner>(
2051                        device, &tw,
2052                    )
2053                })
2054                .transpose()?;
2055
2056            arc_desc.occlusion_query_set =
2057                if let Some(occlusion_query_set) = desc.occlusion_query_set {
2058                    occlusion_query_set.check_is_valid()?;
2059                    occlusion_query_set.same_device(device)?;
2060
2061                    if !matches!(occlusion_query_set.desc.ty, wgt::QueryType::Occlusion) {
2062                        return Err(QueryUseError::IncompatibleType {
2063                            set_type: occlusion_query_set.desc.ty.into(),
2064                            query_type: super::SimplifiedQueryType::Occlusion,
2065                        }
2066                        .into());
2067                    }
2068
2069                    Some(occlusion_query_set)
2070                } else {
2071                    None
2072                };
2073
2074            arc_desc.multiview_mask = desc.multiview_mask;
2075
2076            Ok(())
2077        }
2078
2079        let scope = PassErrorScope::Pass;
2080        let mut cmd_buf_data = self.data.lock();
2081
2082        match cmd_buf_data.lock_encoder() {
2083            Ok(()) => {
2084                drop(cmd_buf_data);
2085                let label = desc.label.clone();
2086                let mut arc_desc = ArcRenderPassDescriptor {
2087                    label: &label,
2088                    timestamp_writes: None,
2089                    color_attachments: ArrayVec::new(),
2090                    depth_stencil_attachment: None,
2091                    occlusion_query_set: None,
2092                    multiview_mask: None,
2093                };
2094                match fill_arc_desc(desc, &mut arc_desc, &self.device) {
2095                    Ok(()) => (RenderPass::new(self.clone(), arc_desc), None),
2096                    Err(err) => (
2097                        RenderPass::new_invalid(self.clone(), &label, err.map_pass_err(scope)),
2098                        None,
2099                    ),
2100                }
2101            }
2102            Err(err @ SErr::Locked) => {
2103                // Attempting to open a new pass while the encoder is locked
2104                // invalidates the encoder, but does not generate a validation
2105                // error.
2106                cmd_buf_data.invalidate(err.clone());
2107                drop(cmd_buf_data);
2108                (
2109                    RenderPass::new_invalid(self.clone(), &desc.label, err.map_pass_err(scope)),
2110                    None,
2111                )
2112            }
2113            Err(err @ (SErr::Ended | SErr::Submitted)) => {
2114                // Attempting to open a new pass after the encode has ended
2115                // generates an immediate validation error.
2116                drop(cmd_buf_data);
2117                (
2118                    RenderPass::new_invalid(
2119                        self.clone(),
2120                        &desc.label,
2121                        err.clone().map_pass_err(scope),
2122                    ),
2123                    Some(err.into()),
2124                )
2125            }
2126            Err(err @ SErr::Invalid) => {
2127                // Passes can be opened even on an invalid encoder. Such passes
2128                // are even valid, but since there's no visible side-effect of
2129                // the pass being valid and there's no point in storing recorded
2130                // commands that will ultimately be discarded, we open an
2131                // invalid pass to save that work.
2132                drop(cmd_buf_data);
2133                (
2134                    RenderPass::new_invalid(self.clone(), &desc.label, err.map_pass_err(scope)),
2135                    None,
2136                )
2137            }
2138            Err(SErr::Unlocked) => {
2139                unreachable!("lock_encoder cannot fail due to the encoder being unlocked")
2140            }
2141        }
2142    }
2143}
2144
2145impl RenderPass {
2146    pub fn end(&mut self) -> Result<(), EncoderStateError> {
2147        profiling::scope!(
2148            "CommandEncoder::run_render_pass {}",
2149            self.base.label.as_deref().unwrap_or("")
2150        );
2151
2152        let cmd_enc = self.parent.take().ok_or(EncoderStateError::Ended)?;
2153        let mut cmd_buf_data = cmd_enc.data.lock();
2154
2155        cmd_buf_data.unlock_encoder()?;
2156
2157        let base = self.base.take();
2158
2159        if let Err(RenderPassError { inner, scope: _ }) = &base {
2160            if let RenderPassErrorInner::EncoderState(
2161                err @ (EncoderStateError::Locked | EncoderStateError::Ended),
2162            ) = inner.as_ref()
2163            {
2164                // Most encoding errors are detected and raised within `finish()`.
2165                //
2166                // However, we raise a validation error here if the pass was opened
2167                // within another pass, or on a finished encoder. The latter is
2168                // particularly important, because in that case reporting errors via
2169                // `CommandEncoder::finish` is not possible.
2170                return Err(err.clone());
2171            }
2172        }
2173
2174        cmd_buf_data.push_with(|| -> Result<_, RenderPassError> {
2175            Ok(ArcCommand::RunRenderPass {
2176                pass: base?,
2177                color_attachments: SmallVec::from(self.color_attachments.as_slice()),
2178                depth_stencil_attachment: self.depth_stencil_attachment.take(),
2179                timestamp_writes: self.timestamp_writes.take(),
2180                occlusion_query_set: self.occlusion_query_set.take(),
2181                multiview_mask: self.multiview_mask,
2182            })
2183        })
2184    }
2185}
2186
2187impl Global {
2188    /// Creates a render pass.
2189    ///
2190    /// If creation fails, an invalid pass is returned. Attempting to record
2191    /// commands into an invalid pass is permitted, but a validation error will
2192    /// ultimately be generated when the parent encoder is finished, and it is
2193    /// not possible to run any commands from the invalid pass.
2194    ///
2195    /// If successful, puts the encoder into the [`Locked`] state.
2196    ///
2197    /// [`Locked`]: crate::command::CommandEncoderStatus::Locked
2198    pub fn command_encoder_begin_render_pass(
2199        &self,
2200        encoder_id: id::CommandEncoderId,
2201        desc: &RenderPassDescriptor<'_>,
2202    ) -> (RenderPass, Option<CommandEncoderError>) {
2203        let hub = &self.hub;
2204
2205        let cmd_enc = hub.command_encoders.get(encoder_id);
2206
2207        let texture_views = hub.texture_views.read();
2208        let query_sets = hub.query_sets.read();
2209
2210        let desc = ResolvedRenderPassDescriptor {
2211            label: desc.label.as_deref().map(Cow::Borrowed),
2212            color_attachments: Cow::Owned(
2213                desc.color_attachments
2214                    .iter()
2215                    .map(|at| {
2216                        at.as_ref().map(|at| RenderPassColorAttachment {
2217                            view: texture_views.get(at.view),
2218                            depth_slice: at.depth_slice,
2219                            resolve_target: at
2220                                .resolve_target
2221                                .as_ref()
2222                                .map(|rt| texture_views.get(*rt)),
2223                            load_op: at.load_op,
2224                            store_op: at.store_op,
2225                        })
2226                    })
2227                    .collect(),
2228            ),
2229            depth_stencil_attachment: desc.depth_stencil_attachment.as_ref().map(|at| {
2230                RenderPassDepthStencilAttachment {
2231                    view: texture_views.get(at.view),
2232                    depth: at.depth.clone(),
2233                    stencil: at.stencil.clone(),
2234                }
2235            }),
2236            timestamp_writes: desc
2237                .timestamp_writes
2238                .as_ref()
2239                .map(|tw| PassTimestampWrites {
2240                    query_set: query_sets.get(tw.query_set),
2241                    beginning_of_pass_write_index: tw.beginning_of_pass_write_index,
2242                    end_of_pass_write_index: tw.end_of_pass_write_index,
2243                }),
2244            occlusion_query_set: desc
2245                .occlusion_query_set
2246                .as_ref()
2247                .map(|query_set| query_sets.get(*query_set)),
2248            multiview_mask: desc.multiview_mask,
2249        };
2250
2251        drop(texture_views);
2252        drop(query_sets);
2253
2254        cmd_enc.begin_render_pass(desc)
2255    }
2256
2257    pub fn command_encoder_begin_render_pass_with_id(
2258        &self,
2259        encoder_id: id::CommandEncoderId,
2260        desc: &RenderPassDescriptor<'_>,
2261        id_in: Option<id::RenderPassEncoderId>,
2262    ) -> (id::RenderPassEncoderId, Option<CommandEncoderError>) {
2263        let hub = &self.hub;
2264        let fid = hub.render_passes.prepare(id_in);
2265        let (render_pass, error) = self.command_encoder_begin_render_pass(encoder_id, desc);
2266        // no lock rank here because only one thread should be using renderpass
2267        // and it's only used by id variants of render pass methods on global
2268        // so no deadlock (or concurrent lock) should happen in practise
2269        let id = fid.assign(Arc::new(Mutex::new(render_pass)));
2270        (id, error)
2271    }
2272
2273    pub fn render_pass_end(&self, pass: &mut RenderPass) -> Result<(), EncoderStateError> {
2274        pass.end()
2275    }
2276
2277    pub fn render_pass_end_with_id(
2278        &self,
2279        pass: id::RenderPassEncoderId,
2280    ) -> Result<(), EncoderStateError> {
2281        let pass = self.hub.render_passes.get(pass);
2282        let mut pass = pass
2283            .try_lock()
2284            .expect("RenderPasses should not be accessed concurrently");
2285        self.render_pass_end(&mut pass)
2286    }
2287
2288    pub fn render_pass_drop(&self, pass: id::RenderPassEncoderId) {
2289        self.hub.render_passes.remove(pass);
2290    }
2291}
2292
2293pub(super) fn encode_render_pass(
2294    parent_state: &mut EncodingState<InnerCommandEncoder>,
2295    mut base: BasePass<ArcRenderCommand, Infallible>,
2296    color_attachments: ColorAttachments<Arc<TextureView>>,
2297    mut depth_stencil_attachment: Option<
2298        ResolvedRenderPassDepthStencilAttachment<Arc<TextureView>>,
2299    >,
2300    mut timestamp_writes: Option<PassTimestampWrites>,
2301    occlusion_query_set: Option<Arc<QuerySet>>,
2302    multiview_mask: Option<NonZeroU32>,
2303) -> Result<(), RenderPassError> {
2304    let pass_scope = PassErrorScope::Pass;
2305
2306    let device = parent_state.device;
2307
2308    let mut indirect_draw_validation_batcher = crate::indirect_validation::DrawBatcher::new();
2309
2310    // We automatically keep extending command buffers over time, and because
2311    // we want to insert a command buffer _before_ what we're about to record,
2312    // we need to make sure to close the previous one.
2313    parent_state
2314        .raw_encoder
2315        .close_if_open()
2316        .map_pass_err(pass_scope)?;
2317    let raw_encoder = parent_state
2318        .raw_encoder
2319        .open_pass(base.label.as_deref())
2320        .map_pass_err(pass_scope)?;
2321
2322    let (scope, pending_discard_init_fixups, mut pending_query_resets) = {
2323        let mut pending_query_resets = QueryResetMap::new();
2324        let mut pending_discard_init_fixups = SurfacesInDiscardState::new();
2325
2326        let info = RenderPassInfo::start(
2327            device,
2328            hal_label(base.label.as_deref(), device.instance_flags),
2329            &color_attachments,
2330            depth_stencil_attachment.take(),
2331            timestamp_writes.take(),
2332            // Still needed down the line.
2333            // TODO(wumpf): by restructuring the code, we could get rid of some of this Arc clone.
2334            occlusion_query_set.clone(),
2335            raw_encoder,
2336            parent_state.tracker,
2337            parent_state.texture_memory_actions,
2338            &mut pending_query_resets,
2339            &mut pending_discard_init_fixups,
2340            parent_state.snatch_guard,
2341            parent_state.query_set_writes,
2342            multiview_mask,
2343        )
2344        .map_pass_err(pass_scope)?;
2345
2346        let indices = &device.tracker_indices;
2347        parent_state
2348            .tracker
2349            .buffers
2350            .set_size(indices.buffers.size());
2351        parent_state
2352            .tracker
2353            .textures
2354            .set_size(indices.textures.size());
2355
2356        let mut debug_scope_depth = 0;
2357
2358        let mut state = State {
2359            pipeline_flags: PipelineFlags::empty(),
2360            blend_constant: OptionalState::Unused,
2361            stencil_reference: 0,
2362            pipeline: None,
2363            index: IndexState::default(),
2364            vertex: VertexState::default(),
2365
2366            info,
2367
2368            pass: pass::PassState {
2369                base: EncodingState {
2370                    device,
2371                    raw_encoder,
2372                    tracker: parent_state.tracker,
2373                    buffer_memory_init_actions: parent_state.buffer_memory_init_actions,
2374                    texture_memory_actions: parent_state.texture_memory_actions,
2375                    as_actions: parent_state.as_actions,
2376                    temp_resources: parent_state.temp_resources,
2377                    indirect_draw_validation_resources: parent_state
2378                        .indirect_draw_validation_resources,
2379                    snatch_guard: parent_state.snatch_guard,
2380                    debug_scope_depth: &mut debug_scope_depth,
2381                    query_set_writes: parent_state.query_set_writes,
2382                    deferred_query_set_resolves: parent_state.deferred_query_set_resolves,
2383                },
2384                pending_discard_init_fixups,
2385                scope: device.new_usage_scope(),
2386                binder: Binder::new(),
2387
2388                temp_offsets: Vec::new(),
2389                dynamic_offset_count: 0,
2390
2391                string_offset: 0,
2392
2393                immediate_state: ImmediateState::default(),
2394            },
2395
2396            active_occlusion_query: None,
2397            active_pipeline_statistics_query: None,
2398        };
2399
2400        for command in base.commands.drain(..) {
2401            match command {
2402                ArcRenderCommand::SetBindGroup {
2403                    index,
2404                    num_dynamic_offsets,
2405                    bind_group,
2406                } => {
2407                    let scope = PassErrorScope::SetBindGroup;
2408                    pass::set_bind_group::<RenderPassErrorInner>(
2409                        &mut state.pass,
2410                        device,
2411                        &base.dynamic_offsets,
2412                        index,
2413                        num_dynamic_offsets,
2414                        bind_group,
2415                        true,
2416                    )
2417                    .map_pass_err(scope)?;
2418                }
2419                ArcRenderCommand::SetPipeline(pipeline) => {
2420                    let scope = PassErrorScope::SetPipelineRender;
2421                    set_pipeline(&mut state, device, pipeline).map_pass_err(scope)?;
2422                }
2423                ArcRenderCommand::SetIndexBuffer {
2424                    buffer,
2425                    index_format,
2426                    offset,
2427                    size,
2428                } => {
2429                    let scope = PassErrorScope::SetIndexBuffer;
2430                    set_index_buffer(&mut state, device, buffer, index_format, offset, size)
2431                        .map_pass_err(scope)?;
2432                }
2433                ArcRenderCommand::SetVertexBuffer {
2434                    slot,
2435                    buffer,
2436                    offset,
2437                    size,
2438                } => {
2439                    let scope = PassErrorScope::SetVertexBuffer;
2440                    set_vertex_buffer(&mut state, device, slot, buffer, offset, size)
2441                        .map_pass_err(scope)?;
2442                }
2443                ArcRenderCommand::SetBlendConstant(ref color) => {
2444                    set_blend_constant(&mut state, color);
2445                }
2446                ArcRenderCommand::SetStencilReference(value) => {
2447                    set_stencil_reference(&mut state, value);
2448                }
2449                ArcRenderCommand::SetViewport {
2450                    rect,
2451                    depth_min,
2452                    depth_max,
2453                } => {
2454                    let scope = PassErrorScope::SetViewport;
2455                    set_viewport(&mut state, rect, depth_min, depth_max).map_pass_err(scope)?;
2456                }
2457                ArcRenderCommand::SetImmediate { offset, data } => {
2458                    let scope = PassErrorScope::SetImmediate;
2459                    state
2460                        .pass
2461                        .immediate_state
2462                        .set_immediates::<RenderPassErrorInner>(
2463                            &state.pass.base.device.limits,
2464                            offset,
2465                            &data,
2466                        )
2467                        .map_pass_err(scope)?;
2468                }
2469                ArcRenderCommand::SetScissor(rect) => {
2470                    let scope = PassErrorScope::SetScissorRect;
2471                    set_scissor(&mut state, rect).map_pass_err(scope)?;
2472                }
2473                ArcRenderCommand::Draw {
2474                    vertex_count,
2475                    instance_count,
2476                    first_vertex,
2477                    first_instance,
2478                } => {
2479                    let scope = PassErrorScope::Draw {
2480                        kind: DrawKind::Draw,
2481                        family: DrawCommandFamily::Draw,
2482                    };
2483                    draw(
2484                        &mut state,
2485                        vertex_count,
2486                        instance_count,
2487                        first_vertex,
2488                        first_instance,
2489                    )
2490                    .map_pass_err(scope)?;
2491                }
2492                ArcRenderCommand::DrawIndexed {
2493                    index_count,
2494                    instance_count,
2495                    first_index,
2496                    base_vertex,
2497                    first_instance,
2498                } => {
2499                    let scope = PassErrorScope::Draw {
2500                        kind: DrawKind::Draw,
2501                        family: DrawCommandFamily::DrawIndexed,
2502                    };
2503                    draw_indexed(
2504                        &mut state,
2505                        index_count,
2506                        instance_count,
2507                        first_index,
2508                        base_vertex,
2509                        first_instance,
2510                    )
2511                    .map_pass_err(scope)?;
2512                }
2513                ArcRenderCommand::DrawMeshTasks {
2514                    group_count_x,
2515                    group_count_y,
2516                    group_count_z,
2517                } => {
2518                    let scope = PassErrorScope::Draw {
2519                        kind: DrawKind::Draw,
2520                        family: DrawCommandFamily::DrawMeshTasks,
2521                    };
2522                    draw_mesh_tasks(&mut state, group_count_x, group_count_y, group_count_z)
2523                        .map_pass_err(scope)?;
2524                }
2525                ArcRenderCommand::DrawIndirect {
2526                    buffer,
2527                    offset,
2528                    count,
2529                    family,
2530
2531                    vertex_or_index_limit: _,
2532                    instance_limit: _,
2533                } => {
2534                    let scope = PassErrorScope::Draw {
2535                        kind: if count != 1 {
2536                            DrawKind::MultiDrawIndirect
2537                        } else {
2538                            DrawKind::DrawIndirect
2539                        },
2540                        family,
2541                    };
2542                    multi_draw_indirect(
2543                        &mut state,
2544                        &mut indirect_draw_validation_batcher,
2545                        device,
2546                        buffer,
2547                        offset,
2548                        count,
2549                        family,
2550                    )
2551                    .map_pass_err(scope)?;
2552                }
2553                ArcRenderCommand::MultiDrawIndirectCount {
2554                    buffer,
2555                    offset,
2556                    count_buffer,
2557                    count_buffer_offset,
2558                    max_count,
2559                    family,
2560                } => {
2561                    let scope = PassErrorScope::Draw {
2562                        kind: DrawKind::MultiDrawIndirectCount,
2563                        family,
2564                    };
2565                    multi_draw_indirect_count(
2566                        &mut state,
2567                        device,
2568                        buffer,
2569                        offset,
2570                        count_buffer,
2571                        count_buffer_offset,
2572                        max_count,
2573                        family,
2574                    )
2575                    .map_pass_err(scope)?;
2576                }
2577                ArcRenderCommand::PushDebugGroup { color: _, len } => {
2578                    pass::push_debug_group(&mut state.pass, &base.string_data, len);
2579                }
2580                ArcRenderCommand::PopDebugGroup => {
2581                    let scope = PassErrorScope::PopDebugGroup;
2582                    pass::pop_debug_group::<RenderPassErrorInner>(&mut state.pass)
2583                        .map_pass_err(scope)?;
2584                }
2585                ArcRenderCommand::InsertDebugMarker { color: _, len } => {
2586                    pass::insert_debug_marker(&mut state.pass, &base.string_data, len);
2587                }
2588                ArcRenderCommand::WriteTimestamp {
2589                    query_set,
2590                    query_index,
2591                } => {
2592                    let scope = PassErrorScope::WriteTimestamp;
2593                    pass::write_timestamp::<RenderPassErrorInner>(
2594                        &mut state.pass,
2595                        device,
2596                        Some(&mut pending_query_resets),
2597                        query_set,
2598                        query_index,
2599                    )
2600                    .map_pass_err(scope)?;
2601                }
2602                ArcRenderCommand::BeginOcclusionQuery { query_index } => {
2603                    api_log!("RenderPass::begin_occlusion_query {query_index}");
2604                    let scope = PassErrorScope::BeginOcclusionQuery;
2605
2606                    let query_set = occlusion_query_set
2607                        .clone()
2608                        .ok_or(RenderPassErrorInner::MissingOcclusionQuerySet)
2609                        .map_pass_err(scope)?;
2610
2611                    validate_and_begin_occlusion_query(
2612                        query_set,
2613                        state.pass.base.raw_encoder,
2614                        &mut state.pass.base.tracker.query_sets,
2615                        query_index,
2616                        Some(&mut pending_query_resets),
2617                        &mut state.active_occlusion_query,
2618                        state.pass.base.snatch_guard,
2619                    )
2620                    .map_pass_err(scope)?;
2621                }
2622                ArcRenderCommand::EndOcclusionQuery => {
2623                    api_log!("RenderPass::end_occlusion_query");
2624                    let scope = PassErrorScope::EndOcclusionQuery;
2625
2626                    end_occlusion_query(
2627                        state.pass.base.raw_encoder,
2628                        &mut state.active_occlusion_query,
2629                        state.pass.base.snatch_guard,
2630                        state.pass.base.query_set_writes,
2631                    )
2632                    .map_pass_err(scope)?;
2633                }
2634                ArcRenderCommand::BeginPipelineStatisticsQuery {
2635                    query_set,
2636                    query_index,
2637                } => {
2638                    api_log!(
2639                        "RenderPass::begin_pipeline_statistics_query {query_index} {}",
2640                        query_set.error_ident()
2641                    );
2642                    let scope = PassErrorScope::BeginPipelineStatisticsQuery;
2643
2644                    validate_and_begin_pipeline_statistics_query(
2645                        query_set,
2646                        state.pass.base.raw_encoder,
2647                        &mut state.pass.base.tracker.query_sets,
2648                        device,
2649                        query_index,
2650                        Some(&mut pending_query_resets),
2651                        &mut state.active_pipeline_statistics_query,
2652                        state.pass.base.snatch_guard,
2653                    )
2654                    .map_pass_err(scope)?;
2655                }
2656                ArcRenderCommand::EndPipelineStatisticsQuery => {
2657                    api_log!("RenderPass::end_pipeline_statistics_query");
2658                    let scope = PassErrorScope::EndPipelineStatisticsQuery;
2659
2660                    end_pipeline_statistics_query(
2661                        state.pass.base.raw_encoder,
2662                        &mut state.active_pipeline_statistics_query,
2663                        state.pass.base.snatch_guard,
2664                        state.pass.base.query_set_writes,
2665                    )
2666                    .map_pass_err(scope)?;
2667                }
2668                ArcRenderCommand::ExecuteBundle(bundle) => {
2669                    let scope = PassErrorScope::ExecuteBundle;
2670                    execute_bundle(
2671                        &mut state,
2672                        &mut indirect_draw_validation_batcher,
2673                        device,
2674                        bundle,
2675                    )
2676                    .map_pass_err(scope)?;
2677                }
2678            }
2679        }
2680
2681        if *state.pass.base.debug_scope_depth > 0 {
2682            Err(
2683                RenderPassErrorInner::DebugGroupError(DebugGroupError::MissingPop)
2684                    .map_pass_err(pass_scope),
2685            )?;
2686        }
2687        if state.active_occlusion_query.is_some() {
2688            Err(RenderPassErrorInner::QueryUse(QueryUseError::MissingEnd {
2689                query_type: super::SimplifiedQueryType::Occlusion,
2690            })
2691            .map_pass_err(pass_scope))?;
2692        }
2693        if state.active_pipeline_statistics_query.is_some() {
2694            Err(RenderPassErrorInner::QueryUse(QueryUseError::MissingEnd {
2695                query_type: super::SimplifiedQueryType::PipelineStatistics,
2696            })
2697            .map_pass_err(pass_scope))?;
2698        }
2699
2700        state
2701            .info
2702            .finish(
2703                device,
2704                state.pass.base.raw_encoder,
2705                state.pass.base.snatch_guard,
2706                &mut state.pass.scope,
2707                device.instance_flags,
2708            )
2709            .map_pass_err(pass_scope)?;
2710
2711        let trackers = state.pass.scope;
2712
2713        let pending_discard_init_fixups = state.pass.pending_discard_init_fixups;
2714
2715        parent_state.raw_encoder.close().map_pass_err(pass_scope)?;
2716        (trackers, pending_discard_init_fixups, pending_query_resets)
2717    };
2718
2719    let encoder = &mut parent_state.raw_encoder;
2720    let tracker = &mut parent_state.tracker;
2721
2722    {
2723        let transit = encoder
2724            .open_pass(hal_label(
2725                Some("(wgpu internal) Pre Pass"),
2726                device.instance_flags,
2727            ))
2728            .map_pass_err(pass_scope)?;
2729
2730        fixup_discarded_surfaces(
2731            pending_discard_init_fixups.into_iter(),
2732            transit,
2733            &mut tracker.textures,
2734            device,
2735            parent_state.snatch_guard,
2736        );
2737
2738        pending_query_resets
2739            .reset_queries(transit, parent_state.snatch_guard)
2740            .map_pass_err(pass_scope)?;
2741
2742        CommandEncoder::insert_barriers_from_scope(
2743            transit,
2744            tracker,
2745            &scope,
2746            parent_state.snatch_guard,
2747        );
2748
2749        if let Some(ref indirect_validation) = device.indirect_validation {
2750            indirect_validation
2751                .draw
2752                .inject_validation_pass(
2753                    device,
2754                    parent_state.snatch_guard,
2755                    parent_state.indirect_draw_validation_resources,
2756                    parent_state.temp_resources,
2757                    transit,
2758                    indirect_draw_validation_batcher,
2759                )
2760                .map_pass_err(pass_scope)?;
2761        }
2762    }
2763
2764    encoder.close_and_swap().map_pass_err(pass_scope)?;
2765
2766    Ok(())
2767}
2768
2769fn set_pipeline(
2770    state: &mut State,
2771    device: &Arc<Device>,
2772    pipeline: Arc<RenderPipeline>,
2773) -> Result<(), RenderPassErrorInner> {
2774    api_log!("RenderPass::set_pipeline {}", pipeline.error_ident());
2775
2776    state.pipeline = Some(pipeline.clone());
2777
2778    let pipeline = state
2779        .pass
2780        .base
2781        .tracker
2782        .render_pipelines
2783        .insert_single(pipeline)
2784        .clone();
2785
2786    pipeline.same_device(device)?;
2787
2788    state
2789        .info
2790        .context
2791        .check_compatible(&pipeline.pass_context, pipeline.as_ref())
2792        .map_err(RenderCommandError::IncompatiblePipelineTargets)?;
2793
2794    state.pipeline_flags = pipeline.flags;
2795
2796    if pipeline.flags.contains(PipelineFlags::WRITES_DEPTH) && state.info.is_depth_read_only {
2797        return Err(RenderCommandError::IncompatibleDepthAccess(pipeline.error_ident()).into());
2798    }
2799    if pipeline.flags.contains(PipelineFlags::WRITES_STENCIL) && state.info.is_stencil_read_only {
2800        return Err(RenderCommandError::IncompatibleStencilAccess(pipeline.error_ident()).into());
2801    }
2802
2803    state
2804        .blend_constant
2805        .require(pipeline.flags.contains(PipelineFlags::BLEND_CONSTANT));
2806
2807    unsafe {
2808        state
2809            .pass
2810            .base
2811            .raw_encoder
2812            .set_render_pipeline(pipeline.raw()?);
2813    }
2814
2815    if pipeline.flags.contains(PipelineFlags::STENCIL_REFERENCE) {
2816        unsafe {
2817            state
2818                .pass
2819                .base
2820                .raw_encoder
2821                .set_stencil_reference(state.stencil_reference);
2822        }
2823    }
2824
2825    // Rebind resource
2826    pass::change_pipeline_layout::<RenderPassErrorInner>(
2827        &mut state.pass,
2828        pipeline.layout()?,
2829        &pipeline.late_sized_buffer_groups,
2830    )?;
2831
2832    // Update vertex buffer limits.
2833    state.vertex.update_limits(&pipeline.vertex_steps);
2834    Ok(())
2835}
2836
2837// This function is duplicative of `bundle::set_index_buffer`.
2838fn set_index_buffer(
2839    state: &mut State,
2840    device: &Arc<Device>,
2841    buffer: Arc<Buffer>,
2842    index_format: IndexFormat,
2843    offset: u64,
2844    size: Option<BufferSize>,
2845) -> Result<(), RenderPassErrorInner> {
2846    api_log!("RenderPass::set_index_buffer {}", buffer.error_ident());
2847
2848    state
2849        .pass
2850        .scope
2851        .buffers
2852        .merge_single(&buffer, wgt::BufferUses::INDEX)?;
2853
2854    buffer.same_device(device)?;
2855
2856    buffer.check_usage(BufferUsages::INDEX)?;
2857
2858    if !offset.is_multiple_of(u64::from(index_format.byte_size())) {
2859        return Err(RenderCommandError::UnalignedIndexBuffer {
2860            offset,
2861            alignment: index_format.byte_size() as usize,
2862        }
2863        .into());
2864    }
2865    let (binding, resolved_size) = buffer
2866        .binding(offset, size, state.pass.base.snatch_guard)
2867        .map_err(RenderCommandError::from)?;
2868    let end = offset + resolved_size;
2869    state.index.update_buffer(offset..end, index_format);
2870
2871    state.pass.base.buffer_memory_init_actions.extend(
2872        buffer.initialization_status.read().create_action(
2873            &buffer,
2874            offset..end,
2875            MemoryInitKind::NeedsInitializedMemory,
2876        ),
2877    );
2878
2879    unsafe {
2880        hal::DynCommandEncoder::set_index_buffer(
2881            state.pass.base.raw_encoder,
2882            binding,
2883            index_format,
2884        );
2885    }
2886    Ok(())
2887}
2888
2889// This function is duplicative of `render::set_vertex_buffer`.
2890fn set_vertex_buffer(
2891    state: &mut State,
2892    device: &Arc<Device>,
2893    slot: u32,
2894    buffer: Option<Arc<Buffer>>,
2895    offset: u64,
2896    size: Option<BufferSize>,
2897) -> Result<(), RenderPassErrorInner> {
2898    if let Some(ref buffer) = buffer {
2899        api_log!(
2900            "RenderPass::set_vertex_buffer {slot} {}",
2901            buffer.error_ident()
2902        );
2903    } else {
2904        api_log!("RenderPass::set_vertex_buffer {slot} None");
2905    }
2906
2907    let max_vertex_buffers = state.pass.base.device.limits.max_vertex_buffers;
2908    if slot >= max_vertex_buffers {
2909        return Err(RenderCommandError::VertexBufferIndexOutOfRange {
2910            index: slot,
2911            max: max_vertex_buffers,
2912        }
2913        .into());
2914    }
2915
2916    if let Some(buffer) = buffer {
2917        buffer.same_device(device)?;
2918        buffer.check_usage(BufferUsages::VERTEX)?;
2919
2920        if !offset.is_multiple_of(wgt::VERTEX_ALIGNMENT) {
2921            return Err(RenderCommandError::UnalignedVertexBuffer { slot, offset }.into());
2922        }
2923        let binding_size = buffer
2924            .resolve_binding_size(offset, size)
2925            .map_err(RenderCommandError::from)?;
2926        let buffer_range = offset..(offset + binding_size);
2927
2928        state
2929            .pass
2930            .scope
2931            .buffers
2932            .merge_single(&buffer, wgt::BufferUses::VERTEX)?;
2933
2934        state.pass.base.buffer_memory_init_actions.extend(
2935            buffer.initialization_status.read().create_action(
2936                &buffer,
2937                buffer_range.clone(),
2938                MemoryInitKind::NeedsInitializedMemory,
2939            ),
2940        );
2941
2942        state
2943            .vertex
2944            .set_buffer(slot as usize, buffer, buffer_range.clone());
2945        if let Some(pipeline) = state.pipeline.as_ref() {
2946            state.vertex.update_limits(&pipeline.vertex_steps);
2947        }
2948    } else {
2949        if offset != 0 {
2950            return Err(RenderCommandError::from(
2951                crate::binding_model::BindingError::UnbindingVertexBufferOffsetNotZero {
2952                    slot,
2953                    offset,
2954                },
2955            )
2956            .into());
2957        }
2958        if let Some(size) = size {
2959            return Err(RenderCommandError::from(
2960                crate::binding_model::BindingError::UnbindingVertexBufferSizeNotZero {
2961                    slot,
2962                    size: size.get(),
2963                },
2964            )
2965            .into());
2966        }
2967
2968        state.vertex.clear_buffer(slot as usize);
2969        if let Some(pipeline) = state.pipeline.as_ref() {
2970            state.vertex.update_limits(&pipeline.vertex_steps);
2971        }
2972    }
2973
2974    Ok(())
2975}
2976
2977fn set_blend_constant(state: &mut State, color: &Color) {
2978    api_log!("RenderPass::set_blend_constant");
2979
2980    state.blend_constant = OptionalState::Set;
2981    let array = [
2982        color.r as f32,
2983        color.g as f32,
2984        color.b as f32,
2985        color.a as f32,
2986    ];
2987    unsafe {
2988        state.pass.base.raw_encoder.set_blend_constants(&array);
2989    }
2990}
2991
2992fn set_stencil_reference(state: &mut State, value: u32) {
2993    api_log!("RenderPass::set_stencil_reference {value}");
2994
2995    state.stencil_reference = value;
2996    if state
2997        .pipeline_flags
2998        .contains(PipelineFlags::STENCIL_REFERENCE)
2999    {
3000        unsafe {
3001            state.pass.base.raw_encoder.set_stencil_reference(value);
3002        }
3003    }
3004}
3005
3006fn set_viewport(
3007    state: &mut State,
3008    rect: Rect<f32>,
3009    depth_min: f32,
3010    depth_max: f32,
3011) -> Result<(), RenderPassErrorInner> {
3012    api_log!("RenderPass::set_viewport {rect:?}");
3013
3014    if rect.w < 0.0
3015        || rect.h < 0.0
3016        || rect.w > state.pass.base.device.limits.max_texture_dimension_2d as f32
3017        || rect.h > state.pass.base.device.limits.max_texture_dimension_2d as f32
3018    {
3019        return Err(RenderCommandError::InvalidViewportRectSize {
3020            w: rect.w,
3021            h: rect.h,
3022            max: state.pass.base.device.limits.max_texture_dimension_2d,
3023        }
3024        .into());
3025    }
3026
3027    let max_viewport_range = state.pass.base.device.limits.max_texture_dimension_2d as f32 * 2.0;
3028
3029    if rect.x < -max_viewport_range
3030        || rect.y < -max_viewport_range
3031        || rect.x + rect.w > max_viewport_range - 1.0
3032        || rect.y + rect.h > max_viewport_range - 1.0
3033    {
3034        return Err(RenderCommandError::InvalidViewportRectPosition {
3035            rect,
3036            min: -max_viewport_range,
3037            max: max_viewport_range - 1.0,
3038        }
3039        .into());
3040    }
3041    if !(0.0..=1.0).contains(&depth_min)
3042        || !(0.0..=1.0).contains(&depth_max)
3043        || depth_min > depth_max
3044    {
3045        return Err(RenderCommandError::InvalidViewportDepth(depth_min, depth_max).into());
3046    }
3047    let r = hal::Rect {
3048        x: rect.x,
3049        y: rect.y,
3050        w: rect.w,
3051        h: rect.h,
3052    };
3053    unsafe {
3054        state
3055            .pass
3056            .base
3057            .raw_encoder
3058            .set_viewport(&r, depth_min..depth_max);
3059    }
3060    Ok(())
3061}
3062
3063fn set_scissor(state: &mut State, rect: Rect<u32>) -> Result<(), RenderPassErrorInner> {
3064    api_log!("RenderPass::set_scissor_rect {rect:?}");
3065
3066    if rect.x.saturating_add(rect.w) > state.info.extent.width
3067        || rect.y.saturating_add(rect.h) > state.info.extent.height
3068    {
3069        return Err(RenderCommandError::InvalidScissorRect(rect, state.info.extent).into());
3070    }
3071    let r = hal::Rect {
3072        x: rect.x,
3073        y: rect.y,
3074        w: rect.w,
3075        h: rect.h,
3076    };
3077    unsafe {
3078        state.pass.base.raw_encoder.set_scissor_rect(&r);
3079    }
3080    Ok(())
3081}
3082
3083fn validate_mesh_draw_multiview(state: &State) -> Result<(), RenderPassErrorInner> {
3084    if let Some(mv) = state.info.multiview_mask {
3085        let highest_bit = 31 - mv.leading_zeros();
3086
3087        let features = state.pass.base.device.features;
3088
3089        if !features.contains(wgt::Features::EXPERIMENTAL_MESH_SHADER_MULTIVIEW)
3090            || highest_bit > state.pass.base.device.limits.max_mesh_multiview_view_count
3091        {
3092            return Err(RenderPassErrorInner::Draw(
3093                DrawError::MeshPipelineMultiviewLimitsViolated {
3094                    highest_view_index: highest_bit,
3095                    max_multiviews: state.pass.base.device.limits.max_mesh_multiview_view_count,
3096                },
3097            ));
3098        }
3099    }
3100    Ok(())
3101}
3102
3103fn draw(
3104    state: &mut State,
3105    vertex_count: u32,
3106    instance_count: u32,
3107    first_vertex: u32,
3108    first_instance: u32,
3109) -> Result<(), RenderPassErrorInner> {
3110    api_log!("RenderPass::draw {vertex_count} {instance_count} {first_vertex} {first_instance}");
3111
3112    state.is_ready(DrawCommandFamily::Draw)?;
3113    state.flush_vertex_buffers()?;
3114    state.flush_bindings()?;
3115    state.flush_immediates();
3116
3117    state
3118        .vertex
3119        .limits
3120        .validate_vertex_limit(first_vertex, vertex_count)?;
3121    state
3122        .vertex
3123        .limits
3124        .validate_instance_limit(first_instance, instance_count)?;
3125
3126    unsafe {
3127        if instance_count > 0 && vertex_count > 0 {
3128            state.pass.base.raw_encoder.draw(
3129                first_vertex,
3130                vertex_count,
3131                first_instance,
3132                instance_count,
3133            );
3134        }
3135    }
3136    Ok(())
3137}
3138
3139fn draw_indexed(
3140    state: &mut State,
3141    index_count: u32,
3142    instance_count: u32,
3143    first_index: u32,
3144    base_vertex: i32,
3145    first_instance: u32,
3146) -> Result<(), RenderPassErrorInner> {
3147    api_log!("RenderPass::draw_indexed {index_count} {instance_count} {first_index} {base_vertex} {first_instance}");
3148
3149    state.is_ready(DrawCommandFamily::DrawIndexed)?;
3150    state.flush_vertex_buffers()?;
3151    state.flush_bindings()?;
3152    state.flush_immediates();
3153
3154    let last_index = first_index as u64 + index_count as u64;
3155    let index_limit = state.index.limit;
3156    if last_index > index_limit {
3157        return Err(DrawError::IndexBeyondLimit {
3158            last_index,
3159            index_limit,
3160        }
3161        .into());
3162    }
3163    state
3164        .vertex
3165        .limits
3166        .validate_instance_limit(first_instance, instance_count)?;
3167
3168    unsafe {
3169        if instance_count > 0 && index_count > 0 {
3170            state.pass.base.raw_encoder.draw_indexed(
3171                first_index,
3172                index_count,
3173                base_vertex,
3174                first_instance,
3175                instance_count,
3176            );
3177        }
3178    }
3179    Ok(())
3180}
3181
3182fn draw_mesh_tasks(
3183    state: &mut State,
3184    group_count_x: u32,
3185    group_count_y: u32,
3186    group_count_z: u32,
3187) -> Result<(), RenderPassErrorInner> {
3188    api_log!("RenderPass::draw_mesh_tasks {group_count_x} {group_count_y} {group_count_z}");
3189
3190    state.is_ready(DrawCommandFamily::DrawMeshTasks)?;
3191
3192    state.flush_bindings()?;
3193    state.flush_immediates();
3194    validate_mesh_draw_multiview(state)?;
3195
3196    let limits = &state.pass.base.device.limits;
3197    let (groups_size_limit, max_groups) = if state.pipeline.as_ref().unwrap().has_task_shader {
3198        (
3199            limits.max_task_workgroups_per_dimension,
3200            limits.max_task_workgroup_total_count,
3201        )
3202    } else {
3203        (
3204            limits.max_mesh_workgroups_per_dimension,
3205            limits.max_mesh_workgroup_total_count,
3206        )
3207    };
3208
3209    let total_count = WorkgroupSizeCheck {
3210        dimensions: &[group_count_x, group_count_y, group_count_z],
3211        per_dimension_limits: &[groups_size_limit, groups_size_limit, groups_size_limit],
3212        per_dimension_limits_desc: "max_task_mesh_workgroups_per_dimension",
3213
3214        total_limit: max_groups,
3215        total_limit_desc: "max_task_mesh_workgroup_total_count",
3216    }
3217    .check_and_compute_total_invocations()
3218    .map_err(|err| RenderPassErrorInner::Draw(err.into()))?;
3219
3220    unsafe {
3221        if total_count > 0 {
3222            state.pass.base.raw_encoder.draw_mesh_tasks(
3223                group_count_x,
3224                group_count_y,
3225                group_count_z,
3226            );
3227        }
3228    }
3229    Ok(())
3230}
3231
3232fn multi_draw_indirect(
3233    state: &mut State,
3234    indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
3235    device: &Arc<Device>,
3236    indirect_buffer: Arc<Buffer>,
3237    offset: u64,
3238    count: u32,
3239    family: DrawCommandFamily,
3240) -> Result<(), RenderPassErrorInner> {
3241    api_log!(
3242        "RenderPass::draw_indirect (family:{family:?}) {} {offset} {count:?}",
3243        indirect_buffer.error_ident()
3244    );
3245
3246    state.is_ready(family)?;
3247    state.flush_vertex_buffers()?;
3248    state.flush_bindings()?;
3249    state.flush_immediates();
3250
3251    if family == DrawCommandFamily::DrawMeshTasks {
3252        validate_mesh_draw_multiview(state)?;
3253    }
3254
3255    state
3256        .pass
3257        .base
3258        .device
3259        .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
3260
3261    indirect_buffer.same_device(device)?;
3262    indirect_buffer.check_usage(BufferUsages::INDIRECT)?;
3263    indirect_buffer.check_destroyed(state.pass.base.snatch_guard)?;
3264
3265    if !offset.is_multiple_of(4) {
3266        return Err(RenderCommandError::UnalignedIndirectBufferOffset(offset).into());
3267    }
3268
3269    let stride = get_src_stride_of_indirect_args(family);
3270    let args_size = match stride.checked_mul(u64::from(count)) {
3271        Some(sz) if sz <= indirect_buffer.size && indirect_buffer.size - sz >= offset => sz,
3272        args_size => {
3273            return Err(RenderCommandError::IndirectBufferOverrun {
3274                count,
3275                offset,
3276                args_size: args_size.unwrap_or(u64::MAX),
3277                buffer_size: indirect_buffer.size,
3278            }
3279            .into());
3280        }
3281    };
3282
3283    state.pass.base.buffer_memory_init_actions.extend(
3284        indirect_buffer.initialization_status.read().create_action(
3285            &indirect_buffer,
3286            offset..offset + args_size,
3287            MemoryInitKind::NeedsInitializedMemory,
3288        ),
3289    );
3290
3291    fn draw(
3292        raw_encoder: &mut dyn hal::DynCommandEncoder,
3293        family: DrawCommandFamily,
3294        indirect_buffer: &dyn hal::DynBuffer,
3295        offset: u64,
3296        count: u32,
3297    ) {
3298        match family {
3299            DrawCommandFamily::Draw => unsafe {
3300                raw_encoder.draw_indirect(indirect_buffer, offset, count);
3301            },
3302            DrawCommandFamily::DrawIndexed => unsafe {
3303                raw_encoder.draw_indexed_indirect(indirect_buffer, offset, count);
3304            },
3305            DrawCommandFamily::DrawMeshTasks => unsafe {
3306                raw_encoder.draw_mesh_tasks_indirect(indirect_buffer, offset, count);
3307            },
3308        }
3309    }
3310
3311    if state.pass.base.device.indirect_validation.is_some()
3312        && family != DrawCommandFamily::DrawMeshTasks
3313    {
3314        state
3315            .pass
3316            .scope
3317            .buffers
3318            .merge_single(&indirect_buffer, wgt::BufferUses::STORAGE_READ_ONLY)?;
3319
3320        struct DrawData {
3321            buffer_index: usize,
3322            offset: u64,
3323            count: u32,
3324        }
3325
3326        struct DrawContext<'a> {
3327            raw_encoder: &'a mut dyn hal::DynCommandEncoder,
3328            device: &'a Device,
3329
3330            indirect_draw_validation_resources: &'a mut crate::indirect_validation::DrawResources,
3331            indirect_draw_validation_batcher: &'a mut crate::indirect_validation::DrawBatcher,
3332
3333            indirect_buffer: Arc<Buffer>,
3334            family: DrawCommandFamily,
3335            vertex_or_index_limit: u64,
3336            instance_limit: u64,
3337        }
3338
3339        impl<'a> DrawContext<'a> {
3340            fn add(&mut self, offset: u64) -> Result<DrawData, DeviceError> {
3341                let (dst_resource_index, dst_offset) = self.indirect_draw_validation_batcher.add(
3342                    self.indirect_draw_validation_resources,
3343                    self.device,
3344                    &self.indirect_buffer,
3345                    offset,
3346                    self.family,
3347                    self.vertex_or_index_limit,
3348                    self.instance_limit,
3349                )?;
3350                Ok(DrawData {
3351                    buffer_index: dst_resource_index,
3352                    offset: dst_offset,
3353                    count: 1,
3354                })
3355            }
3356            fn draw(&mut self, draw_data: DrawData) {
3357                let dst_buffer = self
3358                    .indirect_draw_validation_resources
3359                    .get_dst_buffer(draw_data.buffer_index);
3360                draw(
3361                    self.raw_encoder,
3362                    self.family,
3363                    dst_buffer,
3364                    draw_data.offset,
3365                    draw_data.count,
3366                );
3367            }
3368        }
3369
3370        let mut draw_ctx = DrawContext {
3371            raw_encoder: state.pass.base.raw_encoder,
3372            device: state.pass.base.device,
3373            indirect_draw_validation_resources: state.pass.base.indirect_draw_validation_resources,
3374            indirect_draw_validation_batcher,
3375            indirect_buffer,
3376            family,
3377            vertex_or_index_limit: if family == DrawCommandFamily::DrawIndexed {
3378                state.index.limit
3379            } else {
3380                state.vertex.limits.vertex_limit
3381            },
3382            instance_limit: state.vertex.limits.instance_limit,
3383        };
3384
3385        let mut current_draw_data = draw_ctx.add(offset)?;
3386
3387        for i in 1..count {
3388            let draw_data = draw_ctx.add(offset + stride * i as u64)?;
3389
3390            if draw_data.buffer_index == current_draw_data.buffer_index {
3391                #[cfg(debug_assertions)]
3392                {
3393                    let dst_stride =
3394                        get_dst_stride_of_indirect_args(state.pass.base.device.backend(), family);
3395                    debug_assert_eq!(
3396                        draw_data.offset,
3397                        current_draw_data.offset + dst_stride * current_draw_data.count as u64
3398                    );
3399                }
3400                current_draw_data.count += 1;
3401            } else {
3402                draw_ctx.draw(current_draw_data);
3403                current_draw_data = draw_data;
3404            }
3405        }
3406
3407        draw_ctx.draw(current_draw_data);
3408    } else {
3409        state
3410            .pass
3411            .scope
3412            .buffers
3413            .merge_single(&indirect_buffer, wgt::BufferUses::INDIRECT)?;
3414
3415        draw(
3416            state.pass.base.raw_encoder,
3417            family,
3418            indirect_buffer.try_raw(state.pass.base.snatch_guard)?,
3419            offset,
3420            count,
3421        );
3422    };
3423
3424    Ok(())
3425}
3426
3427fn multi_draw_indirect_count(
3428    state: &mut State,
3429    device: &Arc<Device>,
3430    indirect_buffer: Arc<Buffer>,
3431    offset: u64,
3432    count_buffer: Arc<Buffer>,
3433    count_buffer_offset: u64,
3434    max_count: u32,
3435    family: DrawCommandFamily,
3436) -> Result<(), RenderPassErrorInner> {
3437    api_log!(
3438        "RenderPass::multi_draw_indirect_count (family:{family:?}) {} {offset} {} {count_buffer_offset:?} {max_count:?}",
3439        indirect_buffer.error_ident(),
3440        count_buffer.error_ident()
3441    );
3442
3443    state.is_ready(family)?;
3444    state.flush_vertex_buffers()?;
3445    state.flush_bindings()?;
3446    state.flush_immediates();
3447
3448    if family == DrawCommandFamily::DrawMeshTasks {
3449        validate_mesh_draw_multiview(state)?;
3450    }
3451
3452    let stride = get_src_stride_of_indirect_args(family);
3453
3454    state
3455        .pass
3456        .base
3457        .device
3458        .require_features(wgt::Features::MULTI_DRAW_INDIRECT_COUNT)?;
3459    state
3460        .pass
3461        .base
3462        .device
3463        .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
3464
3465    indirect_buffer.same_device(device)?;
3466    count_buffer.same_device(device)?;
3467
3468    state
3469        .pass
3470        .scope
3471        .buffers
3472        .merge_single(&indirect_buffer, wgt::BufferUses::INDIRECT)?;
3473
3474    indirect_buffer.check_usage(BufferUsages::INDIRECT)?;
3475    let indirect_raw = indirect_buffer.try_raw(state.pass.base.snatch_guard)?;
3476
3477    state
3478        .pass
3479        .scope
3480        .buffers
3481        .merge_single(&count_buffer, wgt::BufferUses::INDIRECT)?;
3482
3483    count_buffer.check_usage(BufferUsages::INDIRECT)?;
3484    let count_raw = count_buffer.try_raw(state.pass.base.snatch_guard)?;
3485
3486    if !offset.is_multiple_of(4) {
3487        return Err(RenderCommandError::UnalignedIndirectBufferOffset(offset).into());
3488    }
3489
3490    let args_size = match stride.checked_mul(u64::from(max_count)) {
3491        Some(sz) if sz <= indirect_buffer.size && indirect_buffer.size - sz >= offset => sz,
3492        args_size => {
3493            return Err(RenderCommandError::IndirectBufferOverrun {
3494                count: 1,
3495                offset,
3496                args_size: args_size.unwrap_or(u64::MAX),
3497                buffer_size: indirect_buffer.size,
3498            }
3499            .into());
3500        }
3501    };
3502
3503    state.pass.base.buffer_memory_init_actions.extend(
3504        indirect_buffer.initialization_status.read().create_action(
3505            &indirect_buffer,
3506            offset..offset + args_size,
3507            MemoryInitKind::NeedsInitializedMemory,
3508        ),
3509    );
3510
3511    let begin_count_offset = count_buffer_offset;
3512    let count_bytes = 4;
3513    if count_buffer.size < count_bytes || count_buffer.size - count_bytes < count_buffer_offset {
3514        return Err(RenderPassErrorInner::IndirectCountBufferOverrun {
3515            begin_count_offset,
3516            count_bytes: 4,
3517            count_buffer_size: count_buffer.size,
3518        });
3519    }
3520    state.pass.base.buffer_memory_init_actions.extend(
3521        count_buffer.initialization_status.read().create_action(
3522            &count_buffer,
3523            count_buffer_offset..count_buffer_offset + count_bytes,
3524            MemoryInitKind::NeedsInitializedMemory,
3525        ),
3526    );
3527
3528    match family {
3529        DrawCommandFamily::Draw => unsafe {
3530            state.pass.base.raw_encoder.draw_indirect_count(
3531                indirect_raw,
3532                offset,
3533                count_raw,
3534                count_buffer_offset,
3535                max_count,
3536            );
3537        },
3538        DrawCommandFamily::DrawIndexed => unsafe {
3539            state.pass.base.raw_encoder.draw_indexed_indirect_count(
3540                indirect_raw,
3541                offset,
3542                count_raw,
3543                count_buffer_offset,
3544                max_count,
3545            );
3546        },
3547        DrawCommandFamily::DrawMeshTasks => unsafe {
3548            state.pass.base.raw_encoder.draw_mesh_tasks_indirect_count(
3549                indirect_raw,
3550                offset,
3551                count_raw,
3552                count_buffer_offset,
3553                max_count,
3554            );
3555        },
3556    }
3557    Ok(())
3558}
3559
3560fn execute_bundle(
3561    state: &mut State,
3562    indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
3563    device: &Arc<Device>,
3564    bundle: Arc<RenderBundle>,
3565) -> Result<(), RenderPassErrorInner> {
3566    api_log!("RenderPass::execute_bundle {}", bundle.error_ident());
3567
3568    let bundle = state.pass.base.tracker.bundles.insert_single(bundle);
3569
3570    let bundle_state = bundle.state()?;
3571    bundle.same_device(device)?;
3572
3573    state
3574        .info
3575        .context
3576        .check_compatible(&bundle_state.context, bundle.as_ref())
3577        .map_err(RenderPassErrorInner::IncompatibleBundleTargets)?;
3578
3579    if (state.info.is_depth_read_only && !bundle.is_depth_read_only)
3580        || (state.info.is_stencil_read_only && !bundle.is_stencil_read_only)
3581    {
3582        return Err(
3583            RenderPassErrorInner::IncompatibleBundleReadOnlyDepthStencil {
3584                pass_depth: state.info.is_depth_read_only,
3585                pass_stencil: state.info.is_stencil_read_only,
3586                bundle_depth: bundle.is_depth_read_only,
3587                bundle_stencil: bundle.is_stencil_read_only,
3588            },
3589        );
3590    }
3591
3592    state.pass.base.buffer_memory_init_actions.extend(
3593        bundle
3594            .buffer_memory_init_actions
3595            .iter()
3596            .filter_map(|action| {
3597                action
3598                    .buffer
3599                    .initialization_status
3600                    .read()
3601                    .check_action(action)
3602            }),
3603    );
3604    for action in bundle.texture_memory_init_actions.iter() {
3605        state.pass.pending_discard_init_fixups.extend(
3606            state
3607                .pass
3608                .base
3609                .texture_memory_actions
3610                .register_init_action(action),
3611        );
3612    }
3613
3614    unsafe {
3615        bundle.execute(
3616            state.pass.base.raw_encoder,
3617            state.pass.base.indirect_draw_validation_resources,
3618            indirect_draw_validation_batcher,
3619            state.pass.base.snatch_guard,
3620        )
3621    }
3622    .map_err(|e| match e {
3623        ExecutionError::Device(e) => RenderPassErrorInner::Device(e),
3624        ExecutionError::DestroyedResource(e) => {
3625            RenderPassErrorInner::RenderCommand(RenderCommandError::DestroyedResource(e))
3626        }
3627        ExecutionError::InvalidResource(e) => {
3628            RenderPassErrorInner::RenderCommand(RenderCommandError::InvalidResource(e))
3629        }
3630        ExecutionError::Unimplemented(what) => {
3631            RenderPassErrorInner::RenderCommand(RenderCommandError::Unimplemented(what))
3632        }
3633    })?;
3634
3635    unsafe {
3636        state.pass.scope.merge_render_bundle(&bundle_state.used)?;
3637    };
3638    state.reset_bundle();
3639    Ok(())
3640}
3641
3642// Recording a render pass.
3643//
3644// The only error that should be returned from these methods is
3645// `EncoderStateError::Ended`, when the pass has already ended and an immediate
3646// validation error is raised.
3647//
3648// All other errors should be stored in the pass for later reporting when
3649// `CommandEncoder.finish()` is called.
3650//
3651// The `pass_try!` macro should be used to handle errors appropriately. Note
3652// that the `pass_try!` and `pass_base!` macros may return early from the
3653// function that invokes them, like the `?` operator.
3654impl RenderPass {
3655    pub fn set_bind_group(
3656        &mut self,
3657        index: u32,
3658        bind_group: Option<Arc<BindGroup>>,
3659        offsets: &[DynamicOffset],
3660    ) -> Result<(), PassStateError> {
3661        let scope = PassErrorScope::SetBindGroup;
3662
3663        // This statement will return an error if the pass is ended. It's
3664        // important the error check comes before the early-out for
3665        // `set_and_check_redundant`.
3666        let base = pass_base!(self, scope);
3667
3668        if self.current_bind_groups.set_and_check_redundant(
3669            &bind_group,
3670            index,
3671            &mut base.dynamic_offsets,
3672            offsets,
3673        ) {
3674            return Ok(());
3675        }
3676
3677        let bind_group = if let Some(bind_group) = bind_group {
3678            pass_try!(base, scope, bind_group.check_is_valid());
3679            Some(bind_group)
3680        } else {
3681            None
3682        };
3683
3684        base.commands.push(ArcRenderCommand::SetBindGroup {
3685            index,
3686            num_dynamic_offsets: offsets.len(),
3687            bind_group,
3688        });
3689
3690        Ok(())
3691    }
3692
3693    pub fn set_pipeline(&mut self, pipeline: Arc<RenderPipeline>) -> Result<(), PassStateError> {
3694        let scope = PassErrorScope::SetPipelineRender;
3695
3696        let redundant = self.current_pipeline.set_and_check_redundant(&pipeline);
3697
3698        // This statement will return an error if the pass is ended.
3699        // Its important the error check comes before the early-out for `redundant`.
3700        let base = pass_base!(self, scope);
3701
3702        if redundant {
3703            return Ok(());
3704        }
3705
3706        pass_try!(base, scope, pipeline.check_valid());
3707
3708        base.commands.push(ArcRenderCommand::SetPipeline(pipeline));
3709
3710        Ok(())
3711    }
3712
3713    pub fn set_index_buffer(
3714        &mut self,
3715        buffer: Arc<Buffer>,
3716        index_format: IndexFormat,
3717        offset: BufferAddress,
3718        size: Option<BufferSize>,
3719    ) -> Result<(), PassStateError> {
3720        let scope = PassErrorScope::SetIndexBuffer;
3721        let base = pass_base!(self, scope);
3722
3723        pass_try!(base, scope, buffer.check_is_valid());
3724
3725        base.commands.push(ArcRenderCommand::SetIndexBuffer {
3726            buffer,
3727            index_format,
3728            offset,
3729            size,
3730        });
3731
3732        Ok(())
3733    }
3734
3735    pub fn set_vertex_buffer(
3736        &mut self,
3737        slot: u32,
3738        buffer: Option<Arc<Buffer>>,
3739        offset: BufferAddress,
3740        size: Option<BufferSize>,
3741    ) -> Result<(), PassStateError> {
3742        let scope = PassErrorScope::SetVertexBuffer;
3743        let base = pass_base!(self, scope);
3744
3745        let buffer = if let Some(buffer) = buffer {
3746            pass_try!(base, scope, buffer.check_is_valid());
3747            Some(buffer)
3748        } else {
3749            None
3750        };
3751
3752        base.commands.push(ArcRenderCommand::SetVertexBuffer {
3753            slot,
3754            buffer,
3755            offset,
3756            size,
3757        });
3758
3759        Ok(())
3760    }
3761
3762    pub fn set_blend_constant(&mut self, color: Color) -> Result<(), PassStateError> {
3763        let scope = PassErrorScope::SetBlendConstant;
3764        let base = pass_base!(self, scope);
3765
3766        base.commands
3767            .push(ArcRenderCommand::SetBlendConstant(color));
3768
3769        Ok(())
3770    }
3771
3772    pub fn set_stencil_reference(&mut self, value: u32) -> Result<(), PassStateError> {
3773        let scope = PassErrorScope::SetStencilReference;
3774        let base = pass_base!(self, scope);
3775        let value = convert_stencil_value(
3776            value,
3777            self.depth_stencil_attachment
3778                .as_ref()
3779                .map(|at| at.view.desc.format),
3780        );
3781        base.commands
3782            .push(ArcRenderCommand::SetStencilReference(value));
3783
3784        Ok(())
3785    }
3786
3787    pub fn set_viewport(
3788        &mut self,
3789        x: f32,
3790        y: f32,
3791        w: f32,
3792        h: f32,
3793        depth_min: f32,
3794        depth_max: f32,
3795    ) -> Result<(), PassStateError> {
3796        let scope = PassErrorScope::SetViewport;
3797        let base = pass_base!(self, scope);
3798
3799        base.commands.push(ArcRenderCommand::SetViewport {
3800            rect: Rect { x, y, w, h },
3801            depth_min,
3802            depth_max,
3803        });
3804
3805        Ok(())
3806    }
3807
3808    pub fn set_scissor_rect(
3809        &mut self,
3810        x: u32,
3811        y: u32,
3812        w: u32,
3813        h: u32,
3814    ) -> Result<(), PassStateError> {
3815        let scope = PassErrorScope::SetScissorRect;
3816        let base = pass_base!(self, scope);
3817
3818        base.commands
3819            .push(ArcRenderCommand::SetScissor(Rect { x, y, w, h }));
3820
3821        Ok(())
3822    }
3823
3824    pub fn set_immediates(&mut self, offset: u32, data: &[u8]) -> Result<(), PassStateError> {
3825        let scope = PassErrorScope::SetImmediate;
3826        let base = pass_base!(self, scope);
3827
3828        pass_try!(
3829            base,
3830            scope,
3831            pass::validate_immediates_alignment(offset, data.len())
3832        );
3833
3834        base.commands.push(ArcRenderCommand::SetImmediate {
3835            offset,
3836            data: data
3837                .chunks_exact(size_of::<u32>())
3838                .map(|ck| u32::from_le_bytes(ck.try_into().unwrap()))
3839                .collect(),
3840        });
3841
3842        Ok(())
3843    }
3844
3845    pub fn draw(
3846        &mut self,
3847        vertex_count: u32,
3848        instance_count: u32,
3849        first_vertex: u32,
3850        first_instance: u32,
3851    ) -> Result<(), PassStateError> {
3852        let scope = PassErrorScope::Draw {
3853            kind: DrawKind::Draw,
3854            family: DrawCommandFamily::Draw,
3855        };
3856        let base = pass_base!(self, scope);
3857
3858        base.commands.push(ArcRenderCommand::Draw {
3859            vertex_count,
3860            instance_count,
3861            first_vertex,
3862            first_instance,
3863        });
3864
3865        Ok(())
3866    }
3867
3868    pub fn draw_indexed(
3869        &mut self,
3870        index_count: u32,
3871        instance_count: u32,
3872        first_index: u32,
3873        base_vertex: i32,
3874        first_instance: u32,
3875    ) -> Result<(), PassStateError> {
3876        let scope = PassErrorScope::Draw {
3877            kind: DrawKind::Draw,
3878            family: DrawCommandFamily::DrawIndexed,
3879        };
3880        let base = pass_base!(self, scope);
3881
3882        base.commands.push(ArcRenderCommand::DrawIndexed {
3883            index_count,
3884            instance_count,
3885            first_index,
3886            base_vertex,
3887            first_instance,
3888        });
3889
3890        Ok(())
3891    }
3892
3893    pub fn draw_mesh_tasks(
3894        &mut self,
3895        group_count_x: u32,
3896        group_count_y: u32,
3897        group_count_z: u32,
3898    ) -> Result<(), RenderPassError> {
3899        let scope = PassErrorScope::Draw {
3900            kind: DrawKind::Draw,
3901            family: DrawCommandFamily::DrawMeshTasks,
3902        };
3903        let base = pass_base!(self, scope);
3904
3905        base.commands.push(ArcRenderCommand::DrawMeshTasks {
3906            group_count_x,
3907            group_count_y,
3908            group_count_z,
3909        });
3910        Ok(())
3911    }
3912
3913    pub fn draw_indirect(
3914        &mut self,
3915        buffer: Arc<Buffer>,
3916        offset: BufferAddress,
3917    ) -> Result<(), PassStateError> {
3918        let scope = PassErrorScope::Draw {
3919            kind: DrawKind::DrawIndirect,
3920            family: DrawCommandFamily::Draw,
3921        };
3922        let base = pass_base!(self, scope);
3923
3924        pass_try!(base, scope, buffer.check_is_valid());
3925
3926        base.commands.push(ArcRenderCommand::DrawIndirect {
3927            buffer,
3928            offset,
3929            count: 1,
3930            family: DrawCommandFamily::Draw,
3931
3932            vertex_or_index_limit: None,
3933            instance_limit: None,
3934        });
3935
3936        Ok(())
3937    }
3938
3939    pub fn draw_indexed_indirect(
3940        &mut self,
3941        buffer: Arc<Buffer>,
3942        offset: BufferAddress,
3943    ) -> Result<(), PassStateError> {
3944        let scope = PassErrorScope::Draw {
3945            kind: DrawKind::DrawIndirect,
3946            family: DrawCommandFamily::DrawIndexed,
3947        };
3948        let base = pass_base!(self, scope);
3949
3950        pass_try!(base, scope, buffer.check_is_valid());
3951
3952        base.commands.push(ArcRenderCommand::DrawIndirect {
3953            buffer,
3954            offset,
3955            count: 1,
3956            family: DrawCommandFamily::DrawIndexed,
3957
3958            vertex_or_index_limit: None,
3959            instance_limit: None,
3960        });
3961
3962        Ok(())
3963    }
3964
3965    pub fn draw_mesh_tasks_indirect(
3966        &mut self,
3967        buffer: Arc<Buffer>,
3968        offset: BufferAddress,
3969    ) -> Result<(), RenderPassError> {
3970        let scope = PassErrorScope::Draw {
3971            kind: DrawKind::DrawIndirect,
3972            family: DrawCommandFamily::DrawMeshTasks,
3973        };
3974        let base = pass_base!(self, scope);
3975
3976        pass_try!(base, scope, buffer.check_is_valid());
3977
3978        base.commands.push(ArcRenderCommand::DrawIndirect {
3979            buffer,
3980            offset,
3981            count: 1,
3982            family: DrawCommandFamily::DrawMeshTasks,
3983
3984            vertex_or_index_limit: None,
3985            instance_limit: None,
3986        });
3987
3988        Ok(())
3989    }
3990
3991    pub fn multi_draw_indirect(
3992        &mut self,
3993        buffer: Arc<Buffer>,
3994        offset: BufferAddress,
3995        count: u32,
3996    ) -> Result<(), PassStateError> {
3997        let scope = PassErrorScope::Draw {
3998            kind: DrawKind::MultiDrawIndirect,
3999            family: DrawCommandFamily::Draw,
4000        };
4001        let base = pass_base!(self, scope);
4002
4003        pass_try!(base, scope, buffer.check_is_valid());
4004
4005        base.commands.push(ArcRenderCommand::DrawIndirect {
4006            buffer,
4007            offset,
4008            count,
4009            family: DrawCommandFamily::Draw,
4010
4011            vertex_or_index_limit: None,
4012            instance_limit: None,
4013        });
4014
4015        Ok(())
4016    }
4017
4018    pub fn multi_draw_indexed_indirect(
4019        &mut self,
4020        buffer: Arc<Buffer>,
4021        offset: BufferAddress,
4022        count: u32,
4023    ) -> Result<(), PassStateError> {
4024        let scope = PassErrorScope::Draw {
4025            kind: DrawKind::MultiDrawIndirect,
4026            family: DrawCommandFamily::DrawIndexed,
4027        };
4028        let base = pass_base!(self, scope);
4029
4030        pass_try!(base, scope, buffer.check_is_valid());
4031
4032        base.commands.push(ArcRenderCommand::DrawIndirect {
4033            buffer,
4034            offset,
4035            count,
4036            family: DrawCommandFamily::DrawIndexed,
4037
4038            vertex_or_index_limit: None,
4039            instance_limit: None,
4040        });
4041
4042        Ok(())
4043    }
4044
4045    pub fn multi_draw_mesh_tasks_indirect(
4046        &mut self,
4047        buffer: Arc<Buffer>,
4048        offset: BufferAddress,
4049        count: u32,
4050    ) -> Result<(), RenderPassError> {
4051        let scope = PassErrorScope::Draw {
4052            kind: DrawKind::MultiDrawIndirect,
4053            family: DrawCommandFamily::DrawMeshTasks,
4054        };
4055        let base = pass_base!(self, scope);
4056
4057        pass_try!(base, scope, buffer.check_is_valid());
4058
4059        base.commands.push(ArcRenderCommand::DrawIndirect {
4060            buffer,
4061            offset,
4062            count,
4063            family: DrawCommandFamily::DrawMeshTasks,
4064
4065            vertex_or_index_limit: None,
4066            instance_limit: None,
4067        });
4068
4069        Ok(())
4070    }
4071
4072    pub fn multi_draw_indirect_count(
4073        &mut self,
4074        buffer: Arc<Buffer>,
4075        offset: BufferAddress,
4076        count_buffer: Arc<Buffer>,
4077        count_buffer_offset: BufferAddress,
4078        max_count: u32,
4079    ) -> Result<(), PassStateError> {
4080        let scope = PassErrorScope::Draw {
4081            kind: DrawKind::MultiDrawIndirectCount,
4082            family: DrawCommandFamily::Draw,
4083        };
4084        let base = pass_base!(self, scope);
4085        pass_try!(base, scope, buffer.check_is_valid());
4086        pass_try!(base, scope, count_buffer.check_is_valid());
4087
4088        base.commands
4089            .push(ArcRenderCommand::MultiDrawIndirectCount {
4090                buffer,
4091                offset,
4092                count_buffer,
4093                count_buffer_offset,
4094                max_count,
4095                family: DrawCommandFamily::Draw,
4096            });
4097
4098        Ok(())
4099    }
4100
4101    pub fn multi_draw_indexed_indirect_count(
4102        &mut self,
4103        buffer: Arc<Buffer>,
4104        offset: BufferAddress,
4105        count_buffer: Arc<Buffer>,
4106        count_buffer_offset: BufferAddress,
4107        max_count: u32,
4108    ) -> Result<(), PassStateError> {
4109        let scope = PassErrorScope::Draw {
4110            kind: DrawKind::MultiDrawIndirectCount,
4111            family: DrawCommandFamily::DrawIndexed,
4112        };
4113        let base = pass_base!(self, scope);
4114
4115        pass_try!(base, scope, buffer.check_is_valid());
4116        pass_try!(base, scope, count_buffer.check_is_valid());
4117
4118        base.commands
4119            .push(ArcRenderCommand::MultiDrawIndirectCount {
4120                buffer,
4121                offset,
4122                count_buffer,
4123                count_buffer_offset,
4124                max_count,
4125                family: DrawCommandFamily::DrawIndexed,
4126            });
4127
4128        Ok(())
4129    }
4130
4131    pub fn multi_draw_mesh_tasks_indirect_count(
4132        &mut self,
4133        buffer: Arc<Buffer>,
4134        offset: BufferAddress,
4135        count_buffer: Arc<Buffer>,
4136        count_buffer_offset: BufferAddress,
4137        max_count: u32,
4138    ) -> Result<(), RenderPassError> {
4139        let scope = PassErrorScope::Draw {
4140            kind: DrawKind::MultiDrawIndirectCount,
4141            family: DrawCommandFamily::DrawMeshTasks,
4142        };
4143        let base = pass_base!(self, scope);
4144
4145        pass_try!(base, scope, buffer.check_is_valid());
4146        pass_try!(base, scope, count_buffer.check_is_valid());
4147
4148        base.commands
4149            .push(ArcRenderCommand::MultiDrawIndirectCount {
4150                buffer,
4151                offset,
4152                count_buffer,
4153                count_buffer_offset,
4154                max_count,
4155                family: DrawCommandFamily::DrawMeshTasks,
4156            });
4157
4158        Ok(())
4159    }
4160
4161    pub fn push_debug_group(&mut self, label: &str, color: u32) -> Result<(), PassStateError> {
4162        let base = pass_base!(self, PassErrorScope::PushDebugGroup);
4163
4164        let bytes = label.as_bytes();
4165        base.string_data.extend_from_slice(bytes);
4166
4167        base.commands.push(ArcRenderCommand::PushDebugGroup {
4168            color,
4169            len: bytes.len(),
4170        });
4171
4172        Ok(())
4173    }
4174
4175    pub fn pop_debug_group(&mut self) -> Result<(), PassStateError> {
4176        let base = pass_base!(self, PassErrorScope::PopDebugGroup);
4177
4178        base.commands.push(ArcRenderCommand::PopDebugGroup);
4179
4180        Ok(())
4181    }
4182
4183    pub fn insert_debug_marker(&mut self, label: &str, color: u32) -> Result<(), PassStateError> {
4184        let base = pass_base!(self, PassErrorScope::InsertDebugMarker);
4185
4186        let bytes = label.as_bytes();
4187        base.string_data.extend_from_slice(bytes);
4188
4189        base.commands.push(ArcRenderCommand::InsertDebugMarker {
4190            color,
4191            len: bytes.len(),
4192        });
4193
4194        Ok(())
4195    }
4196
4197    pub fn write_timestamp(
4198        &mut self,
4199        query_set: Arc<QuerySet>,
4200        query_index: u32,
4201    ) -> Result<(), PassStateError> {
4202        let scope = PassErrorScope::WriteTimestamp;
4203        let base = pass_base!(self, scope);
4204
4205        pass_try!(base, scope, query_set.check_is_valid());
4206        base.commands.push(ArcRenderCommand::WriteTimestamp {
4207            query_set,
4208            query_index,
4209        });
4210
4211        Ok(())
4212    }
4213
4214    pub fn begin_occlusion_query(&mut self, query_index: u32) -> Result<(), PassStateError> {
4215        let scope = PassErrorScope::BeginOcclusionQuery;
4216        let base = pass_base!(self, scope);
4217
4218        base.commands
4219            .push(ArcRenderCommand::BeginOcclusionQuery { query_index });
4220
4221        Ok(())
4222    }
4223
4224    pub fn end_occlusion_query(&mut self) -> Result<(), PassStateError> {
4225        let scope = PassErrorScope::EndOcclusionQuery;
4226        let base = pass_base!(self, scope);
4227
4228        base.commands.push(ArcRenderCommand::EndOcclusionQuery);
4229
4230        Ok(())
4231    }
4232
4233    pub fn begin_pipeline_statistics_query(
4234        &mut self,
4235        query_set: Arc<QuerySet>,
4236        query_index: u32,
4237    ) -> Result<(), PassStateError> {
4238        let scope = PassErrorScope::BeginPipelineStatisticsQuery;
4239        let base = pass_base!(self, scope);
4240
4241        pass_try!(base, scope, query_set.check_is_valid());
4242        base.commands
4243            .push(ArcRenderCommand::BeginPipelineStatisticsQuery {
4244                query_set,
4245                query_index,
4246            });
4247
4248        Ok(())
4249    }
4250
4251    pub fn end_pipeline_statistics_query(&mut self) -> Result<(), PassStateError> {
4252        let scope = PassErrorScope::EndPipelineStatisticsQuery;
4253        let base = pass_base!(self, scope);
4254
4255        base.commands
4256            .push(ArcRenderCommand::EndPipelineStatisticsQuery);
4257
4258        Ok(())
4259    }
4260
4261    pub fn execute_bundles(
4262        &mut self,
4263        render_bundles: &[Arc<RenderBundle>],
4264    ) -> Result<(), PassStateError> {
4265        let scope = PassErrorScope::ExecuteBundle;
4266        let base = pass_base!(self, scope);
4267
4268        for bundle in render_bundles {
4269            pass_try!(base, scope, bundle.check_is_valid());
4270
4271            base.commands
4272                .push(ArcRenderCommand::ExecuteBundle(bundle.clone()));
4273        }
4274        self.current_pipeline.reset();
4275        self.current_bind_groups.reset();
4276
4277        Ok(())
4278    }
4279}
4280
4281pub(crate) const fn get_src_stride_of_indirect_args(family: DrawCommandFamily) -> u64 {
4282    match family {
4283        DrawCommandFamily::Draw => size_of::<wgt::DrawIndirectArgs>() as u64,
4284        DrawCommandFamily::DrawIndexed => size_of::<wgt::DrawIndexedIndirectArgs>() as u64,
4285        DrawCommandFamily::DrawMeshTasks => size_of::<wgt::DispatchIndirectArgs>() as u64,
4286    }
4287}
4288
4289pub(crate) const fn get_dst_stride_of_indirect_args(
4290    backend: wgt::Backend,
4291    family: DrawCommandFamily,
4292) -> u64 {
4293    // space for D3D12 special constants
4294    let extra = if matches!(backend, wgt::Backend::Dx12) {
4295        3 * size_of::<u32>() as u64
4296    } else {
4297        0
4298    };
4299    extra + get_src_stride_of_indirect_args(family)
4300}
4301
4302// Recording a render pass.
4303//
4304// The only error that should be returned from these methods is
4305// `EncoderStateError::Ended`, when the pass has already ended and an immediate
4306// validation error is raised.
4307//
4308// All other errors should be stored in the pass for later reporting when
4309// `CommandEncoder.finish()` is called.
4310//
4311// The `pass_try!` macro should be used to handle errors appropriately. Note
4312// that the `pass_try!` and `pass_base!` macros may return early from the
4313// function that invokes them, like the `?` operator.
4314impl Global {
4315    pub fn render_pass_set_bind_group(
4316        &self,
4317        pass: &mut RenderPass,
4318        index: u32,
4319        bind_group_id: Option<id::BindGroupId>,
4320        offsets: &[DynamicOffset],
4321    ) -> Result<(), PassStateError> {
4322        pass.set_bind_group(
4323            index,
4324            bind_group_id.map(|id| self.hub.bind_groups.get(id)),
4325            offsets,
4326        )
4327    }
4328
4329    pub fn render_pass_set_bind_group_with_id(
4330        &self,
4331        pass: id::RenderPassEncoderId,
4332        index: u32,
4333        bind_group_id: Option<id::BindGroupId>,
4334        offsets: &[DynamicOffset],
4335    ) -> Result<(), PassStateError> {
4336        let pass = self.hub.render_passes.get(pass);
4337        let mut pass = pass
4338            .try_lock()
4339            .expect("RenderPasses should not be used concurrently");
4340        self.render_pass_set_bind_group(&mut pass, index, bind_group_id, offsets)
4341    }
4342
4343    pub fn render_pass_set_pipeline(
4344        &self,
4345        pass: &mut RenderPass,
4346        pipeline_id: id::RenderPipelineId,
4347    ) -> Result<(), PassStateError> {
4348        let pipeline = self.resolve_render_pipeline_id(pipeline_id);
4349        pass.set_pipeline(pipeline)
4350    }
4351
4352    pub fn render_pass_set_pipeline_with_id(
4353        &self,
4354        pass: id::RenderPassEncoderId,
4355        pipeline_id: id::RenderPipelineId,
4356    ) -> Result<(), PassStateError> {
4357        let pass = self.hub.render_passes.get(pass);
4358        let mut pass = pass
4359            .try_lock()
4360            .expect("RenderPasses should not be used concurrently");
4361        self.render_pass_set_pipeline(&mut pass, pipeline_id)
4362    }
4363
4364    pub fn render_pass_set_index_buffer(
4365        &self,
4366        pass: &mut RenderPass,
4367        buffer_id: id::BufferId,
4368        index_format: IndexFormat,
4369        offset: BufferAddress,
4370        size: Option<BufferSize>,
4371    ) -> Result<(), PassStateError> {
4372        pass.set_index_buffer(
4373            self.resolve_buffer_id(buffer_id),
4374            index_format,
4375            offset,
4376            size,
4377        )
4378    }
4379
4380    pub fn render_pass_set_index_buffer_with_id(
4381        &self,
4382        pass: id::RenderPassEncoderId,
4383        buffer_id: id::BufferId,
4384        index_format: IndexFormat,
4385        offset: BufferAddress,
4386        size: Option<BufferSize>,
4387    ) -> Result<(), PassStateError> {
4388        let pass = self.hub.render_passes.get(pass);
4389        let mut pass = pass
4390            .try_lock()
4391            .expect("RenderPasses should not be used concurrently");
4392        self.render_pass_set_index_buffer(&mut pass, buffer_id, index_format, offset, size)
4393    }
4394
4395    pub fn render_pass_set_vertex_buffer(
4396        &self,
4397        pass: &mut RenderPass,
4398        slot: u32,
4399        buffer_id: Option<id::BufferId>,
4400        offset: BufferAddress,
4401        size: Option<BufferSize>,
4402    ) -> Result<(), PassStateError> {
4403        pass.set_vertex_buffer(
4404            slot,
4405            buffer_id.map(|id| self.resolve_buffer_id(id)),
4406            offset,
4407            size,
4408        )
4409    }
4410
4411    pub fn render_pass_set_vertex_buffer_with_id(
4412        &self,
4413        pass: id::RenderPassEncoderId,
4414        slot: u32,
4415        buffer_id: Option<id::BufferId>,
4416        offset: BufferAddress,
4417        size: Option<BufferSize>,
4418    ) -> Result<(), PassStateError> {
4419        let pass = self.hub.render_passes.get(pass);
4420        let mut pass = pass
4421            .try_lock()
4422            .expect("RenderPasses should not be used concurrently");
4423        self.render_pass_set_vertex_buffer(&mut pass, slot, buffer_id, offset, size)
4424    }
4425
4426    pub fn render_pass_set_blend_constant(
4427        &self,
4428        pass: &mut RenderPass,
4429        color: Color,
4430    ) -> Result<(), PassStateError> {
4431        pass.set_blend_constant(color)
4432    }
4433
4434    pub fn render_pass_set_blend_constant_with_id(
4435        &self,
4436        pass: id::RenderPassEncoderId,
4437        color: Color,
4438    ) -> Result<(), PassStateError> {
4439        let pass = self.hub.render_passes.get(pass);
4440        let mut pass = pass
4441            .try_lock()
4442            .expect("RenderPasses should not be used concurrently");
4443        self.render_pass_set_blend_constant(&mut pass, color)
4444    }
4445
4446    pub fn render_pass_set_stencil_reference(
4447        &self,
4448        pass: &mut RenderPass,
4449        value: u32,
4450    ) -> Result<(), PassStateError> {
4451        pass.set_stencil_reference(value)
4452    }
4453
4454    pub fn render_pass_set_stencil_reference_with_id(
4455        &self,
4456        pass: id::RenderPassEncoderId,
4457        value: u32,
4458    ) -> Result<(), PassStateError> {
4459        let pass = self.hub.render_passes.get(pass);
4460        let mut pass = pass
4461            .try_lock()
4462            .expect("RenderPasses should not be used concurrently");
4463        self.render_pass_set_stencil_reference(&mut pass, value)
4464    }
4465
4466    pub fn render_pass_set_viewport(
4467        &self,
4468        pass: &mut RenderPass,
4469        x: f32,
4470        y: f32,
4471        w: f32,
4472        h: f32,
4473        depth_min: f32,
4474        depth_max: f32,
4475    ) -> Result<(), PassStateError> {
4476        pass.set_viewport(x, y, w, h, depth_min, depth_max)
4477    }
4478
4479    pub fn render_pass_set_viewport_with_id(
4480        &self,
4481        pass: id::RenderPassEncoderId,
4482        x: f32,
4483        y: f32,
4484        w: f32,
4485        h: f32,
4486        depth_min: f32,
4487        depth_max: f32,
4488    ) -> Result<(), PassStateError> {
4489        let pass = self.hub.render_passes.get(pass);
4490        let mut pass = pass
4491            .try_lock()
4492            .expect("RenderPasses should not be used concurrently");
4493        self.render_pass_set_viewport(&mut pass, x, y, w, h, depth_min, depth_max)
4494    }
4495
4496    pub fn render_pass_set_scissor_rect(
4497        &self,
4498        pass: &mut RenderPass,
4499        x: u32,
4500        y: u32,
4501        w: u32,
4502        h: u32,
4503    ) -> Result<(), PassStateError> {
4504        pass.set_scissor_rect(x, y, w, h)
4505    }
4506
4507    pub fn render_pass_set_scissor_rect_with_id(
4508        &self,
4509        pass: id::RenderPassEncoderId,
4510        x: u32,
4511        y: u32,
4512        w: u32,
4513        h: u32,
4514    ) -> Result<(), PassStateError> {
4515        let pass = self.hub.render_passes.get(pass);
4516        let mut pass = pass
4517            .try_lock()
4518            .expect("RenderPasses should not be used concurrently");
4519        self.render_pass_set_scissor_rect(&mut pass, x, y, w, h)
4520    }
4521
4522    pub fn render_pass_set_immediates(
4523        &self,
4524        pass: &mut RenderPass,
4525        offset: u32,
4526        data: &[u8],
4527    ) -> Result<(), PassStateError> {
4528        pass.set_immediates(offset, data)
4529    }
4530
4531    pub fn render_pass_set_immediates_with_id(
4532        &self,
4533        pass: id::RenderPassEncoderId,
4534        offset: u32,
4535        data: &[u8],
4536    ) -> Result<(), PassStateError> {
4537        let pass = self.hub.render_passes.get(pass);
4538        let mut pass = pass
4539            .try_lock()
4540            .expect("RenderPasses should not be used concurrently");
4541        self.render_pass_set_immediates(&mut pass, offset, data)
4542    }
4543
4544    pub fn render_pass_draw(
4545        &self,
4546        pass: &mut RenderPass,
4547        vertex_count: u32,
4548        instance_count: u32,
4549        first_vertex: u32,
4550        first_instance: u32,
4551    ) -> Result<(), PassStateError> {
4552        pass.draw(vertex_count, instance_count, first_vertex, first_instance)
4553    }
4554
4555    pub fn render_pass_draw_with_id(
4556        &self,
4557        pass: id::RenderPassEncoderId,
4558        vertex_count: u32,
4559        instance_count: u32,
4560        first_vertex: u32,
4561        first_instance: u32,
4562    ) -> Result<(), PassStateError> {
4563        let pass = self.hub.render_passes.get(pass);
4564        let mut pass = pass
4565            .try_lock()
4566            .expect("RenderPasses should not be used concurrently");
4567        self.render_pass_draw(
4568            &mut pass,
4569            vertex_count,
4570            instance_count,
4571            first_vertex,
4572            first_instance,
4573        )
4574    }
4575
4576    pub fn render_pass_draw_indexed(
4577        &self,
4578        pass: &mut RenderPass,
4579        index_count: u32,
4580        instance_count: u32,
4581        first_index: u32,
4582        base_vertex: i32,
4583        first_instance: u32,
4584    ) -> Result<(), PassStateError> {
4585        pass.draw_indexed(
4586            index_count,
4587            instance_count,
4588            first_index,
4589            base_vertex,
4590            first_instance,
4591        )
4592    }
4593
4594    pub fn render_pass_draw_indexed_with_id(
4595        &self,
4596        pass: id::RenderPassEncoderId,
4597        index_count: u32,
4598        instance_count: u32,
4599        first_index: u32,
4600        base_vertex: i32,
4601        first_instance: u32,
4602    ) -> Result<(), PassStateError> {
4603        let pass = self.hub.render_passes.get(pass);
4604        let mut pass = pass
4605            .try_lock()
4606            .expect("RenderPasses should not be used concurrently");
4607        self.render_pass_draw_indexed(
4608            &mut pass,
4609            index_count,
4610            instance_count,
4611            first_index,
4612            base_vertex,
4613            first_instance,
4614        )
4615    }
4616
4617    pub fn render_pass_draw_mesh_tasks(
4618        &self,
4619        pass: &mut RenderPass,
4620        group_count_x: u32,
4621        group_count_y: u32,
4622        group_count_z: u32,
4623    ) -> Result<(), RenderPassError> {
4624        pass.draw_mesh_tasks(group_count_x, group_count_y, group_count_z)
4625    }
4626
4627    pub fn render_pass_draw_indirect(
4628        &self,
4629        pass: &mut RenderPass,
4630        buffer_id: id::BufferId,
4631        offset: BufferAddress,
4632    ) -> Result<(), PassStateError> {
4633        pass.draw_indirect(self.resolve_buffer_id(buffer_id), offset)
4634    }
4635
4636    pub fn render_pass_draw_indirect_with_id(
4637        &self,
4638        pass: id::RenderPassEncoderId,
4639        buffer_id: id::BufferId,
4640        offset: BufferAddress,
4641    ) -> Result<(), PassStateError> {
4642        let pass = self.hub.render_passes.get(pass);
4643        let mut pass = pass
4644            .try_lock()
4645            .expect("RenderPasses should not be used concurrently");
4646        self.render_pass_draw_indirect(&mut pass, buffer_id, offset)
4647    }
4648
4649    pub fn render_pass_draw_indexed_indirect(
4650        &self,
4651        pass: &mut RenderPass,
4652        buffer_id: id::BufferId,
4653        offset: BufferAddress,
4654    ) -> Result<(), PassStateError> {
4655        pass.draw_indexed_indirect(self.resolve_buffer_id(buffer_id), offset)
4656    }
4657
4658    pub fn render_pass_draw_indexed_indirect_with_id(
4659        &self,
4660        pass: id::RenderPassEncoderId,
4661        buffer_id: id::BufferId,
4662        offset: BufferAddress,
4663    ) -> Result<(), PassStateError> {
4664        let pass = self.hub.render_passes.get(pass);
4665        let mut pass = pass
4666            .try_lock()
4667            .expect("RenderPasses should not be used concurrently");
4668        self.render_pass_draw_indexed_indirect(&mut pass, buffer_id, offset)
4669    }
4670
4671    pub fn render_pass_draw_mesh_tasks_indirect(
4672        &self,
4673        pass: &mut RenderPass,
4674        buffer_id: id::BufferId,
4675        offset: BufferAddress,
4676    ) -> Result<(), RenderPassError> {
4677        pass.draw_mesh_tasks_indirect(self.resolve_buffer_id(buffer_id), offset)
4678    }
4679
4680    pub fn render_pass_multi_draw_indirect(
4681        &self,
4682        pass: &mut RenderPass,
4683        buffer_id: id::BufferId,
4684        offset: BufferAddress,
4685        count: u32,
4686    ) -> Result<(), PassStateError> {
4687        pass.multi_draw_indirect(self.resolve_buffer_id(buffer_id), offset, count)
4688    }
4689
4690    pub fn render_pass_multi_draw_indexed_indirect(
4691        &self,
4692        pass: &mut RenderPass,
4693        buffer_id: id::BufferId,
4694        offset: BufferAddress,
4695        count: u32,
4696    ) -> Result<(), PassStateError> {
4697        pass.multi_draw_indexed_indirect(self.resolve_buffer_id(buffer_id), offset, count)
4698    }
4699
4700    pub fn render_pass_multi_draw_mesh_tasks_indirect(
4701        &self,
4702        pass: &mut RenderPass,
4703        buffer_id: id::BufferId,
4704        offset: BufferAddress,
4705        count: u32,
4706    ) -> Result<(), RenderPassError> {
4707        pass.multi_draw_mesh_tasks_indirect(self.resolve_buffer_id(buffer_id), offset, count)
4708    }
4709
4710    pub fn render_pass_multi_draw_indirect_count(
4711        &self,
4712        pass: &mut RenderPass,
4713        buffer_id: id::BufferId,
4714        offset: BufferAddress,
4715        count_buffer_id: id::BufferId,
4716        count_buffer_offset: BufferAddress,
4717        max_count: u32,
4718    ) -> Result<(), PassStateError> {
4719        pass.multi_draw_indirect_count(
4720            self.resolve_buffer_id(buffer_id),
4721            offset,
4722            self.resolve_buffer_id(count_buffer_id),
4723            count_buffer_offset,
4724            max_count,
4725        )
4726    }
4727
4728    pub fn render_pass_multi_draw_indexed_indirect_count(
4729        &self,
4730        pass: &mut RenderPass,
4731        buffer_id: id::BufferId,
4732        offset: BufferAddress,
4733        count_buffer_id: id::BufferId,
4734        count_buffer_offset: BufferAddress,
4735        max_count: u32,
4736    ) -> Result<(), PassStateError> {
4737        pass.multi_draw_indexed_indirect_count(
4738            self.resolve_buffer_id(buffer_id),
4739            offset,
4740            self.resolve_buffer_id(count_buffer_id),
4741            count_buffer_offset,
4742            max_count,
4743        )
4744    }
4745
4746    pub fn render_pass_multi_draw_mesh_tasks_indirect_count(
4747        &self,
4748        pass: &mut RenderPass,
4749        buffer_id: id::BufferId,
4750        offset: BufferAddress,
4751        count_buffer_id: id::BufferId,
4752        count_buffer_offset: BufferAddress,
4753        max_count: u32,
4754    ) -> Result<(), RenderPassError> {
4755        pass.multi_draw_mesh_tasks_indirect_count(
4756            self.resolve_buffer_id(buffer_id),
4757            offset,
4758            self.resolve_buffer_id(count_buffer_id),
4759            count_buffer_offset,
4760            max_count,
4761        )
4762    }
4763
4764    pub fn render_pass_push_debug_group(
4765        &self,
4766        pass: &mut RenderPass,
4767        label: &str,
4768        color: u32,
4769    ) -> Result<(), PassStateError> {
4770        pass.push_debug_group(label, color)
4771    }
4772
4773    pub fn render_pass_push_debug_group_with_id(
4774        &self,
4775        pass: id::RenderPassEncoderId,
4776        label: &str,
4777        color: u32,
4778    ) -> Result<(), PassStateError> {
4779        let pass = self.hub.render_passes.get(pass);
4780        let mut pass = pass
4781            .try_lock()
4782            .expect("RenderPasses should not be used concurrently");
4783        self.render_pass_push_debug_group(&mut pass, label, color)
4784    }
4785
4786    pub fn render_pass_pop_debug_group(&self, pass: &mut RenderPass) -> Result<(), PassStateError> {
4787        pass.pop_debug_group()
4788    }
4789
4790    pub fn render_pass_pop_debug_group_with_id(
4791        &self,
4792        pass: id::RenderPassEncoderId,
4793    ) -> Result<(), PassStateError> {
4794        let pass = self.hub.render_passes.get(pass);
4795        let mut pass = pass
4796            .try_lock()
4797            .expect("RenderPasses should not be used concurrently");
4798        self.render_pass_pop_debug_group(&mut pass)
4799    }
4800
4801    pub fn render_pass_insert_debug_marker(
4802        &self,
4803        pass: &mut RenderPass,
4804        label: &str,
4805        color: u32,
4806    ) -> Result<(), PassStateError> {
4807        pass.insert_debug_marker(label, color)
4808    }
4809
4810    pub fn render_pass_insert_debug_marker_with_id(
4811        &self,
4812        pass: id::RenderPassEncoderId,
4813        label: &str,
4814        color: u32,
4815    ) -> Result<(), PassStateError> {
4816        let pass = self.hub.render_passes.get(pass);
4817        let mut pass = pass
4818            .try_lock()
4819            .expect("RenderPasses should not be used concurrently");
4820        self.render_pass_insert_debug_marker(&mut pass, label, color)
4821    }
4822
4823    pub fn render_pass_write_timestamp(
4824        &self,
4825        pass: &mut RenderPass,
4826        query_set_id: id::QuerySetId,
4827        query_index: u32,
4828    ) -> Result<(), PassStateError> {
4829        pass.write_timestamp(self.resolve_query_set_id(query_set_id), query_index)
4830    }
4831
4832    pub fn render_pass_write_timestamp_with_id(
4833        &self,
4834        pass: id::RenderPassEncoderId,
4835        query_set_id: id::QuerySetId,
4836        query_index: u32,
4837    ) -> Result<(), PassStateError> {
4838        let pass = self.hub.render_passes.get(pass);
4839        let mut pass = pass
4840            .try_lock()
4841            .expect("RenderPasses should not be used concurrently");
4842        self.render_pass_write_timestamp(&mut pass, query_set_id, query_index)
4843    }
4844
4845    pub fn render_pass_begin_occlusion_query(
4846        &self,
4847        pass: &mut RenderPass,
4848        query_index: u32,
4849    ) -> Result<(), PassStateError> {
4850        pass.begin_occlusion_query(query_index)
4851    }
4852
4853    pub fn render_pass_begin_occlusion_query_with_id(
4854        &self,
4855        pass: id::RenderPassEncoderId,
4856        query_index: u32,
4857    ) -> Result<(), PassStateError> {
4858        let pass = self.hub.render_passes.get(pass);
4859        let mut pass = pass
4860            .try_lock()
4861            .expect("RenderPasses should not be used concurrently");
4862        self.render_pass_begin_occlusion_query(&mut pass, query_index)
4863    }
4864
4865    pub fn render_pass_end_occlusion_query(
4866        &self,
4867        pass: &mut RenderPass,
4868    ) -> Result<(), PassStateError> {
4869        pass.end_occlusion_query()
4870    }
4871
4872    pub fn render_pass_end_occlusion_query_with_id(
4873        &self,
4874        pass: id::RenderPassEncoderId,
4875    ) -> Result<(), PassStateError> {
4876        let pass = self.hub.render_passes.get(pass);
4877        let mut pass = pass
4878            .try_lock()
4879            .expect("RenderPasses should not be used concurrently");
4880        self.render_pass_end_occlusion_query(&mut pass)
4881    }
4882
4883    pub fn render_pass_begin_pipeline_statistics_query(
4884        &self,
4885        pass: &mut RenderPass,
4886        query_set_id: id::QuerySetId,
4887        query_index: u32,
4888    ) -> Result<(), PassStateError> {
4889        pass.begin_pipeline_statistics_query(self.resolve_query_set_id(query_set_id), query_index)
4890    }
4891
4892    pub fn render_pass_begin_pipeline_statistics_query_with_id(
4893        &self,
4894        pass: id::RenderPassEncoderId,
4895        query_set_id: id::QuerySetId,
4896        query_index: u32,
4897    ) -> Result<(), PassStateError> {
4898        let pass = self.hub.render_passes.get(pass);
4899        let mut pass = pass
4900            .try_lock()
4901            .expect("RenderPasses should not be used concurrently");
4902        self.render_pass_begin_pipeline_statistics_query(&mut pass, query_set_id, query_index)
4903    }
4904
4905    pub fn render_pass_end_pipeline_statistics_query(
4906        &self,
4907        pass: &mut RenderPass,
4908    ) -> Result<(), PassStateError> {
4909        pass.end_pipeline_statistics_query()
4910    }
4911
4912    pub fn render_pass_end_pipeline_statistics_query_with_id(
4913        &self,
4914        pass: id::RenderPassEncoderId,
4915    ) -> Result<(), PassStateError> {
4916        let pass = self.hub.render_passes.get(pass);
4917        let mut pass = pass
4918            .try_lock()
4919            .expect("RenderPasses should not be used concurrently");
4920        self.render_pass_end_pipeline_statistics_query(&mut pass)
4921    }
4922
4923    pub fn render_pass_execute_bundles(
4924        &self,
4925        pass: &mut RenderPass,
4926        render_bundle_ids: &[id::RenderBundleId],
4927    ) -> Result<(), PassStateError> {
4928        let hub = &self.hub;
4929        let bundles = hub.render_bundles.read();
4930        let render_bundles = render_bundle_ids
4931            .iter()
4932            .map(|&id| bundles.get(id))
4933            .collect::<Vec<_>>();
4934
4935        pass.execute_bundles(&render_bundles)
4936    }
4937
4938    pub fn render_pass_execute_bundles_with_id(
4939        &self,
4940        pass: id::RenderPassEncoderId,
4941        render_bundle_ids: &[id::RenderBundleId],
4942    ) -> Result<(), PassStateError> {
4943        let pass = self.hub.render_passes.get(pass);
4944        let mut pass = pass
4945            .try_lock()
4946            .expect("RenderPasses should not be used concurrently");
4947        self.render_pass_execute_bundles(&mut pass, render_bundle_ids)
4948    }
4949}