1#![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#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
137#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
138pub struct RenderBundleEncoderDescriptor<'a> {
139 pub label: Label<'a>,
143 pub color_formats: Cow<'a, [Option<wgt::TextureFormat>]>,
149 pub depth_stencil: Option<wgt::RenderBundleDepthStencil>,
155 pub sample_count: u32,
159 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 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 #[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
189fn 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 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 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 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 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 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 &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 Ok(())
828 }
829
830 pub fn pop_debug_group(&mut self) -> Result<(), PassStateError> {
831 pass_base!(self, PassErrorScope::PopDebugGroup);
832 Ok(())
834 }
835
836 pub fn insert_debug_marker(&mut self, _label: &str) -> Result<(), PassStateError> {
837 pass_base!(self, PassErrorScope::InsertDebugMarker);
838 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 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
935fn 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
974fn 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 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#[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#[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#[derive(Debug)]
1312pub struct RenderBundle {
1313 pub(crate) state: ResourceState<RenderBundleState>,
1314 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 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 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 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 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#[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 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 fn flush(&mut self) -> Option<ArcRenderCommand> {
1658 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#[derive(Debug)]
1693struct State {
1704 trackers: RenderBundleScope,
1706
1707 pipeline: Option<Arc<RenderPipeline>>,
1709
1710 vertex: super::VertexState,
1712
1713 index: Option<IndexState>,
1716
1717 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 immediate_slots_set: naga::valid::ImmediateSlots,
1734}
1735
1736impl State {
1737 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 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 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 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#[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#[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 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 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 pub unsafe fn wgpu_render_bundle_push_debug_group(
2360 _bundle: &mut RenderBundleEncoder,
2361 _label: RawString,
2362 ) {
2363 }
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 }
2370
2371 #[deprecated(note = "Use `Global::render_bundle_encoder_insert_debug_marker` instead.")]
2372 pub unsafe fn wgpu_render_bundle_insert_debug_marker(
2377 _bundle: &mut RenderBundleEncoder,
2378 _label: RawString,
2379 ) {
2380 }
2382}