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