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