1#![allow(clippy::reversed_empty_ranges)]
80
81use alloc::{
82 borrow::{Cow, ToOwned as _},
83 string::String,
84 sync::Arc,
85 vec::Vec,
86};
87use core::{
88 convert::Infallible,
89 num::{NonZeroU32, NonZeroU64},
90 ops::Range,
91};
92
93use arrayvec::ArrayVec;
94use thiserror::Error;
95
96use wgpu_hal::ShouldBeNonZeroExt;
97use wgt::error::{ErrorType, WebGpuError};
98
99#[cfg(feature = "trace")]
100use crate::command::ArcReferences;
101use crate::{
102 binding_model::{BindError, BindGroup, PipelineLayout},
103 command::{
104 bind::Binder, BasePass, BindGroupStateChange, ColorAttachmentError, DrawError,
105 IdReferences, MapPassErr, PassErrorScope, RenderCommand, RenderCommandError, StateChange,
106 },
107 device::{
108 AttachmentData, Device, DeviceError, MissingDownlevelFlags, MissingFeatures,
109 RenderPassContext,
110 },
111 hub::Hub,
112 id,
113 init_tracker::{BufferInitTrackerAction, MemoryInitKind, TextureInitTrackerAction},
114 pipeline::{PipelineFlags, RenderPipeline},
115 resource::{
116 Buffer, DestroyedResourceError, Fallible, InvalidResourceError, Labeled, ParentDevice,
117 RawResourceAccess, TrackingData,
118 },
119 resource_log,
120 snatch::SnatchGuard,
121 track::RenderBundleScope,
122 validation::{
123 check_color_attachment_count, check_workgroup_sizes,
124 validate_color_attachment_bytes_per_sample,
125 },
126 Label, LabelHelpers,
127};
128
129use super::{pass, render_command::ArcRenderCommand, DrawCommandFamily, DrawKind};
130
131#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
133#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
134pub struct RenderBundleEncoderDescriptor<'a> {
135 pub label: Label<'a>,
139 pub color_formats: Cow<'a, [Option<wgt::TextureFormat>]>,
145 pub depth_stencil: Option<wgt::RenderBundleDepthStencil>,
151 pub sample_count: u32,
155 pub multiview: Option<NonZeroU32>,
158}
159
160#[derive(Debug)]
161#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
162pub struct RenderBundleEncoder {
163 base: BasePass<RenderCommand<IdReferences>, Infallible>,
164 parent_id: id::DeviceId,
165 pub(crate) context: RenderPassContext,
166 pub(crate) is_depth_read_only: bool,
167 pub(crate) is_stencil_read_only: bool,
168
169 #[cfg_attr(feature = "serde", serde(skip))]
171 current_bind_groups: BindGroupStateChange,
172 #[cfg_attr(feature = "serde", serde(skip))]
173 current_pipeline: StateChange<id::RenderPipelineId>,
174}
175
176fn validate_render_bundle_encoder_descriptor(
183 desc: &RenderBundleEncoderDescriptor,
184 device: Option<&Arc<Device>>,
185) -> Result<(bool, bool), CreateRenderBundleError> {
186 let mut have_attachment = false;
187
188 let max_color_attachments = device.map_or(hal::MAX_COLOR_ATTACHMENTS as u32, |device| {
189 assert!(device.limits.max_color_attachments <= hal::MAX_COLOR_ATTACHMENTS as u32);
190 device.limits.max_color_attachments
191 });
192 check_color_attachment_count(desc.color_formats.len(), max_color_attachments)?;
193
194 for &format in desc.color_formats.iter().flatten() {
195 have_attachment = true;
196 if !format.has_color_aspect() {
197 return Err(CreateRenderBundleError::FormatNotColor(format));
198 }
199 if let Some(device) = device {
200 let format_features = device.describe_format_features(format)?;
201 if !format_features
202 .allowed_usages
203 .contains(wgt::TextureUsages::RENDER_ATTACHMENT)
204 {
205 return Err(CreateRenderBundleError::FormatNotRenderable(format));
206 }
207 }
208 }
209
210 if let Some(device) = device {
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
217 let (is_depth_read_only, is_stencil_read_only) = match desc.depth_stencil {
218 Some(ds) => {
219 have_attachment = true;
220 let has_depth = ds.format.has_depth_aspect();
221 let has_stencil = ds.format.has_stencil_aspect();
222 if !has_depth && !has_stencil {
223 return Err(CreateRenderBundleError::FormatNotDepthOrStencil(ds.format));
224 } else {
225 (
226 !has_depth || ds.depth_read_only,
227 !has_stencil || ds.stencil_read_only,
228 )
229 }
230 }
231 None => (true, true),
235 };
236
237 if !have_attachment {
238 return Err(CreateRenderBundleError::NoAttachment);
239 }
240
241 Ok((is_depth_read_only, is_stencil_read_only))
242}
243
244impl RenderBundleEncoder {
245 pub fn new(
251 desc: &RenderBundleEncoderDescriptor,
252 device: Option<&Arc<Device>>,
253 parent_id: id::DeviceId,
254 ) -> Result<Self, CreateRenderBundleError> {
255 let (is_depth_read_only, is_stencil_read_only) =
256 validate_render_bundle_encoder_descriptor(desc, device)?;
257
258 Ok(Self {
259 base: BasePass::new(&desc.label),
260 parent_id,
261 context: RenderPassContext {
262 attachments: AttachmentData {
263 colors: desc.color_formats.iter().cloned().collect(),
264 resolves: ArrayVec::new(),
265 depth_stencil: desc.depth_stencil.map(|ds| ds.format),
266 },
267 sample_count: desc.sample_count,
268 multiview_mask: desc.multiview,
269 },
270
271 is_depth_read_only,
272 is_stencil_read_only,
273 current_bind_groups: BindGroupStateChange::new(),
274 current_pipeline: StateChange::new(),
275 })
276 }
277
278 pub fn dummy(parent_id: id::DeviceId) -> Self {
279 Self {
280 base: BasePass::new(&None),
281 parent_id,
282 context: RenderPassContext {
283 attachments: AttachmentData {
284 colors: ArrayVec::new(),
285 resolves: ArrayVec::new(),
286 depth_stencil: None,
287 },
288 sample_count: 0,
289 multiview_mask: None,
290 },
291 is_depth_read_only: false,
292 is_stencil_read_only: false,
293
294 current_bind_groups: BindGroupStateChange::new(),
295 current_pipeline: StateChange::new(),
296 }
297 }
298
299 pub fn parent(&self) -> id::DeviceId {
300 self.parent_id
301 }
302
303 pub(crate) fn finish(
314 self,
315 desc: &RenderBundleDescriptor,
316 device: &Arc<Device>,
317 hub: &Hub,
318 ) -> Result<Arc<RenderBundle>, RenderBundleError> {
319 let scope = PassErrorScope::Bundle;
320
321 device.check_is_valid().map_pass_err(scope)?;
322
323 {
324 let encoder_desc = RenderBundleEncoderDescriptor {
327 label: self.base.label.as_ref().map(Cow::from),
328 color_formats: Cow::Borrowed(&self.context.attachments.colors),
329 depth_stencil: self.context.attachments.depth_stencil.map(|format| {
330 wgt::RenderBundleDepthStencil {
331 format,
332 depth_read_only: self.is_depth_read_only,
333 stencil_read_only: self.is_stencil_read_only,
334 }
335 }),
336 sample_count: self.context.sample_count,
337 multiview: self.context.multiview_mask,
338 };
339
340 validate_render_bundle_encoder_descriptor(&encoder_desc, Some(device))
341 .map_pass_err(scope)?;
342 };
343
344 let bind_group_guard = hub.bind_groups.read();
345 let pipeline_guard = hub.render_pipelines.read();
346 let buffer_guard = hub.buffers.read();
347
348 let mut state = State {
349 trackers: RenderBundleScope::new(),
350 pipeline: None,
351 vertex: Default::default(),
352 index: None,
353 flat_dynamic_offsets: Vec::new(),
354 device: device.clone(),
355 commands: Vec::new(),
356 buffer_memory_init_actions: Vec::new(),
357 texture_memory_init_actions: Vec::new(),
358 next_dynamic_offset: 0,
359 binder: Binder::new(),
360 immediate_slots_set: Default::default(),
361 };
362
363 let indices = &state.device.tracker_indices;
364 state.trackers.buffers.set_size(indices.buffers.size());
365 state.trackers.textures.set_size(indices.textures.size());
366
367 let base = &self.base;
368
369 for command in &base.commands {
370 match command {
371 &RenderCommand::SetBindGroup {
372 index,
373 num_dynamic_offsets,
374 bind_group,
375 } => {
376 let scope = PassErrorScope::SetBindGroup;
377 set_bind_group(
378 &mut state,
379 &bind_group_guard,
380 &base.dynamic_offsets,
381 index,
382 num_dynamic_offsets,
383 bind_group,
384 )
385 .map_pass_err(scope)?;
386 }
387 &RenderCommand::SetPipeline(pipeline) => {
388 let scope = PassErrorScope::SetPipelineRender;
389 set_pipeline(
390 &mut state,
391 &pipeline_guard,
392 &self.context,
393 self.is_depth_read_only,
394 self.is_stencil_read_only,
395 pipeline,
396 )
397 .map_pass_err(scope)?;
398 }
399 &RenderCommand::SetIndexBuffer {
400 buffer,
401 index_format,
402 offset,
403 size,
404 } => {
405 let scope = PassErrorScope::SetIndexBuffer;
406 set_index_buffer(
407 &mut state,
408 &buffer_guard,
409 buffer,
410 index_format,
411 offset,
412 size,
413 )
414 .map_pass_err(scope)?;
415 }
416 &RenderCommand::SetVertexBuffer {
417 slot,
418 buffer,
419 offset,
420 size,
421 } => {
422 let scope = PassErrorScope::SetVertexBuffer;
423 set_vertex_buffer(&mut state, &buffer_guard, slot, buffer, offset, size)
424 .map_pass_err(scope)?;
425 }
426 &RenderCommand::SetImmediate {
427 offset,
428 size_bytes,
429 values_offset,
430 } => {
431 let scope = PassErrorScope::SetImmediate;
432 set_immediates(&mut state, offset, size_bytes, values_offset)
433 .map_pass_err(scope)?;
434 }
435 &RenderCommand::Draw {
436 vertex_count,
437 instance_count,
438 first_vertex,
439 first_instance,
440 } => {
441 let scope = PassErrorScope::Draw {
442 kind: DrawKind::Draw,
443 family: DrawCommandFamily::Draw,
444 };
445 draw(
446 &mut state,
447 vertex_count,
448 instance_count,
449 first_vertex,
450 first_instance,
451 )
452 .map_pass_err(scope)?;
453 }
454 &RenderCommand::DrawIndexed {
455 index_count,
456 instance_count,
457 first_index,
458 base_vertex,
459 first_instance,
460 } => {
461 let scope = PassErrorScope::Draw {
462 kind: DrawKind::Draw,
463 family: DrawCommandFamily::DrawIndexed,
464 };
465 draw_indexed(
466 &mut state,
467 index_count,
468 instance_count,
469 first_index,
470 base_vertex,
471 first_instance,
472 )
473 .map_pass_err(scope)?;
474 }
475 &RenderCommand::DrawMeshTasks {
476 group_count_x,
477 group_count_y,
478 group_count_z,
479 } => {
480 let scope = PassErrorScope::Draw {
481 kind: DrawKind::Draw,
482 family: DrawCommandFamily::DrawMeshTasks,
483 };
484 draw_mesh_tasks(&mut state, group_count_x, group_count_y, group_count_z)
485 .map_pass_err(scope)?;
486 }
487 &RenderCommand::DrawIndirect {
488 buffer,
489 offset,
490 count: 1,
491 family,
492 vertex_or_index_limit: None,
493 instance_limit: None,
494 } => {
495 let scope = PassErrorScope::Draw {
496 kind: DrawKind::DrawIndirect,
497 family,
498 };
499 multi_draw_indirect(&mut state, &buffer_guard, buffer, offset, family)
500 .map_pass_err(scope)?;
501 }
502 &RenderCommand::DrawIndirect {
503 count,
504 vertex_or_index_limit,
505 instance_limit,
506 ..
507 } => {
508 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");
509 }
510 &RenderCommand::MultiDrawIndirectCount { .. }
511 | &RenderCommand::PushDebugGroup { color: _, len: _ }
512 | &RenderCommand::InsertDebugMarker { color: _, len: _ }
513 | &RenderCommand::PopDebugGroup => {
514 unimplemented!("not supported by a render bundle")
515 }
516 &RenderCommand::WriteTimestamp { .. }
518 | &RenderCommand::BeginOcclusionQuery { .. }
519 | &RenderCommand::EndOcclusionQuery
520 | &RenderCommand::BeginPipelineStatisticsQuery { .. }
521 | &RenderCommand::EndPipelineStatisticsQuery => {
522 unimplemented!("not supported by a render bundle")
523 }
524 &RenderCommand::ExecuteBundle(_)
525 | &RenderCommand::SetBlendConstant(_)
526 | &RenderCommand::SetStencilReference(_)
527 | &RenderCommand::SetViewport { .. }
528 | &RenderCommand::SetScissor(_) => unreachable!("not supported by a render bundle"),
529 }
530 }
531
532 let State {
533 trackers,
534 flat_dynamic_offsets,
535 device,
536 commands,
537 buffer_memory_init_actions,
538 texture_memory_init_actions,
539 ..
540 } = state;
541
542 let tracker_indices = device.tracker_indices.bundles.clone();
543 let discard_hal_labels = device
544 .instance_flags
545 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS);
546
547 let render_bundle = RenderBundle {
548 base: BasePass {
549 label: desc.label.as_deref().map(str::to_owned),
550 error: None,
551 commands,
552 dynamic_offsets: flat_dynamic_offsets,
553 string_data: self.base.string_data,
554 immediates_data: self.base.immediates_data,
555 },
556 is_depth_read_only: self.is_depth_read_only,
557 is_stencil_read_only: self.is_stencil_read_only,
558 device: device.clone(),
559 used: trackers,
560 buffer_memory_init_actions,
561 texture_memory_init_actions,
562 context: self.context,
563 label: desc.label.to_string(),
564 tracking_data: TrackingData::new(tracker_indices),
565 discard_hal_labels,
566 };
567
568 let render_bundle = Arc::new(render_bundle);
569
570 Ok(render_bundle)
571 }
572
573 pub fn set_index_buffer(
574 &mut self,
575 buffer: id::BufferId,
576 index_format: wgt::IndexFormat,
577 offset: wgt::BufferAddress,
578 size: Option<wgt::BufferSize>,
579 ) {
580 self.base.commands.push(RenderCommand::SetIndexBuffer {
581 buffer,
582 index_format,
583 offset,
584 size,
585 });
586 }
587}
588
589fn set_bind_group(
590 state: &mut State,
591 bind_group_guard: &crate::storage::Storage<Fallible<BindGroup>>,
592 dynamic_offsets: &[u32],
593 index: u32,
594 num_dynamic_offsets: usize,
595 bind_group_id: Option<id::Id<id::markers::BindGroup>>,
596) -> Result<(), RenderBundleErrorInner> {
597 let max_bind_groups = state.device.limits.max_bind_groups;
598 if index >= max_bind_groups {
599 return Err(
600 RenderCommandError::BindGroupIndexOutOfRange(pass::BindGroupIndexOutOfRange {
601 index,
602 max: max_bind_groups,
603 })
604 .into(),
605 );
606 }
607
608 let offsets_range = state.next_dynamic_offset..state.next_dynamic_offset + num_dynamic_offsets;
610 state.next_dynamic_offset = offsets_range.end;
611 let offsets = &dynamic_offsets[offsets_range.clone()];
612
613 let bind_group = bind_group_id.map(|id| bind_group_guard.get(id));
614
615 if let Some(bind_group) = bind_group {
616 let bind_group = bind_group.get()?;
617 bind_group.same_device(&state.device)?;
618 bind_group.validate_dynamic_bindings(index, offsets)?;
619
620 unsafe { state.trackers.merge_bind_group(&bind_group.used)? };
621 let bind_group = state.trackers.bind_groups.insert_single(bind_group);
622
623 state
624 .binder
625 .assign_group(index as usize, bind_group, offsets);
626 } else {
627 if !offsets.is_empty() {
628 return Err(RenderBundleErrorInner::Bind(
629 BindError::DynamicOffsetCountNotZero {
630 group: index,
631 actual: offsets.len(),
632 },
633 ));
634 }
635
636 state.binder.clear_group(index as usize);
637 }
638
639 Ok(())
640}
641
642fn set_pipeline(
643 state: &mut State,
644 pipeline_guard: &crate::storage::Storage<Fallible<RenderPipeline>>,
645 context: &RenderPassContext,
646 is_depth_read_only: bool,
647 is_stencil_read_only: bool,
648 pipeline_id: id::Id<id::markers::RenderPipeline>,
649) -> Result<(), RenderBundleErrorInner> {
650 let pipeline = pipeline_guard.get(pipeline_id).get()?;
651
652 pipeline.same_device(&state.device)?;
653
654 context
655 .check_compatible(&pipeline.pass_context, pipeline.as_ref())
656 .map_err(RenderCommandError::IncompatiblePipelineTargets)?;
657
658 if pipeline.flags.contains(PipelineFlags::WRITES_DEPTH) && is_depth_read_only {
659 return Err(RenderCommandError::IncompatibleDepthAccess(pipeline.error_ident()).into());
660 }
661 if pipeline.flags.contains(PipelineFlags::WRITES_STENCIL) && is_stencil_read_only {
662 return Err(RenderCommandError::IncompatibleStencilAccess(pipeline.error_ident()).into());
663 }
664
665 state
666 .commands
667 .push(ArcRenderCommand::SetPipeline(pipeline.clone()));
668
669 state.pipeline = Some(pipeline.clone());
670
671 state
672 .binder
673 .change_pipeline_layout(&pipeline.layout, &pipeline.late_sized_buffer_groups);
674
675 state.vertex.update_limits(&pipeline.vertex_steps);
676
677 state.trackers.render_pipelines.insert_single(pipeline);
678 Ok(())
679}
680
681fn set_index_buffer(
683 state: &mut State,
684 buffer_guard: &crate::storage::Storage<Fallible<Buffer>>,
685 buffer_id: id::Id<id::markers::Buffer>,
686 index_format: wgt::IndexFormat,
687 offset: u64,
688 size: Option<NonZeroU64>,
689) -> Result<(), RenderBundleErrorInner> {
690 let buffer = buffer_guard.get(buffer_id).get()?;
691
692 state
693 .trackers
694 .buffers
695 .merge_single(&buffer, wgt::BufferUses::INDEX)?;
696
697 buffer.same_device(&state.device)?;
698 buffer.check_usage(wgt::BufferUsages::INDEX)?;
699
700 if !offset.is_multiple_of(u64::try_from(index_format.byte_size()).unwrap()) {
701 return Err(RenderCommandError::UnalignedIndexBuffer {
702 offset,
703 alignment: index_format.byte_size(),
704 }
705 .into());
706 }
707 let end = offset + buffer.resolve_binding_size(offset, size)?;
708
709 state
710 .buffer_memory_init_actions
711 .extend(buffer.initialization_status.read().create_action(
712 &buffer,
713 offset..end.get(),
714 MemoryInitKind::NeedsInitializedMemory,
715 ));
716 state.set_index_buffer(buffer, index_format, offset..end.get());
717 Ok(())
718}
719
720fn set_vertex_buffer(
722 state: &mut State,
723 buffer_guard: &crate::storage::Storage<Fallible<Buffer>>,
724 slot: u32,
725 buffer_id: Option<id::Id<id::markers::Buffer>>,
726 offset: u64,
727 size: Option<NonZeroU64>,
728) -> Result<(), RenderBundleErrorInner> {
729 let max_vertex_buffers = state.device.limits.max_vertex_buffers;
730 if slot >= max_vertex_buffers {
731 return Err(RenderCommandError::VertexBufferIndexOutOfRange {
732 index: slot,
733 max: max_vertex_buffers,
734 }
735 .into());
736 }
737
738 if let Some(buffer_id) = buffer_id {
739 let buffer = buffer_guard.get(buffer_id).get()?;
740
741 state
742 .trackers
743 .buffers
744 .merge_single(&buffer, wgt::BufferUses::VERTEX)?;
745
746 buffer.same_device(&state.device)?;
747 buffer.check_usage(wgt::BufferUsages::VERTEX)?;
748
749 if !offset.is_multiple_of(wgt::VERTEX_ALIGNMENT) {
750 return Err(RenderCommandError::UnalignedVertexBuffer { slot, offset }.into());
751 }
752 let binding_size = buffer.resolve_binding_size(offset, size)?;
753 let buffer_range = offset..(offset + binding_size);
754
755 state
756 .buffer_memory_init_actions
757 .extend(buffer.initialization_status.read().create_action(
758 &buffer,
759 buffer_range.clone(),
760 MemoryInitKind::NeedsInitializedMemory,
761 ));
762 state.vertex.set_buffer(slot as usize, buffer, buffer_range);
763 if let Some(pipeline) = state.pipeline.as_deref() {
764 state.vertex.update_limits(&pipeline.vertex_steps);
765 }
766 } else {
767 if offset != 0 {
768 return Err(RenderCommandError::from(
769 crate::binding_model::BindingError::UnbindingVertexBufferOffsetNotZero {
770 slot,
771 offset,
772 },
773 )
774 .into());
775 }
776 if let Some(size) = size {
777 return Err(RenderCommandError::from(
778 crate::binding_model::BindingError::UnbindingVertexBufferSizeNotZero {
779 slot,
780 size: size.get(),
781 },
782 )
783 .into());
784 }
785
786 state.vertex.clear_buffer(slot as usize);
787 if let Some(pipeline) = state.pipeline.as_deref() {
788 state.vertex.update_limits(&pipeline.vertex_steps);
789 }
790 }
791
792 Ok(())
793}
794
795fn set_immediates(
796 state: &mut State,
797 offset: u32,
798 size_bytes: u32,
799 values_offset: Option<u32>,
800) -> Result<(), RenderBundleErrorInner> {
801 let pipeline = state
802 .pipeline
803 .as_deref()
804 .ok_or(DrawError::MissingPipeline(pass::MissingPipeline))?;
805
806 pipeline
807 .layout
808 .validate_immediates_ranges(offset, size_bytes)?;
809
810 state.commands.push(ArcRenderCommand::SetImmediate {
811 offset,
812 size_bytes,
813 values_offset,
814 });
815 state.immediate_slots_set |= naga::valid::ImmediateSlots::from_range(offset, size_bytes);
816 Ok(())
817}
818
819fn draw(
820 state: &mut State,
821 vertex_count: u32,
822 instance_count: u32,
823 first_vertex: u32,
824 first_instance: u32,
825) -> Result<(), RenderBundleErrorInner> {
826 state.is_ready(DrawCommandFamily::Draw)?;
827
828 state
829 .vertex
830 .limits
831 .validate_vertex_limit(first_vertex, vertex_count)?;
832 state
833 .vertex
834 .limits
835 .validate_instance_limit(first_instance, instance_count)?;
836
837 if instance_count > 0 && vertex_count > 0 {
838 state.flush_vertex_buffers();
839 state.flush_bindings();
840 state.commands.push(ArcRenderCommand::Draw {
841 vertex_count,
842 instance_count,
843 first_vertex,
844 first_instance,
845 });
846 }
847 Ok(())
848}
849
850fn draw_indexed(
851 state: &mut State,
852 index_count: u32,
853 instance_count: u32,
854 first_index: u32,
855 base_vertex: i32,
856 first_instance: u32,
857) -> Result<(), RenderBundleErrorInner> {
858 state.is_ready(DrawCommandFamily::DrawIndexed)?;
859
860 let index = state.index.as_ref().unwrap();
861
862 let last_index = first_index as u64 + index_count as u64;
863 let index_limit = index.limit();
864 if last_index > index_limit {
865 return Err(DrawError::IndexBeyondLimit {
866 last_index,
867 index_limit,
868 }
869 .into());
870 }
871 state
872 .vertex
873 .limits
874 .validate_instance_limit(first_instance, instance_count)?;
875
876 if instance_count > 0 && index_count > 0 {
877 state.flush_index();
878 state.flush_vertex_buffers();
879 state.flush_bindings();
880 state.commands.push(ArcRenderCommand::DrawIndexed {
881 index_count,
882 instance_count,
883 first_index,
884 base_vertex,
885 first_instance,
886 });
887 }
888 Ok(())
889}
890
891fn draw_mesh_tasks(
892 state: &mut State,
893 group_count_x: u32,
894 group_count_y: u32,
895 group_count_z: u32,
896) -> Result<(), RenderBundleErrorInner> {
897 state.is_ready(DrawCommandFamily::DrawMeshTasks)?;
898
899 let limits = &state.device.limits;
900 let (groups_size_limit, max_groups) = if state.pipeline.as_ref().unwrap().has_task_shader {
901 (
902 limits.max_task_workgroups_per_dimension,
903 limits.max_task_workgroup_total_count,
904 )
905 } else {
906 (
907 limits.max_mesh_workgroups_per_dimension,
908 limits.max_mesh_workgroup_total_count,
909 )
910 };
911
912 let total_count = check_workgroup_sizes(
913 &[group_count_x, group_count_y, group_count_z],
914 &[groups_size_limit, groups_size_limit, groups_size_limit],
915 "max_task_mesh_workgroups_per_dimension",
916 max_groups,
917 "max_task_mesh_workgroup_total_count",
918 )
919 .map_err(|err| RenderBundleErrorInner::Draw(err.into()))?;
920
921 if total_count > 0 {
922 state.flush_bindings();
923 state.commands.push(ArcRenderCommand::DrawMeshTasks {
924 group_count_x,
925 group_count_y,
926 group_count_z,
927 });
928 }
929 Ok(())
930}
931
932fn multi_draw_indirect(
933 state: &mut State,
934 buffer_guard: &crate::storage::Storage<Fallible<Buffer>>,
935 buffer_id: id::Id<id::markers::Buffer>,
936 offset: u64,
937 family: DrawCommandFamily,
938) -> Result<(), RenderBundleErrorInner> {
939 state.is_ready(family)?;
940 state
941 .device
942 .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
943
944 let buffer = buffer_guard.get(buffer_id).get()?;
945
946 buffer.same_device(&state.device)?;
947 buffer.check_usage(wgt::BufferUsages::INDIRECT)?;
948
949 let stride = super::get_src_stride_of_indirect_args(family);
950 assert!(offset <= wgt::BufferAddress::MAX - stride);
954 state
955 .buffer_memory_init_actions
956 .extend(buffer.initialization_status.read().create_action(
957 &buffer,
958 offset..(offset + stride),
959 MemoryInitKind::NeedsInitializedMemory,
960 ));
961
962 let vertex_or_index_limit = if family == DrawCommandFamily::DrawIndexed {
963 let index = state.index.as_mut().unwrap();
964 state.commands.extend(index.flush());
965 index.limit()
966 } else {
967 state.vertex.limits.vertex_limit
968 };
969 let instance_limit = state.vertex.limits.instance_limit;
970
971 let buffer_uses = if state.device.indirect_validation.is_some() {
972 wgt::BufferUses::STORAGE_READ_ONLY
973 } else {
974 wgt::BufferUses::INDIRECT
975 };
976
977 state.trackers.buffers.merge_single(&buffer, buffer_uses)?;
978
979 state.flush_vertex_buffers();
980 state.flush_bindings();
981 state.commands.push(ArcRenderCommand::DrawIndirect {
982 buffer,
983 offset,
984 count: 1,
985 family,
986
987 vertex_or_index_limit: Some(vertex_or_index_limit),
988 instance_limit: Some(instance_limit),
989 });
990 Ok(())
991}
992
993#[derive(Clone, Debug, Error)]
995#[non_exhaustive]
996pub enum CreateRenderBundleError {
997 #[error(transparent)]
998 ColorAttachment(#[from] ColorAttachmentError),
999 #[error("Format {0:?} does not have a color aspect")]
1000 FormatNotColor(wgt::TextureFormat),
1001 #[error("Color attachment format {0:?} is not renderable")]
1002 FormatNotRenderable(wgt::TextureFormat),
1003 #[error("Format {0:?} is not a depth/stencil format")]
1004 FormatNotDepthOrStencil(wgt::TextureFormat),
1005 #[error("Render bundle must have at least one attachment (color or depth/stencil)")]
1006 NoAttachment,
1007 #[error("Invalid number of samples {0}")]
1008 InvalidSampleCount(u32),
1009 #[error(transparent)]
1010 MissingFeatures(#[from] MissingFeatures),
1011}
1012
1013impl WebGpuError for CreateRenderBundleError {
1014 fn webgpu_error_type(&self) -> ErrorType {
1015 match self {
1016 Self::ColorAttachment(e) => e.webgpu_error_type(),
1017 Self::FormatNotColor(_)
1018 | Self::FormatNotRenderable(_)
1019 | Self::FormatNotDepthOrStencil(_)
1020 | Self::NoAttachment
1021 | Self::InvalidSampleCount(_) => ErrorType::Validation,
1022 Self::MissingFeatures(e) => e.webgpu_error_type(),
1023 }
1024 }
1025}
1026
1027#[derive(Clone, Debug, Error)]
1029#[non_exhaustive]
1030pub enum ExecutionError {
1031 #[error(transparent)]
1032 Device(#[from] DeviceError),
1033 #[error(transparent)]
1034 DestroyedResource(#[from] DestroyedResourceError),
1035 #[error("Using {0} in a render bundle is not implemented")]
1036 Unimplemented(&'static str),
1037}
1038
1039pub type RenderBundleDescriptor<'a> = wgt::RenderBundleDescriptor<Label<'a>>;
1040
1041#[derive(Debug)]
1046pub struct RenderBundle {
1047 base: BasePass<ArcRenderCommand, Infallible>,
1050 pub(super) is_depth_read_only: bool,
1051 pub(super) is_stencil_read_only: bool,
1052 pub(crate) device: Arc<Device>,
1053 pub(crate) used: RenderBundleScope,
1054 pub(super) buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
1055 pub(super) texture_memory_init_actions: Vec<TextureInitTrackerAction>,
1056 pub(super) context: RenderPassContext,
1057 label: String,
1059 pub(crate) tracking_data: TrackingData,
1060 discard_hal_labels: bool,
1061}
1062
1063impl Drop for RenderBundle {
1064 fn drop(&mut self) {
1065 resource_log!("Drop {}", self.error_ident());
1066 }
1067}
1068
1069#[cfg(send_sync)]
1070unsafe impl Send for RenderBundle {}
1071#[cfg(send_sync)]
1072unsafe impl Sync for RenderBundle {}
1073
1074impl RenderBundle {
1075 #[cfg(feature = "trace")]
1076 pub(crate) fn to_base_pass(&self) -> BasePass<RenderCommand<ArcReferences>, Infallible> {
1077 self.base.clone()
1078 }
1079
1080 pub(super) unsafe fn execute(
1090 &self,
1091 raw: &mut dyn hal::DynCommandEncoder,
1092 indirect_draw_validation_resources: &mut crate::indirect_validation::DrawResources,
1093 indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
1094 snatch_guard: &SnatchGuard,
1095 ) -> Result<(), ExecutionError> {
1096 let mut offsets = self.base.dynamic_offsets.as_slice();
1097 let mut pipeline_layout = None::<Arc<PipelineLayout>>;
1098 if !self.discard_hal_labels {
1099 if let Some(ref label) = self.base.label {
1100 unsafe { raw.begin_debug_marker(label) };
1101 }
1102 }
1103
1104 use ArcRenderCommand as Cmd;
1105 for command in self.base.commands.iter() {
1106 match command {
1107 Cmd::SetBindGroup {
1108 index,
1109 num_dynamic_offsets,
1110 bind_group,
1111 } => {
1112 let raw_bg = bind_group.as_ref().unwrap().try_raw(snatch_guard)?;
1113 unsafe {
1114 raw.set_bind_group(
1115 pipeline_layout.as_ref().unwrap().raw(),
1116 *index,
1117 raw_bg,
1118 &offsets[..*num_dynamic_offsets],
1119 )
1120 };
1121 offsets = &offsets[*num_dynamic_offsets..];
1122 }
1123 Cmd::SetPipeline(pipeline) => {
1124 unsafe { raw.set_render_pipeline(pipeline.raw()) };
1125
1126 pipeline_layout = Some(pipeline.layout.clone());
1127 }
1128 Cmd::SetIndexBuffer {
1129 buffer,
1130 index_format,
1131 offset,
1132 size,
1133 } => {
1134 let buffer = buffer.try_raw(snatch_guard)?;
1135 let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size);
1138 unsafe { raw.set_index_buffer(bb, *index_format) };
1139 }
1140 Cmd::SetVertexBuffer {
1141 slot,
1142 buffer,
1143 offset,
1144 size,
1145 } => {
1146 let buffer = buffer.as_ref().unwrap().try_raw(snatch_guard)?;
1147 let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size);
1150 unsafe { raw.set_vertex_buffer(*slot, bb) };
1151 }
1152 Cmd::SetImmediate {
1153 offset,
1154 size_bytes,
1155 values_offset,
1156 } => {
1157 let pipeline_layout = pipeline_layout.as_ref().unwrap();
1158
1159 if let Some(values_offset) = *values_offset {
1160 let values_end_offset =
1161 (values_offset + size_bytes / wgt::IMMEDIATE_DATA_ALIGNMENT) as usize;
1162 let data_slice =
1163 &self.base.immediates_data[(values_offset as usize)..values_end_offset];
1164
1165 unsafe { raw.set_immediates(pipeline_layout.raw(), *offset, data_slice) }
1166 } else {
1167 super::immediates_clear(
1168 *offset,
1169 *size_bytes,
1170 |clear_offset, clear_data| {
1171 unsafe {
1172 raw.set_immediates(
1173 pipeline_layout.raw(),
1174 clear_offset,
1175 clear_data,
1176 )
1177 };
1178 },
1179 );
1180 }
1181 }
1182 Cmd::Draw {
1183 vertex_count,
1184 instance_count,
1185 first_vertex,
1186 first_instance,
1187 } => {
1188 unsafe {
1189 raw.draw(
1190 *first_vertex,
1191 *vertex_count,
1192 *first_instance,
1193 *instance_count,
1194 )
1195 };
1196 }
1197 Cmd::DrawIndexed {
1198 index_count,
1199 instance_count,
1200 first_index,
1201 base_vertex,
1202 first_instance,
1203 } => {
1204 unsafe {
1205 raw.draw_indexed(
1206 *first_index,
1207 *index_count,
1208 *base_vertex,
1209 *first_instance,
1210 *instance_count,
1211 )
1212 };
1213 }
1214 Cmd::DrawMeshTasks {
1215 group_count_x,
1216 group_count_y,
1217 group_count_z,
1218 } => unsafe {
1219 raw.draw_mesh_tasks(*group_count_x, *group_count_y, *group_count_z);
1220 },
1221 Cmd::DrawIndirect {
1222 buffer,
1223 offset,
1224 count: 1,
1225 family,
1226
1227 vertex_or_index_limit,
1228 instance_limit,
1229 } => {
1230 let (buffer, offset) = if self.device.indirect_validation.is_some() {
1231 let (dst_resource_index, offset) = indirect_draw_validation_batcher.add(
1232 indirect_draw_validation_resources,
1233 &self.device,
1234 buffer,
1235 *offset,
1236 *family,
1237 vertex_or_index_limit
1238 .expect("finalized render bundle missing vertex_or_index_limit"),
1239 instance_limit.expect("finalized render bundle missing instance_limit"),
1240 )?;
1241
1242 let dst_buffer =
1243 indirect_draw_validation_resources.get_dst_buffer(dst_resource_index);
1244 (dst_buffer, offset)
1245 } else {
1246 (buffer.try_raw(snatch_guard)?, *offset)
1247 };
1248 match family {
1249 DrawCommandFamily::Draw => unsafe { raw.draw_indirect(buffer, offset, 1) },
1250 DrawCommandFamily::DrawIndexed => unsafe {
1251 raw.draw_indexed_indirect(buffer, offset, 1)
1252 },
1253 DrawCommandFamily::DrawMeshTasks => unsafe {
1254 raw.draw_mesh_tasks_indirect(buffer, offset, 1);
1255 },
1256 }
1257 }
1258 Cmd::DrawIndirect { .. } | Cmd::MultiDrawIndirectCount { .. } => {
1259 return Err(ExecutionError::Unimplemented("multi-draw-indirect"))
1260 }
1261 Cmd::PushDebugGroup { .. } | Cmd::InsertDebugMarker { .. } | Cmd::PopDebugGroup => {
1262 return Err(ExecutionError::Unimplemented("debug-markers"))
1263 }
1264 Cmd::WriteTimestamp { .. }
1265 | Cmd::BeginOcclusionQuery { .. }
1266 | Cmd::EndOcclusionQuery
1267 | Cmd::BeginPipelineStatisticsQuery { .. }
1268 | Cmd::EndPipelineStatisticsQuery => {
1269 return Err(ExecutionError::Unimplemented("queries"))
1270 }
1271 Cmd::ExecuteBundle(_)
1272 | Cmd::SetBlendConstant(_)
1273 | Cmd::SetStencilReference(_)
1274 | Cmd::SetViewport { .. }
1275 | Cmd::SetScissor(_) => unreachable!(),
1276 }
1277 }
1278
1279 if !self.discard_hal_labels {
1280 if let Some(_) = self.base.label {
1281 unsafe { raw.end_debug_marker() };
1282 }
1283 }
1284
1285 Ok(())
1286 }
1287}
1288
1289crate::impl_resource_type!(RenderBundle);
1290crate::impl_labeled!(RenderBundle);
1291crate::impl_parent_device!(RenderBundle);
1292crate::impl_storage_item!(RenderBundle);
1293crate::impl_trackable!(RenderBundle);
1294
1295#[derive(Debug)]
1304struct IndexState {
1305 buffer: Arc<Buffer>,
1306 format: wgt::IndexFormat,
1307 range: Range<wgt::BufferAddress>,
1308 is_dirty: bool,
1309}
1310
1311impl IndexState {
1312 fn limit(&self) -> u64 {
1316 let bytes_per_index = self.format.byte_size() as u64;
1317
1318 (self.range.end - self.range.start) / bytes_per_index
1319 }
1320
1321 fn flush(&mut self) -> Option<ArcRenderCommand> {
1324 let binding_size = self
1326 .range
1327 .end
1328 .checked_sub(self.range.start)
1329 .filter(|_| self.range.end <= self.buffer.size)
1330 .expect("index range must be contained in buffer");
1331
1332 if self.is_dirty {
1333 self.is_dirty = false;
1334 Some(ArcRenderCommand::SetIndexBuffer {
1335 buffer: self.buffer.clone(),
1336 index_format: self.format,
1337 offset: self.range.start,
1338 size: NonZeroU64::new(binding_size),
1339 })
1340 } else {
1341 None
1342 }
1343 }
1344}
1345
1346#[derive(Debug)]
1359struct State {
1370 trackers: RenderBundleScope,
1372
1373 pipeline: Option<Arc<RenderPipeline>>,
1375
1376 vertex: super::VertexState,
1378
1379 index: Option<IndexState>,
1382
1383 flat_dynamic_offsets: Vec<wgt::DynamicOffset>,
1390
1391 device: Arc<Device>,
1392 commands: Vec<ArcRenderCommand>,
1393 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
1394 texture_memory_init_actions: Vec<TextureInitTrackerAction>,
1395 next_dynamic_offset: usize,
1396 binder: Binder,
1397 immediate_slots_set: naga::valid::ImmediateSlots,
1400}
1401
1402impl State {
1403 fn set_index_buffer(
1405 &mut self,
1406 buffer: Arc<Buffer>,
1407 format: wgt::IndexFormat,
1408 range: Range<wgt::BufferAddress>,
1409 ) {
1410 match self.index {
1411 Some(ref current)
1412 if current.buffer.is_equal(&buffer)
1413 && current.format == format
1414 && current.range == range =>
1415 {
1416 return
1417 }
1418 _ => (),
1419 }
1420
1421 self.index = Some(IndexState {
1422 buffer,
1423 format,
1424 range,
1425 is_dirty: true,
1426 });
1427 }
1428
1429 fn flush_index(&mut self) {
1432 let commands = self.index.as_mut().and_then(|index| index.flush());
1433 self.commands.extend(commands);
1434 }
1435
1436 fn flush_vertex_buffers(&mut self) {
1437 let vertex = &mut self.vertex;
1438 let commands = &mut self.commands;
1439 vertex.flush(|slot, buffer, offset, size| {
1440 commands.push(ArcRenderCommand::SetVertexBuffer {
1441 slot,
1442 buffer: Some(buffer.clone()),
1443 offset,
1444 size,
1445 });
1446 });
1447 }
1448
1449 fn is_ready(&mut self, family: DrawCommandFamily) -> Result<(), DrawError> {
1453 if let Some(pipeline) = self.pipeline.as_ref() {
1454 self.binder.check_compatibility(pipeline.as_ref())?;
1455 self.binder.check_late_buffer_bindings()?;
1456
1457 self.vertex.validate(pipeline.as_ref(), &self.binder)?;
1458
1459 if family == DrawCommandFamily::DrawIndexed {
1460 let index_format = match &self.index {
1461 Some(index) => index.format,
1462 None => return Err(DrawError::MissingIndexBuffer),
1463 };
1464
1465 if pipeline.topology.is_strip() && pipeline.strip_index_format != Some(index_format)
1466 {
1467 return Err(DrawError::UnmatchedStripIndexFormat {
1468 pipeline: pipeline.error_ident(),
1469 strip_index_format: pipeline.strip_index_format,
1470 buffer_format: index_format,
1471 });
1472 }
1473 }
1474
1475 if !self
1476 .immediate_slots_set
1477 .contains(pipeline.immediate_slots_required)
1478 {
1479 return Err(DrawError::MissingImmediateData {
1480 missing: pipeline
1481 .immediate_slots_required
1482 .difference(self.immediate_slots_set),
1483 });
1484 }
1485
1486 Ok(())
1487 } else {
1488 Err(DrawError::MissingPipeline(pass::MissingPipeline))
1489 }
1490 }
1491
1492 fn flush_bindings(&mut self) {
1496 let start = self.binder.take_rebind_start_index();
1497 let entries = self.binder.list_valid_with_start(start);
1498
1499 self.commands
1500 .extend(entries.map(|(i, bind_group, dynamic_offsets)| {
1501 self.buffer_memory_init_actions
1502 .extend_from_slice(&bind_group.used_buffer_ranges);
1503 self.texture_memory_init_actions
1504 .extend_from_slice(&bind_group.used_texture_ranges);
1505
1506 self.flat_dynamic_offsets.extend_from_slice(dynamic_offsets);
1507
1508 ArcRenderCommand::SetBindGroup {
1509 index: i.try_into().unwrap(),
1510 bind_group: Some(bind_group.clone()),
1511 num_dynamic_offsets: dynamic_offsets.len(),
1512 }
1513 }));
1514 }
1515}
1516
1517#[derive(Clone, Debug, Error)]
1519pub enum RenderBundleErrorInner {
1520 #[error(transparent)]
1521 Create(#[from] CreateRenderBundleError),
1522 #[error(transparent)]
1523 Device(#[from] DeviceError),
1524 #[error(transparent)]
1525 RenderCommand(RenderCommandError),
1526 #[error(transparent)]
1527 Draw(#[from] DrawError),
1528 #[error(transparent)]
1529 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1530 #[error(transparent)]
1531 Bind(#[from] BindError),
1532 #[error(transparent)]
1533 InvalidResource(#[from] InvalidResourceError),
1534}
1535
1536impl<T> From<T> for RenderBundleErrorInner
1537where
1538 T: Into<RenderCommandError>,
1539{
1540 fn from(t: T) -> Self {
1541 Self::RenderCommand(t.into())
1542 }
1543}
1544
1545#[derive(Clone, Debug, Error)]
1547#[error("{scope}")]
1548pub struct RenderBundleError {
1549 pub scope: PassErrorScope,
1550 #[source]
1551 inner: RenderBundleErrorInner,
1552}
1553
1554impl WebGpuError for RenderBundleError {
1555 fn webgpu_error_type(&self) -> ErrorType {
1556 let Self { scope: _, inner } = self;
1557 match inner {
1558 RenderBundleErrorInner::Create(e) => e.webgpu_error_type(),
1559 RenderBundleErrorInner::Device(e) => e.webgpu_error_type(),
1560 RenderBundleErrorInner::RenderCommand(e) => e.webgpu_error_type(),
1561 RenderBundleErrorInner::Draw(e) => e.webgpu_error_type(),
1562 RenderBundleErrorInner::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1563 RenderBundleErrorInner::Bind(e) => e.webgpu_error_type(),
1564 RenderBundleErrorInner::InvalidResource(e) => e.webgpu_error_type(),
1565 }
1566 }
1567}
1568
1569impl RenderBundleError {
1570 pub fn from_device_error(e: DeviceError) -> Self {
1571 Self {
1572 scope: PassErrorScope::Bundle,
1573 inner: e.into(),
1574 }
1575 }
1576}
1577
1578impl<E> MapPassErr<RenderBundleError> for E
1579where
1580 E: Into<RenderBundleErrorInner>,
1581{
1582 fn map_pass_err(self, scope: PassErrorScope) -> RenderBundleError {
1583 RenderBundleError {
1584 scope,
1585 inner: self.into(),
1586 }
1587 }
1588}
1589
1590pub mod bundle_ffi {
1591 use super::{RenderBundleEncoder, RenderCommand};
1592 use crate::{command::DrawCommandFamily, id, RawString};
1593 use core::{convert::TryInto, slice};
1594 use wgt::{BufferAddress, BufferSize, DynamicOffset, IndexFormat};
1595
1596 pub unsafe fn wgpu_render_bundle_set_bind_group(
1601 bundle: &mut RenderBundleEncoder,
1602 index: u32,
1603 bind_group_id: Option<id::BindGroupId>,
1604 offsets: *const DynamicOffset,
1605 offset_length: usize,
1606 ) {
1607 let offsets = unsafe { slice::from_raw_parts(offsets, offset_length) };
1608
1609 let redundant = bundle.current_bind_groups.set_and_check_redundant(
1610 bind_group_id,
1611 index,
1612 &mut bundle.base.dynamic_offsets,
1613 offsets,
1614 );
1615
1616 if redundant {
1617 return;
1618 }
1619
1620 bundle.base.commands.push(RenderCommand::SetBindGroup {
1621 index,
1622 num_dynamic_offsets: offset_length,
1623 bind_group: bind_group_id,
1624 });
1625 }
1626
1627 pub fn wgpu_render_bundle_set_pipeline(
1628 bundle: &mut RenderBundleEncoder,
1629 pipeline_id: id::RenderPipelineId,
1630 ) {
1631 if bundle.current_pipeline.set_and_check_redundant(pipeline_id) {
1632 return;
1633 }
1634
1635 bundle
1636 .base
1637 .commands
1638 .push(RenderCommand::SetPipeline(pipeline_id));
1639 }
1640
1641 pub fn wgpu_render_bundle_set_vertex_buffer(
1642 bundle: &mut RenderBundleEncoder,
1643 slot: u32,
1644 buffer_id: Option<id::BufferId>,
1645 offset: BufferAddress,
1646 size: Option<BufferSize>,
1647 ) {
1648 bundle.base.commands.push(RenderCommand::SetVertexBuffer {
1649 slot,
1650 buffer: buffer_id,
1651 offset,
1652 size,
1653 });
1654 }
1655
1656 pub fn wgpu_render_bundle_set_index_buffer(
1657 encoder: &mut RenderBundleEncoder,
1658 buffer: id::BufferId,
1659 index_format: IndexFormat,
1660 offset: BufferAddress,
1661 size: Option<BufferSize>,
1662 ) {
1663 encoder.set_index_buffer(buffer, index_format, offset, size);
1664 }
1665
1666 pub unsafe fn wgpu_render_bundle_set_immediates(
1671 pass: &mut RenderBundleEncoder,
1672 offset: u32,
1673 size_bytes: u32,
1674 data: *const u8,
1675 ) {
1676 assert_eq!(
1677 offset & (wgt::IMMEDIATE_DATA_ALIGNMENT - 1),
1678 0,
1679 "Immediate data offset must be aligned to 4 bytes."
1680 );
1681 assert_eq!(
1682 size_bytes & (wgt::IMMEDIATE_DATA_ALIGNMENT - 1),
1683 0,
1684 "Immediate data size must be aligned to 4 bytes."
1685 );
1686 let data_slice = unsafe { slice::from_raw_parts(data, size_bytes as usize) };
1687 let value_offset = pass.base.immediates_data.len().try_into().expect(
1688 "Ran out of immediate data space. Don't set 4gb of immediates per RenderBundle.",
1689 );
1690
1691 pass.base.immediates_data.extend(
1692 data_slice
1693 .chunks_exact(wgt::IMMEDIATE_DATA_ALIGNMENT as usize)
1694 .map(|arr| u32::from_ne_bytes([arr[0], arr[1], arr[2], arr[3]])),
1695 );
1696
1697 pass.base.commands.push(RenderCommand::SetImmediate {
1698 offset,
1699 size_bytes,
1700 values_offset: Some(value_offset),
1701 });
1702 }
1703
1704 pub fn wgpu_render_bundle_draw(
1705 bundle: &mut RenderBundleEncoder,
1706 vertex_count: u32,
1707 instance_count: u32,
1708 first_vertex: u32,
1709 first_instance: u32,
1710 ) {
1711 bundle.base.commands.push(RenderCommand::Draw {
1712 vertex_count,
1713 instance_count,
1714 first_vertex,
1715 first_instance,
1716 });
1717 }
1718
1719 pub fn wgpu_render_bundle_draw_indexed(
1720 bundle: &mut RenderBundleEncoder,
1721 index_count: u32,
1722 instance_count: u32,
1723 first_index: u32,
1724 base_vertex: i32,
1725 first_instance: u32,
1726 ) {
1727 bundle.base.commands.push(RenderCommand::DrawIndexed {
1728 index_count,
1729 instance_count,
1730 first_index,
1731 base_vertex,
1732 first_instance,
1733 });
1734 }
1735
1736 pub fn wgpu_render_bundle_draw_indirect(
1737 bundle: &mut RenderBundleEncoder,
1738 buffer_id: id::BufferId,
1739 offset: BufferAddress,
1740 ) {
1741 bundle.base.commands.push(RenderCommand::DrawIndirect {
1742 buffer: buffer_id,
1743 offset,
1744 count: 1,
1745 family: DrawCommandFamily::Draw,
1746 vertex_or_index_limit: None,
1747 instance_limit: None,
1748 });
1749 }
1750
1751 pub fn wgpu_render_bundle_draw_indexed_indirect(
1752 bundle: &mut RenderBundleEncoder,
1753 buffer_id: id::BufferId,
1754 offset: BufferAddress,
1755 ) {
1756 bundle.base.commands.push(RenderCommand::DrawIndirect {
1757 buffer: buffer_id,
1758 offset,
1759 count: 1,
1760 family: DrawCommandFamily::DrawIndexed,
1761 vertex_or_index_limit: None,
1762 instance_limit: None,
1763 });
1764 }
1765
1766 pub unsafe fn wgpu_render_bundle_push_debug_group(
1771 _bundle: &mut RenderBundleEncoder,
1772 _label: RawString,
1773 ) {
1774 }
1776
1777 pub fn wgpu_render_bundle_pop_debug_group(_bundle: &mut RenderBundleEncoder) {
1778 }
1780
1781 pub unsafe fn wgpu_render_bundle_insert_debug_marker(
1786 _bundle: &mut RenderBundleEncoder,
1787 _label: RawString,
1788 ) {
1789 }
1791}