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::from(index_format.byte_size())) {
701 return Err(RenderCommandError::UnalignedIndexBuffer {
702 offset,
703 alignment: index_format.byte_size() as usize,
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 && family != DrawCommandFamily::DrawMeshTasks
973 {
974 wgt::BufferUses::STORAGE_READ_ONLY
975 } else {
976 wgt::BufferUses::INDIRECT
977 };
978
979 state.trackers.buffers.merge_single(&buffer, buffer_uses)?;
980
981 state.flush_vertex_buffers();
982 state.flush_bindings();
983 state.commands.push(ArcRenderCommand::DrawIndirect {
984 buffer,
985 offset,
986 count: 1,
987 family,
988
989 vertex_or_index_limit: Some(vertex_or_index_limit),
990 instance_limit: Some(instance_limit),
991 });
992 Ok(())
993}
994
995#[derive(Clone, Debug, Error)]
997#[non_exhaustive]
998pub enum CreateRenderBundleError {
999 #[error(transparent)]
1000 ColorAttachment(#[from] ColorAttachmentError),
1001 #[error("Format {0:?} does not have a color aspect")]
1002 FormatNotColor(wgt::TextureFormat),
1003 #[error("Color attachment format {0:?} is not renderable")]
1004 FormatNotRenderable(wgt::TextureFormat),
1005 #[error("Format {0:?} is not a depth/stencil format")]
1006 FormatNotDepthOrStencil(wgt::TextureFormat),
1007 #[error("Render bundle must have at least one attachment (color or depth/stencil)")]
1008 NoAttachment,
1009 #[error("Invalid number of samples {0}")]
1010 InvalidSampleCount(u32),
1011 #[error(transparent)]
1012 MissingFeatures(#[from] MissingFeatures),
1013}
1014
1015impl WebGpuError for CreateRenderBundleError {
1016 fn webgpu_error_type(&self) -> ErrorType {
1017 match self {
1018 Self::ColorAttachment(e) => e.webgpu_error_type(),
1019 Self::FormatNotColor(_)
1020 | Self::FormatNotRenderable(_)
1021 | Self::FormatNotDepthOrStencil(_)
1022 | Self::NoAttachment
1023 | Self::InvalidSampleCount(_) => ErrorType::Validation,
1024 Self::MissingFeatures(e) => e.webgpu_error_type(),
1025 }
1026 }
1027}
1028
1029#[derive(Clone, Debug, Error)]
1031#[non_exhaustive]
1032pub enum ExecutionError {
1033 #[error(transparent)]
1034 Device(#[from] DeviceError),
1035 #[error(transparent)]
1036 DestroyedResource(#[from] DestroyedResourceError),
1037 #[error("Using {0} in a render bundle is not implemented")]
1038 Unimplemented(&'static str),
1039}
1040
1041pub type RenderBundleDescriptor<'a> = wgt::RenderBundleDescriptor<Label<'a>>;
1042
1043#[derive(Debug)]
1048pub struct RenderBundle {
1049 base: BasePass<ArcRenderCommand, Infallible>,
1052 pub(super) is_depth_read_only: bool,
1053 pub(super) is_stencil_read_only: bool,
1054 pub(crate) device: Arc<Device>,
1055 pub(crate) used: RenderBundleScope,
1056 pub(super) buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
1057 pub(super) texture_memory_init_actions: Vec<TextureInitTrackerAction>,
1058 pub(super) context: RenderPassContext,
1059 label: String,
1061 pub(crate) tracking_data: TrackingData,
1062 discard_hal_labels: bool,
1063}
1064
1065impl Drop for RenderBundle {
1066 fn drop(&mut self) {
1067 resource_log!("Drop {}", self.error_ident());
1068 }
1069}
1070
1071#[cfg(send_sync)]
1072unsafe impl Send for RenderBundle {}
1073#[cfg(send_sync)]
1074unsafe impl Sync for RenderBundle {}
1075
1076impl RenderBundle {
1077 #[cfg(feature = "trace")]
1078 pub(crate) fn to_base_pass(&self) -> BasePass<RenderCommand<ArcReferences>, Infallible> {
1079 self.base.clone()
1080 }
1081
1082 pub(super) unsafe fn execute(
1092 &self,
1093 raw: &mut dyn hal::DynCommandEncoder,
1094 indirect_draw_validation_resources: &mut crate::indirect_validation::DrawResources,
1095 indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
1096 snatch_guard: &SnatchGuard,
1097 ) -> Result<(), ExecutionError> {
1098 let mut offsets = self.base.dynamic_offsets.as_slice();
1099 let mut pipeline_layout = None::<Arc<PipelineLayout>>;
1100 if !self.discard_hal_labels {
1101 if let Some(ref label) = self.base.label {
1102 unsafe { raw.begin_debug_marker(label) };
1103 }
1104 }
1105
1106 use ArcRenderCommand as Cmd;
1107 for command in self.base.commands.iter() {
1108 match command {
1109 Cmd::SetBindGroup {
1110 index,
1111 num_dynamic_offsets,
1112 bind_group,
1113 } => {
1114 let raw_bg = bind_group.as_ref().unwrap().try_raw(snatch_guard)?;
1115 unsafe {
1116 raw.set_bind_group(
1117 pipeline_layout.as_ref().unwrap().raw(),
1118 *index,
1119 raw_bg,
1120 &offsets[..*num_dynamic_offsets],
1121 )
1122 };
1123 offsets = &offsets[*num_dynamic_offsets..];
1124 }
1125 Cmd::SetPipeline(pipeline) => {
1126 unsafe { raw.set_render_pipeline(pipeline.raw()) };
1127
1128 pipeline_layout = Some(pipeline.layout.clone());
1129 }
1130 Cmd::SetIndexBuffer {
1131 buffer,
1132 index_format,
1133 offset,
1134 size,
1135 } => {
1136 let buffer = buffer.try_raw(snatch_guard)?;
1137 let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size);
1140 unsafe { raw.set_index_buffer(bb, *index_format) };
1141 }
1142 Cmd::SetVertexBuffer {
1143 slot,
1144 buffer,
1145 offset,
1146 size,
1147 } => {
1148 let buffer = buffer.as_ref().unwrap().try_raw(snatch_guard)?;
1149 let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size);
1152 unsafe { raw.set_vertex_buffer(*slot, bb) };
1153 }
1154 Cmd::SetImmediate {
1155 offset,
1156 size_bytes,
1157 values_offset,
1158 } => {
1159 let pipeline_layout = pipeline_layout.as_ref().unwrap();
1160
1161 if let Some(values_offset) = *values_offset {
1162 let values_end_offset =
1163 (values_offset + size_bytes / wgt::IMMEDIATE_DATA_ALIGNMENT) as usize;
1164 let data_slice =
1165 &self.base.immediates_data[(values_offset as usize)..values_end_offset];
1166
1167 unsafe { raw.set_immediates(pipeline_layout.raw(), *offset, data_slice) }
1168 } else {
1169 super::immediates_clear(
1170 *offset,
1171 *size_bytes,
1172 |clear_offset, clear_data| {
1173 unsafe {
1174 raw.set_immediates(
1175 pipeline_layout.raw(),
1176 clear_offset,
1177 clear_data,
1178 )
1179 };
1180 },
1181 );
1182 }
1183 }
1184 Cmd::Draw {
1185 vertex_count,
1186 instance_count,
1187 first_vertex,
1188 first_instance,
1189 } => {
1190 unsafe {
1191 raw.draw(
1192 *first_vertex,
1193 *vertex_count,
1194 *first_instance,
1195 *instance_count,
1196 )
1197 };
1198 }
1199 Cmd::DrawIndexed {
1200 index_count,
1201 instance_count,
1202 first_index,
1203 base_vertex,
1204 first_instance,
1205 } => {
1206 unsafe {
1207 raw.draw_indexed(
1208 *first_index,
1209 *index_count,
1210 *base_vertex,
1211 *first_instance,
1212 *instance_count,
1213 )
1214 };
1215 }
1216 Cmd::DrawMeshTasks {
1217 group_count_x,
1218 group_count_y,
1219 group_count_z,
1220 } => unsafe {
1221 raw.draw_mesh_tasks(*group_count_x, *group_count_y, *group_count_z);
1222 },
1223 Cmd::DrawIndirect {
1224 buffer,
1225 offset,
1226 count: 1,
1227 family,
1228
1229 vertex_or_index_limit,
1230 instance_limit,
1231 } => {
1232 let (buffer, offset) = if self.device.indirect_validation.is_some()
1233 && *family != DrawCommandFamily::DrawMeshTasks
1234 {
1235 let (dst_resource_index, offset) = indirect_draw_validation_batcher.add(
1236 indirect_draw_validation_resources,
1237 &self.device,
1238 buffer,
1239 *offset,
1240 *family,
1241 vertex_or_index_limit
1242 .expect("finalized render bundle missing vertex_or_index_limit"),
1243 instance_limit.expect("finalized render bundle missing instance_limit"),
1244 )?;
1245
1246 let dst_buffer =
1247 indirect_draw_validation_resources.get_dst_buffer(dst_resource_index);
1248 (dst_buffer, offset)
1249 } else {
1250 (buffer.try_raw(snatch_guard)?, *offset)
1251 };
1252 match family {
1253 DrawCommandFamily::Draw => unsafe { raw.draw_indirect(buffer, offset, 1) },
1254 DrawCommandFamily::DrawIndexed => unsafe {
1255 raw.draw_indexed_indirect(buffer, offset, 1)
1256 },
1257 DrawCommandFamily::DrawMeshTasks => unsafe {
1258 raw.draw_mesh_tasks_indirect(buffer, offset, 1);
1259 },
1260 }
1261 }
1262 Cmd::DrawIndirect { .. } | Cmd::MultiDrawIndirectCount { .. } => {
1263 return Err(ExecutionError::Unimplemented("multi-draw-indirect"))
1264 }
1265 Cmd::PushDebugGroup { .. } | Cmd::InsertDebugMarker { .. } | Cmd::PopDebugGroup => {
1266 return Err(ExecutionError::Unimplemented("debug-markers"))
1267 }
1268 Cmd::WriteTimestamp { .. }
1269 | Cmd::BeginOcclusionQuery { .. }
1270 | Cmd::EndOcclusionQuery
1271 | Cmd::BeginPipelineStatisticsQuery { .. }
1272 | Cmd::EndPipelineStatisticsQuery => {
1273 return Err(ExecutionError::Unimplemented("queries"))
1274 }
1275 Cmd::ExecuteBundle(_)
1276 | Cmd::SetBlendConstant(_)
1277 | Cmd::SetStencilReference(_)
1278 | Cmd::SetViewport { .. }
1279 | Cmd::SetScissor(_) => unreachable!(),
1280 }
1281 }
1282
1283 if !self.discard_hal_labels {
1284 if let Some(_) = self.base.label {
1285 unsafe { raw.end_debug_marker() };
1286 }
1287 }
1288
1289 Ok(())
1290 }
1291}
1292
1293crate::impl_resource_type!(RenderBundle);
1294crate::impl_labeled!(RenderBundle);
1295crate::impl_parent_device!(RenderBundle);
1296crate::impl_storage_item!(RenderBundle);
1297crate::impl_trackable!(RenderBundle);
1298
1299#[derive(Debug)]
1308struct IndexState {
1309 buffer: Arc<Buffer>,
1310 format: wgt::IndexFormat,
1311 range: Range<wgt::BufferAddress>,
1312 is_dirty: bool,
1313}
1314
1315impl IndexState {
1316 fn limit(&self) -> u64 {
1320 let bytes_per_index = self.format.byte_size() as u64;
1321
1322 (self.range.end - self.range.start) / bytes_per_index
1323 }
1324
1325 fn flush(&mut self) -> Option<ArcRenderCommand> {
1328 let binding_size = self
1330 .range
1331 .end
1332 .checked_sub(self.range.start)
1333 .filter(|_| self.range.end <= self.buffer.size)
1334 .expect("index range must be contained in buffer");
1335
1336 if self.is_dirty {
1337 self.is_dirty = false;
1338 Some(ArcRenderCommand::SetIndexBuffer {
1339 buffer: self.buffer.clone(),
1340 index_format: self.format,
1341 offset: self.range.start,
1342 size: NonZeroU64::new(binding_size),
1343 })
1344 } else {
1345 None
1346 }
1347 }
1348}
1349
1350#[derive(Debug)]
1363struct State {
1374 trackers: RenderBundleScope,
1376
1377 pipeline: Option<Arc<RenderPipeline>>,
1379
1380 vertex: super::VertexState,
1382
1383 index: Option<IndexState>,
1386
1387 flat_dynamic_offsets: Vec<wgt::DynamicOffset>,
1394
1395 device: Arc<Device>,
1396 commands: Vec<ArcRenderCommand>,
1397 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
1398 texture_memory_init_actions: Vec<TextureInitTrackerAction>,
1399 next_dynamic_offset: usize,
1400 binder: Binder,
1401 immediate_slots_set: naga::valid::ImmediateSlots,
1404}
1405
1406impl State {
1407 fn set_index_buffer(
1409 &mut self,
1410 buffer: Arc<Buffer>,
1411 format: wgt::IndexFormat,
1412 range: Range<wgt::BufferAddress>,
1413 ) {
1414 match self.index {
1415 Some(ref current)
1416 if current.buffer.is_equal(&buffer)
1417 && current.format == format
1418 && current.range == range =>
1419 {
1420 return
1421 }
1422 _ => (),
1423 }
1424
1425 self.index = Some(IndexState {
1426 buffer,
1427 format,
1428 range,
1429 is_dirty: true,
1430 });
1431 }
1432
1433 fn flush_index(&mut self) {
1436 let commands = self.index.as_mut().and_then(|index| index.flush());
1437 self.commands.extend(commands);
1438 }
1439
1440 fn flush_vertex_buffers(&mut self) {
1441 let vertex = &mut self.vertex;
1442 let commands = &mut self.commands;
1443 vertex.flush(|slot, buffer, offset, size| {
1444 commands.push(ArcRenderCommand::SetVertexBuffer {
1445 slot,
1446 buffer: Some(buffer.clone()),
1447 offset,
1448 size,
1449 });
1450 });
1451 }
1452
1453 fn is_ready(&mut self, family: DrawCommandFamily) -> Result<(), DrawError> {
1457 if let Some(pipeline) = self.pipeline.as_ref() {
1458 self.binder.check_compatibility(pipeline.as_ref())?;
1459 self.binder.check_late_buffer_bindings()?;
1460
1461 self.vertex.validate(pipeline.as_ref(), &self.binder)?;
1462
1463 if family == DrawCommandFamily::DrawIndexed {
1464 let index_format = match &self.index {
1465 Some(index) => index.format,
1466 None => return Err(DrawError::MissingIndexBuffer),
1467 };
1468
1469 if pipeline.topology.is_strip() && pipeline.strip_index_format != Some(index_format)
1470 {
1471 return Err(DrawError::UnmatchedStripIndexFormat {
1472 pipeline: pipeline.error_ident(),
1473 strip_index_format: pipeline.strip_index_format,
1474 buffer_format: index_format,
1475 });
1476 }
1477 }
1478
1479 if !self
1480 .immediate_slots_set
1481 .contains(pipeline.immediate_slots_required)
1482 {
1483 return Err(DrawError::MissingImmediateData {
1484 missing: pipeline
1485 .immediate_slots_required
1486 .difference(self.immediate_slots_set),
1487 });
1488 }
1489
1490 Ok(())
1491 } else {
1492 Err(DrawError::MissingPipeline(pass::MissingPipeline))
1493 }
1494 }
1495
1496 fn flush_bindings(&mut self) {
1500 let start = self.binder.take_rebind_start_index();
1501 let entries = self.binder.list_valid_with_start(start);
1502
1503 self.commands
1504 .extend(entries.map(|(i, bind_group, dynamic_offsets)| {
1505 self.buffer_memory_init_actions
1506 .extend_from_slice(&bind_group.buffer_init_actions);
1507 self.texture_memory_init_actions
1508 .extend_from_slice(&bind_group.texture_init_actions);
1509
1510 self.flat_dynamic_offsets.extend_from_slice(dynamic_offsets);
1511
1512 ArcRenderCommand::SetBindGroup {
1513 index: i.try_into().unwrap(),
1514 bind_group: Some(bind_group.clone()),
1515 num_dynamic_offsets: dynamic_offsets.len(),
1516 }
1517 }));
1518 }
1519}
1520
1521#[derive(Clone, Debug, Error)]
1523pub enum RenderBundleErrorInner {
1524 #[error(transparent)]
1525 Create(#[from] CreateRenderBundleError),
1526 #[error(transparent)]
1527 Device(#[from] DeviceError),
1528 #[error(transparent)]
1529 RenderCommand(RenderCommandError),
1530 #[error(transparent)]
1531 Draw(#[from] DrawError),
1532 #[error(transparent)]
1533 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1534 #[error(transparent)]
1535 Bind(#[from] BindError),
1536 #[error(transparent)]
1537 InvalidResource(#[from] InvalidResourceError),
1538}
1539
1540impl<T> From<T> for RenderBundleErrorInner
1541where
1542 T: Into<RenderCommandError>,
1543{
1544 fn from(t: T) -> Self {
1545 Self::RenderCommand(t.into())
1546 }
1547}
1548
1549#[derive(Clone, Debug, Error)]
1551#[error("{scope}")]
1552pub struct RenderBundleError {
1553 pub scope: PassErrorScope,
1554 #[source]
1555 inner: RenderBundleErrorInner,
1556}
1557
1558impl WebGpuError for RenderBundleError {
1559 fn webgpu_error_type(&self) -> ErrorType {
1560 let Self { scope: _, inner } = self;
1561 match inner {
1562 RenderBundleErrorInner::Create(e) => e.webgpu_error_type(),
1563 RenderBundleErrorInner::Device(e) => e.webgpu_error_type(),
1564 RenderBundleErrorInner::RenderCommand(e) => e.webgpu_error_type(),
1565 RenderBundleErrorInner::Draw(e) => e.webgpu_error_type(),
1566 RenderBundleErrorInner::MissingDownlevelFlags(e) => e.webgpu_error_type(),
1567 RenderBundleErrorInner::Bind(e) => e.webgpu_error_type(),
1568 RenderBundleErrorInner::InvalidResource(e) => e.webgpu_error_type(),
1569 }
1570 }
1571}
1572
1573impl RenderBundleError {
1574 pub fn from_device_error(e: DeviceError) -> Self {
1575 Self {
1576 scope: PassErrorScope::Bundle,
1577 inner: e.into(),
1578 }
1579 }
1580}
1581
1582impl<E> MapPassErr<RenderBundleError> for E
1583where
1584 E: Into<RenderBundleErrorInner>,
1585{
1586 fn map_pass_err(self, scope: PassErrorScope) -> RenderBundleError {
1587 RenderBundleError {
1588 scope,
1589 inner: self.into(),
1590 }
1591 }
1592}
1593
1594pub mod bundle_ffi {
1595 use super::{RenderBundleEncoder, RenderCommand};
1596 use crate::{command::DrawCommandFamily, id, RawString};
1597 use core::{convert::TryInto, slice};
1598 use wgt::{BufferAddress, BufferSize, DynamicOffset, IndexFormat};
1599
1600 pub unsafe fn wgpu_render_bundle_set_bind_group(
1605 bundle: &mut RenderBundleEncoder,
1606 index: u32,
1607 bind_group_id: Option<id::BindGroupId>,
1608 offsets: *const DynamicOffset,
1609 offset_length: usize,
1610 ) {
1611 let offsets = unsafe { slice::from_raw_parts(offsets, offset_length) };
1612
1613 let redundant = bundle.current_bind_groups.set_and_check_redundant(
1614 bind_group_id,
1615 index,
1616 &mut bundle.base.dynamic_offsets,
1617 offsets,
1618 );
1619
1620 if redundant {
1621 return;
1622 }
1623
1624 bundle.base.commands.push(RenderCommand::SetBindGroup {
1625 index,
1626 num_dynamic_offsets: offset_length,
1627 bind_group: bind_group_id,
1628 });
1629 }
1630
1631 pub fn wgpu_render_bundle_set_pipeline(
1632 bundle: &mut RenderBundleEncoder,
1633 pipeline_id: id::RenderPipelineId,
1634 ) {
1635 if bundle.current_pipeline.set_and_check_redundant(pipeline_id) {
1636 return;
1637 }
1638
1639 bundle
1640 .base
1641 .commands
1642 .push(RenderCommand::SetPipeline(pipeline_id));
1643 }
1644
1645 pub fn wgpu_render_bundle_set_vertex_buffer(
1646 bundle: &mut RenderBundleEncoder,
1647 slot: u32,
1648 buffer_id: Option<id::BufferId>,
1649 offset: BufferAddress,
1650 size: Option<BufferSize>,
1651 ) {
1652 bundle.base.commands.push(RenderCommand::SetVertexBuffer {
1653 slot,
1654 buffer: buffer_id,
1655 offset,
1656 size,
1657 });
1658 }
1659
1660 pub fn wgpu_render_bundle_set_index_buffer(
1661 encoder: &mut RenderBundleEncoder,
1662 buffer: id::BufferId,
1663 index_format: IndexFormat,
1664 offset: BufferAddress,
1665 size: Option<BufferSize>,
1666 ) {
1667 encoder.set_index_buffer(buffer, index_format, offset, size);
1668 }
1669
1670 pub unsafe fn wgpu_render_bundle_set_immediates(
1675 pass: &mut RenderBundleEncoder,
1676 offset: u32,
1677 size_bytes: u32,
1678 data: *const u8,
1679 ) {
1680 assert_eq!(
1681 offset & (wgt::IMMEDIATE_DATA_ALIGNMENT - 1),
1682 0,
1683 "Immediate data offset must be aligned to 4 bytes."
1684 );
1685 assert_eq!(
1686 size_bytes & (wgt::IMMEDIATE_DATA_ALIGNMENT - 1),
1687 0,
1688 "Immediate data size must be aligned to 4 bytes."
1689 );
1690 let data_slice = unsafe { slice::from_raw_parts(data, size_bytes as usize) };
1691 let value_offset = pass.base.immediates_data.len().try_into().expect(
1692 "Ran out of immediate data space. Don't set 4gb of immediates per RenderBundle.",
1693 );
1694
1695 pass.base.immediates_data.extend(
1696 data_slice
1697 .chunks_exact(wgt::IMMEDIATE_DATA_ALIGNMENT as usize)
1698 .map(|arr| u32::from_ne_bytes([arr[0], arr[1], arr[2], arr[3]])),
1699 );
1700
1701 pass.base.commands.push(RenderCommand::SetImmediate {
1702 offset,
1703 size_bytes,
1704 values_offset: Some(value_offset),
1705 });
1706 }
1707
1708 pub fn wgpu_render_bundle_draw(
1709 bundle: &mut RenderBundleEncoder,
1710 vertex_count: u32,
1711 instance_count: u32,
1712 first_vertex: u32,
1713 first_instance: u32,
1714 ) {
1715 bundle.base.commands.push(RenderCommand::Draw {
1716 vertex_count,
1717 instance_count,
1718 first_vertex,
1719 first_instance,
1720 });
1721 }
1722
1723 pub fn wgpu_render_bundle_draw_indexed(
1724 bundle: &mut RenderBundleEncoder,
1725 index_count: u32,
1726 instance_count: u32,
1727 first_index: u32,
1728 base_vertex: i32,
1729 first_instance: u32,
1730 ) {
1731 bundle.base.commands.push(RenderCommand::DrawIndexed {
1732 index_count,
1733 instance_count,
1734 first_index,
1735 base_vertex,
1736 first_instance,
1737 });
1738 }
1739
1740 pub fn wgpu_render_bundle_draw_indirect(
1741 bundle: &mut RenderBundleEncoder,
1742 buffer_id: id::BufferId,
1743 offset: BufferAddress,
1744 ) {
1745 bundle.base.commands.push(RenderCommand::DrawIndirect {
1746 buffer: buffer_id,
1747 offset,
1748 count: 1,
1749 family: DrawCommandFamily::Draw,
1750 vertex_or_index_limit: None,
1751 instance_limit: None,
1752 });
1753 }
1754
1755 pub fn wgpu_render_bundle_draw_indexed_indirect(
1756 bundle: &mut RenderBundleEncoder,
1757 buffer_id: id::BufferId,
1758 offset: BufferAddress,
1759 ) {
1760 bundle.base.commands.push(RenderCommand::DrawIndirect {
1761 buffer: buffer_id,
1762 offset,
1763 count: 1,
1764 family: DrawCommandFamily::DrawIndexed,
1765 vertex_or_index_limit: None,
1766 instance_limit: None,
1767 });
1768 }
1769
1770 pub unsafe fn wgpu_render_bundle_push_debug_group(
1775 _bundle: &mut RenderBundleEncoder,
1776 _label: RawString,
1777 ) {
1778 }
1780
1781 pub fn wgpu_render_bundle_pop_debug_group(_bundle: &mut RenderBundleEncoder) {
1782 }
1784
1785 pub unsafe fn wgpu_render_bundle_insert_debug_marker(
1790 _bundle: &mut RenderBundleEncoder,
1791 _label: RawString,
1792 ) {
1793 }
1795}