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 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,
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(
248 device: &Arc<Device>,
249 desc: &RenderBundleEncoderDescriptor,
250 ) -> Result<Self, CreateRenderBundleError> {
251 for &format in desc.color_formats.iter().flatten() {
253 device.require_features(format.required_features())?;
254 }
255
256 if let Some(ds) = desc.depth_stencil {
258 device.require_features(ds.format.required_features())?;
260 }
261
262 device.check_is_valid()?;
263 let (is_depth_read_only, is_stencil_read_only) =
264 validate_render_bundle_encoder_descriptor(desc, device)?;
265
266 Ok(Self {
267 base: BasePass::new(&desc.label),
268 device: Arc::clone(device),
269 parent: Some(()),
270 context: RenderPassContext {
271 attachments: AttachmentData {
272 colors: desc.color_formats.iter().cloned().collect(),
273 resolves: ArrayVec::new(),
274 depth_stencil: desc.depth_stencil.map(|ds| ds.format),
275 },
276 sample_count: desc.sample_count,
277 multiview_mask: desc.multiview,
278 },
279
280 is_depth_read_only,
281 is_stencil_read_only,
282 current_bind_groups: BindGroupStateChange::new(),
283 current_pipeline: StateChange::new(),
284 })
285 }
286
287 pub fn dummy(device: &Arc<Device>) -> Self {
288 Self {
289 base: BasePass::new(&None),
290 parent: None,
291 device: Arc::clone(device),
292 context: RenderPassContext::default(),
293 is_depth_read_only: false,
294 is_stencil_read_only: false,
295
296 current_bind_groups: BindGroupStateChange::new(),
297 current_pipeline: StateChange::new(),
298 }
299 }
300
301 pub fn label(&self) -> Option<&str> {
302 self.base.label.as_deref()
303 }
304
305 pub fn device(&self) -> &Arc<Device> {
306 &self.device
307 }
308
309 pub fn finish(&mut self, desc: &RenderBundleDescriptor) -> Arc<RenderBundle> {
320 profiling::scope!("RenderBundleEncoder::finish");
321 #[cfg(feature = "trace")]
322 let trace_desc = crate::device::trace::new_render_bundle_encoder_descriptor(
323 desc.label.clone(),
324 &self.context,
325 self.is_depth_read_only,
326 self.is_stencil_read_only,
327 );
328
329 let render_bundle = self.finish_inner(desc).unwrap_or_else(|error| {
330 self.device
331 .handle_error(error, self.label(), "RenderBundleEncoder::finish");
332 RenderBundle::invalid(Arc::clone(&self.device), desc)
333 });
334
335 #[cfg(feature = "trace")]
336 if let Some(ref mut trace) = *self.device.trace.lock() {
337 use crate::device::trace::{Action, IntoTrace};
338 trace.add(Action::CreateRenderBundle {
339 id: render_bundle.to_trace(),
340 desc: trace_desc,
341 base: render_bundle.to_base_pass().to_trace(),
342 });
343 }
344
345 api_log!(
346 "RenderBundleEncoder::finish -> {:?}",
347 Arc::as_ptr(&render_bundle)
348 );
349
350 render_bundle
351 }
352
353 pub(crate) fn finish_inner(
364 &mut self,
365 desc: &RenderBundleDescriptor,
366 ) -> Result<Arc<RenderBundle>, RenderBundleError> {
367 let scope = PassErrorScope::Bundle;
368
369 self.parent
370 .take()
371 .ok_or(RenderBundleErrorInner::Ended)
372 .map_pass_err(scope)?;
373
374 self.device.check_is_valid().map_pass_err(scope)?;
375
376 let mut state = State {
377 trackers: RenderBundleScope::new(),
378 pipeline: None,
379 vertex: Default::default(),
380 index: None,
381 flat_dynamic_offsets: Vec::new(),
382 device: Arc::clone(&self.device),
383 commands: Vec::new(),
384 buffer_memory_init_actions: Vec::new(),
385 texture_memory_init_actions: Vec::new(),
386 next_dynamic_offset: 0,
387 binder: Binder::new(),
388 immediate_state: ImmediateState::default(),
389 };
390
391 let indices = &state.device.tracker_indices;
392 state.trackers.buffers.set_size(indices.buffers.size());
393 state.trackers.textures.set_size(indices.textures.size());
394
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::MultiDrawIndirectCount { .. }
522 | RenderCommand::PushDebugGroup { color: _, len: _ }
523 | RenderCommand::InsertDebugMarker { color: _, len: _ }
524 | RenderCommand::PopDebugGroup => {
525 unimplemented!("not supported by a render bundle")
526 }
527 RenderCommand::WriteTimestamp { .. }
529 | RenderCommand::BeginOcclusionQuery { .. }
530 | RenderCommand::EndOcclusionQuery
531 | RenderCommand::BeginPipelineStatisticsQuery { .. }
532 | RenderCommand::EndPipelineStatisticsQuery => {
533 unimplemented!("not supported by a render bundle")
534 }
535 RenderCommand::ExecuteBundle(_)
536 | RenderCommand::SetBlendConstant(_)
537 | RenderCommand::SetStencilReference(_)
538 | RenderCommand::SetViewport { .. }
539 | RenderCommand::SetScissor(_) => unreachable!("not supported by a render bundle"),
540 }
541 }
542
543 let State {
544 trackers,
545 flat_dynamic_offsets,
546 device,
547 commands,
548 buffer_memory_init_actions,
549 texture_memory_init_actions,
550 ..
551 } = state;
552
553 let tracker_indices = device.tracker_indices.bundles.clone();
554 let discard_hal_labels = device
555 .instance_flags
556 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS);
557
558 let string_data = mem::take(&mut self.base.string_data);
559 let context = mem::take(&mut self.context);
560 let render_bundle = RenderBundle {
561 state: ResourceState::Valid(RenderBundleState {
562 context,
563 used: trackers,
564 }),
565 base: BasePass {
566 label: desc.label.as_deref().map(str::to_owned),
567 error: None,
568 commands,
569 dynamic_offsets: flat_dynamic_offsets,
570 string_data,
571 },
572 is_depth_read_only: self.is_depth_read_only,
573 is_stencil_read_only: self.is_stencil_read_only,
574 device: device.clone(),
575 buffer_memory_init_actions,
576 texture_memory_init_actions,
577 label: desc.label.to_string(),
578 tracking_data: TrackingData::new(tracker_indices),
579 discard_hal_labels,
580 };
581
582 let render_bundle = Arc::new(render_bundle);
583
584 Ok(render_bundle)
585 }
586
587 fn set_index_buffer_inner(
588 &mut self,
589 buffer: Arc<Buffer>,
590 index_format: wgt::IndexFormat,
591 offset: wgt::BufferAddress,
592 size: Option<wgt::BufferSize>,
593 ) -> Result<(), PassStateError> {
594 pass_base!(self, PassErrorScope::SetIndexBuffer);
595 self.base.commands.push(RenderCommand::SetIndexBuffer {
596 buffer,
597 index_format,
598 offset,
599 size,
600 });
601 Ok(())
602 }
603
604 pub fn set_index_buffer(
605 &mut self,
606 buffer: Arc<Buffer>,
607 index_format: wgt::IndexFormat,
608 offset: wgt::BufferAddress,
609 size: Option<wgt::BufferSize>,
610 ) {
611 if let Err(err) = self.set_index_buffer_inner(buffer, index_format, offset, size) {
612 self.device
613 .handle_error(err, self.label(), "RenderBundleEncoder::set_index_buffer");
614 }
615 }
616
617 fn set_bind_group_inner(
618 &mut self,
619 index: u32,
620 bind_group: Option<Arc<BindGroup>>,
621 offsets: &[wgt::DynamicOffset],
622 ) -> Result<(), PassStateError> {
623 pass_base!(self, PassErrorScope::SetBindGroup);
624 let redundant = self.current_bind_groups.set_and_check_redundant(
625 &bind_group,
626 index,
627 &mut self.base.dynamic_offsets,
628 offsets,
629 );
630
631 if redundant {
632 return Ok(());
633 }
634
635 self.base.commands.push(RenderCommand::SetBindGroup {
636 index,
637 num_dynamic_offsets: offsets.len(),
638 bind_group,
639 });
640 Ok(())
641 }
642
643 pub fn set_bind_group(
644 &mut self,
645 index: u32,
646 bind_group: Option<Arc<BindGroup>>,
647 offsets: &[wgt::DynamicOffset],
648 ) {
649 if let Err(err) = self.set_bind_group_inner(index, bind_group, offsets) {
650 self.device
651 .handle_error(err, self.label(), "RenderBundleEncoder::set_bind_group");
652 }
653 }
654
655 fn set_pipeline_inner(&mut self, pipeline: Arc<RenderPipeline>) -> Result<(), PassStateError> {
656 pass_base!(self, PassErrorScope::SetPipelineRender);
657 if self.current_pipeline.set_and_check_redundant(&pipeline) {
658 return Ok(());
659 }
660
661 self.base
662 .commands
663 .push(RenderCommand::SetPipeline(pipeline));
664 Ok(())
665 }
666
667 pub fn set_pipeline(&mut self, pipeline: Arc<RenderPipeline>) {
668 if let Err(err) = self.set_pipeline_inner(pipeline) {
669 self.device
670 .handle_error(err, self.label(), "RenderBundleEncoder::set_pipeline");
671 }
672 }
673
674 fn set_vertex_buffer_inner(
675 &mut self,
676 slot: u32,
677 buffer: Option<Arc<Buffer>>,
678 offset: wgt::BufferAddress,
679 size: Option<wgt::BufferSize>,
680 ) -> Result<(), PassStateError> {
681 pass_base!(self, PassErrorScope::SetVertexBuffer);
682 self.base.commands.push(RenderCommand::SetVertexBuffer {
683 slot,
684 buffer,
685 offset,
686 size,
687 });
688 Ok(())
689 }
690
691 pub fn set_vertex_buffer(
692 &mut self,
693 slot: u32,
694 buffer: Option<Arc<Buffer>>,
695 offset: wgt::BufferAddress,
696 size: Option<wgt::BufferSize>,
697 ) {
698 if let Err(err) = self.set_vertex_buffer_inner(slot, buffer, offset, size) {
699 self.device
700 .handle_error(err, self.label(), "RenderBundleEncoder::set_vertex_buffer");
701 }
702 }
703
704 fn set_immediates_inner(&mut self, offset: u32, data: &[u8]) -> Result<(), PassStateError> {
705 pass_base!(self, PassErrorScope::SetImmediate);
706
707 assert!(data.len().is_multiple_of(4));
709
710 self.base.commands.push(RenderCommand::SetImmediate {
711 offset,
712 data: data
713 .chunks_exact(size_of::<u32>())
714 .map(|ck| u32::from_le_bytes(ck.try_into().unwrap()))
715 .collect(),
716 });
717 Ok(())
718 }
719
720 pub fn set_immediates(&mut self, offset: u32, data: &[u8]) {
721 if let Err(err) = self.set_immediates_inner(offset, data) {
722 self.device
723 .handle_error(err, self.label(), "RenderBundleEncoder::set_immediates");
724 }
725 }
726
727 fn draw_inner(
728 &mut self,
729 vertex_count: u32,
730 instance_count: u32,
731 first_vertex: u32,
732 first_instance: u32,
733 ) -> Result<(), PassStateError> {
734 pass_base!(
735 self,
736 PassErrorScope::Draw {
737 kind: DrawKind::Draw,
738 family: DrawCommandFamily::Draw
739 }
740 );
741 self.base.commands.push(RenderCommand::Draw {
742 vertex_count,
743 instance_count,
744 first_vertex,
745 first_instance,
746 });
747 Ok(())
748 }
749
750 pub fn draw(
751 &mut self,
752 vertex_count: u32,
753 instance_count: u32,
754 first_vertex: u32,
755 first_instance: u32,
756 ) {
757 if let Err(err) =
758 self.draw_inner(vertex_count, instance_count, first_vertex, first_instance)
759 {
760 self.device
761 .handle_error(err, self.label(), "RenderBundleEncoder::draw");
762 }
763 }
764
765 fn draw_indexed_inner(
766 &mut self,
767 index_count: u32,
768 instance_count: u32,
769 first_index: u32,
770 base_vertex: i32,
771 first_instance: u32,
772 ) -> Result<(), PassStateError> {
773 pass_base!(
774 self,
775 PassErrorScope::Draw {
776 kind: DrawKind::Draw,
777 family: DrawCommandFamily::DrawIndexed
778 }
779 );
780 self.base.commands.push(RenderCommand::DrawIndexed {
781 index_count,
782 instance_count,
783 first_index,
784 base_vertex,
785 first_instance,
786 });
787 Ok(())
788 }
789
790 pub fn draw_indexed(
791 &mut self,
792 index_count: u32,
793 instance_count: u32,
794 first_index: u32,
795 base_vertex: i32,
796 first_instance: u32,
797 ) {
798 if let Err(err) = self.draw_indexed_inner(
799 index_count,
800 instance_count,
801 first_index,
802 base_vertex,
803 first_instance,
804 ) {
805 self.device
806 .handle_error(err, self.label(), "RenderBundleEncoder::draw_indexed");
807 }
808 }
809
810 fn draw_indirect_inner(
811 &mut self,
812 buffer: Arc<Buffer>,
813 offset: wgt::BufferAddress,
814 ) -> Result<(), PassStateError> {
815 pass_base!(
816 self,
817 PassErrorScope::Draw {
818 kind: DrawKind::DrawIndirect,
819 family: DrawCommandFamily::Draw
820 }
821 );
822 self.base.commands.push(RenderCommand::DrawIndirect {
823 buffer,
824 offset,
825 count: 1,
826 family: DrawCommandFamily::Draw,
827 vertex_or_index_limit: None,
828 instance_limit: None,
829 });
830 Ok(())
831 }
832
833 pub fn draw_indirect(&mut self, buffer: Arc<Buffer>, offset: wgt::BufferAddress) {
834 if let Err(err) = self.draw_indirect_inner(buffer, offset) {
835 self.device
836 .handle_error(err, self.label(), "RenderBundleEncoder::draw_indirect");
837 }
838 }
839
840 fn draw_indexed_indirect_inner(
841 &mut self,
842 buffer: Arc<Buffer>,
843 offset: wgt::BufferAddress,
844 ) -> Result<(), PassStateError> {
845 pass_base!(
846 self,
847 PassErrorScope::Draw {
848 kind: DrawKind::DrawIndirect,
849 family: DrawCommandFamily::DrawIndexed
850 }
851 );
852 self.base.commands.push(RenderCommand::DrawIndirect {
853 buffer,
854 offset,
855 count: 1,
856 family: DrawCommandFamily::DrawIndexed,
857 vertex_or_index_limit: None,
858 instance_limit: None,
859 });
860 Ok(())
861 }
862
863 pub fn draw_indexed_indirect(&mut self, buffer: Arc<Buffer>, offset: wgt::BufferAddress) {
864 if let Err(err) = self.draw_indexed_indirect_inner(buffer, offset) {
865 self.device.handle_error(
866 err,
867 self.label(),
868 "RenderBundleEncoder::draw_indexed_indirect",
869 );
870 }
871 }
872
873 fn push_debug_group_inner(&mut self, _label: &str) -> Result<(), PassStateError> {
874 pass_base!(self, PassErrorScope::PushDebugGroup);
875 Ok(())
877 }
878
879 pub fn push_debug_group(&mut self, label: &str) {
880 if let Err(err) = self.push_debug_group_inner(label) {
881 self.device
882 .handle_error(err, self.label(), "RenderBundleEncoder::push_debug_group");
883 }
884 }
885
886 fn pop_debug_group_inner(&mut self) -> Result<(), PassStateError> {
887 pass_base!(self, PassErrorScope::PopDebugGroup);
888 Ok(())
890 }
891
892 pub fn pop_debug_group(&mut self) {
893 if let Err(err) = self.pop_debug_group_inner() {
894 self.device
895 .handle_error(err, self.label(), "RenderBundleEncoder::pop_debug_group");
896 }
897 }
898
899 fn insert_debug_marker_inner(&mut self, _label: &str) -> Result<(), PassStateError> {
900 pass_base!(self, PassErrorScope::InsertDebugMarker);
901 Ok(())
903 }
904
905 pub fn insert_debug_marker(&mut self, label: &str) {
906 if let Err(err) = self.insert_debug_marker_inner(label) {
907 self.device.handle_error(
908 err,
909 self.label(),
910 "RenderBundleEncoder::insert_debug_marker",
911 );
912 }
913 }
914}
915
916fn set_bind_group(
917 state: &mut State,
918 dynamic_offsets: &[u32],
919 index: u32,
920 num_dynamic_offsets: usize,
921 bind_group: Option<Arc<BindGroup>>,
922) -> Result<(), RenderBundleErrorInner> {
923 let max_bind_groups = state.device.limits.max_bind_groups;
924 if index >= max_bind_groups {
925 return Err(
926 RenderCommandError::BindGroupIndexOutOfRange(pass::BindGroupIndexOutOfRange {
927 index,
928 max: max_bind_groups,
929 })
930 .into(),
931 );
932 }
933
934 let offsets_range = state.next_dynamic_offset..state.next_dynamic_offset + num_dynamic_offsets;
936 state.next_dynamic_offset = offsets_range.end;
937 let offsets = &dynamic_offsets[offsets_range.clone()];
938
939 if let Some(bind_group) = bind_group {
940 bind_group.check_is_valid()?;
941 bind_group.same_device(&state.device)?;
942 bind_group.validate_dynamic_bindings(index, offsets)?;
943
944 unsafe { state.trackers.merge_bind_group(&bind_group.used)? };
945 let bind_group = state.trackers.bind_groups.insert_single(bind_group);
946
947 state
948 .binder
949 .assign_group(index as usize, bind_group, offsets);
950 } else {
951 if !offsets.is_empty() {
952 return Err(RenderBundleErrorInner::Bind(
953 BindError::DynamicOffsetCountNotZero {
954 group: index,
955 actual: offsets.len(),
956 },
957 ));
958 }
959
960 state.binder.clear_group(index as usize);
961 }
962
963 Ok(())
964}
965
966fn set_pipeline(
967 state: &mut State,
968 context: &RenderPassContext,
969 is_depth_read_only: bool,
970 is_stencil_read_only: bool,
971 pipeline: Arc<RenderPipeline>,
972) -> Result<(), RenderBundleErrorInner> {
973 pipeline.same_device(&state.device)?;
974
975 context
976 .check_compatible(&pipeline.pass_context, pipeline.as_ref())
977 .map_err(RenderCommandError::IncompatiblePipelineTargets)?;
978
979 if pipeline.flags.contains(PipelineFlags::WRITES_DEPTH) && is_depth_read_only {
980 return Err(RenderCommandError::IncompatibleDepthAccess(pipeline.error_ident()).into());
981 }
982 if pipeline.flags.contains(PipelineFlags::WRITES_STENCIL) && is_stencil_read_only {
983 return Err(RenderCommandError::IncompatibleStencilAccess(pipeline.error_ident()).into());
984 }
985
986 state
987 .commands
988 .push(ArcRenderCommand::SetPipeline(pipeline.clone()));
989
990 state.pipeline = Some(pipeline.clone());
991
992 state
993 .binder
994 .change_pipeline_layout(pipeline.layout()?, &pipeline.late_sized_buffer_groups);
995
996 state.vertex.update_limits(&pipeline.vertex_steps);
997
998 state.trackers.render_pipelines.insert_single(pipeline);
999 Ok(())
1000}
1001
1002fn set_index_buffer(
1004 state: &mut State,
1005 buffer: Arc<Buffer>,
1006 index_format: wgt::IndexFormat,
1007 offset: u64,
1008 size: Option<NonZeroU64>,
1009) -> Result<(), RenderBundleErrorInner> {
1010 buffer.check_is_valid()?;
1011
1012 state
1013 .trackers
1014 .buffers
1015 .merge_single(&buffer, wgt::BufferUses::INDEX)?;
1016
1017 buffer.same_device(&state.device)?;
1018 buffer.check_usage(wgt::BufferUsages::INDEX)?;
1019
1020 if !offset.is_multiple_of(u64::from(index_format.byte_size())) {
1021 return Err(RenderCommandError::UnalignedIndexBuffer {
1022 offset,
1023 alignment: index_format.byte_size() as usize,
1024 }
1025 .into());
1026 }
1027 let end = offset + buffer.resolve_binding_size(offset, size)?;
1028
1029 state
1030 .buffer_memory_init_actions
1031 .extend(buffer.initialization_status.read().create_action(
1032 &buffer,
1033 offset..end.get(),
1034 MemoryInitKind::NeedsInitializedMemory,
1035 ));
1036 state.set_index_buffer(buffer, index_format, offset..end.get());
1037 Ok(())
1038}
1039
1040fn set_vertex_buffer(
1042 state: &mut State,
1043 slot: u32,
1044 buffer: Option<Arc<Buffer>>,
1045 offset: u64,
1046 size: Option<NonZeroU64>,
1047) -> Result<(), RenderBundleErrorInner> {
1048 let max_vertex_buffers = state.device.limits.max_vertex_buffers;
1049 if slot >= max_vertex_buffers {
1050 return Err(RenderCommandError::VertexBufferIndexOutOfRange {
1051 index: slot,
1052 max: max_vertex_buffers,
1053 }
1054 .into());
1055 }
1056
1057 if let Some(buffer) = buffer {
1058 buffer.check_is_valid()?;
1059
1060 state
1061 .trackers
1062 .buffers
1063 .merge_single(&buffer, wgt::BufferUses::VERTEX)?;
1064
1065 buffer.same_device(&state.device)?;
1066 buffer.check_usage(wgt::BufferUsages::VERTEX)?;
1067
1068 if !offset.is_multiple_of(wgt::VERTEX_ALIGNMENT) {
1069 return Err(RenderCommandError::UnalignedVertexBuffer { slot, offset }.into());
1070 }
1071 let binding_size = buffer.resolve_binding_size(offset, size)?;
1072 let buffer_range = offset..(offset + binding_size);
1073
1074 state
1075 .buffer_memory_init_actions
1076 .extend(buffer.initialization_status.read().create_action(
1077 &buffer,
1078 buffer_range.clone(),
1079 MemoryInitKind::NeedsInitializedMemory,
1080 ));
1081 state.vertex.set_buffer(slot as usize, buffer, buffer_range);
1082 if let Some(pipeline) = state.pipeline.as_deref() {
1083 state.vertex.update_limits(&pipeline.vertex_steps);
1084 }
1085 } else {
1086 if offset != 0 {
1087 return Err(RenderCommandError::from(
1088 crate::binding_model::BindingError::UnbindingVertexBufferOffsetNotZero {
1089 slot,
1090 offset,
1091 },
1092 )
1093 .into());
1094 }
1095 if let Some(size) = size {
1096 return Err(RenderCommandError::from(
1097 crate::binding_model::BindingError::UnbindingVertexBufferSizeNotZero {
1098 slot,
1099 size: size.get(),
1100 },
1101 )
1102 .into());
1103 }
1104
1105 state.vertex.clear_buffer(slot as usize);
1106 if let Some(pipeline) = state.pipeline.as_deref() {
1107 state.vertex.update_limits(&pipeline.vertex_steps);
1108 }
1109 }
1110
1111 Ok(())
1112}
1113
1114fn set_immediates(
1115 state: &mut State,
1116 offset: u32,
1117 data: &[u32],
1118) -> Result<(), ImmediateUploadError> {
1119 validate_immediates_alignment(offset, size_of_val(data))?;
1120
1121 state
1122 .immediate_state
1123 .set_immediates::<ImmediateUploadError>(&state.device.limits, offset, data)?;
1124 Ok(())
1125}
1126
1127fn draw(
1128 state: &mut State,
1129 vertex_count: u32,
1130 instance_count: u32,
1131 first_vertex: u32,
1132 first_instance: u32,
1133) -> Result<(), RenderBundleErrorInner> {
1134 state.is_ready(DrawCommandFamily::Draw)?;
1135
1136 state
1137 .vertex
1138 .limits
1139 .validate_vertex_limit(first_vertex, vertex_count)?;
1140 state
1141 .vertex
1142 .limits
1143 .validate_instance_limit(first_instance, instance_count)?;
1144
1145 if instance_count > 0 && vertex_count > 0 {
1146 state.flush_vertex_buffers();
1147 state.flush_bindings();
1148 state.flush_immediates();
1149 state.commands.push(ArcRenderCommand::Draw {
1150 vertex_count,
1151 instance_count,
1152 first_vertex,
1153 first_instance,
1154 });
1155 }
1156 Ok(())
1157}
1158
1159fn draw_indexed(
1160 state: &mut State,
1161 index_count: u32,
1162 instance_count: u32,
1163 first_index: u32,
1164 base_vertex: i32,
1165 first_instance: u32,
1166) -> Result<(), RenderBundleErrorInner> {
1167 state.is_ready(DrawCommandFamily::DrawIndexed)?;
1168
1169 let index = state.index.as_ref().unwrap();
1170
1171 let last_index = first_index as u64 + index_count as u64;
1172 let index_limit = index.limit();
1173 if last_index > index_limit {
1174 return Err(DrawError::IndexBeyondLimit {
1175 last_index,
1176 index_limit,
1177 }
1178 .into());
1179 }
1180 state
1181 .vertex
1182 .limits
1183 .validate_instance_limit(first_instance, instance_count)?;
1184
1185 if instance_count > 0 && index_count > 0 {
1186 state.flush_index();
1187 state.flush_vertex_buffers();
1188 state.flush_bindings();
1189 state.flush_immediates();
1190 state.commands.push(ArcRenderCommand::DrawIndexed {
1191 index_count,
1192 instance_count,
1193 first_index,
1194 base_vertex,
1195 first_instance,
1196 });
1197 }
1198 Ok(())
1199}
1200
1201fn draw_mesh_tasks(
1202 state: &mut State,
1203 group_count_x: u32,
1204 group_count_y: u32,
1205 group_count_z: u32,
1206) -> Result<(), RenderBundleErrorInner> {
1207 state.is_ready(DrawCommandFamily::DrawMeshTasks)?;
1208
1209 let limits = &state.device.limits;
1210 let (groups_size_limit, max_groups) = if state.pipeline.as_ref().unwrap().has_task_shader {
1211 (
1212 limits.max_task_workgroups_per_dimension,
1213 limits.max_task_workgroup_total_count,
1214 )
1215 } else {
1216 (
1217 limits.max_mesh_workgroups_per_dimension,
1218 limits.max_mesh_workgroup_total_count,
1219 )
1220 };
1221
1222 let total_count = WorkgroupSizeCheck {
1223 dimensions: &[group_count_x, group_count_y, group_count_z],
1224 per_dimension_limits: &[groups_size_limit, groups_size_limit, groups_size_limit],
1225 per_dimension_limits_desc: "max_task_mesh_workgroups_per_dimension",
1226
1227 total_limit: max_groups,
1228 total_limit_desc: "max_task_mesh_workgroup_total_count",
1229 }
1230 .check_and_compute_total_invocations()
1231 .map_err(|err| RenderBundleErrorInner::Draw(err.into()))?;
1232
1233 if total_count > 0 {
1234 state.flush_bindings();
1235 state.flush_immediates();
1236 state.commands.push(ArcRenderCommand::DrawMeshTasks {
1237 group_count_x,
1238 group_count_y,
1239 group_count_z,
1240 });
1241 }
1242 Ok(())
1243}
1244
1245fn multi_draw_indirect(
1246 state: &mut State,
1247 buffer: Arc<Buffer>,
1248 offset: u64,
1249 family: DrawCommandFamily,
1250) -> Result<(), RenderBundleErrorInner> {
1251 state.is_ready(family)?;
1252 state
1253 .device
1254 .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
1255
1256 buffer.check_is_valid()?;
1257 buffer.same_device(&state.device)?;
1258 buffer.check_usage(wgt::BufferUsages::INDIRECT)?;
1259
1260 if !offset.is_multiple_of(4) {
1261 return Err(RenderCommandError::UnalignedIndirectBufferOffset(offset).into());
1262 }
1263
1264 let stride = super::get_src_stride_of_indirect_args(family);
1265 match offset.checked_add(stride) {
1266 Some(end_offset) if end_offset <= buffer.size => {}
1267 _ => {
1268 return Err(RenderCommandError::IndirectBufferOverrun {
1269 count: 1,
1270 offset,
1271 args_size: stride,
1272 buffer_size: buffer.size,
1273 }
1274 .into());
1275 }
1276 }
1277 state
1278 .buffer_memory_init_actions
1279 .extend(buffer.initialization_status.read().create_action(
1280 &buffer,
1281 offset..(offset + stride),
1282 MemoryInitKind::NeedsInitializedMemory,
1283 ));
1284
1285 let vertex_or_index_limit = if family == DrawCommandFamily::DrawIndexed {
1286 let index = state.index.as_mut().unwrap();
1287 state.commands.extend(index.flush());
1288 index.limit()
1289 } else {
1290 state.vertex.limits.vertex_limit
1291 };
1292 let instance_limit = state.vertex.limits.instance_limit;
1293
1294 let buffer_uses = if state.device.indirect_validation.is_some()
1295 && family != DrawCommandFamily::DrawMeshTasks
1296 {
1297 wgt::BufferUses::STORAGE_READ_ONLY
1298 } else {
1299 wgt::BufferUses::INDIRECT
1300 };
1301
1302 state.trackers.buffers.merge_single(&buffer, buffer_uses)?;
1303
1304 state.flush_vertex_buffers();
1305 state.flush_bindings();
1306 state.flush_immediates();
1307 state.commands.push(ArcRenderCommand::DrawIndirect {
1308 buffer,
1309 offset,
1310 count: 1,
1311 family,
1312
1313 vertex_or_index_limit: Some(vertex_or_index_limit),
1314 instance_limit: Some(instance_limit),
1315 });
1316 Ok(())
1317}
1318
1319#[derive(Clone, Debug, Error)]
1321#[non_exhaustive]
1322pub enum CreateRenderBundleError {
1323 #[error(transparent)]
1324 ColorAttachment(#[from] ColorAttachmentError),
1325 #[error("Format {0:?} does not have a color aspect")]
1326 FormatNotColor(wgt::TextureFormat),
1327 #[error("Color attachment format {0:?} is not renderable")]
1328 FormatNotRenderable(wgt::TextureFormat),
1329 #[error("Format {0:?} is not a depth/stencil format")]
1330 FormatNotDepthOrStencil(wgt::TextureFormat),
1331 #[error("Render bundle must have at least one attachment (color or depth/stencil)")]
1332 NoAttachment,
1333 #[error("Invalid number of samples {0}")]
1334 InvalidSampleCount(u32),
1335 #[error(transparent)]
1336 MissingFeatures(#[from] MissingFeatures),
1337 #[error(transparent)]
1338 Device(#[from] DeviceError),
1339}
1340
1341impl WebGpuError for CreateRenderBundleError {
1342 fn webgpu_error_type(&self) -> ErrorType {
1343 match self {
1344 Self::ColorAttachment(e) => e.webgpu_error_type(),
1345 Self::FormatNotColor(_)
1346 | Self::FormatNotRenderable(_)
1347 | Self::FormatNotDepthOrStencil(_)
1348 | Self::NoAttachment
1349 | Self::InvalidSampleCount(_) => ErrorType::Validation,
1350 Self::MissingFeatures(e) => e.webgpu_error_type(),
1351 Self::Device(e) => e.webgpu_error_type(),
1352 }
1353 }
1354}
1355
1356#[derive(Clone, Debug, Error)]
1358#[non_exhaustive]
1359pub enum ExecutionError {
1360 #[error(transparent)]
1361 Device(#[from] DeviceError),
1362 #[error(transparent)]
1363 DestroyedResource(#[from] DestroyedResourceError),
1364 #[error(transparent)]
1365 InvalidResource(#[from] InvalidResourceError),
1366 #[error("Using {0} in a render bundle is not implemented")]
1367 Unimplemented(&'static str),
1368}
1369
1370impl From<InvalidOrDestroyedResourceError> for ExecutionError {
1371 fn from(e: InvalidOrDestroyedResourceError) -> Self {
1372 match e {
1373 InvalidOrDestroyedResourceError::InvalidResource(e) => Self::InvalidResource(e),
1374 InvalidOrDestroyedResourceError::DestroyedResource(e) => Self::DestroyedResource(e),
1375 }
1376 }
1377}
1378
1379pub type RenderBundleDescriptor<'a> = wgt::RenderBundleDescriptor<Label<'a>>;
1380
1381#[derive(Debug)]
1382pub(crate) struct RenderBundleState {
1383 pub(crate) used: RenderBundleScope,
1384 pub(super) context: RenderPassContext,
1385}
1386
1387#[derive(Debug)]
1392pub struct RenderBundle {
1393 pub(crate) state: ResourceState<RenderBundleState>,
1394 base: BasePass<ArcRenderCommand, Infallible>,
1397 pub(super) is_depth_read_only: bool,
1398 pub(super) is_stencil_read_only: bool,
1399 pub(crate) device: Arc<Device>,
1400 pub(super) buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
1401 pub(super) texture_memory_init_actions: Vec<TextureInitTrackerAction>,
1402 label: String,
1404 pub(crate) tracking_data: TrackingData,
1405 discard_hal_labels: bool,
1406}
1407
1408impl Drop for RenderBundle {
1409 #[expect(trivial_casts)]
1410 fn drop(&mut self) {
1411 profiling::scope!("RenderBundle::drop");
1412 api_log!("RenderBundle::drop {:?}", self as *const _);
1413 resource_log!("Drop {}", self.error_ident());
1414 #[cfg(feature = "trace")]
1415 if let Some(t) = self.device.trace.lock().as_mut() {
1416 use crate::device::trace::{to_trace, Action};
1417
1418 t.add(Action::DropRenderBundle(unsafe { to_trace(self) }));
1419 }
1420 }
1421}
1422
1423#[cfg(send_sync)]
1424unsafe impl Send for RenderBundle {}
1425#[cfg(send_sync)]
1426unsafe impl Sync for RenderBundle {}
1427
1428impl RenderBundle {
1429 pub(crate) fn state(&self) -> Result<&RenderBundleState, InvalidResourceError> {
1430 self.state
1431 .as_ref()
1432 .valid()
1433 .ok_or_else(|| InvalidResourceError(self.error_ident()))
1434 }
1435
1436 pub(crate) fn check_is_valid(&self) -> Result<(), InvalidResourceError> {
1437 self.state().map(|_| ())
1438 }
1439
1440 pub fn invalid(device: Arc<Device>, desc: &RenderBundleDescriptor) -> Arc<Self> {
1441 Arc::new(RenderBundle {
1442 state: ResourceState::Invalid,
1443 base: BasePass {
1444 label: desc.label.as_ref().map(|l| l.to_string()),
1445 error: None,
1446 commands: Vec::new(),
1447 dynamic_offsets: Vec::new(),
1448 string_data: Vec::new(),
1449 },
1450 is_depth_read_only: false,
1451 is_stencil_read_only: false,
1452 buffer_memory_init_actions: Vec::new(),
1453 texture_memory_init_actions: Vec::new(),
1454 label: desc.label.to_string(),
1455 tracking_data: TrackingData::new(device.tracker_indices.bundles.clone()),
1456 discard_hal_labels: false,
1457 device,
1458 })
1459 }
1460
1461 #[cfg(feature = "trace")]
1462 pub(crate) fn to_base_pass(&self) -> BasePass<RenderCommand<ArcReferences>, Infallible> {
1463 self.base.clone()
1464 }
1465
1466 pub(super) unsafe fn execute(
1476 &self,
1477 raw: &mut dyn hal::DynCommandEncoder,
1478 indirect_draw_validation_resources: &mut crate::indirect_validation::DrawResources,
1479 indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
1480 snatch_guard: &SnatchGuard,
1481 ) -> Result<(), ExecutionError> {
1482 let mut offsets = self.base.dynamic_offsets.as_slice();
1483 let mut pipeline_layout = None::<Arc<PipelineLayout>>;
1484 if !self.discard_hal_labels {
1485 if let Some(ref label) = self.base.label {
1486 unsafe { raw.begin_debug_marker(label) };
1487 }
1488 }
1489
1490 use ArcRenderCommand as Cmd;
1491 for command in self.base.commands.iter() {
1492 match command {
1493 Cmd::SetBindGroup {
1494 index,
1495 num_dynamic_offsets,
1496 bind_group,
1497 } => {
1498 let raw_bg = bind_group.as_ref().unwrap().try_raw(snatch_guard)?;
1499 unsafe {
1500 raw.set_bind_group(
1501 pipeline_layout
1502 .as_ref()
1503 .unwrap()
1504 .raw()
1505 .expect("PipelineLayout should be valid at this point"),
1506 *index,
1507 raw_bg,
1508 &offsets[..*num_dynamic_offsets],
1509 )
1510 };
1511 offsets = &offsets[*num_dynamic_offsets..];
1512 }
1513 Cmd::SetPipeline(pipeline) => {
1514 unsafe {
1515 raw.set_render_pipeline(
1516 pipeline
1517 .raw()
1518 .expect("RenderPipeline should be valid when executing bundle"),
1519 )
1520 };
1521
1522 pipeline_layout = Some(
1523 pipeline
1524 .layout()
1525 .expect("PipelineLayout should be valid when executing bundle")
1526 .clone(),
1527 );
1528 }
1529 Cmd::SetIndexBuffer {
1530 buffer,
1531 index_format,
1532 offset,
1533 size,
1534 } => {
1535 let buffer = buffer.try_raw(snatch_guard)?;
1536 let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size);
1539 unsafe { raw.set_index_buffer(bb, *index_format) };
1540 }
1541 Cmd::SetVertexBuffer {
1542 slot,
1543 buffer,
1544 offset,
1545 size,
1546 } => {
1547 let buffer = buffer.as_ref().unwrap().try_raw(snatch_guard)?;
1548 let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size);
1551 unsafe { raw.set_vertex_buffer(*slot, bb) };
1552 }
1553 Cmd::SetImmediate { offset, data } => {
1554 let pipeline_layout = pipeline_layout.as_ref().unwrap();
1555
1556 unsafe { raw.set_immediates(pipeline_layout.raw().unwrap(), *offset, data) }
1558 }
1559 Cmd::Draw {
1560 vertex_count,
1561 instance_count,
1562 first_vertex,
1563 first_instance,
1564 } => {
1565 unsafe {
1566 raw.draw(
1567 *first_vertex,
1568 *vertex_count,
1569 *first_instance,
1570 *instance_count,
1571 )
1572 };
1573 }
1574 Cmd::DrawIndexed {
1575 index_count,
1576 instance_count,
1577 first_index,
1578 base_vertex,
1579 first_instance,
1580 } => {
1581 unsafe {
1582 raw.draw_indexed(
1583 *first_index,
1584 *index_count,
1585 *base_vertex,
1586 *first_instance,
1587 *instance_count,
1588 )
1589 };
1590 }
1591 Cmd::DrawMeshTasks {
1592 group_count_x,
1593 group_count_y,
1594 group_count_z,
1595 } => unsafe {
1596 raw.draw_mesh_tasks(*group_count_x, *group_count_y, *group_count_z);
1597 },
1598 Cmd::DrawIndirect {
1599 buffer,
1600 offset,
1601 count: 1,
1602 family,
1603
1604 vertex_or_index_limit,
1605 instance_limit,
1606 } => {
1607 let (buffer, offset) = if self.device.indirect_validation.is_some()
1608 && *family != DrawCommandFamily::DrawMeshTasks
1609 {
1610 let (dst_resource_index, offset) = indirect_draw_validation_batcher.add(
1611 indirect_draw_validation_resources,
1612 &self.device,
1613 buffer,
1614 *offset,
1615 *family,
1616 vertex_or_index_limit
1617 .expect("finalized render bundle missing vertex_or_index_limit"),
1618 instance_limit.expect("finalized render bundle missing instance_limit"),
1619 )?;
1620
1621 let dst_buffer =
1622 indirect_draw_validation_resources.get_dst_buffer(dst_resource_index);
1623 (dst_buffer, offset)
1624 } else {
1625 (buffer.try_raw(snatch_guard)?, *offset)
1626 };
1627 match family {
1628 DrawCommandFamily::Draw => unsafe { raw.draw_indirect(buffer, offset, 1) },
1629 DrawCommandFamily::DrawIndexed => unsafe {
1630 raw.draw_indexed_indirect(buffer, offset, 1)
1631 },
1632 DrawCommandFamily::DrawMeshTasks => unsafe {
1633 raw.draw_mesh_tasks_indirect(buffer, offset, 1);
1634 },
1635 }
1636 }
1637 Cmd::DrawIndirect { .. } | Cmd::MultiDrawIndirectCount { .. } => {
1638 return Err(ExecutionError::Unimplemented("multi-draw-indirect"))
1639 }
1640 Cmd::PushDebugGroup { .. } | Cmd::InsertDebugMarker { .. } | Cmd::PopDebugGroup => {
1641 return Err(ExecutionError::Unimplemented("debug-markers"))
1642 }
1643 Cmd::WriteTimestamp { .. }
1644 | Cmd::BeginOcclusionQuery { .. }
1645 | Cmd::EndOcclusionQuery
1646 | Cmd::BeginPipelineStatisticsQuery { .. }
1647 | Cmd::EndPipelineStatisticsQuery => {
1648 return Err(ExecutionError::Unimplemented("queries"))
1649 }
1650 Cmd::ExecuteBundle(_)
1651 | Cmd::SetBlendConstant(_)
1652 | Cmd::SetStencilReference(_)
1653 | Cmd::SetViewport { .. }
1654 | Cmd::SetScissor(_) => unreachable!(),
1655 }
1656 }
1657
1658 if !self.discard_hal_labels {
1659 if let Some(_) = self.base.label {
1660 unsafe { raw.end_debug_marker() };
1661 }
1662 }
1663
1664 Ok(())
1665 }
1666}
1667
1668crate::impl_resource_type!(RenderBundle);
1669crate::impl_labeled!(RenderBundle);
1670crate::impl_parent_device!(RenderBundle);
1671crate::impl_storage_item!(RenderBundle);
1672crate::impl_trackable!(RenderBundle);
1673
1674#[derive(Debug)]
1683struct IndexState {
1684 buffer: Arc<Buffer>,
1685 format: wgt::IndexFormat,
1686 range: Range<wgt::BufferAddress>,
1687 is_dirty: bool,
1688}
1689
1690impl IndexState {
1691 fn limit(&self) -> u64 {
1695 let bytes_per_index = self.format.byte_size() as u64;
1696
1697 (self.range.end - self.range.start) / bytes_per_index
1698 }
1699
1700 fn flush(&mut self) -> Option<ArcRenderCommand> {
1703 let binding_size = self
1705 .range
1706 .end
1707 .checked_sub(self.range.start)
1708 .filter(|_| self.range.end <= self.buffer.size)
1709 .expect("index range must be contained in buffer");
1710
1711 if self.is_dirty {
1712 self.is_dirty = false;
1713 Some(ArcRenderCommand::SetIndexBuffer {
1714 buffer: self.buffer.clone(),
1715 index_format: self.format,
1716 offset: self.range.start,
1717 size: NonZeroU64::new(binding_size),
1718 })
1719 } else {
1720 None
1721 }
1722 }
1723}
1724
1725#[derive(Debug)]
1738struct State {
1749 trackers: RenderBundleScope,
1751
1752 pipeline: Option<Arc<RenderPipeline>>,
1754
1755 vertex: super::VertexState,
1757
1758 index: Option<IndexState>,
1761
1762 flat_dynamic_offsets: Vec<wgt::DynamicOffset>,
1769
1770 device: Arc<Device>,
1771 commands: Vec<ArcRenderCommand>,
1772 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
1773 texture_memory_init_actions: Vec<TextureInitTrackerAction>,
1774 next_dynamic_offset: usize,
1775 binder: Binder,
1776 immediate_state: ImmediateState,
1777}
1778
1779impl State {
1780 fn set_index_buffer(
1782 &mut self,
1783 buffer: Arc<Buffer>,
1784 format: wgt::IndexFormat,
1785 range: Range<wgt::BufferAddress>,
1786 ) {
1787 match self.index {
1788 Some(ref current)
1789 if current.buffer.is_equal(&buffer)
1790 && current.format == format
1791 && current.range == range =>
1792 {
1793 return
1794 }
1795 _ => (),
1796 }
1797
1798 self.index = Some(IndexState {
1799 buffer,
1800 format,
1801 range,
1802 is_dirty: true,
1803 });
1804 }
1805
1806 fn flush_immediates(&mut self) {
1807 if !self.immediate_state.immediates.is_empty() && self.immediate_state.immediates_dirty {
1808 self.commands.push(ArcRenderCommand::SetImmediate {
1809 offset: 0,
1810 data: self.immediate_state.immediates.clone(),
1811 });
1812 self.immediate_state.immediates_dirty = false;
1813 }
1814 }
1815
1816 fn flush_index(&mut self) {
1819 let commands = self.index.as_mut().and_then(|index| index.flush());
1820 self.commands.extend(commands);
1821 }
1822
1823 fn flush_vertex_buffers(&mut self) {
1824 let vertex = &mut self.vertex;
1825 let commands = &mut self.commands;
1826 vertex.flush(|slot, buffer, offset, size| {
1827 commands.push(ArcRenderCommand::SetVertexBuffer {
1828 slot,
1829 buffer: Some(buffer.clone()),
1830 offset,
1831 size,
1832 });
1833 });
1834 }
1835
1836 fn is_ready(&mut self, family: DrawCommandFamily) -> Result<(), DrawError> {
1840 if let Some(pipeline) = self.pipeline.as_ref() {
1841 self.binder.check_compatibility(pipeline.as_ref())?;
1842 self.binder.check_late_buffer_bindings()?;
1843
1844 self.vertex.validate(pipeline.as_ref(), &self.binder)?;
1845
1846 if family == DrawCommandFamily::DrawIndexed {
1847 let index_format = match &self.index {
1848 Some(index) => index.format,
1849 None => return Err(DrawError::MissingIndexBuffer),
1850 };
1851
1852 if pipeline.topology.is_strip() && pipeline.strip_index_format != Some(index_format)
1853 {
1854 return Err(DrawError::UnmatchedStripIndexFormat {
1855 pipeline: pipeline.error_ident(),
1856 strip_index_format: pipeline.strip_index_format,
1857 buffer_format: index_format,
1858 });
1859 }
1860 }
1861
1862 if !self
1863 .immediate_state
1864 .immediate_slots_set
1865 .contains(pipeline.immediate_slots_required)
1866 {
1867 return Err(DrawError::MissingImmediateData {
1868 missing: pipeline
1869 .immediate_slots_required
1870 .difference(self.immediate_state.immediate_slots_set),
1871 });
1872 }
1873
1874 Ok(())
1875 } else {
1876 Err(DrawError::MissingPipeline(pass::MissingPipeline))
1877 }
1878 }
1879
1880 fn flush_bindings(&mut self) {
1884 let start = self.binder.take_rebind_start_index();
1885 let entries = self.binder.list_valid_with_start(start);
1886
1887 self.commands
1888 .extend(entries.map(|(i, bind_group, dynamic_offsets)| {
1889 self.buffer_memory_init_actions
1890 .extend_from_slice(&bind_group.buffer_init_actions);
1891 self.texture_memory_init_actions
1892 .extend_from_slice(&bind_group.texture_init_actions);
1893
1894 self.flat_dynamic_offsets.extend_from_slice(dynamic_offsets);
1895
1896 ArcRenderCommand::SetBindGroup {
1897 index: i.try_into().unwrap(),
1898 bind_group: Some(bind_group.clone()),
1899 num_dynamic_offsets: dynamic_offsets.len(),
1900 }
1901 }));
1902 }
1903}
1904
1905#[derive(Clone, Debug, Error)]
1907pub enum RenderBundleErrorInner {
1908 #[error(transparent)]
1909 Create(#[from] CreateRenderBundleError),
1910 #[error(transparent)]
1911 Device(#[from] DeviceError),
1912 #[error(transparent)]
1913 RenderCommand(RenderCommandError),
1914 #[error(transparent)]
1915 Draw(#[from] DrawError),
1916 #[error(transparent)]
1917 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1918 #[error(transparent)]
1919 Bind(#[from] BindError),
1920 #[error("Render bundle encoder has already ended")]
1921 Ended,
1922}
1923
1924impl<T> From<T> for RenderBundleErrorInner
1925where
1926 T: Into<RenderCommandError>,
1927{
1928 fn from(t: T) -> Self {
1929 Self::RenderCommand(t.into())
1930 }
1931}
1932
1933#[derive(Clone, Debug, Error)]
1935#[error("{scope}")]
1936pub struct RenderBundleError {
1937 pub scope: PassErrorScope,
1938 #[source]
1939 inner: Box<RenderBundleErrorInner>,
1940}
1941
1942impl WebGpuError for RenderBundleError {
1943 fn webgpu_error_type(&self) -> ErrorType {
1944 match self.inner.as_ref() {
1945 RenderBundleErrorInner::Create(e) => e.webgpu_error_type(),
1946 RenderBundleErrorInner::Device(e) => e.webgpu_error_type(),
1947 RenderBundleErrorInner::RenderCommand(e) => e.webgpu_error_type(),
1948 RenderBundleErrorInner::Draw(e) => e.webgpu_error_type(),
1949 RenderBundleErrorInner::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1950 RenderBundleErrorInner::Bind(e) => e.webgpu_error_type(),
1951 RenderBundleErrorInner::Ended => ErrorType::Validation,
1952 }
1953 }
1954}
1955
1956impl RenderBundleError {
1957 pub fn from_device_error(e: DeviceError) -> Self {
1958 Self {
1959 scope: PassErrorScope::Bundle,
1960 inner: Box::new(e.into()),
1961 }
1962 }
1963}
1964
1965impl<E> MapPassErr<RenderBundleError> for E
1966where
1967 E: Into<RenderBundleErrorInner>,
1968{
1969 fn map_pass_err(self, scope: PassErrorScope) -> RenderBundleError {
1970 RenderBundleError {
1971 scope,
1972 inner: Box::new(self.into()),
1973 }
1974 }
1975}