Skip to main content

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