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