wgpu_core/command/
bundle.rs

1/*! Render Bundles
2
3A render bundle is a prerecorded sequence of commands that can be replayed on a
4command encoder with a single call. A single bundle can replayed any number of
5times, on different encoders. Constructing a render bundle lets `wgpu` validate
6and analyze its commands up front, so that replaying a bundle can be more
7efficient than simply re-recording its commands each time.
8
9Not all commands are available in bundles; for example, a render bundle may not
10contain a [`RenderCommand::SetViewport`] command.
11
12Most of `wgpu`'s backend graphics APIs have something like bundles. For example,
13Vulkan calls them "secondary command buffers", and Metal calls them "indirect
14command buffers". Although we plan to take advantage of these platform features
15at some point in the future, for now `wgpu`'s implementation of render bundles
16does not use them: at the hal level, `wgpu` render bundles just replay the
17commands.
18
19## Render Bundle Isolation
20
21One important property of render bundles is that the draw calls in a render
22bundle depend solely on the pipeline and state established within the render
23bundle itself. A draw call in a bundle will never use a vertex buffer, say, that
24was set in the `RenderPass` before executing the bundle. We call this property
25'isolation', in that a render bundle is somewhat isolated from the passes that
26use it.
27
28Render passes are also isolated from the effects of bundles. After executing a
29render bundle, a render pass's pipeline, bind groups, and vertex and index
30buffers are are unset, so the bundle cannot affect later draw calls in the pass.
31
32A render pass is isolated from a bundle's effects on immediate data
33values. When encoding a render bundle, calls to `set_immediates` snapshot the immediate data
34content at encoding time, and the immediate values cannot be changed after `finish`.
35Before and after executing each individual bundle, all required immediate slots are cleared/reset,
36therefore immediate data must be set again.
37
38## Render Bundle Lifecycle
39
40To create a render bundle:
41
421) Create a [`RenderBundleEncoder`] by calling
43   [`Device::create_render_bundle_encoder`][Dcrbe].
44
452) Record commands in the `RenderBundleEncoder` using methods on [`RenderBundleEncoder`].
46
473) Call [`RenderBundleEncoder::finish`], which analyzes and cleans up
48   the command stream and returns a [`RenderBundle`].
49
504) Then, any number of times, call [`RenderPass::execute_bundles`][rpeb] to
51   execute the bundle as part of some render pass.
52
53## Implementation
54
55The most complex part of render bundles is the "finish" step, mostly implemented
56in [`RenderBundleEncoder::finish`]. This consumes the commands stored in the
57encoder's [`BasePass`], while validating everything, tracking the state,
58dropping redundant or unnecessary commands, and presenting the results as a new
59[`RenderBundle`]. It doesn't actually execute any commands.
60
61This step also enforces the 'isolation' property mentioned above: every draw
62call is checked to ensure that the resources it uses on were established since
63the last time the pipeline was set. This means the bundle can be executed
64verbatim without any state tracking.
65
66### Execution
67
68When the bundle is used in an actual render pass, `RenderBundle::execute` is
69called. It goes through the commands and issues them into the native command
70buffer. Thanks to isolation, it doesn't track any bind group invalidations or
71index format changes.
72
73[Dcrbe]: crate::device::Device::create_render_bundle_encoder
74[rpeb]: crate::command::RenderPass::execute_bundles
75!*/
76
77#![allow(clippy::reversed_empty_ranges)]
78
79use alloc::{
80    borrow::{Cow, ToOwned as _},
81    boxed::Box,
82    string::String,
83    string::ToString as _,
84    sync::Arc,
85    vec::Vec,
86};
87use core::{
88    convert::Infallible,
89    mem,
90    num::{NonZeroU32, NonZeroU64},
91    ops::Range,
92};
93
94use arrayvec::ArrayVec;
95use thiserror::Error;
96
97use wgpu_hal::ShouldBeNonZeroExt;
98use wgt::error::{ErrorType, WebGpuError};
99
100use crate::{
101    api_log,
102    binding_model::{BindError, BindGroup, ImmediateUploadError, PipelineLayout},
103    command::{
104        bind::Binder,
105        pass::{validate_immediates_alignment, ImmediateState},
106        pass_base, ArcReferences, BasePass, BindGroupStateChange, ColorAttachmentError, DrawError,
107        EncoderStateError, MapPassErr, PassErrorScope, PassStateError, RenderCommand,
108        RenderCommandError, StateChange,
109    },
110    device::{
111        AttachmentData, Device, DeviceError, MissingDownlevelFlags, MissingFeatures,
112        RenderPassContext,
113    },
114    id, impl_resource_type, impl_storage_item,
115    init_tracker::{BufferInitTrackerAction, MemoryInitKind, TextureInitTrackerAction},
116    pipeline::{PipelineFlags, RenderPipeline},
117    resource::{
118        Buffer, DestroyedResourceError, InvalidOrDestroyedResourceError, InvalidResourceError,
119        Labeled, ParentDevice, RawResourceAccess, ResourceState, TrackingData,
120    },
121    resource_log,
122    snatch::SnatchGuard,
123    track::RenderBundleScope,
124    validation::{
125        check_color_attachment_count, validate_color_attachment_bytes_per_sample,
126        WorkgroupSizeCheck,
127    },
128    Label, LabelHelpers,
129};
130
131use super::{pass, render_command::ArcRenderCommand, DrawCommandFamily, DrawKind};
132
133/// Describes a [`RenderBundleEncoder`].
134#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
135#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
136pub struct RenderBundleEncoderDescriptor<'a> {
137    /// Debug label of the render bundle encoder.
138    ///
139    /// This will show up in graphics debuggers for easy identification.
140    pub label: Label<'a>,
141    /// The formats of the color attachments that this render bundle is capable
142    /// to rendering to.
143    ///
144    /// This must match the formats of the color attachments in the
145    /// renderpass this render bundle is executed in.
146    pub color_formats: Cow<'a, [Option<wgt::TextureFormat>]>,
147    /// Information about the depth attachment that this render bundle is
148    /// capable to rendering to.
149    ///
150    /// The format must match the format of the depth attachments in the
151    /// renderpass this render bundle is executed in.
152    pub depth_stencil: Option<wgt::RenderBundleDepthStencil>,
153    /// Sample count this render bundle is capable of rendering to.
154    ///
155    /// This must match the pipelines and the renderpasses it is used in.
156    pub sample_count: u32,
157    /// If this render bundle will rendering to multiple array layers in the
158    /// attachments at the same time.
159    pub multiview: Option<NonZeroU32>,
160}
161
162#[derive(Debug)]
163pub struct RenderBundleEncoder {
164    pub(crate) base: BasePass<RenderCommand<ArcReferences>, Infallible>,
165    device: Arc<Device>,
166    /// State of the render bundle encoder. Encoded to be compatible with pass macros.
167    ///
168    /// If this is `Some`, then the pass is in WebGPU's "open" state. If it is
169    /// `None`, then the pass is in the "ended" state.
170    /// See <https://www.w3.org/TR/webgpu/#encoder-state>
171    parent: Option<()>,
172    pub(crate) context: RenderPassContext,
173    pub(crate) is_depth_read_only: bool,
174    pub(crate) is_stencil_read_only: bool,
175
176    // Resource binding dedupe state.
177    current_bind_groups: BindGroupStateChange<Arc<BindGroup>>,
178    current_pipeline: StateChange<Arc<RenderPipeline>>,
179}
180
181impl_resource_type!(RenderBundleEncoder);
182impl_storage_item!(RenderBundleEncoder);
183
184/// Validate a render bundle descriptor.
185///
186/// Returns a tuple (is_depth_read_only, is_stencil_read_only).
187fn validate_render_bundle_encoder_descriptor(
188    desc: &RenderBundleEncoderDescriptor,
189    device: &Arc<Device>,
190) -> Result<(bool, bool), CreateRenderBundleError> {
191    let mut have_attachment = false;
192
193    let max_color_attachments = device.limits.max_color_attachments;
194    assert!(max_color_attachments <= hal::MAX_COLOR_ATTACHMENTS as u32);
195    check_color_attachment_count(desc.color_formats.len(), max_color_attachments)?;
196
197    for &format in desc.color_formats.iter().flatten() {
198        have_attachment = true;
199        if !format.has_color_aspect() {
200            return Err(CreateRenderBundleError::FormatNotColor(format));
201        }
202        let format_features = device.describe_format_features(format)?;
203        if !format_features
204            .allowed_usages
205            .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
206        {
207            return Err(CreateRenderBundleError::FormatNotRenderable(format));
208        }
209    }
210
211    validate_color_attachment_bytes_per_sample(
212        desc.color_formats.iter().flatten().copied(),
213        device.limits.max_color_attachment_bytes_per_sample,
214    )?;
215
216    let (is_depth_read_only, is_stencil_read_only) = match desc.depth_stencil {
217        Some(ds) => {
218            have_attachment = true;
219            let has_depth = ds.format.has_depth_aspect();
220            let has_stencil = ds.format.has_stencil_aspect();
221            if !has_depth && !has_stencil {
222                return Err(CreateRenderBundleError::FormatNotDepthOrStencil(ds.format));
223            } else {
224                (
225                    !has_depth || ds.depth_read_only,
226                    !has_stencil || ds.stencil_read_only,
227                )
228            }
229        }
230        // There's no depth/stencil attachment, so these values just don't
231        // matter.  Choose the most accommodating value, to simplify
232        // validation.
233        None => (true, true),
234    };
235
236    if !have_attachment {
237        return Err(CreateRenderBundleError::NoAttachment);
238    }
239
240    Ok((is_depth_read_only, is_stencil_read_only))
241}
242
243impl RenderBundleEncoder {
244    /// Create a new `RenderBundleEncoder`.
245    pub fn new(
246        device: &Arc<Device>,
247        desc: &RenderBundleEncoderDescriptor,
248    ) -> Result<Self, CreateRenderBundleError> {
249        device.check_is_valid()?;
250        let (is_depth_read_only, is_stencil_read_only) =
251            validate_render_bundle_encoder_descriptor(desc, device)?;
252
253        Ok(Self {
254            base: BasePass::new(&desc.label),
255            device: Arc::clone(device),
256            parent: Some(()),
257            context: RenderPassContext {
258                attachments: AttachmentData {
259                    colors: desc.color_formats.iter().cloned().collect(),
260                    resolves: ArrayVec::new(),
261                    depth_stencil: desc.depth_stencil.map(|ds| ds.format),
262                },
263                sample_count: desc.sample_count,
264                multiview_mask: desc.multiview,
265            },
266
267            is_depth_read_only,
268            is_stencil_read_only,
269            current_bind_groups: BindGroupStateChange::new(),
270            current_pipeline: StateChange::new(),
271        })
272    }
273
274    pub fn dummy(device: &Arc<Device>) -> Self {
275        Self {
276            base: BasePass::new(&None),
277            parent: None,
278            device: Arc::clone(device),
279            context: RenderPassContext::default(),
280            is_depth_read_only: false,
281            is_stencil_read_only: false,
282
283            current_bind_groups: BindGroupStateChange::new(),
284            current_pipeline: StateChange::new(),
285        }
286    }
287
288    pub fn label(&self) -> Option<&str> {
289        self.base.label.as_deref()
290    }
291
292    /// Convert this encoder's commands into a [`RenderBundle`].
293    ///
294    /// We want executing a [`RenderBundle`] to be quick, so we take
295    /// this opportunity to clean up the [`RenderBundleEncoder`]'s
296    /// command stream and gather metadata about it that will help
297    /// keep [`ExecuteBundle`] simple and fast. We remove redundant
298    /// commands (along with their side data), note resource usage,
299    /// and accumulate buffer and texture initialization actions.
300    ///
301    /// [`ExecuteBundle`]: RenderCommand::ExecuteBundle
302    pub fn finish(
303        &mut self,
304        desc: &RenderBundleDescriptor,
305    ) -> (Arc<RenderBundle>, Option<RenderBundleError>) {
306        profiling::scope!("RenderBundleEncoder::finish");
307        #[cfg(feature = "trace")]
308        let trace_desc = crate::device::trace::new_render_bundle_encoder_descriptor(
309            desc.label.clone(),
310            &self.context,
311            self.is_depth_read_only,
312            self.is_stencil_read_only,
313        );
314
315        let (render_bundle, error) = match self.finish_inner(desc) {
316            Ok(render_bundle) => (render_bundle, None),
317            Err(e) => (
318                RenderBundle::invalid(Arc::clone(&self.device), desc),
319                Some(e),
320            ),
321        };
322
323        #[cfg(feature = "trace")]
324        if let Some(ref mut trace) = *self.device.trace.lock() {
325            use crate::device::trace::{Action, IntoTrace};
326            trace.add(Action::CreateRenderBundle {
327                id: render_bundle.to_trace(),
328                desc: trace_desc,
329                base: render_bundle.to_base_pass().to_trace(),
330            });
331        }
332
333        api_log!(
334            "RenderBundleEncoder::finish -> {:?}",
335            Arc::as_ptr(&render_bundle)
336        );
337
338        (render_bundle, error)
339    }
340
341    /// Convert this encoder's commands into a [`RenderBundle`].
342    ///
343    /// We want executing a [`RenderBundle`] to be quick, so we take
344    /// this opportunity to clean up the [`RenderBundleEncoder`]'s
345    /// command stream and gather metadata about it that will help
346    /// keep [`ExecuteBundle`] simple and fast. We remove redundant
347    /// commands (along with their side data), note resource usage,
348    /// and accumulate buffer and texture initialization actions.
349    ///
350    /// [`ExecuteBundle`]: RenderCommand::ExecuteBundle
351    pub(crate) fn finish_inner(
352        &mut self,
353        desc: &RenderBundleDescriptor,
354    ) -> Result<Arc<RenderBundle>, RenderBundleError> {
355        let scope = PassErrorScope::Bundle;
356
357        self.parent
358            .take()
359            .ok_or(RenderBundleErrorInner::Ended)
360            .map_pass_err(scope)?;
361
362        self.device.check_is_valid().map_pass_err(scope)?;
363
364        let mut state = State {
365            trackers: RenderBundleScope::new(),
366            pipeline: None,
367            vertex: Default::default(),
368            index: None,
369            flat_dynamic_offsets: Vec::new(),
370            device: Arc::clone(&self.device),
371            commands: Vec::new(),
372            buffer_memory_init_actions: Vec::new(),
373            texture_memory_init_actions: Vec::new(),
374            next_dynamic_offset: 0,
375            binder: Binder::new(),
376            immediate_state: ImmediateState::default(),
377        };
378
379        let indices = &state.device.tracker_indices;
380        state.trackers.buffers.set_size(indices.buffers.size());
381        state.trackers.textures.set_size(indices.textures.size());
382
383        for command in self.base.commands.drain(..) {
384            match command {
385                RenderCommand::SetBindGroup {
386                    index,
387                    num_dynamic_offsets,
388                    bind_group,
389                } => {
390                    let scope = PassErrorScope::SetBindGroup;
391                    set_bind_group(
392                        &mut state,
393                        &self.base.dynamic_offsets,
394                        index,
395                        num_dynamic_offsets,
396                        bind_group,
397                    )
398                    .map_pass_err(scope)?;
399                }
400                RenderCommand::SetPipeline(pipeline) => {
401                    let scope = PassErrorScope::SetPipelineRender;
402                    set_pipeline(
403                        &mut state,
404                        &self.context,
405                        self.is_depth_read_only,
406                        self.is_stencil_read_only,
407                        pipeline,
408                    )
409                    .map_pass_err(scope)?;
410                }
411                RenderCommand::SetIndexBuffer {
412                    buffer,
413                    index_format,
414                    offset,
415                    size,
416                } => {
417                    let scope = PassErrorScope::SetIndexBuffer;
418                    set_index_buffer(&mut state, buffer, index_format, offset, size)
419                        .map_pass_err(scope)?;
420                }
421                RenderCommand::SetVertexBuffer {
422                    slot,
423                    buffer,
424                    offset,
425                    size,
426                } => {
427                    let scope = PassErrorScope::SetVertexBuffer;
428                    set_vertex_buffer(&mut state, slot, buffer, offset, size)
429                        .map_pass_err(scope)?;
430                }
431                RenderCommand::SetImmediate { offset, ref data } => {
432                    let scope = PassErrorScope::SetImmediate;
433                    set_immediates(&mut state, offset, data).map_pass_err(scope)?;
434                }
435                RenderCommand::Draw {
436                    vertex_count,
437                    instance_count,
438                    first_vertex,
439                    first_instance,
440                } => {
441                    let scope = PassErrorScope::Draw {
442                        kind: DrawKind::Draw,
443                        family: DrawCommandFamily::Draw,
444                    };
445                    draw(
446                        &mut state,
447                        vertex_count,
448                        instance_count,
449                        first_vertex,
450                        first_instance,
451                    )
452                    .map_pass_err(scope)?;
453                }
454                RenderCommand::DrawIndexed {
455                    index_count,
456                    instance_count,
457                    first_index,
458                    base_vertex,
459                    first_instance,
460                } => {
461                    let scope = PassErrorScope::Draw {
462                        kind: DrawKind::Draw,
463                        family: DrawCommandFamily::DrawIndexed,
464                    };
465                    draw_indexed(
466                        &mut state,
467                        index_count,
468                        instance_count,
469                        first_index,
470                        base_vertex,
471                        first_instance,
472                    )
473                    .map_pass_err(scope)?;
474                }
475                RenderCommand::DrawMeshTasks {
476                    group_count_x,
477                    group_count_y,
478                    group_count_z,
479                } => {
480                    let scope = PassErrorScope::Draw {
481                        kind: DrawKind::Draw,
482                        family: DrawCommandFamily::DrawMeshTasks,
483                    };
484                    draw_mesh_tasks(&mut state, group_count_x, group_count_y, group_count_z)
485                        .map_pass_err(scope)?;
486                }
487                RenderCommand::DrawIndirect {
488                    buffer,
489                    offset,
490                    count: 1,
491                    family,
492                    vertex_or_index_limit: None,
493                    instance_limit: None,
494                } => {
495                    let scope = PassErrorScope::Draw {
496                        kind: DrawKind::DrawIndirect,
497                        family,
498                    };
499                    multi_draw_indirect(&mut state, buffer, offset, family).map_pass_err(scope)?;
500                }
501                RenderCommand::DrawIndirect {
502                    count,
503                    vertex_or_index_limit,
504                    instance_limit,
505                    ..
506                } => {
507                    unreachable!("unexpected (multi-)draw indirect with count {count}, vertex_or_index_limits {vertex_or_index_limit:?}, instance_limit {instance_limit:?} found in a render bundle");
508                }
509                RenderCommand::MultiDrawIndirectCount { .. }
510                | RenderCommand::PushDebugGroup { color: _, len: _ }
511                | RenderCommand::InsertDebugMarker { color: _, len: _ }
512                | RenderCommand::PopDebugGroup => {
513                    unimplemented!("not supported by a render bundle")
514                }
515                // Must check the TIMESTAMP_QUERY_INSIDE_PASSES feature
516                RenderCommand::WriteTimestamp { .. }
517                | RenderCommand::BeginOcclusionQuery { .. }
518                | RenderCommand::EndOcclusionQuery
519                | RenderCommand::BeginPipelineStatisticsQuery { .. }
520                | RenderCommand::EndPipelineStatisticsQuery => {
521                    unimplemented!("not supported by a render bundle")
522                }
523                RenderCommand::ExecuteBundle(_)
524                | RenderCommand::SetBlendConstant(_)
525                | RenderCommand::SetStencilReference(_)
526                | RenderCommand::SetViewport { .. }
527                | RenderCommand::SetScissor(_) => unreachable!("not supported by a render bundle"),
528            }
529        }
530
531        let State {
532            trackers,
533            flat_dynamic_offsets,
534            device,
535            commands,
536            buffer_memory_init_actions,
537            texture_memory_init_actions,
538            ..
539        } = state;
540
541        let tracker_indices = device.tracker_indices.bundles.clone();
542        let discard_hal_labels = device
543            .instance_flags
544            .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS);
545
546        let string_data = mem::take(&mut self.base.string_data);
547        let context = mem::take(&mut self.context);
548        let render_bundle = RenderBundle {
549            state: ResourceState::Valid(RenderBundleState {
550                context,
551                used: trackers,
552            }),
553            base: BasePass {
554                label: desc.label.as_deref().map(str::to_owned),
555                error: None,
556                commands,
557                dynamic_offsets: flat_dynamic_offsets,
558                string_data,
559            },
560            is_depth_read_only: self.is_depth_read_only,
561            is_stencil_read_only: self.is_stencil_read_only,
562            device: device.clone(),
563            buffer_memory_init_actions,
564            texture_memory_init_actions,
565            label: desc.label.to_string(),
566            tracking_data: TrackingData::new(tracker_indices),
567            discard_hal_labels,
568        };
569
570        let render_bundle = Arc::new(render_bundle);
571
572        Ok(render_bundle)
573    }
574
575    pub fn set_index_buffer(
576        &mut self,
577        buffer: Arc<Buffer>,
578        index_format: wgt::IndexFormat,
579        offset: wgt::BufferAddress,
580        size: Option<wgt::BufferSize>,
581    ) -> Result<(), PassStateError> {
582        pass_base!(self, PassErrorScope::SetIndexBuffer);
583        self.base.commands.push(RenderCommand::SetIndexBuffer {
584            buffer,
585            index_format,
586            offset,
587            size,
588        });
589        Ok(())
590    }
591
592    pub fn set_bind_group(
593        &mut self,
594        index: u32,
595        bind_group: Option<Arc<BindGroup>>,
596        offsets: &[wgt::DynamicOffset],
597    ) -> Result<(), PassStateError> {
598        pass_base!(self, PassErrorScope::SetBindGroup);
599        let redundant = self.current_bind_groups.set_and_check_redundant(
600            &bind_group,
601            index,
602            &mut self.base.dynamic_offsets,
603            offsets,
604        );
605
606        if redundant {
607            return Ok(());
608        }
609
610        self.base.commands.push(RenderCommand::SetBindGroup {
611            index,
612            num_dynamic_offsets: offsets.len(),
613            bind_group,
614        });
615        Ok(())
616    }
617
618    pub fn set_pipeline(&mut self, pipeline: Arc<RenderPipeline>) -> Result<(), PassStateError> {
619        pass_base!(self, PassErrorScope::SetPipelineRender);
620        if self.current_pipeline.set_and_check_redundant(&pipeline) {
621            return Ok(());
622        }
623
624        self.base
625            .commands
626            .push(RenderCommand::SetPipeline(pipeline));
627        Ok(())
628    }
629
630    pub fn set_vertex_buffer(
631        &mut self,
632        slot: u32,
633        buffer: Option<Arc<Buffer>>,
634        offset: wgt::BufferAddress,
635        size: Option<wgt::BufferSize>,
636    ) -> Result<(), PassStateError> {
637        pass_base!(self, PassErrorScope::SetVertexBuffer);
638        self.base.commands.push(RenderCommand::SetVertexBuffer {
639            slot,
640            buffer,
641            offset,
642            size,
643        });
644        Ok(())
645    }
646
647    pub fn set_immediates(&mut self, offset: u32, data: &[u8]) -> Result<(), PassStateError> {
648        pass_base!(self, PassErrorScope::SetImmediate);
649
650        // This should have been validated in content timeline
651        assert!(data.len().is_multiple_of(4));
652
653        self.base.commands.push(RenderCommand::SetImmediate {
654            offset,
655            data: data
656                .chunks_exact(size_of::<u32>())
657                .map(|ck| u32::from_le_bytes(ck.try_into().unwrap()))
658                .collect(),
659        });
660        Ok(())
661    }
662
663    pub fn draw(
664        &mut self,
665        vertex_count: u32,
666        instance_count: u32,
667        first_vertex: u32,
668        first_instance: u32,
669    ) -> Result<(), PassStateError> {
670        pass_base!(
671            self,
672            PassErrorScope::Draw {
673                kind: DrawKind::Draw,
674                family: DrawCommandFamily::Draw
675            }
676        );
677        self.base.commands.push(RenderCommand::Draw {
678            vertex_count,
679            instance_count,
680            first_vertex,
681            first_instance,
682        });
683        Ok(())
684    }
685
686    pub fn draw_indexed(
687        &mut self,
688        index_count: u32,
689        instance_count: u32,
690        first_index: u32,
691        base_vertex: i32,
692        first_instance: u32,
693    ) -> Result<(), PassStateError> {
694        pass_base!(
695            self,
696            PassErrorScope::Draw {
697                kind: DrawKind::Draw,
698                family: DrawCommandFamily::DrawIndexed
699            }
700        );
701        self.base.commands.push(RenderCommand::DrawIndexed {
702            index_count,
703            instance_count,
704            first_index,
705            base_vertex,
706            first_instance,
707        });
708        Ok(())
709    }
710
711    pub fn draw_indirect(
712        &mut self,
713        buffer: Arc<Buffer>,
714        offset: wgt::BufferAddress,
715    ) -> Result<(), PassStateError> {
716        pass_base!(
717            self,
718            PassErrorScope::Draw {
719                kind: DrawKind::DrawIndirect,
720                family: DrawCommandFamily::Draw
721            }
722        );
723        self.base.commands.push(RenderCommand::DrawIndirect {
724            buffer,
725            offset,
726            count: 1,
727            family: DrawCommandFamily::Draw,
728            vertex_or_index_limit: None,
729            instance_limit: None,
730        });
731        Ok(())
732    }
733
734    pub fn draw_indexed_indirect(
735        &mut self,
736        buffer: Arc<Buffer>,
737        offset: wgt::BufferAddress,
738    ) -> Result<(), PassStateError> {
739        pass_base!(
740            self,
741            PassErrorScope::Draw {
742                kind: DrawKind::DrawIndirect,
743                family: DrawCommandFamily::DrawIndexed
744            }
745        );
746        self.base.commands.push(RenderCommand::DrawIndirect {
747            buffer,
748            offset,
749            count: 1,
750            family: DrawCommandFamily::DrawIndexed,
751            vertex_or_index_limit: None,
752            instance_limit: None,
753        });
754        Ok(())
755    }
756
757    pub fn push_debug_group(&mut self, _label: &str) -> Result<(), PassStateError> {
758        pass_base!(self, PassErrorScope::PushDebugGroup);
759        //TODO
760        Ok(())
761    }
762
763    pub fn pop_debug_group(&mut self) -> Result<(), PassStateError> {
764        pass_base!(self, PassErrorScope::PopDebugGroup);
765        //TODO
766        Ok(())
767    }
768
769    pub fn insert_debug_marker(&mut self, _label: &str) -> Result<(), PassStateError> {
770        pass_base!(self, PassErrorScope::InsertDebugMarker);
771        //TODO
772        Ok(())
773    }
774}
775
776fn set_bind_group(
777    state: &mut State,
778    dynamic_offsets: &[u32],
779    index: u32,
780    num_dynamic_offsets: usize,
781    bind_group: Option<Arc<BindGroup>>,
782) -> Result<(), RenderBundleErrorInner> {
783    let max_bind_groups = state.device.limits.max_bind_groups;
784    if index >= max_bind_groups {
785        return Err(
786            RenderCommandError::BindGroupIndexOutOfRange(pass::BindGroupIndexOutOfRange {
787                index,
788                max: max_bind_groups,
789            })
790            .into(),
791        );
792    }
793
794    // Identify the next `num_dynamic_offsets` entries from `dynamic_offsets`.
795    let offsets_range = state.next_dynamic_offset..state.next_dynamic_offset + num_dynamic_offsets;
796    state.next_dynamic_offset = offsets_range.end;
797    let offsets = &dynamic_offsets[offsets_range.clone()];
798
799    if let Some(bind_group) = bind_group {
800        bind_group.check_is_valid()?;
801        bind_group.same_device(&state.device)?;
802        bind_group.validate_dynamic_bindings(index, offsets)?;
803
804        unsafe { state.trackers.merge_bind_group(&bind_group.used)? };
805        let bind_group = state.trackers.bind_groups.insert_single(bind_group);
806
807        state
808            .binder
809            .assign_group(index as usize, bind_group, offsets);
810    } else {
811        if !offsets.is_empty() {
812            return Err(RenderBundleErrorInner::Bind(
813                BindError::DynamicOffsetCountNotZero {
814                    group: index,
815                    actual: offsets.len(),
816                },
817            ));
818        }
819
820        state.binder.clear_group(index as usize);
821    }
822
823    Ok(())
824}
825
826fn set_pipeline(
827    state: &mut State,
828    context: &RenderPassContext,
829    is_depth_read_only: bool,
830    is_stencil_read_only: bool,
831    pipeline: Arc<RenderPipeline>,
832) -> Result<(), RenderBundleErrorInner> {
833    pipeline.same_device(&state.device)?;
834
835    context
836        .check_compatible(&pipeline.pass_context, pipeline.as_ref())
837        .map_err(RenderCommandError::IncompatiblePipelineTargets)?;
838
839    if pipeline.flags.contains(PipelineFlags::WRITES_DEPTH) && is_depth_read_only {
840        return Err(RenderCommandError::IncompatibleDepthAccess(pipeline.error_ident()).into());
841    }
842    if pipeline.flags.contains(PipelineFlags::WRITES_STENCIL) && is_stencil_read_only {
843        return Err(RenderCommandError::IncompatibleStencilAccess(pipeline.error_ident()).into());
844    }
845
846    state
847        .commands
848        .push(ArcRenderCommand::SetPipeline(pipeline.clone()));
849
850    state.pipeline = Some(pipeline.clone());
851
852    state
853        .binder
854        .change_pipeline_layout(pipeline.layout()?, &pipeline.late_sized_buffer_groups);
855
856    state.vertex.update_limits(&pipeline.vertex_steps);
857
858    state.trackers.render_pipelines.insert_single(pipeline);
859    Ok(())
860}
861
862// This function is duplicative of `render::set_index_buffer`.
863fn set_index_buffer(
864    state: &mut State,
865    buffer: Arc<Buffer>,
866    index_format: wgt::IndexFormat,
867    offset: u64,
868    size: Option<NonZeroU64>,
869) -> Result<(), RenderBundleErrorInner> {
870    buffer.check_is_valid()?;
871
872    state
873        .trackers
874        .buffers
875        .merge_single(&buffer, wgt::BufferUses::INDEX)?;
876
877    buffer.same_device(&state.device)?;
878    buffer.check_usage(wgt::BufferUsages::INDEX)?;
879
880    if !offset.is_multiple_of(u64::from(index_format.byte_size())) {
881        return Err(RenderCommandError::UnalignedIndexBuffer {
882            offset,
883            alignment: index_format.byte_size() as usize,
884        }
885        .into());
886    }
887    let end = offset + buffer.resolve_binding_size(offset, size)?;
888
889    state
890        .buffer_memory_init_actions
891        .extend(buffer.initialization_status.read().create_action(
892            &buffer,
893            offset..end.get(),
894            MemoryInitKind::NeedsInitializedMemory,
895        ));
896    state.set_index_buffer(buffer, index_format, offset..end.get());
897    Ok(())
898}
899
900// This function is duplicative of `render::set_vertex_buffer`.
901fn set_vertex_buffer(
902    state: &mut State,
903    slot: u32,
904    buffer: Option<Arc<Buffer>>,
905    offset: u64,
906    size: Option<NonZeroU64>,
907) -> Result<(), RenderBundleErrorInner> {
908    let max_vertex_buffers = state.device.limits.max_vertex_buffers;
909    if slot >= max_vertex_buffers {
910        return Err(RenderCommandError::VertexBufferIndexOutOfRange {
911            index: slot,
912            max: max_vertex_buffers,
913        }
914        .into());
915    }
916
917    if let Some(buffer) = buffer {
918        buffer.check_is_valid()?;
919
920        state
921            .trackers
922            .buffers
923            .merge_single(&buffer, wgt::BufferUses::VERTEX)?;
924
925        buffer.same_device(&state.device)?;
926        buffer.check_usage(wgt::BufferUsages::VERTEX)?;
927
928        if !offset.is_multiple_of(wgt::VERTEX_ALIGNMENT) {
929            return Err(RenderCommandError::UnalignedVertexBuffer { slot, offset }.into());
930        }
931        let binding_size = buffer.resolve_binding_size(offset, size)?;
932        let buffer_range = offset..(offset + binding_size);
933
934        state
935            .buffer_memory_init_actions
936            .extend(buffer.initialization_status.read().create_action(
937                &buffer,
938                buffer_range.clone(),
939                MemoryInitKind::NeedsInitializedMemory,
940            ));
941        state.vertex.set_buffer(slot as usize, buffer, buffer_range);
942        if let Some(pipeline) = state.pipeline.as_deref() {
943            state.vertex.update_limits(&pipeline.vertex_steps);
944        }
945    } else {
946        if offset != 0 {
947            return Err(RenderCommandError::from(
948                crate::binding_model::BindingError::UnbindingVertexBufferOffsetNotZero {
949                    slot,
950                    offset,
951                },
952            )
953            .into());
954        }
955        if let Some(size) = size {
956            return Err(RenderCommandError::from(
957                crate::binding_model::BindingError::UnbindingVertexBufferSizeNotZero {
958                    slot,
959                    size: size.get(),
960                },
961            )
962            .into());
963        }
964
965        state.vertex.clear_buffer(slot as usize);
966        if let Some(pipeline) = state.pipeline.as_deref() {
967            state.vertex.update_limits(&pipeline.vertex_steps);
968        }
969    }
970
971    Ok(())
972}
973
974fn set_immediates(
975    state: &mut State,
976    offset: u32,
977    data: &[u32],
978) -> Result<(), ImmediateUploadError> {
979    validate_immediates_alignment(offset, size_of_val(data))?;
980
981    state
982        .immediate_state
983        .set_immediates::<ImmediateUploadError>(&state.device.limits, offset, data)?;
984    Ok(())
985}
986
987fn draw(
988    state: &mut State,
989    vertex_count: u32,
990    instance_count: u32,
991    first_vertex: u32,
992    first_instance: u32,
993) -> Result<(), RenderBundleErrorInner> {
994    state.is_ready(DrawCommandFamily::Draw)?;
995
996    state
997        .vertex
998        .limits
999        .validate_vertex_limit(first_vertex, vertex_count)?;
1000    state
1001        .vertex
1002        .limits
1003        .validate_instance_limit(first_instance, instance_count)?;
1004
1005    if instance_count > 0 && vertex_count > 0 {
1006        state.flush_vertex_buffers();
1007        state.flush_bindings();
1008        state.flush_immediates();
1009        state.commands.push(ArcRenderCommand::Draw {
1010            vertex_count,
1011            instance_count,
1012            first_vertex,
1013            first_instance,
1014        });
1015    }
1016    Ok(())
1017}
1018
1019fn draw_indexed(
1020    state: &mut State,
1021    index_count: u32,
1022    instance_count: u32,
1023    first_index: u32,
1024    base_vertex: i32,
1025    first_instance: u32,
1026) -> Result<(), RenderBundleErrorInner> {
1027    state.is_ready(DrawCommandFamily::DrawIndexed)?;
1028
1029    let index = state.index.as_ref().unwrap();
1030
1031    let last_index = first_index as u64 + index_count as u64;
1032    let index_limit = index.limit();
1033    if last_index > index_limit {
1034        return Err(DrawError::IndexBeyondLimit {
1035            last_index,
1036            index_limit,
1037        }
1038        .into());
1039    }
1040    state
1041        .vertex
1042        .limits
1043        .validate_instance_limit(first_instance, instance_count)?;
1044
1045    if instance_count > 0 && index_count > 0 {
1046        state.flush_index();
1047        state.flush_vertex_buffers();
1048        state.flush_bindings();
1049        state.flush_immediates();
1050        state.commands.push(ArcRenderCommand::DrawIndexed {
1051            index_count,
1052            instance_count,
1053            first_index,
1054            base_vertex,
1055            first_instance,
1056        });
1057    }
1058    Ok(())
1059}
1060
1061fn draw_mesh_tasks(
1062    state: &mut State,
1063    group_count_x: u32,
1064    group_count_y: u32,
1065    group_count_z: u32,
1066) -> Result<(), RenderBundleErrorInner> {
1067    state.is_ready(DrawCommandFamily::DrawMeshTasks)?;
1068
1069    let limits = &state.device.limits;
1070    let (groups_size_limit, max_groups) = if state.pipeline.as_ref().unwrap().has_task_shader {
1071        (
1072            limits.max_task_workgroups_per_dimension,
1073            limits.max_task_workgroup_total_count,
1074        )
1075    } else {
1076        (
1077            limits.max_mesh_workgroups_per_dimension,
1078            limits.max_mesh_workgroup_total_count,
1079        )
1080    };
1081
1082    let total_count = WorkgroupSizeCheck {
1083        dimensions: &[group_count_x, group_count_y, group_count_z],
1084        per_dimension_limits: &[groups_size_limit, groups_size_limit, groups_size_limit],
1085        per_dimension_limits_desc: "max_task_mesh_workgroups_per_dimension",
1086
1087        total_limit: max_groups,
1088        total_limit_desc: "max_task_mesh_workgroup_total_count",
1089    }
1090    .check_and_compute_total_invocations()
1091    .map_err(|err| RenderBundleErrorInner::Draw(err.into()))?;
1092
1093    if total_count > 0 {
1094        state.flush_bindings();
1095        state.flush_immediates();
1096        state.commands.push(ArcRenderCommand::DrawMeshTasks {
1097            group_count_x,
1098            group_count_y,
1099            group_count_z,
1100        });
1101    }
1102    Ok(())
1103}
1104
1105fn multi_draw_indirect(
1106    state: &mut State,
1107    buffer: Arc<Buffer>,
1108    offset: u64,
1109    family: DrawCommandFamily,
1110) -> Result<(), RenderBundleErrorInner> {
1111    state.is_ready(family)?;
1112    state
1113        .device
1114        .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
1115
1116    buffer.check_is_valid()?;
1117    buffer.same_device(&state.device)?;
1118    buffer.check_usage(wgt::BufferUsages::INDIRECT)?;
1119
1120    if !offset.is_multiple_of(4) {
1121        return Err(RenderCommandError::UnalignedIndirectBufferOffset(offset).into());
1122    }
1123
1124    let stride = super::get_src_stride_of_indirect_args(family);
1125    match offset.checked_add(stride) {
1126        Some(end_offset) if end_offset <= buffer.size => {}
1127        _ => {
1128            return Err(RenderCommandError::IndirectBufferOverrun {
1129                count: 1,
1130                offset,
1131                args_size: stride,
1132                buffer_size: buffer.size,
1133            }
1134            .into());
1135        }
1136    }
1137    state
1138        .buffer_memory_init_actions
1139        .extend(buffer.initialization_status.read().create_action(
1140            &buffer,
1141            offset..(offset + stride),
1142            MemoryInitKind::NeedsInitializedMemory,
1143        ));
1144
1145    let vertex_or_index_limit = if family == DrawCommandFamily::DrawIndexed {
1146        let index = state.index.as_mut().unwrap();
1147        state.commands.extend(index.flush());
1148        index.limit()
1149    } else {
1150        state.vertex.limits.vertex_limit
1151    };
1152    let instance_limit = state.vertex.limits.instance_limit;
1153
1154    let buffer_uses = if state.device.indirect_validation.is_some()
1155        && family != DrawCommandFamily::DrawMeshTasks
1156    {
1157        wgt::BufferUses::STORAGE_READ_ONLY
1158    } else {
1159        wgt::BufferUses::INDIRECT
1160    };
1161
1162    state.trackers.buffers.merge_single(&buffer, buffer_uses)?;
1163
1164    state.flush_vertex_buffers();
1165    state.flush_bindings();
1166    state.flush_immediates();
1167    state.commands.push(ArcRenderCommand::DrawIndirect {
1168        buffer,
1169        offset,
1170        count: 1,
1171        family,
1172
1173        vertex_or_index_limit: Some(vertex_or_index_limit),
1174        instance_limit: Some(instance_limit),
1175    });
1176    Ok(())
1177}
1178
1179/// Error type returned from `RenderBundleEncoder::new` if the sample count is invalid.
1180#[derive(Clone, Debug, Error)]
1181#[non_exhaustive]
1182pub enum CreateRenderBundleError {
1183    #[error(transparent)]
1184    ColorAttachment(#[from] ColorAttachmentError),
1185    #[error("Format {0:?} does not have a color aspect")]
1186    FormatNotColor(wgt::TextureFormat),
1187    #[error("Color attachment format {0:?} is not renderable")]
1188    FormatNotRenderable(wgt::TextureFormat),
1189    #[error("Format {0:?} is not a depth/stencil format")]
1190    FormatNotDepthOrStencil(wgt::TextureFormat),
1191    #[error("Render bundle must have at least one attachment (color or depth/stencil)")]
1192    NoAttachment,
1193    #[error("Invalid number of samples {0}")]
1194    InvalidSampleCount(u32),
1195    #[error(transparent)]
1196    MissingFeatures(#[from] MissingFeatures),
1197    #[error(transparent)]
1198    Device(#[from] DeviceError),
1199}
1200
1201impl WebGpuError for CreateRenderBundleError {
1202    fn webgpu_error_type(&self) -> ErrorType {
1203        match self {
1204            Self::ColorAttachment(e) => e.webgpu_error_type(),
1205            Self::FormatNotColor(_)
1206            | Self::FormatNotRenderable(_)
1207            | Self::FormatNotDepthOrStencil(_)
1208            | Self::NoAttachment
1209            | Self::InvalidSampleCount(_) => ErrorType::Validation,
1210            Self::MissingFeatures(e) => e.webgpu_error_type(),
1211            Self::Device(e) => e.webgpu_error_type(),
1212        }
1213    }
1214}
1215
1216/// Error type returned from `RenderBundleEncoder::new` if the sample count is invalid.
1217#[derive(Clone, Debug, Error)]
1218#[non_exhaustive]
1219pub enum ExecutionError {
1220    #[error(transparent)]
1221    Device(#[from] DeviceError),
1222    #[error(transparent)]
1223    DestroyedResource(#[from] DestroyedResourceError),
1224    #[error(transparent)]
1225    InvalidResource(#[from] InvalidResourceError),
1226    #[error("Using {0} in a render bundle is not implemented")]
1227    Unimplemented(&'static str),
1228}
1229
1230impl From<InvalidOrDestroyedResourceError> for ExecutionError {
1231    fn from(e: InvalidOrDestroyedResourceError) -> Self {
1232        match e {
1233            InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
1234            InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
1235        }
1236    }
1237}
1238
1239pub type RenderBundleDescriptor<'a> = wgt::RenderBundleDescriptor<Label<'a>>;
1240
1241#[derive(Debug)]
1242pub(crate) struct RenderBundleState {
1243    pub(crate) used: RenderBundleScope,
1244    pub(super) context: RenderPassContext,
1245}
1246
1247//Note: here, `RenderBundle` is just wrapping a raw stream of render commands.
1248// The plan is to back it by an actual Vulkan secondary buffer, D3D12 Bundle,
1249// or Metal indirect command buffer.
1250/// cbindgen:ignore
1251#[derive(Debug)]
1252pub struct RenderBundle {
1253    pub(crate) state: ResourceState<RenderBundleState>,
1254    // Normalized command stream. It can be executed verbatim,
1255    // without re-binding anything on the pipeline change.
1256    base: BasePass<ArcRenderCommand, Infallible>,
1257    pub(super) is_depth_read_only: bool,
1258    pub(super) is_stencil_read_only: bool,
1259    pub(crate) device: Arc<Device>,
1260    pub(super) buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
1261    pub(super) texture_memory_init_actions: Vec<TextureInitTrackerAction>,
1262    /// The `label` from the descriptor used to create the resource.
1263    label: String,
1264    pub(crate) tracking_data: TrackingData,
1265    discard_hal_labels: bool,
1266}
1267
1268impl Drop for RenderBundle {
1269    #[expect(trivial_casts)]
1270    fn drop(&mut self) {
1271        profiling::scope!("RenderBundle::drop");
1272        api_log!("RenderBundle::drop {:?}", self as *const _);
1273        resource_log!("Drop {}", self.error_ident());
1274        #[cfg(feature = "trace")]
1275        if let Some(t) = self.device.trace.lock().as_mut() {
1276            use crate::device::trace::{to_trace, Action};
1277
1278            t.add(Action::DropRenderBundle(unsafe { to_trace(self) }));
1279        }
1280    }
1281}
1282
1283#[cfg(send_sync)]
1284unsafe impl Send for RenderBundle {}
1285#[cfg(send_sync)]
1286unsafe impl Sync for RenderBundle {}
1287
1288impl RenderBundle {
1289    pub(crate) fn state(&self) -> Result<&RenderBundleState, InvalidResourceError> {
1290        self.state
1291            .as_ref()
1292            .valid()
1293            .ok_or_else(|| InvalidResourceError(self.error_ident()))
1294    }
1295
1296    pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
1297        self.state().map(|_| ())
1298    }
1299
1300    pub fn invalid(device: Arc<Device>, desc: &RenderBundleDescriptor) -> Arc<Self> {
1301        Arc::new(RenderBundle {
1302            state: ResourceState::Invalid,
1303            base: BasePass {
1304                label: desc.label.as_ref().map(|l| l.to_string()),
1305                error: None,
1306                commands: Vec::new(),
1307                dynamic_offsets: Vec::new(),
1308                string_data: Vec::new(),
1309            },
1310            is_depth_read_only: false,
1311            is_stencil_read_only: false,
1312            buffer_memory_init_actions: Vec::new(),
1313            texture_memory_init_actions: Vec::new(),
1314            label: desc.label.to_string(),
1315            tracking_data: TrackingData::new(device.tracker_indices.bundles.clone()),
1316            discard_hal_labels: false,
1317            device,
1318        })
1319    }
1320
1321    #[cfg(feature = "trace")]
1322    pub(crate) fn to_base_pass(&self) -> BasePass<RenderCommand<ArcReferences>, Infallible> {
1323        self.base.clone()
1324    }
1325
1326    /// Actually encode the contents into a native command buffer.
1327    ///
1328    /// This is partially duplicating the logic of `render_pass_end`.
1329    /// However the point of this function is to be lighter, since we already had
1330    /// a chance to go through the commands in `render_bundle_encoder_finish`.
1331    ///
1332    /// Note that the function isn't expected to fail, generally.
1333    /// All the validation has already been done by this point.
1334    /// The only failure condition is if some of the used buffers are destroyed.
1335    pub(super) unsafe fn execute(
1336        &self,
1337        raw: &mut dyn hal::DynCommandEncoder,
1338        indirect_draw_validation_resources: &mut crate::indirect_validation::DrawResources,
1339        indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
1340        snatch_guard: &SnatchGuard,
1341    ) -> Result<(), ExecutionError> {
1342        let mut offsets = self.base.dynamic_offsets.as_slice();
1343        let mut pipeline_layout = None::<Arc<PipelineLayout>>;
1344        if !self.discard_hal_labels {
1345            if let Some(ref label) = self.base.label {
1346                unsafe { raw.begin_debug_marker(label) };
1347            }
1348        }
1349
1350        use ArcRenderCommand as Cmd;
1351        for command in self.base.commands.iter() {
1352            match command {
1353                Cmd::SetBindGroup {
1354                    index,
1355                    num_dynamic_offsets,
1356                    bind_group,
1357                } => {
1358                    let raw_bg = bind_group.as_ref().unwrap().try_raw(snatch_guard)?;
1359                    unsafe {
1360                        raw.set_bind_group(
1361                            pipeline_layout
1362                                .as_ref()
1363                                .unwrap()
1364                                .raw()
1365                                .expect("PipelineLayout should be valid at this point"),
1366                            *index,
1367                            raw_bg,
1368                            &offsets[..*num_dynamic_offsets],
1369                        )
1370                    };
1371                    offsets = &offsets[*num_dynamic_offsets..];
1372                }
1373                Cmd::SetPipeline(pipeline) => {
1374                    unsafe {
1375                        raw.set_render_pipeline(
1376                            pipeline
1377                                .raw()
1378                                .expect("RenderPipeline should be valid when executing bundle"),
1379                        )
1380                    };
1381
1382                    pipeline_layout = Some(
1383                        pipeline
1384                            .layout()
1385                            .expect("PipelineLayout should be valid when executing bundle")
1386                            .clone(),
1387                    );
1388                }
1389                Cmd::SetIndexBuffer {
1390                    buffer,
1391                    index_format,
1392                    offset,
1393                    size,
1394                } => {
1395                    let buffer = buffer.try_raw(snatch_guard)?;
1396                    // SAFETY: The binding size was checked against the buffer size
1397                    // in `set_index_buffer` and again in `IndexState::flush`.
1398                    let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size);
1399                    unsafe { raw.set_index_buffer(bb, *index_format) };
1400                }
1401                Cmd::SetVertexBuffer {
1402                    slot,
1403                    buffer,
1404                    offset,
1405                    size,
1406                } => {
1407                    let buffer = buffer.as_ref().unwrap().try_raw(snatch_guard)?;
1408                    // SAFETY: The binding size was checked against the buffer size
1409                    // in `set_vertex_buffer` and again in `VertexState::flush`.
1410                    let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size);
1411                    unsafe { raw.set_vertex_buffer(*slot, bb) };
1412                }
1413                Cmd::SetImmediate { offset, data } => {
1414                    let pipeline_layout = pipeline_layout.as_ref().unwrap();
1415
1416                    // SAFETY: The range of immediates written was validated in `is_ready` before each `flush_immediates`.
1417                    unsafe { raw.set_immediates(pipeline_layout.raw().unwrap(), *offset, data) }
1418                }
1419                Cmd::Draw {
1420                    vertex_count,
1421                    instance_count,
1422                    first_vertex,
1423                    first_instance,
1424                } => {
1425                    unsafe {
1426                        raw.draw(
1427                            *first_vertex,
1428                            *vertex_count,
1429                            *first_instance,
1430                            *instance_count,
1431                        )
1432                    };
1433                }
1434                Cmd::DrawIndexed {
1435                    index_count,
1436                    instance_count,
1437                    first_index,
1438                    base_vertex,
1439                    first_instance,
1440                } => {
1441                    unsafe {
1442                        raw.draw_indexed(
1443                            *first_index,
1444                            *index_count,
1445                            *base_vertex,
1446                            *first_instance,
1447                            *instance_count,
1448                        )
1449                    };
1450                }
1451                Cmd::DrawMeshTasks {
1452                    group_count_x,
1453                    group_count_y,
1454                    group_count_z,
1455                } => unsafe {
1456                    raw.draw_mesh_tasks(*group_count_x, *group_count_y, *group_count_z);
1457                },
1458                Cmd::DrawIndirect {
1459                    buffer,
1460                    offset,
1461                    count: 1,
1462                    family,
1463
1464                    vertex_or_index_limit,
1465                    instance_limit,
1466                } => {
1467                    let (buffer, offset) = if self.device.indirect_validation.is_some()
1468                        && *family != DrawCommandFamily::DrawMeshTasks
1469                    {
1470                        let (dst_resource_index, offset) = indirect_draw_validation_batcher.add(
1471                            indirect_draw_validation_resources,
1472                            &self.device,
1473                            buffer,
1474                            *offset,
1475                            *family,
1476                            vertex_or_index_limit
1477                                .expect("finalized render bundle missing vertex_or_index_limit"),
1478                            instance_limit.expect("finalized render bundle missing instance_limit"),
1479                        )?;
1480
1481                        let dst_buffer =
1482                            indirect_draw_validation_resources.get_dst_buffer(dst_resource_index);
1483                        (dst_buffer, offset)
1484                    } else {
1485                        (buffer.try_raw(snatch_guard)?, *offset)
1486                    };
1487                    match family {
1488                        DrawCommandFamily::Draw => unsafe { raw.draw_indirect(buffer, offset, 1) },
1489                        DrawCommandFamily::DrawIndexed => unsafe {
1490                            raw.draw_indexed_indirect(buffer, offset, 1)
1491                        },
1492                        DrawCommandFamily::DrawMeshTasks => unsafe {
1493                            raw.draw_mesh_tasks_indirect(buffer, offset, 1);
1494                        },
1495                    }
1496                }
1497                Cmd::DrawIndirect { .. } | Cmd::MultiDrawIndirectCount { .. } => {
1498                    return Err(ExecutionError::Unimplemented("multi-draw-indirect"))
1499                }
1500                Cmd::PushDebugGroup { .. } | Cmd::InsertDebugMarker { .. } | Cmd::PopDebugGroup => {
1501                    return Err(ExecutionError::Unimplemented("debug-markers"))
1502                }
1503                Cmd::WriteTimestamp { .. }
1504                | Cmd::BeginOcclusionQuery { .. }
1505                | Cmd::EndOcclusionQuery
1506                | Cmd::BeginPipelineStatisticsQuery { .. }
1507                | Cmd::EndPipelineStatisticsQuery => {
1508                    return Err(ExecutionError::Unimplemented("queries"))
1509                }
1510                Cmd::ExecuteBundle(_)
1511                | Cmd::SetBlendConstant(_)
1512                | Cmd::SetStencilReference(_)
1513                | Cmd::SetViewport { .. }
1514                | Cmd::SetScissor(_) => unreachable!(),
1515            }
1516        }
1517
1518        if !self.discard_hal_labels {
1519            if let Some(_) = self.base.label {
1520                unsafe { raw.end_debug_marker() };
1521            }
1522        }
1523
1524        Ok(())
1525    }
1526}
1527
1528crate::impl_resource_type!(RenderBundle);
1529crate::impl_labeled!(RenderBundle);
1530crate::impl_parent_device!(RenderBundle);
1531crate::impl_storage_item!(RenderBundle);
1532crate::impl_trackable!(RenderBundle);
1533
1534/// A render bundle's current index buffer state.
1535///
1536/// [`RenderBundleEncoder::finish`] records the currently set index buffer here,
1537/// and calls [`State::flush_index`] before any indexed draw command to produce
1538/// a `SetIndexBuffer` command if one is necessary.
1539///
1540/// Binding ranges must be validated against the size of the buffer before
1541/// being stored in `IndexState`.
1542#[derive(Debug)]
1543struct IndexState {
1544    buffer: Arc<Buffer>,
1545    format: wgt::IndexFormat,
1546    range: Range<wgt::BufferAddress>,
1547    is_dirty: bool,
1548}
1549
1550impl IndexState {
1551    /// Return the number of entries in the current index buffer.
1552    ///
1553    /// Panic if no index buffer has been set.
1554    fn limit(&self) -> u64 {
1555        let bytes_per_index = self.format.byte_size() as u64;
1556
1557        (self.range.end - self.range.start) / bytes_per_index
1558    }
1559
1560    /// Generate a `SetIndexBuffer` command to prepare for an indexed draw
1561    /// command, if needed.
1562    fn flush(&mut self) -> Option<ArcRenderCommand> {
1563        // This was all checked before, but let's check again just in case.
1564        let binding_size = self
1565            .range
1566            .end
1567            .checked_sub(self.range.start)
1568            .filter(|_| self.range.end <= self.buffer.size)
1569            .expect("index range must be contained in buffer");
1570
1571        if self.is_dirty {
1572            self.is_dirty = false;
1573            Some(ArcRenderCommand::SetIndexBuffer {
1574                buffer: self.buffer.clone(),
1575                index_format: self.format,
1576                offset: self.range.start,
1577                size: NonZeroU64::new(binding_size),
1578            })
1579        } else {
1580            None
1581        }
1582    }
1583}
1584
1585/// The state of a single vertex buffer slot during render bundle encoding.
1586///
1587/// [`RenderBundleEncoder::finish`] uses this to drop redundant
1588/// `SetVertexBuffer` commands from the final [`RenderBundle`]. It
1589/// records one vertex buffer slot's state changes here, and then
1590/// calls this type's [`flush`] method just before any draw command to
1591/// produce a `SetVertexBuffer` commands if one is necessary.
1592///
1593/// Binding ranges must be validated against the size of the buffer before
1594/// being stored in `VertexState`.
1595///
1596/// [`flush`]: IndexState::flush
1597#[derive(Debug)]
1598/// State for analyzing and cleaning up bundle command streams.
1599///
1600/// To minimize state updates, [`RenderBundleEncoder::finish`]
1601/// actually just applies commands like [`SetBindGroup`] and
1602/// [`SetIndexBuffer`] to the simulated state stored here, and then
1603/// calls the `flush_foo` methods before draw calls to produce the
1604/// update commands we actually need.
1605///
1606/// [`SetBindGroup`]: RenderCommand::SetBindGroup
1607/// [`SetIndexBuffer`]: RenderCommand::SetIndexBuffer
1608struct State {
1609    /// Resources used by this bundle. This will become [`RenderBundleState::used`].
1610    trackers: RenderBundleScope,
1611
1612    /// The currently set pipeline, if any.
1613    pipeline: Option<Arc<RenderPipeline>>,
1614
1615    /// The state of each vertex buffer slot.
1616    vertex: super::VertexState,
1617
1618    /// The current index buffer, if one has been set. We flush this state
1619    /// before indexed draw commands.
1620    index: Option<IndexState>,
1621
1622    /// Dynamic offset values used by the cleaned-up command sequence.
1623    ///
1624    /// This becomes the final [`RenderBundle`]'s [`BasePass`]'s
1625    /// [`dynamic_offsets`] list.
1626    ///
1627    /// [`dynamic_offsets`]: BasePass::dynamic_offsets
1628    flat_dynamic_offsets: Vec<wgt::DynamicOffset>,
1629
1630    device: Arc<Device>,
1631    commands: Vec<ArcRenderCommand>,
1632    buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
1633    texture_memory_init_actions: Vec<TextureInitTrackerAction>,
1634    next_dynamic_offset: usize,
1635    binder: Binder,
1636    immediate_state: ImmediateState,
1637}
1638
1639impl State {
1640    /// Set the bundle's current index buffer and its associated parameters.
1641    fn set_index_buffer(
1642        &mut self,
1643        buffer: Arc<Buffer>,
1644        format: wgt::IndexFormat,
1645        range: Range<wgt::BufferAddress>,
1646    ) {
1647        match self.index {
1648            Some(ref current)
1649                if current.buffer.is_equal(&buffer)
1650                    && current.format == format
1651                    && current.range == range =>
1652            {
1653                return
1654            }
1655            _ => (),
1656        }
1657
1658        self.index = Some(IndexState {
1659            buffer,
1660            format,
1661            range,
1662            is_dirty: true,
1663        });
1664    }
1665
1666    fn flush_immediates(&mut self) {
1667        if !self.immediate_state.immediates.is_empty() && self.immediate_state.immediates_dirty {
1668            self.commands.push(ArcRenderCommand::SetImmediate {
1669                offset: 0,
1670                data: self.immediate_state.immediates.clone(),
1671            });
1672            self.immediate_state.immediates_dirty = false;
1673        }
1674    }
1675
1676    /// Generate a `SetIndexBuffer` command to prepare for an indexed draw
1677    /// command, if needed.
1678    fn flush_index(&mut self) {
1679        let commands = self.index.as_mut().and_then(|index| index.flush());
1680        self.commands.extend(commands);
1681    }
1682
1683    fn flush_vertex_buffers(&mut self) {
1684        let vertex = &mut self.vertex;
1685        let commands = &mut self.commands;
1686        vertex.flush(|slot, buffer, offset, size| {
1687            commands.push(ArcRenderCommand::SetVertexBuffer {
1688                slot,
1689                buffer: Some(buffer.clone()),
1690                offset,
1691                size,
1692            });
1693        });
1694    }
1695
1696    /// Validation for a draw command.
1697    ///
1698    /// This should be further deduplicated with similar validation on render/compute passes.
1699    fn is_ready(&mut self, family: DrawCommandFamily) -> Result<(), DrawError> {
1700        if let Some(pipeline) = self.pipeline.as_ref() {
1701            self.binder.check_compatibility(pipeline.as_ref())?;
1702            self.binder.check_late_buffer_bindings()?;
1703
1704            self.vertex.validate(pipeline.as_ref(), &self.binder)?;
1705
1706            if family == DrawCommandFamily::DrawIndexed {
1707                let index_format = match &self.index {
1708                    Some(index) => index.format,
1709                    None => return Err(DrawError::MissingIndexBuffer),
1710                };
1711
1712                if pipeline.topology.is_strip() && pipeline.strip_index_format != Some(index_format)
1713                {
1714                    return Err(DrawError::UnmatchedStripIndexFormat {
1715                        pipeline: pipeline.error_ident(),
1716                        strip_index_format: pipeline.strip_index_format,
1717                        buffer_format: index_format,
1718                    });
1719                }
1720            }
1721
1722            if !self
1723                .immediate_state
1724                .immediate_slots_set
1725                .contains(pipeline.immediate_slots_required)
1726            {
1727                return Err(DrawError::MissingImmediateData {
1728                    missing: pipeline
1729                        .immediate_slots_required
1730                        .difference(self.immediate_state.immediate_slots_set),
1731                });
1732            }
1733
1734            Ok(())
1735        } else {
1736            Err(DrawError::MissingPipeline(pass::MissingPipeline))
1737        }
1738    }
1739
1740    /// Generate `SetBindGroup` commands for any bind groups that need to be updated.
1741    ///
1742    /// This should be further deduplicated with similar code on render/compute passes.
1743    fn flush_bindings(&mut self) {
1744        let start = self.binder.take_rebind_start_index();
1745        let entries = self.binder.list_valid_with_start(start);
1746
1747        self.commands
1748            .extend(entries.map(|(i, bind_group, dynamic_offsets)| {
1749                self.buffer_memory_init_actions
1750                    .extend_from_slice(&bind_group.buffer_init_actions);
1751                self.texture_memory_init_actions
1752                    .extend_from_slice(&bind_group.texture_init_actions);
1753
1754                self.flat_dynamic_offsets.extend_from_slice(dynamic_offsets);
1755
1756                ArcRenderCommand::SetBindGroup {
1757                    index: i.try_into().unwrap(),
1758                    bind_group: Some(bind_group.clone()),
1759                    num_dynamic_offsets: dynamic_offsets.len(),
1760                }
1761            }));
1762    }
1763}
1764
1765/// Error encountered when finishing recording a render bundle.
1766#[derive(Clone, Debug, Error)]
1767pub enum RenderBundleErrorInner {
1768    #[error(transparent)]
1769    Create(#[from] CreateRenderBundleError),
1770    #[error(transparent)]
1771    Device(#[from] DeviceError),
1772    #[error(transparent)]
1773    RenderCommand(RenderCommandError),
1774    #[error(transparent)]
1775    Draw(#[from] DrawError),
1776    #[error(transparent)]
1777    MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1778    #[error(transparent)]
1779    Bind(#[from] BindError),
1780    #[error("Render bundle encoder has already ended")]
1781    Ended,
1782}
1783
1784impl<T> From<T> for RenderBundleErrorInner
1785where
1786    T: Into<RenderCommandError>,
1787{
1788    fn from(t: T) -> Self {
1789        Self::RenderCommand(t.into())
1790    }
1791}
1792
1793/// Error encountered when finishing recording a render bundle.
1794#[derive(Clone, Debug, Error)]
1795#[error("{scope}")]
1796pub struct RenderBundleError {
1797    pub scope: PassErrorScope,
1798    #[source]
1799    inner: Box<RenderBundleErrorInner>,
1800}
1801
1802impl WebGpuError for RenderBundleError {
1803    fn webgpu_error_type(&self) -> ErrorType {
1804        match self.inner.as_ref() {
1805            RenderBundleErrorInner::Create(e) => e.webgpu_error_type(),
1806            RenderBundleErrorInner::Device(e) => e.webgpu_error_type(),
1807            RenderBundleErrorInner::RenderCommand(e) => e.webgpu_error_type(),
1808            RenderBundleErrorInner::Draw(e) => e.webgpu_error_type(),
1809            RenderBundleErrorInner::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1810            RenderBundleErrorInner::Bind(e) => e.webgpu_error_type(),
1811            RenderBundleErrorInner::Ended => ErrorType::Validation,
1812        }
1813    }
1814}
1815
1816impl RenderBundleError {
1817    pub fn from_device_error(e: DeviceError) -> Self {
1818        Self {
1819            scope: PassErrorScope::Bundle,
1820            inner: Box::new(e.into()),
1821        }
1822    }
1823}
1824
1825impl<E> MapPassErr<RenderBundleError> for E
1826where
1827    E: Into<RenderBundleErrorInner>,
1828{
1829    fn map_pass_err(self, scope: PassErrorScope) -> RenderBundleError {
1830        RenderBundleError {
1831            scope,
1832            inner: Box::new(self.into()),
1833        }
1834    }
1835}
1836
1837impl crate::global::Global {
1838    pub fn render_bundle_encoder_set_bind_group(
1839        &self,
1840        bundle: &mut RenderBundleEncoder,
1841        index: u32,
1842        bind_group_id: Option<id::BindGroupId>,
1843        offsets: &[wgt::DynamicOffset],
1844    ) -> Result<(), PassStateError> {
1845        bundle.set_bind_group(
1846            index,
1847            bind_group_id.map(|id| self.hub.bind_groups.get(id)),
1848            offsets,
1849        )
1850    }
1851
1852    pub fn render_bundle_encoder_set_bind_group_with_id(
1853        &self,
1854        bundle_encoder: id::RenderBundleEncoderId,
1855        index: u32,
1856        bind_group_id: Option<id::BindGroupId>,
1857        offsets: &[wgt::DynamicOffset],
1858    ) -> Result<(), PassStateError> {
1859        let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
1860
1861        let mut bundle_encoder = bundle_encoder
1862            .try_lock()
1863            .expect("RenderBundleEncoders should not be accessed concurrently");
1864
1865        bundle_encoder.set_bind_group(
1866            index,
1867            bind_group_id.map(|id| self.hub.bind_groups.get(id)),
1868            offsets,
1869        )
1870    }
1871
1872    pub fn render_bundle_encoder_set_pipeline(
1873        &self,
1874        bundle: &mut RenderBundleEncoder,
1875        pipeline_id: id::RenderPipelineId,
1876    ) -> Result<(), PassStateError> {
1877        bundle.set_pipeline(self.hub.render_pipelines.get(pipeline_id))
1878    }
1879
1880    pub fn render_bundle_encoder_set_pipeline_with_id(
1881        &self,
1882        bundle_encoder: id::RenderBundleEncoderId,
1883        pipeline_id: id::RenderPipelineId,
1884    ) -> Result<(), PassStateError> {
1885        let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
1886
1887        let mut bundle_encoder = bundle_encoder
1888            .try_lock()
1889            .expect("RenderBundleEncoders should not be accessed concurrently");
1890
1891        bundle_encoder.set_pipeline(self.hub.render_pipelines.get(pipeline_id))
1892    }
1893
1894    pub fn render_bundle_encoder_set_vertex_buffer(
1895        &self,
1896        bundle: &mut RenderBundleEncoder,
1897        slot: u32,
1898        buffer_id: Option<id::BufferId>,
1899        offset: wgt::BufferAddress,
1900        size: Option<wgt::BufferSize>,
1901    ) -> Result<(), PassStateError> {
1902        bundle.set_vertex_buffer(
1903            slot,
1904            buffer_id.map(|id| self.hub.buffers.get(id)),
1905            offset,
1906            size,
1907        )
1908    }
1909
1910    pub fn render_bundle_encoder_set_vertex_buffer_with_id(
1911        &self,
1912        bundle_encoder: id::RenderBundleEncoderId,
1913        slot: u32,
1914        buffer_id: Option<id::BufferId>,
1915        offset: wgt::BufferAddress,
1916        size: Option<wgt::BufferSize>,
1917    ) -> Result<(), PassStateError> {
1918        let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
1919
1920        let mut bundle_encoder = bundle_encoder
1921            .try_lock()
1922            .expect("RenderBundleEncoders should not be accessed concurrently");
1923
1924        bundle_encoder.set_vertex_buffer(
1925            slot,
1926            buffer_id.map(|id| self.hub.buffers.get(id)),
1927            offset,
1928            size,
1929        )
1930    }
1931
1932    pub fn render_bundle_encoder_set_index_buffer(
1933        &self,
1934        encoder: &mut RenderBundleEncoder,
1935        buffer: id::BufferId,
1936        index_format: wgt::IndexFormat,
1937        offset: wgt::BufferAddress,
1938        size: Option<wgt::BufferSize>,
1939    ) -> Result<(), PassStateError> {
1940        encoder.set_index_buffer(self.hub.buffers.get(buffer), index_format, offset, size)
1941    }
1942
1943    pub fn render_bundle_encoder_set_index_buffer_with_id(
1944        &self,
1945        bundle_encoder: id::RenderBundleEncoderId,
1946        buffer: id::BufferId,
1947        index_format: wgt::IndexFormat,
1948        offset: wgt::BufferAddress,
1949        size: Option<wgt::BufferSize>,
1950    ) -> Result<(), PassStateError> {
1951        let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
1952
1953        let mut bundle_encoder = bundle_encoder
1954            .try_lock()
1955            .expect("RenderBundleEncoders should not be accessed concurrently");
1956
1957        bundle_encoder.set_index_buffer(self.hub.buffers.get(buffer), index_format, offset, size)
1958    }
1959
1960    pub fn render_bundle_encoder_set_immediates(
1961        &self,
1962        pass: &mut RenderBundleEncoder,
1963        offset: u32,
1964        data: &[u8],
1965    ) -> Result<(), PassStateError> {
1966        pass.set_immediates(offset, data)
1967    }
1968
1969    pub fn render_bundle_encoder_set_immediates_with_id(
1970        &self,
1971        bundle_encoder: id::RenderBundleEncoderId,
1972        offset: u32,
1973        data: &[u8],
1974    ) -> Result<(), PassStateError> {
1975        let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
1976
1977        let mut bundle_encoder = bundle_encoder
1978            .try_lock()
1979            .expect("RenderBundleEncoders should not be accessed concurrently");
1980
1981        bundle_encoder.set_immediates(offset, data)
1982    }
1983
1984    pub fn render_bundle_encoder_draw(
1985        &self,
1986        bundle: &mut RenderBundleEncoder,
1987        vertex_count: u32,
1988        instance_count: u32,
1989        first_vertex: u32,
1990        first_instance: u32,
1991    ) -> Result<(), PassStateError> {
1992        bundle.draw(vertex_count, instance_count, first_vertex, first_instance)
1993    }
1994
1995    pub fn render_bundle_encoder_draw_with_id(
1996        &self,
1997        bundle_encoder: id::RenderBundleEncoderId,
1998        vertex_count: u32,
1999        instance_count: u32,
2000        first_vertex: u32,
2001        first_instance: u32,
2002    ) -> Result<(), PassStateError> {
2003        let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
2004
2005        let mut bundle_encoder = bundle_encoder
2006            .try_lock()
2007            .expect("RenderBundleEncoders should not be accessed concurrently");
2008
2009        bundle_encoder.draw(vertex_count, instance_count, first_vertex, first_instance)
2010    }
2011
2012    pub fn render_bundle_encoder_draw_indexed(
2013        &self,
2014        bundle: &mut RenderBundleEncoder,
2015        index_count: u32,
2016        instance_count: u32,
2017        first_index: u32,
2018        base_vertex: i32,
2019        first_instance: u32,
2020    ) -> Result<(), PassStateError> {
2021        bundle.draw_indexed(
2022            index_count,
2023            instance_count,
2024            first_index,
2025            base_vertex,
2026            first_instance,
2027        )
2028    }
2029
2030    pub fn render_bundle_encoder_draw_indexed_with_id(
2031        &self,
2032        bundle_encoder: id::RenderBundleEncoderId,
2033        index_count: u32,
2034        instance_count: u32,
2035        first_index: u32,
2036        base_vertex: i32,
2037        first_instance: u32,
2038    ) -> Result<(), PassStateError> {
2039        let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
2040
2041        let mut bundle_encoder = bundle_encoder
2042            .try_lock()
2043            .expect("RenderBundleEncoders should not be accessed concurrently");
2044
2045        bundle_encoder.draw_indexed(
2046            index_count,
2047            instance_count,
2048            first_index,
2049            base_vertex,
2050            first_instance,
2051        )
2052    }
2053
2054    pub fn render_bundle_encoder_draw_indirect(
2055        &self,
2056        bundle: &mut RenderBundleEncoder,
2057        buffer_id: id::BufferId,
2058        offset: wgt::BufferAddress,
2059    ) -> Result<(), PassStateError> {
2060        bundle.draw_indirect(self.hub.buffers.get(buffer_id), offset)
2061    }
2062
2063    pub fn render_bundle_encoder_draw_indirect_with_id(
2064        &self,
2065        bundle_encoder: id::RenderBundleEncoderId,
2066        buffer_id: id::BufferId,
2067        offset: wgt::BufferAddress,
2068    ) -> Result<(), PassStateError> {
2069        let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
2070
2071        let mut bundle_encoder = bundle_encoder
2072            .try_lock()
2073            .expect("RenderBundleEncoders should not be accessed concurrently");
2074
2075        bundle_encoder.draw_indirect(self.hub.buffers.get(buffer_id), offset)
2076    }
2077
2078    pub fn render_bundle_encoder_draw_indexed_indirect(
2079        &self,
2080        bundle: &mut RenderBundleEncoder,
2081        buffer_id: id::BufferId,
2082        offset: wgt::BufferAddress,
2083    ) -> Result<(), PassStateError> {
2084        bundle.draw_indexed_indirect(self.hub.buffers.get(buffer_id), offset)
2085    }
2086
2087    pub fn render_bundle_encoder_draw_indexed_indirect_with_id(
2088        &self,
2089        bundle_encoder: id::RenderBundleEncoderId,
2090        buffer_id: id::BufferId,
2091        offset: wgt::BufferAddress,
2092    ) -> Result<(), PassStateError> {
2093        let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
2094
2095        let mut bundle_encoder = bundle_encoder
2096            .try_lock()
2097            .expect("RenderBundleEncoders should not be accessed concurrently");
2098
2099        bundle_encoder.draw_indexed_indirect(self.hub.buffers.get(buffer_id), offset)
2100    }
2101
2102    pub fn render_bundle_encoder_push_debug_group(
2103        &self,
2104        bundle: &mut RenderBundleEncoder,
2105        label: &str,
2106    ) -> Result<(), PassStateError> {
2107        bundle.push_debug_group(label)
2108    }
2109
2110    pub fn render_bundle_encoder_push_debug_group_with_id(
2111        &self,
2112        bundle_encoder: id::RenderBundleEncoderId,
2113        label: &str,
2114    ) -> Result<(), PassStateError> {
2115        let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
2116
2117        let mut bundle_encoder = bundle_encoder
2118            .try_lock()
2119            .expect("RenderBundleEncoders should not be accessed concurrently");
2120
2121        bundle_encoder.push_debug_group(label)
2122    }
2123
2124    pub fn render_bundle_encoder_pop_debug_group(
2125        &self,
2126        bundle: &mut RenderBundleEncoder,
2127    ) -> Result<(), PassStateError> {
2128        bundle.pop_debug_group()
2129    }
2130
2131    pub fn render_bundle_encoder_pop_debug_group_with_id(
2132        &self,
2133        bundle_encoder: id::RenderBundleEncoderId,
2134    ) -> Result<(), PassStateError> {
2135        let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
2136
2137        let mut bundle_encoder = bundle_encoder
2138            .try_lock()
2139            .expect("RenderBundleEncoders should not be accessed concurrently");
2140
2141        bundle_encoder.pop_debug_group()
2142    }
2143
2144    pub fn render_bundle_encoder_insert_debug_marker(
2145        &self,
2146        bundle: &mut RenderBundleEncoder,
2147        label: &str,
2148    ) -> Result<(), PassStateError> {
2149        bundle.insert_debug_marker(label)
2150    }
2151
2152    pub fn render_bundle_encoder_insert_debug_marker_with_id(
2153        &self,
2154        bundle_encoder: id::RenderBundleEncoderId,
2155        label: &str,
2156    ) -> Result<(), PassStateError> {
2157        let bundle_encoder = self.hub.render_bundle_encoders.get(bundle_encoder);
2158
2159        let mut bundle_encoder = bundle_encoder
2160            .try_lock()
2161            .expect("RenderBundleEncoders should not be accessed concurrently");
2162
2163        bundle_encoder.insert_debug_marker(label)
2164    }
2165}