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