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
99use crate::{
100 binding_model::{BindError, BindGroup, PipelineLayout},
101 command::{
102 BasePass, BindGroupStateChange, ColorAttachmentError, DrawError, MapPassErr,
103 PassErrorScope, RenderCommandError, StateChange,
104 },
105 device::{
106 AttachmentData, Device, DeviceError, MissingDownlevelFlags, RenderPassContext,
107 SHADER_STAGE_COUNT,
108 },
109 hub::Hub,
110 id,
111 init_tracker::{BufferInitTrackerAction, MemoryInitKind, TextureInitTrackerAction},
112 pipeline::{PipelineFlags, RenderPipeline, VertexStep},
113 resource::{
114 Buffer, DestroyedResourceError, Fallible, InvalidResourceError, Labeled, ParentDevice,
115 RawResourceAccess, TrackingData,
116 },
117 resource_log,
118 snatch::SnatchGuard,
119 track::RenderBundleScope,
120 Label, LabelHelpers,
121};
122
123use super::{
124 pass,
125 render_command::{ArcRenderCommand, RenderCommand},
126 DrawCommandFamily, DrawKind,
127};
128
129#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
131#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
132pub struct RenderBundleEncoderDescriptor<'a> {
133 pub label: Label<'a>,
137 pub color_formats: Cow<'a, [Option<wgt::TextureFormat>]>,
143 pub depth_stencil: Option<wgt::RenderBundleDepthStencil>,
149 pub sample_count: u32,
153 pub multiview: Option<NonZeroU32>,
156}
157
158#[derive(Debug)]
159#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
160pub struct RenderBundleEncoder {
161 base: BasePass<RenderCommand, Infallible>,
162 parent_id: id::DeviceId,
163 pub(crate) context: RenderPassContext,
164 pub(crate) is_depth_read_only: bool,
165 pub(crate) is_stencil_read_only: bool,
166
167 #[cfg_attr(feature = "serde", serde(skip))]
169 current_bind_groups: BindGroupStateChange,
170 #[cfg_attr(feature = "serde", serde(skip))]
171 current_pipeline: StateChange<id::RenderPipelineId>,
172}
173
174impl RenderBundleEncoder {
175 pub fn new(
176 desc: &RenderBundleEncoderDescriptor,
177 parent_id: id::DeviceId,
178 base: Option<BasePass<RenderCommand, Infallible>>,
179 ) -> Result<Self, CreateRenderBundleError> {
180 let (is_depth_read_only, is_stencil_read_only) = match desc.depth_stencil {
181 Some(ds) => {
182 let aspects = hal::FormatAspects::from(ds.format);
183 (
184 !aspects.contains(hal::FormatAspects::DEPTH) || ds.depth_read_only,
185 !aspects.contains(hal::FormatAspects::STENCIL) || ds.stencil_read_only,
186 )
187 }
188 None => (true, true),
192 };
193
194 let max_color_attachments = hal::MAX_COLOR_ATTACHMENTS;
196
197 Ok(Self {
200 base: base.unwrap_or_else(|| BasePass::new(&desc.label)),
201 parent_id,
202 context: RenderPassContext {
203 attachments: AttachmentData {
204 colors: if desc.color_formats.len() > max_color_attachments {
205 return Err(CreateRenderBundleError::ColorAttachment(
206 ColorAttachmentError::TooMany {
207 given: desc.color_formats.len(),
208 limit: max_color_attachments,
209 },
210 ));
211 } else {
212 desc.color_formats.iter().cloned().collect()
213 },
214 resolves: ArrayVec::new(),
215 depth_stencil: desc.depth_stencil.map(|ds| ds.format),
216 },
217 sample_count: {
218 let sc = desc.sample_count;
219 if sc == 0 || sc > 32 || !sc.is_power_of_two() {
220 return Err(CreateRenderBundleError::InvalidSampleCount(sc));
221 }
222 sc
223 },
224 multiview: desc.multiview,
225 },
226
227 is_depth_read_only,
228 is_stencil_read_only,
229 current_bind_groups: BindGroupStateChange::new(),
230 current_pipeline: StateChange::new(),
231 })
232 }
233
234 pub fn dummy(parent_id: id::DeviceId) -> Self {
235 Self {
236 base: BasePass::new(&None),
237 parent_id,
238 context: RenderPassContext {
239 attachments: AttachmentData {
240 colors: ArrayVec::new(),
241 resolves: ArrayVec::new(),
242 depth_stencil: None,
243 },
244 sample_count: 0,
245 multiview: None,
246 },
247 is_depth_read_only: false,
248 is_stencil_read_only: false,
249
250 current_bind_groups: BindGroupStateChange::new(),
251 current_pipeline: StateChange::new(),
252 }
253 }
254
255 #[cfg(feature = "trace")]
256 pub(crate) fn to_base_pass(&self) -> BasePass<RenderCommand, Infallible> {
257 self.base.clone()
258 }
259
260 pub fn parent(&self) -> id::DeviceId {
261 self.parent_id
262 }
263
264 pub(crate) fn finish(
275 self,
276 desc: &RenderBundleDescriptor,
277 device: &Arc<Device>,
278 hub: &Hub,
279 ) -> Result<Arc<RenderBundle>, RenderBundleError> {
280 let scope = PassErrorScope::Bundle;
281
282 device.check_is_valid().map_pass_err(scope)?;
283
284 let bind_group_guard = hub.bind_groups.read();
285 let pipeline_guard = hub.render_pipelines.read();
286 let buffer_guard = hub.buffers.read();
287
288 let mut state = State {
289 trackers: RenderBundleScope::new(),
290 pipeline: None,
291 bind: (0..hal::MAX_BIND_GROUPS).map(|_| None).collect(),
292 vertex: Default::default(),
293 index: None,
294 flat_dynamic_offsets: Vec::new(),
295 device: device.clone(),
296 commands: Vec::new(),
297 buffer_memory_init_actions: Vec::new(),
298 texture_memory_init_actions: Vec::new(),
299 next_dynamic_offset: 0,
300 };
301
302 let indices = &state.device.tracker_indices;
303 state.trackers.buffers.set_size(indices.buffers.size());
304 state.trackers.textures.set_size(indices.textures.size());
305
306 let base = &self.base;
307
308 for &command in &base.commands {
309 match command {
310 RenderCommand::SetBindGroup {
311 index,
312 num_dynamic_offsets,
313 bind_group_id,
314 } => {
315 let scope = PassErrorScope::SetBindGroup;
316 set_bind_group(
317 &mut state,
318 &bind_group_guard,
319 &base.dynamic_offsets,
320 index,
321 num_dynamic_offsets,
322 bind_group_id,
323 )
324 .map_pass_err(scope)?;
325 }
326 RenderCommand::SetPipeline(pipeline_id) => {
327 let scope = PassErrorScope::SetPipelineRender;
328 set_pipeline(
329 &mut state,
330 &pipeline_guard,
331 &self.context,
332 self.is_depth_read_only,
333 self.is_stencil_read_only,
334 pipeline_id,
335 )
336 .map_pass_err(scope)?;
337 }
338 RenderCommand::SetIndexBuffer {
339 buffer_id,
340 index_format,
341 offset,
342 size,
343 } => {
344 let scope = PassErrorScope::SetIndexBuffer;
345 set_index_buffer(
346 &mut state,
347 &buffer_guard,
348 buffer_id,
349 index_format,
350 offset,
351 size,
352 )
353 .map_pass_err(scope)?;
354 }
355 RenderCommand::SetVertexBuffer {
356 slot,
357 buffer_id,
358 offset,
359 size,
360 } => {
361 let scope = PassErrorScope::SetVertexBuffer;
362 set_vertex_buffer(&mut state, &buffer_guard, slot, buffer_id, offset, size)
363 .map_pass_err(scope)?;
364 }
365 RenderCommand::SetPushConstant {
366 stages,
367 offset,
368 size_bytes,
369 values_offset,
370 } => {
371 let scope = PassErrorScope::SetPushConstant;
372 set_push_constant(&mut state, stages, offset, size_bytes, values_offset)
373 .map_pass_err(scope)?;
374 }
375 RenderCommand::Draw {
376 vertex_count,
377 instance_count,
378 first_vertex,
379 first_instance,
380 } => {
381 let scope = PassErrorScope::Draw {
382 kind: DrawKind::Draw,
383 family: DrawCommandFamily::Draw,
384 };
385 draw(
386 &mut state,
387 &base.dynamic_offsets,
388 vertex_count,
389 instance_count,
390 first_vertex,
391 first_instance,
392 )
393 .map_pass_err(scope)?;
394 }
395 RenderCommand::DrawIndexed {
396 index_count,
397 instance_count,
398 first_index,
399 base_vertex,
400 first_instance,
401 } => {
402 let scope = PassErrorScope::Draw {
403 kind: DrawKind::Draw,
404 family: DrawCommandFamily::DrawIndexed,
405 };
406 draw_indexed(
407 &mut state,
408 &base.dynamic_offsets,
409 index_count,
410 instance_count,
411 first_index,
412 base_vertex,
413 first_instance,
414 )
415 .map_pass_err(scope)?;
416 }
417 RenderCommand::DrawMeshTasks {
418 group_count_x,
419 group_count_y,
420 group_count_z,
421 } => {
422 let scope = PassErrorScope::Draw {
423 kind: DrawKind::Draw,
424 family: DrawCommandFamily::DrawMeshTasks,
425 };
426 draw_mesh_tasks(
427 &mut state,
428 &base.dynamic_offsets,
429 group_count_x,
430 group_count_y,
431 group_count_z,
432 )
433 .map_pass_err(scope)?;
434 }
435 RenderCommand::DrawIndirect {
436 buffer_id,
437 offset,
438 count: 1,
439 family,
440 } => {
441 let scope = PassErrorScope::Draw {
442 kind: DrawKind::DrawIndirect,
443 family,
444 };
445 multi_draw_indirect(
446 &mut state,
447 &base.dynamic_offsets,
448 &buffer_guard,
449 buffer_id,
450 offset,
451 family,
452 )
453 .map_pass_err(scope)?;
454 }
455 RenderCommand::DrawIndirect { .. }
456 | RenderCommand::MultiDrawIndirectCount { .. }
457 | RenderCommand::PushDebugGroup { color: _, len: _ }
458 | RenderCommand::InsertDebugMarker { color: _, len: _ }
459 | RenderCommand::PopDebugGroup => {
460 unimplemented!("not supported by a render bundle")
461 }
462 RenderCommand::WriteTimestamp { .. }
464 | RenderCommand::BeginOcclusionQuery { .. }
465 | RenderCommand::EndOcclusionQuery
466 | RenderCommand::BeginPipelineStatisticsQuery { .. }
467 | RenderCommand::EndPipelineStatisticsQuery => {
468 unimplemented!("not supported by a render bundle")
469 }
470 RenderCommand::ExecuteBundle(_)
471 | RenderCommand::SetBlendConstant(_)
472 | RenderCommand::SetStencilReference(_)
473 | RenderCommand::SetViewport { .. }
474 | RenderCommand::SetScissor(_) => unreachable!("not supported by a render bundle"),
475 }
476 }
477
478 let State {
479 trackers,
480 flat_dynamic_offsets,
481 device,
482 commands,
483 buffer_memory_init_actions,
484 texture_memory_init_actions,
485 ..
486 } = state;
487
488 let tracker_indices = device.tracker_indices.bundles.clone();
489 let discard_hal_labels = device
490 .instance_flags
491 .contains(wgt::InstanceFlags::DISCARD_HAL_LABELS);
492
493 let render_bundle = RenderBundle {
494 base: BasePass {
495 label: desc.label.as_deref().map(str::to_owned),
496 error: None,
497 commands,
498 dynamic_offsets: flat_dynamic_offsets,
499 string_data: self.base.string_data,
500 push_constant_data: self.base.push_constant_data,
501 },
502 is_depth_read_only: self.is_depth_read_only,
503 is_stencil_read_only: self.is_stencil_read_only,
504 device: device.clone(),
505 used: trackers,
506 buffer_memory_init_actions,
507 texture_memory_init_actions,
508 context: self.context,
509 label: desc.label.to_string(),
510 tracking_data: TrackingData::new(tracker_indices),
511 discard_hal_labels,
512 };
513
514 let render_bundle = Arc::new(render_bundle);
515
516 Ok(render_bundle)
517 }
518
519 pub fn set_index_buffer(
520 &mut self,
521 buffer_id: id::BufferId,
522 index_format: wgt::IndexFormat,
523 offset: wgt::BufferAddress,
524 size: Option<wgt::BufferSize>,
525 ) {
526 self.base.commands.push(RenderCommand::SetIndexBuffer {
527 buffer_id,
528 index_format,
529 offset,
530 size,
531 });
532 }
533}
534
535fn set_bind_group(
536 state: &mut State,
537 bind_group_guard: &crate::storage::Storage<Fallible<BindGroup>>,
538 dynamic_offsets: &[u32],
539 index: u32,
540 num_dynamic_offsets: usize,
541 bind_group_id: Option<id::Id<id::markers::BindGroup>>,
542) -> Result<(), RenderBundleErrorInner> {
543 if bind_group_id.is_none() {
544 return Ok(());
546 }
547
548 let bind_group_id = bind_group_id.unwrap();
549
550 let bind_group = bind_group_guard.get(bind_group_id).get()?;
551
552 bind_group.same_device(&state.device)?;
553
554 let max_bind_groups = state.device.limits.max_bind_groups;
555 if index >= max_bind_groups {
556 return Err(
557 RenderCommandError::BindGroupIndexOutOfRange(pass::BindGroupIndexOutOfRange {
558 index,
559 max: max_bind_groups,
560 })
561 .into(),
562 );
563 }
564
565 let offsets_range = state.next_dynamic_offset..state.next_dynamic_offset + num_dynamic_offsets;
567 state.next_dynamic_offset = offsets_range.end;
568 let offsets = &dynamic_offsets[offsets_range.clone()];
569
570 bind_group.validate_dynamic_bindings(index, offsets)?;
571
572 state
573 .buffer_memory_init_actions
574 .extend_from_slice(&bind_group.used_buffer_ranges);
575 state
576 .texture_memory_init_actions
577 .extend_from_slice(&bind_group.used_texture_ranges);
578
579 state.set_bind_group(index, &bind_group, offsets_range);
580 unsafe { state.trackers.merge_bind_group(&bind_group.used)? };
581 state.trackers.bind_groups.insert_single(bind_group);
582 Ok(())
585}
586
587fn set_pipeline(
588 state: &mut State,
589 pipeline_guard: &crate::storage::Storage<Fallible<RenderPipeline>>,
590 context: &RenderPassContext,
591 is_depth_read_only: bool,
592 is_stencil_read_only: bool,
593 pipeline_id: id::Id<id::markers::RenderPipeline>,
594) -> Result<(), RenderBundleErrorInner> {
595 let pipeline = pipeline_guard.get(pipeline_id).get()?;
596
597 pipeline.same_device(&state.device)?;
598
599 context
600 .check_compatible(&pipeline.pass_context, pipeline.as_ref())
601 .map_err(RenderCommandError::IncompatiblePipelineTargets)?;
602
603 if pipeline.flags.contains(PipelineFlags::WRITES_DEPTH) && is_depth_read_only {
604 return Err(RenderCommandError::IncompatibleDepthAccess(pipeline.error_ident()).into());
605 }
606 if pipeline.flags.contains(PipelineFlags::WRITES_STENCIL) && is_stencil_read_only {
607 return Err(RenderCommandError::IncompatibleStencilAccess(pipeline.error_ident()).into());
608 }
609
610 let pipeline_state = PipelineState::new(&pipeline);
611
612 state
613 .commands
614 .push(ArcRenderCommand::SetPipeline(pipeline.clone()));
615
616 if let Some(iter) = pipeline_state.zero_push_constants() {
618 state.commands.extend(iter)
619 }
620
621 state.invalidate_bind_groups(&pipeline_state, &pipeline.layout);
622 state.pipeline = Some(pipeline_state);
623
624 state.trackers.render_pipelines.insert_single(pipeline);
625 Ok(())
626}
627
628fn set_index_buffer(
630 state: &mut State,
631 buffer_guard: &crate::storage::Storage<Fallible<Buffer>>,
632 buffer_id: id::Id<id::markers::Buffer>,
633 index_format: wgt::IndexFormat,
634 offset: u64,
635 size: Option<NonZeroU64>,
636) -> Result<(), RenderBundleErrorInner> {
637 let buffer = buffer_guard.get(buffer_id).get()?;
638
639 state
640 .trackers
641 .buffers
642 .merge_single(&buffer, wgt::BufferUses::INDEX)?;
643
644 buffer.same_device(&state.device)?;
645 buffer.check_usage(wgt::BufferUsages::INDEX)?;
646
647 if offset % u64::try_from(index_format.byte_size()).unwrap() != 0 {
648 return Err(RenderCommandError::UnalignedIndexBuffer {
649 offset,
650 alignment: index_format.byte_size(),
651 }
652 .into());
653 }
654 let end = offset + buffer.resolve_binding_size(offset, size)?;
655
656 state
657 .buffer_memory_init_actions
658 .extend(buffer.initialization_status.read().create_action(
659 &buffer,
660 offset..end.get(),
661 MemoryInitKind::NeedsInitializedMemory,
662 ));
663 state.set_index_buffer(buffer, index_format, offset..end.get());
664 Ok(())
665}
666
667fn set_vertex_buffer(
669 state: &mut State,
670 buffer_guard: &crate::storage::Storage<Fallible<Buffer>>,
671 slot: u32,
672 buffer_id: id::Id<id::markers::Buffer>,
673 offset: u64,
674 size: Option<NonZeroU64>,
675) -> Result<(), RenderBundleErrorInner> {
676 let max_vertex_buffers = state.device.limits.max_vertex_buffers;
677 if slot >= max_vertex_buffers {
678 return Err(RenderCommandError::VertexBufferIndexOutOfRange {
679 index: slot,
680 max: max_vertex_buffers,
681 }
682 .into());
683 }
684
685 let buffer = buffer_guard.get(buffer_id).get()?;
686
687 state
688 .trackers
689 .buffers
690 .merge_single(&buffer, wgt::BufferUses::VERTEX)?;
691
692 buffer.same_device(&state.device)?;
693 buffer.check_usage(wgt::BufferUsages::VERTEX)?;
694
695 if offset % wgt::VERTEX_ALIGNMENT != 0 {
696 return Err(RenderCommandError::UnalignedVertexBuffer { slot, offset }.into());
697 }
698 let end = offset + buffer.resolve_binding_size(offset, size)?;
699
700 state
701 .buffer_memory_init_actions
702 .extend(buffer.initialization_status.read().create_action(
703 &buffer,
704 offset..end.get(),
705 MemoryInitKind::NeedsInitializedMemory,
706 ));
707 state.vertex[slot as usize] = Some(VertexState::new(buffer, offset..end.get()));
708 Ok(())
709}
710
711fn set_push_constant(
712 state: &mut State,
713 stages: wgt::ShaderStages,
714 offset: u32,
715 size_bytes: u32,
716 values_offset: Option<u32>,
717) -> Result<(), RenderBundleErrorInner> {
718 let end_offset = offset + size_bytes;
719
720 let pipeline_state = state.pipeline()?;
721
722 pipeline_state
723 .pipeline
724 .layout
725 .validate_push_constant_ranges(stages, offset, end_offset)?;
726
727 state.commands.push(ArcRenderCommand::SetPushConstant {
728 stages,
729 offset,
730 size_bytes,
731 values_offset,
732 });
733 Ok(())
734}
735
736fn draw(
737 state: &mut State,
738 dynamic_offsets: &[u32],
739 vertex_count: u32,
740 instance_count: u32,
741 first_vertex: u32,
742 first_instance: u32,
743) -> Result<(), RenderBundleErrorInner> {
744 let pipeline = state.pipeline()?;
745 let used_bind_groups = pipeline.used_bind_groups;
746
747 let vertex_limits = super::VertexLimits::new(state.vertex_buffer_sizes(), &pipeline.steps);
748 vertex_limits.validate_vertex_limit(first_vertex, vertex_count)?;
749 vertex_limits.validate_instance_limit(first_instance, instance_count)?;
750
751 if instance_count > 0 && vertex_count > 0 {
752 state.flush_vertices();
753 state.flush_binds(used_bind_groups, dynamic_offsets);
754 state.commands.push(ArcRenderCommand::Draw {
755 vertex_count,
756 instance_count,
757 first_vertex,
758 first_instance,
759 });
760 }
761 Ok(())
762}
763
764fn draw_indexed(
765 state: &mut State,
766 dynamic_offsets: &[u32],
767 index_count: u32,
768 instance_count: u32,
769 first_index: u32,
770 base_vertex: i32,
771 first_instance: u32,
772) -> Result<(), RenderBundleErrorInner> {
773 let pipeline = state.pipeline()?;
774 let used_bind_groups = pipeline.used_bind_groups;
775 let index = match state.index {
776 Some(ref index) => index,
777 None => return Err(DrawError::MissingIndexBuffer.into()),
778 };
779
780 let vertex_limits = super::VertexLimits::new(state.vertex_buffer_sizes(), &pipeline.steps);
781
782 let last_index = first_index as u64 + index_count as u64;
783 let index_limit = index.limit();
784 if last_index > index_limit {
785 return Err(DrawError::IndexBeyondLimit {
786 last_index,
787 index_limit,
788 }
789 .into());
790 }
791 vertex_limits.validate_instance_limit(first_instance, instance_count)?;
792
793 if instance_count > 0 && index_count > 0 {
794 state.flush_index();
795 state.flush_vertices();
796 state.flush_binds(used_bind_groups, dynamic_offsets);
797 state.commands.push(ArcRenderCommand::DrawIndexed {
798 index_count,
799 instance_count,
800 first_index,
801 base_vertex,
802 first_instance,
803 });
804 }
805 Ok(())
806}
807
808fn draw_mesh_tasks(
809 state: &mut State,
810 dynamic_offsets: &[u32],
811 group_count_x: u32,
812 group_count_y: u32,
813 group_count_z: u32,
814) -> Result<(), RenderBundleErrorInner> {
815 let pipeline = state.pipeline()?;
816 let used_bind_groups = pipeline.used_bind_groups;
817
818 let groups_size_limit = state.device.limits.max_task_workgroups_per_dimension;
819 let max_groups = state.device.limits.max_task_workgroup_total_count;
820 if group_count_x > groups_size_limit
821 || group_count_y > groups_size_limit
822 || group_count_z > groups_size_limit
823 || group_count_x * group_count_y * group_count_z > max_groups
824 {
825 return Err(RenderBundleErrorInner::Draw(DrawError::InvalidGroupSize {
826 current: [group_count_x, group_count_y, group_count_z],
827 limit: groups_size_limit,
828 max_total: max_groups,
829 }));
830 }
831
832 if group_count_x > 0 && group_count_y > 0 && group_count_z > 0 {
833 state.flush_binds(used_bind_groups, dynamic_offsets);
834 state.commands.push(ArcRenderCommand::DrawMeshTasks {
835 group_count_x,
836 group_count_y,
837 group_count_z,
838 });
839 }
840 Ok(())
841}
842
843fn multi_draw_indirect(
844 state: &mut State,
845 dynamic_offsets: &[u32],
846 buffer_guard: &crate::storage::Storage<Fallible<Buffer>>,
847 buffer_id: id::Id<id::markers::Buffer>,
848 offset: u64,
849 family: DrawCommandFamily,
850) -> Result<(), RenderBundleErrorInner> {
851 state
852 .device
853 .require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)?;
854
855 let pipeline = state.pipeline()?;
856 let used_bind_groups = pipeline.used_bind_groups;
857
858 let buffer = buffer_guard.get(buffer_id).get()?;
859
860 buffer.same_device(&state.device)?;
861 buffer.check_usage(wgt::BufferUsages::INDIRECT)?;
862
863 let vertex_limits = super::VertexLimits::new(state.vertex_buffer_sizes(), &pipeline.steps);
864
865 let stride = super::get_stride_of_indirect_args(family);
866 state
867 .buffer_memory_init_actions
868 .extend(buffer.initialization_status.read().create_action(
869 &buffer,
870 offset..(offset + stride),
871 MemoryInitKind::NeedsInitializedMemory,
872 ));
873
874 let vertex_or_index_limit = if family == DrawCommandFamily::DrawIndexed {
875 let index = match state.index {
876 Some(ref mut index) => index,
877 None => return Err(DrawError::MissingIndexBuffer.into()),
878 };
879 state.commands.extend(index.flush());
880 index.limit()
881 } else {
882 vertex_limits.vertex_limit
883 };
884 let instance_limit = vertex_limits.instance_limit;
885
886 let buffer_uses = if state.device.indirect_validation.is_some() {
887 wgt::BufferUses::STORAGE_READ_ONLY
888 } else {
889 wgt::BufferUses::INDIRECT
890 };
891
892 state.trackers.buffers.merge_single(&buffer, buffer_uses)?;
893
894 state.flush_vertices();
895 state.flush_binds(used_bind_groups, dynamic_offsets);
896 state.commands.push(ArcRenderCommand::DrawIndirect {
897 buffer,
898 offset,
899 count: 1,
900 family,
901
902 vertex_or_index_limit,
903 instance_limit,
904 });
905 Ok(())
906}
907
908#[derive(Clone, Debug, Error)]
910#[non_exhaustive]
911pub enum CreateRenderBundleError {
912 #[error(transparent)]
913 ColorAttachment(#[from] ColorAttachmentError),
914 #[error("Invalid number of samples {0}")]
915 InvalidSampleCount(u32),
916}
917
918impl WebGpuError for CreateRenderBundleError {
919 fn webgpu_error_type(&self) -> ErrorType {
920 match self {
921 Self::ColorAttachment(e) => e.webgpu_error_type(),
922 Self::InvalidSampleCount(_) => ErrorType::Validation,
923 }
924 }
925}
926
927#[derive(Clone, Debug, Error)]
929#[non_exhaustive]
930pub enum ExecutionError {
931 #[error(transparent)]
932 Device(#[from] DeviceError),
933 #[error(transparent)]
934 DestroyedResource(#[from] DestroyedResourceError),
935 #[error("Using {0} in a render bundle is not implemented")]
936 Unimplemented(&'static str),
937}
938
939pub type RenderBundleDescriptor<'a> = wgt::RenderBundleDescriptor<Label<'a>>;
940
941#[derive(Debug)]
945pub struct RenderBundle {
946 base: BasePass<ArcRenderCommand, Infallible>,
949 pub(super) is_depth_read_only: bool,
950 pub(super) is_stencil_read_only: bool,
951 pub(crate) device: Arc<Device>,
952 pub(crate) used: RenderBundleScope,
953 pub(super) buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
954 pub(super) texture_memory_init_actions: Vec<TextureInitTrackerAction>,
955 pub(super) context: RenderPassContext,
956 label: String,
958 pub(crate) tracking_data: TrackingData,
959 discard_hal_labels: bool,
960}
961
962impl Drop for RenderBundle {
963 fn drop(&mut self) {
964 resource_log!("Drop {}", self.error_ident());
965 }
966}
967
968#[cfg(send_sync)]
969unsafe impl Send for RenderBundle {}
970#[cfg(send_sync)]
971unsafe impl Sync for RenderBundle {}
972
973impl RenderBundle {
974 pub(super) unsafe fn execute(
984 &self,
985 raw: &mut dyn hal::DynCommandEncoder,
986 indirect_draw_validation_resources: &mut crate::indirect_validation::DrawResources,
987 indirect_draw_validation_batcher: &mut crate::indirect_validation::DrawBatcher,
988 snatch_guard: &SnatchGuard,
989 ) -> Result<(), ExecutionError> {
990 let mut offsets = self.base.dynamic_offsets.as_slice();
991 let mut pipeline_layout = None::<Arc<PipelineLayout>>;
992 if !self.discard_hal_labels {
993 if let Some(ref label) = self.base.label {
994 unsafe { raw.begin_debug_marker(label) };
995 }
996 }
997
998 use ArcRenderCommand as Cmd;
999 for command in self.base.commands.iter() {
1000 match command {
1001 Cmd::SetBindGroup {
1002 index,
1003 num_dynamic_offsets,
1004 bind_group,
1005 } => {
1006 let mut bg = None;
1007 if bind_group.is_some() {
1008 let bind_group = bind_group.as_ref().unwrap();
1009 let raw_bg = bind_group.try_raw(snatch_guard)?;
1010 bg = Some(raw_bg);
1011 }
1012 unsafe {
1013 raw.set_bind_group(
1014 pipeline_layout.as_ref().unwrap().raw(),
1015 *index,
1016 bg,
1017 &offsets[..*num_dynamic_offsets],
1018 )
1019 };
1020 offsets = &offsets[*num_dynamic_offsets..];
1021 }
1022 Cmd::SetPipeline(pipeline) => {
1023 unsafe { raw.set_render_pipeline(pipeline.raw()) };
1024
1025 pipeline_layout = Some(pipeline.layout.clone());
1026 }
1027 Cmd::SetIndexBuffer {
1028 buffer,
1029 index_format,
1030 offset,
1031 size,
1032 } => {
1033 let buffer = buffer.try_raw(snatch_guard)?;
1034 let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size);
1037 unsafe { raw.set_index_buffer(bb, *index_format) };
1038 }
1039 Cmd::SetVertexBuffer {
1040 slot,
1041 buffer,
1042 offset,
1043 size,
1044 } => {
1045 let buffer = buffer.try_raw(snatch_guard)?;
1046 let bb = hal::BufferBinding::new_unchecked(buffer, *offset, *size);
1049 unsafe { raw.set_vertex_buffer(*slot, bb) };
1050 }
1051 Cmd::SetPushConstant {
1052 stages,
1053 offset,
1054 size_bytes,
1055 values_offset,
1056 } => {
1057 let pipeline_layout = pipeline_layout.as_ref().unwrap();
1058
1059 if let Some(values_offset) = *values_offset {
1060 let values_end_offset =
1061 (values_offset + size_bytes / wgt::PUSH_CONSTANT_ALIGNMENT) as usize;
1062 let data_slice = &self.base.push_constant_data
1063 [(values_offset as usize)..values_end_offset];
1064
1065 unsafe {
1066 raw.set_push_constants(
1067 pipeline_layout.raw(),
1068 *stages,
1069 *offset,
1070 data_slice,
1071 )
1072 }
1073 } else {
1074 super::push_constant_clear(
1075 *offset,
1076 *size_bytes,
1077 |clear_offset, clear_data| {
1078 unsafe {
1079 raw.set_push_constants(
1080 pipeline_layout.raw(),
1081 *stages,
1082 clear_offset,
1083 clear_data,
1084 )
1085 };
1086 },
1087 );
1088 }
1089 }
1090 Cmd::Draw {
1091 vertex_count,
1092 instance_count,
1093 first_vertex,
1094 first_instance,
1095 } => {
1096 unsafe {
1097 raw.draw(
1098 *first_vertex,
1099 *vertex_count,
1100 *first_instance,
1101 *instance_count,
1102 )
1103 };
1104 }
1105 Cmd::DrawIndexed {
1106 index_count,
1107 instance_count,
1108 first_index,
1109 base_vertex,
1110 first_instance,
1111 } => {
1112 unsafe {
1113 raw.draw_indexed(
1114 *first_index,
1115 *index_count,
1116 *base_vertex,
1117 *first_instance,
1118 *instance_count,
1119 )
1120 };
1121 }
1122 Cmd::DrawMeshTasks {
1123 group_count_x,
1124 group_count_y,
1125 group_count_z,
1126 } => unsafe {
1127 raw.draw_mesh_tasks(*group_count_x, *group_count_y, *group_count_z);
1128 },
1129 Cmd::DrawIndirect {
1130 buffer,
1131 offset,
1132 count: 1,
1133 family,
1134
1135 vertex_or_index_limit,
1136 instance_limit,
1137 } => {
1138 let (buffer, offset) = if self.device.indirect_validation.is_some() {
1139 let (dst_resource_index, offset) = indirect_draw_validation_batcher.add(
1140 indirect_draw_validation_resources,
1141 &self.device,
1142 buffer,
1143 *offset,
1144 *family,
1145 *vertex_or_index_limit,
1146 *instance_limit,
1147 )?;
1148
1149 let dst_buffer =
1150 indirect_draw_validation_resources.get_dst_buffer(dst_resource_index);
1151 (dst_buffer, offset)
1152 } else {
1153 (buffer.try_raw(snatch_guard)?, *offset)
1154 };
1155 match family {
1156 DrawCommandFamily::Draw => unsafe { raw.draw_indirect(buffer, offset, 1) },
1157 DrawCommandFamily::DrawIndexed => unsafe {
1158 raw.draw_indexed_indirect(buffer, offset, 1)
1159 },
1160 DrawCommandFamily::DrawMeshTasks => unsafe {
1161 raw.draw_mesh_tasks_indirect(buffer, offset, 1);
1162 },
1163 }
1164 }
1165 Cmd::DrawIndirect { .. } | Cmd::MultiDrawIndirectCount { .. } => {
1166 return Err(ExecutionError::Unimplemented("multi-draw-indirect"))
1167 }
1168 Cmd::PushDebugGroup { .. } | Cmd::InsertDebugMarker { .. } | Cmd::PopDebugGroup => {
1169 return Err(ExecutionError::Unimplemented("debug-markers"))
1170 }
1171 Cmd::WriteTimestamp { .. }
1172 | Cmd::BeginOcclusionQuery { .. }
1173 | Cmd::EndOcclusionQuery
1174 | Cmd::BeginPipelineStatisticsQuery { .. }
1175 | Cmd::EndPipelineStatisticsQuery => {
1176 return Err(ExecutionError::Unimplemented("queries"))
1177 }
1178 Cmd::ExecuteBundle(_)
1179 | Cmd::SetBlendConstant(_)
1180 | Cmd::SetStencilReference(_)
1181 | Cmd::SetViewport { .. }
1182 | Cmd::SetScissor(_) => unreachable!(),
1183 }
1184 }
1185
1186 if !self.discard_hal_labels {
1187 if let Some(_) = self.base.label {
1188 unsafe { raw.end_debug_marker() };
1189 }
1190 }
1191
1192 Ok(())
1193 }
1194}
1195
1196crate::impl_resource_type!(RenderBundle);
1197crate::impl_labeled!(RenderBundle);
1198crate::impl_parent_device!(RenderBundle);
1199crate::impl_storage_item!(RenderBundle);
1200crate::impl_trackable!(RenderBundle);
1201
1202#[derive(Debug)]
1211struct IndexState {
1212 buffer: Arc<Buffer>,
1213 format: wgt::IndexFormat,
1214 range: Range<wgt::BufferAddress>,
1215 is_dirty: bool,
1216}
1217
1218impl IndexState {
1219 fn limit(&self) -> u64 {
1223 let bytes_per_index = self.format.byte_size() as u64;
1224
1225 (self.range.end - self.range.start) / bytes_per_index
1226 }
1227
1228 fn flush(&mut self) -> Option<ArcRenderCommand> {
1231 let binding_size = self
1233 .range
1234 .end
1235 .checked_sub(self.range.start)
1236 .filter(|_| self.range.end <= self.buffer.size)
1237 .expect("index range must be contained in buffer");
1238
1239 if self.is_dirty {
1240 self.is_dirty = false;
1241 Some(ArcRenderCommand::SetIndexBuffer {
1242 buffer: self.buffer.clone(),
1243 index_format: self.format,
1244 offset: self.range.start,
1245 size: NonZeroU64::new(binding_size),
1246 })
1247 } else {
1248 None
1249 }
1250 }
1251}
1252
1253#[derive(Debug)]
1266struct VertexState {
1267 buffer: Arc<Buffer>,
1268 range: Range<wgt::BufferAddress>,
1269 is_dirty: bool,
1270}
1271
1272impl VertexState {
1273 fn new(buffer: Arc<Buffer>, range: Range<wgt::BufferAddress>) -> Self {
1277 Self {
1278 buffer,
1279 range,
1280 is_dirty: true,
1281 }
1282 }
1283
1284 fn flush(&mut self, slot: u32) -> Option<ArcRenderCommand> {
1288 let binding_size = self
1289 .range
1290 .end
1291 .checked_sub(self.range.start)
1292 .filter(|_| self.range.end <= self.buffer.size)
1293 .expect("vertex range must be contained in buffer");
1294
1295 if self.is_dirty {
1296 self.is_dirty = false;
1297 Some(ArcRenderCommand::SetVertexBuffer {
1298 slot,
1299 buffer: self.buffer.clone(),
1300 offset: self.range.start,
1301 size: NonZeroU64::new(binding_size),
1302 })
1303 } else {
1304 None
1305 }
1306 }
1307}
1308
1309#[derive(Debug)]
1311struct BindState {
1312 bind_group: Arc<BindGroup>,
1314
1315 dynamic_offsets: Range<usize>,
1318
1319 is_dirty: bool,
1322}
1323
1324struct PipelineState {
1326 pipeline: Arc<RenderPipeline>,
1328
1329 steps: Vec<VertexStep>,
1332
1333 push_constant_ranges: ArrayVec<wgt::PushConstantRange, { SHADER_STAGE_COUNT }>,
1336
1337 used_bind_groups: usize,
1339}
1340
1341impl PipelineState {
1342 fn new(pipeline: &Arc<RenderPipeline>) -> Self {
1343 Self {
1344 pipeline: pipeline.clone(),
1345 steps: pipeline.vertex_steps.to_vec(),
1346 push_constant_ranges: pipeline
1347 .layout
1348 .push_constant_ranges
1349 .iter()
1350 .cloned()
1351 .collect(),
1352 used_bind_groups: pipeline.layout.bind_group_layouts.len(),
1353 }
1354 }
1355
1356 fn zero_push_constants(&self) -> Option<impl Iterator<Item = ArcRenderCommand>> {
1359 if !self.push_constant_ranges.is_empty() {
1360 let nonoverlapping_ranges =
1361 super::bind::compute_nonoverlapping_ranges(&self.push_constant_ranges);
1362
1363 Some(
1364 nonoverlapping_ranges
1365 .into_iter()
1366 .map(|range| ArcRenderCommand::SetPushConstant {
1367 stages: range.stages,
1368 offset: range.range.start,
1369 size_bytes: range.range.end - range.range.start,
1370 values_offset: None, }),
1372 )
1373 } else {
1374 None
1375 }
1376 }
1377}
1378
1379struct State {
1390 trackers: RenderBundleScope,
1392
1393 pipeline: Option<PipelineState>,
1395
1396 bind: ArrayVec<Option<BindState>, { hal::MAX_BIND_GROUPS }>,
1398
1399 vertex: [Option<VertexState>; hal::MAX_VERTEX_BUFFERS],
1401
1402 index: Option<IndexState>,
1405
1406 flat_dynamic_offsets: Vec<wgt::DynamicOffset>,
1413
1414 device: Arc<Device>,
1415 commands: Vec<ArcRenderCommand>,
1416 buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
1417 texture_memory_init_actions: Vec<TextureInitTrackerAction>,
1418 next_dynamic_offset: usize,
1419}
1420
1421impl State {
1422 fn pipeline(&self) -> Result<&PipelineState, RenderBundleErrorInner> {
1424 self.pipeline
1425 .as_ref()
1426 .ok_or(DrawError::MissingPipeline(pass::MissingPipeline).into())
1427 }
1428
1429 fn invalidate_bind_group_from(&mut self, index: usize) {
1431 for contents in self.bind[index..].iter_mut().flatten() {
1432 contents.is_dirty = true;
1433 }
1434 }
1435
1436 fn set_bind_group(
1437 &mut self,
1438 slot: u32,
1439 bind_group: &Arc<BindGroup>,
1440 dynamic_offsets: Range<usize>,
1441 ) {
1442 if dynamic_offsets.is_empty() {
1446 if let Some(ref contents) = self.bind[slot as usize] {
1447 if contents.bind_group.is_equal(bind_group) {
1448 return;
1449 }
1450 }
1451 }
1452
1453 self.bind[slot as usize] = Some(BindState {
1455 bind_group: bind_group.clone(),
1456 dynamic_offsets,
1457 is_dirty: true,
1458 });
1459
1460 self.invalidate_bind_group_from(slot as usize + 1);
1463 }
1464
1465 fn invalidate_bind_groups(&mut self, new: &PipelineState, layout: &PipelineLayout) {
1479 match self.pipeline {
1480 None => {
1481 self.invalidate_bind_group_from(0);
1483 }
1484 Some(ref old) => {
1485 if old.pipeline.is_equal(&new.pipeline) {
1486 return;
1489 }
1490
1491 if old.push_constant_ranges != new.push_constant_ranges {
1493 self.invalidate_bind_group_from(0);
1494 } else {
1495 let first_changed = self.bind.iter().zip(&layout.bind_group_layouts).position(
1496 |(entry, layout)| match *entry {
1497 Some(ref contents) => !contents.bind_group.layout.is_equal(layout),
1498 None => false,
1499 },
1500 );
1501 if let Some(slot) = first_changed {
1502 self.invalidate_bind_group_from(slot);
1503 }
1504 }
1505 }
1506 }
1507 }
1508
1509 fn set_index_buffer(
1511 &mut self,
1512 buffer: Arc<Buffer>,
1513 format: wgt::IndexFormat,
1514 range: Range<wgt::BufferAddress>,
1515 ) {
1516 match self.index {
1517 Some(ref current)
1518 if current.buffer.is_equal(&buffer)
1519 && current.format == format
1520 && current.range == range =>
1521 {
1522 return
1523 }
1524 _ => (),
1525 }
1526
1527 self.index = Some(IndexState {
1528 buffer,
1529 format,
1530 range,
1531 is_dirty: true,
1532 });
1533 }
1534
1535 fn flush_index(&mut self) {
1538 let commands = self.index.as_mut().and_then(|index| index.flush());
1539 self.commands.extend(commands);
1540 }
1541
1542 fn flush_vertices(&mut self) {
1543 let commands = self
1544 .vertex
1545 .iter_mut()
1546 .enumerate()
1547 .flat_map(|(i, vs)| vs.as_mut().and_then(|vs| vs.flush(i as u32)));
1548 self.commands.extend(commands);
1549 }
1550
1551 fn flush_binds(&mut self, used_bind_groups: usize, dynamic_offsets: &[wgt::DynamicOffset]) {
1553 for contents in self.bind[..used_bind_groups].iter().flatten() {
1555 if contents.is_dirty {
1556 self.flat_dynamic_offsets
1557 .extend_from_slice(&dynamic_offsets[contents.dynamic_offsets.clone()]);
1558 }
1559 }
1560
1561 let commands = self.bind[..used_bind_groups]
1564 .iter_mut()
1565 .enumerate()
1566 .flat_map(|(i, entry)| {
1567 if let Some(ref mut contents) = *entry {
1568 if contents.is_dirty {
1569 contents.is_dirty = false;
1570 let offsets = &contents.dynamic_offsets;
1571 return Some(ArcRenderCommand::SetBindGroup {
1572 index: i.try_into().unwrap(),
1573 bind_group: Some(contents.bind_group.clone()),
1574 num_dynamic_offsets: offsets.end - offsets.start,
1575 });
1576 }
1577 }
1578 None
1579 });
1580
1581 self.commands.extend(commands);
1582 }
1583
1584 fn vertex_buffer_sizes(&self) -> impl Iterator<Item = Option<wgt::BufferAddress>> + '_ {
1585 self.vertex
1586 .iter()
1587 .map(|vbs| vbs.as_ref().map(|vbs| vbs.range.end - vbs.range.start))
1588 }
1589}
1590
1591#[derive(Clone, Debug, Error)]
1593pub(super) enum RenderBundleErrorInner {
1594 #[error(transparent)]
1595 Device(#[from] DeviceError),
1596 #[error(transparent)]
1597 RenderCommand(RenderCommandError),
1598 #[error(transparent)]
1599 Draw(#[from] DrawError),
1600 #[error(transparent)]
1601 MissingDownlevelFlags(#[from] MissingDownlevelFlags),
1602 #[error(transparent)]
1603 Bind(#[from] BindError),
1604 #[error(transparent)]
1605 InvalidResource(#[from] InvalidResourceError),
1606}
1607
1608impl<T> From<T> for RenderBundleErrorInner
1609where
1610 T: Into<RenderCommandError>,
1611{
1612 fn from(t: T) -> Self {
1613 Self::RenderCommand(t.into())
1614 }
1615}
1616
1617#[derive(Clone, Debug, Error)]
1619#[error("{scope}")]
1620pub struct RenderBundleError {
1621 pub scope: PassErrorScope,
1622 #[source]
1623 inner: RenderBundleErrorInner,
1624}
1625
1626impl WebGpuError for RenderBundleError {
1627 fn webgpu_error_type(&self) -> ErrorType {
1628 let Self { scope: _, inner } = self;
1629 let e: &dyn WebGpuError = match inner {
1630 RenderBundleErrorInner::Device(e) => e,
1631 RenderBundleErrorInner::RenderCommand(e) => e,
1632 RenderBundleErrorInner::Draw(e) => e,
1633 RenderBundleErrorInner::MissingDownlevelFlags(e) => e,
1634 RenderBundleErrorInner::Bind(e) => e,
1635 RenderBundleErrorInner::InvalidResource(e) => e,
1636 };
1637 e.webgpu_error_type()
1638 }
1639}
1640
1641impl RenderBundleError {
1642 pub fn from_device_error(e: DeviceError) -> Self {
1643 Self {
1644 scope: PassErrorScope::Bundle,
1645 inner: e.into(),
1646 }
1647 }
1648}
1649
1650impl<E> MapPassErr<RenderBundleError> for E
1651where
1652 E: Into<RenderBundleErrorInner>,
1653{
1654 fn map_pass_err(self, scope: PassErrorScope) -> RenderBundleError {
1655 RenderBundleError {
1656 scope,
1657 inner: self.into(),
1658 }
1659 }
1660}
1661
1662pub mod bundle_ffi {
1663 use super::{RenderBundleEncoder, RenderCommand};
1664 use crate::{command::DrawCommandFamily, id, RawString};
1665 use core::{convert::TryInto, slice};
1666 use wgt::{BufferAddress, BufferSize, DynamicOffset, IndexFormat};
1667
1668 pub unsafe fn wgpu_render_bundle_set_bind_group(
1673 bundle: &mut RenderBundleEncoder,
1674 index: u32,
1675 bind_group_id: Option<id::BindGroupId>,
1676 offsets: *const DynamicOffset,
1677 offset_length: usize,
1678 ) {
1679 let offsets = unsafe { slice::from_raw_parts(offsets, offset_length) };
1680
1681 let redundant = bundle.current_bind_groups.set_and_check_redundant(
1682 bind_group_id,
1683 index,
1684 &mut bundle.base.dynamic_offsets,
1685 offsets,
1686 );
1687
1688 if redundant {
1689 return;
1690 }
1691
1692 bundle.base.commands.push(RenderCommand::SetBindGroup {
1693 index,
1694 num_dynamic_offsets: offset_length,
1695 bind_group_id,
1696 });
1697 }
1698
1699 pub fn wgpu_render_bundle_set_pipeline(
1700 bundle: &mut RenderBundleEncoder,
1701 pipeline_id: id::RenderPipelineId,
1702 ) {
1703 if bundle.current_pipeline.set_and_check_redundant(pipeline_id) {
1704 return;
1705 }
1706
1707 bundle
1708 .base
1709 .commands
1710 .push(RenderCommand::SetPipeline(pipeline_id));
1711 }
1712
1713 pub fn wgpu_render_bundle_set_vertex_buffer(
1714 bundle: &mut RenderBundleEncoder,
1715 slot: u32,
1716 buffer_id: id::BufferId,
1717 offset: BufferAddress,
1718 size: Option<BufferSize>,
1719 ) {
1720 bundle.base.commands.push(RenderCommand::SetVertexBuffer {
1721 slot,
1722 buffer_id,
1723 offset,
1724 size,
1725 });
1726 }
1727
1728 pub fn wgpu_render_bundle_set_index_buffer(
1729 encoder: &mut RenderBundleEncoder,
1730 buffer: id::BufferId,
1731 index_format: IndexFormat,
1732 offset: BufferAddress,
1733 size: Option<BufferSize>,
1734 ) {
1735 encoder.set_index_buffer(buffer, index_format, offset, size);
1736 }
1737
1738 pub unsafe fn wgpu_render_bundle_set_push_constants(
1743 pass: &mut RenderBundleEncoder,
1744 stages: wgt::ShaderStages,
1745 offset: u32,
1746 size_bytes: u32,
1747 data: *const u8,
1748 ) {
1749 assert_eq!(
1750 offset & (wgt::PUSH_CONSTANT_ALIGNMENT - 1),
1751 0,
1752 "Push constant offset must be aligned to 4 bytes."
1753 );
1754 assert_eq!(
1755 size_bytes & (wgt::PUSH_CONSTANT_ALIGNMENT - 1),
1756 0,
1757 "Push constant size must be aligned to 4 bytes."
1758 );
1759 let data_slice = unsafe { slice::from_raw_parts(data, size_bytes as usize) };
1760 let value_offset = pass.base.push_constant_data.len().try_into().expect(
1761 "Ran out of push constant space. Don't set 4gb of push constants per RenderBundle.",
1762 );
1763
1764 pass.base.push_constant_data.extend(
1765 data_slice
1766 .chunks_exact(wgt::PUSH_CONSTANT_ALIGNMENT as usize)
1767 .map(|arr| u32::from_ne_bytes([arr[0], arr[1], arr[2], arr[3]])),
1768 );
1769
1770 pass.base.commands.push(RenderCommand::SetPushConstant {
1771 stages,
1772 offset,
1773 size_bytes,
1774 values_offset: Some(value_offset),
1775 });
1776 }
1777
1778 pub fn wgpu_render_bundle_draw(
1779 bundle: &mut RenderBundleEncoder,
1780 vertex_count: u32,
1781 instance_count: u32,
1782 first_vertex: u32,
1783 first_instance: u32,
1784 ) {
1785 bundle.base.commands.push(RenderCommand::Draw {
1786 vertex_count,
1787 instance_count,
1788 first_vertex,
1789 first_instance,
1790 });
1791 }
1792
1793 pub fn wgpu_render_bundle_draw_indexed(
1794 bundle: &mut RenderBundleEncoder,
1795 index_count: u32,
1796 instance_count: u32,
1797 first_index: u32,
1798 base_vertex: i32,
1799 first_instance: u32,
1800 ) {
1801 bundle.base.commands.push(RenderCommand::DrawIndexed {
1802 index_count,
1803 instance_count,
1804 first_index,
1805 base_vertex,
1806 first_instance,
1807 });
1808 }
1809
1810 pub fn wgpu_render_bundle_draw_indirect(
1811 bundle: &mut RenderBundleEncoder,
1812 buffer_id: id::BufferId,
1813 offset: BufferAddress,
1814 ) {
1815 bundle.base.commands.push(RenderCommand::DrawIndirect {
1816 buffer_id,
1817 offset,
1818 count: 1,
1819 family: DrawCommandFamily::Draw,
1820 });
1821 }
1822
1823 pub fn wgpu_render_bundle_draw_indexed_indirect(
1824 bundle: &mut RenderBundleEncoder,
1825 buffer_id: id::BufferId,
1826 offset: BufferAddress,
1827 ) {
1828 bundle.base.commands.push(RenderCommand::DrawIndirect {
1829 buffer_id,
1830 offset,
1831 count: 1,
1832 family: DrawCommandFamily::DrawIndexed,
1833 });
1834 }
1835
1836 pub unsafe fn wgpu_render_bundle_push_debug_group(
1841 _bundle: &mut RenderBundleEncoder,
1842 _label: RawString,
1843 ) {
1844 }
1846
1847 pub fn wgpu_render_bundle_pop_debug_group(_bundle: &mut RenderBundleEncoder) {
1848 }
1850
1851 pub unsafe fn wgpu_render_bundle_insert_debug_marker(
1856 _bundle: &mut RenderBundleEncoder,
1857 _label: RawString,
1858 ) {
1859 }
1861}