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