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