1use alloc::sync::Arc;
4use core::{
5 any::TypeId,
6 marker::PhantomData,
7 mem,
8 ops::Range,
9 sync::atomic::{AtomicU32, Ordering},
10};
11
12use bevy_app::{App, Plugin};
13use bevy_derive::{Deref, DerefMut};
14use bevy_ecs::{
15 prelude::Entity,
16 query::{Has, With},
17 resource::Resource,
18 schedule::IntoScheduleConfigs as _,
19 system::{Query, Res, ResMut, StaticSystemParam},
20 world::{FromWorld, World},
21};
22use bevy_encase_derive::ShaderType;
23use bevy_log::{error, info_once};
24use bevy_math::UVec4;
25use bevy_platform::collections::{hash_map::Entry, HashMap, HashSet};
26use bevy_tasks::ComputeTaskPool;
27use bevy_utils::{default, TypeIdHashMap};
28use bytemuck::{Pod, Zeroable};
29use encase::{internal::WriteInto, ShaderSize};
30use nonmax::NonMaxU32;
31use static_assertions::assert_eq_size;
32use wgpu::{BindingResource, BufferUsages, DownlevelFlags, Features};
33
34use crate::{
35 occlusion_culling::OcclusionCulling,
36 render_phase::{
37 BinnedPhaseItem, BinnedRenderPhaseBatch, BinnedRenderPhaseBatchSet,
38 BinnedRenderPhaseBatchSets, CachedRenderPipelinePhaseItem, PhaseItem,
39 PhaseItemBatchSetKey as _, PhaseItemExtraIndex, RenderMultidrawableBatchSet,
40 SortedPhaseItem, SortedRenderPhase, UnbatchableBinnedEntityIndices, ViewBinnedRenderPhases,
41 ViewSortedRenderPhases,
42 },
43 render_resource::{
44 AtomicPod, AtomicRawBufferVec, AtomicSparseBufferVec, Buffer, GpuArrayBufferable,
45 PartialBufferVec, PipelineCache, RawBufferVec, SparseBufferUpdateBindGroups,
46 SparseBufferUpdateJobs, SparseBufferUpdatePipelines, UninitBufferVec,
47 },
48 renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue},
49 sync_world::{MainEntity, MainEntityHashMap},
50 view::{ExtractedView, NoIndirectDrawing, RetainedViewEntity},
51 GpuResourceAppExt, Render, RenderApp, RenderDebugFlags, RenderSystems,
52};
53
54use super::{BatchSetMeta, GetBatchData, GetFullBatchData};
55
56#[derive(Default)]
57pub struct BatchingPlugin {
58 pub debug_flags: RenderDebugFlags,
60}
61
62impl Plugin for BatchingPlugin {
63 fn build(&self, app: &mut App) {
64 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
65 return;
66 };
67
68 render_app
69 .insert_resource(IndirectParametersBuffersSettings {
70 allow_copies_from_indirect_parameter_buffers: self
71 .debug_flags
72 .contains(RenderDebugFlags::ALLOW_COPIES_FROM_INDIRECT_PARAMETERS),
73 })
74 .init_gpu_resource::<IndirectParametersBuffers>()
75 .allow_ambiguous_resource::<IndirectParametersBuffers>()
76 .init_gpu_resource::<SceneUnpackingBuffers>()
77 .init_gpu_resource::<IndirectParametersBuildJobs>()
78 .init_gpu_resource::<BuildIndirectParametersMetadata>()
79 .add_systems(
80 Render,
81 write_indirect_parameters_buffers.in_set(RenderSystems::PrepareResourcesFlush),
82 )
83 .add_systems(
84 Render,
85 (
86 prepare_indirect_parameters_build_jobs,
87 write_indirect_parameters_build_jobs,
88 )
89 .chain()
90 .in_set(RenderSystems::PrepareResourcesFlush),
91 )
92 .add_systems(
93 Render,
94 clear_indirect_parameters_buffers.in_set(RenderSystems::PrepareViews),
95 );
96 }
97
98 fn finish(&self, app: &mut App) {
99 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
100 return;
101 };
102
103 render_app.init_gpu_resource::<GpuPreprocessingSupport>();
104 }
105}
106
107#[derive(Clone, Copy, PartialEq, Resource)]
116pub struct GpuPreprocessingSupport {
117 pub max_supported_mode: GpuPreprocessingMode,
119}
120
121impl GpuPreprocessingSupport {
122 #[inline]
124 pub fn is_available(&self) -> bool {
125 self.max_supported_mode != GpuPreprocessingMode::None
126 }
127
128 pub fn min(&self, mode: GpuPreprocessingMode) -> GpuPreprocessingMode {
131 match (self.max_supported_mode, mode) {
132 (GpuPreprocessingMode::None, _) | (_, GpuPreprocessingMode::None) => {
133 GpuPreprocessingMode::None
134 }
135 (mode, GpuPreprocessingMode::Culling) | (GpuPreprocessingMode::Culling, mode) => mode,
136 (GpuPreprocessingMode::PreprocessingOnly, GpuPreprocessingMode::PreprocessingOnly) => {
137 GpuPreprocessingMode::PreprocessingOnly
138 }
139 }
140 }
141
142 pub fn is_culling_supported(&self) -> bool {
144 self.max_supported_mode == GpuPreprocessingMode::Culling
145 }
146}
147
148#[derive(Clone, Copy, PartialEq)]
150pub enum GpuPreprocessingMode {
151 None,
155
156 PreprocessingOnly,
161
162 Culling,
166}
167
168#[derive(Resource)]
179pub struct BatchedInstanceBuffers<BD, BDI>
180where
181 BD: GpuArrayBufferable + Sync + Send + 'static,
182 BDI: BufferDataInput,
183{
184 pub current_input_buffer: InstanceInputUniformBuffer<BDI>,
188
189 pub previous_input_buffer: PreviousInstanceInputUniformBuffer<BDI::Previous>,
197
198 pub phase_instance_buffers: TypeIdHashMap<UntypedPhaseBatchedInstanceBuffers<BD>>,
203}
204
205impl<BD, BDI> Default for BatchedInstanceBuffers<BD, BDI>
206where
207 BD: GpuArrayBufferable + Sync + Send + 'static,
208 BDI: BufferDataInput,
209{
210 fn default() -> Self {
211 BatchedInstanceBuffers {
212 current_input_buffer: InstanceInputUniformBuffer::new(),
213 previous_input_buffer: PreviousInstanceInputUniformBuffer::new(),
214 phase_instance_buffers: TypeIdHashMap::default(),
215 }
216 }
217}
218
219pub trait BufferDataInput: AtomicPod {
225 type Previous: AtomicPod;
227}
228
229impl BufferDataInput for () {
232 type Previous = ();
233}
234
235#[derive(Resource)]
245pub struct PhaseBatchedInstanceBuffers<PI, BD>
246where
247 PI: PhaseItem,
248 BD: GpuArrayBufferable + Sync + Send + 'static,
249{
250 pub buffers: UntypedPhaseBatchedInstanceBuffers<BD>,
252 phantom: PhantomData<PI>,
253}
254
255impl<PI, BD> Default for PhaseBatchedInstanceBuffers<PI, BD>
256where
257 PI: PhaseItem,
258 BD: GpuArrayBufferable + Sync + Send + 'static,
259{
260 fn default() -> Self {
261 PhaseBatchedInstanceBuffers {
262 buffers: UntypedPhaseBatchedInstanceBuffers::default(),
263 phantom: PhantomData,
264 }
265 }
266}
267
268pub struct UntypedPhaseBatchedInstanceBuffers<BD>
274where
275 BD: GpuArrayBufferable + Sync + Send + 'static,
276{
277 pub data_buffer: UninitBufferVec<BD>,
282
283 pub work_item_buffers: HashMap<RetainedViewEntity, PreprocessWorkItemBuffers>,
288
289 pub late_indexed_indirect_parameters_buffer:
297 RawBufferVec<LatePreprocessWorkItemIndirectParameters>,
298
299 pub late_non_indexed_indirect_parameters_buffer:
307 RawBufferVec<LatePreprocessWorkItemIndirectParameters>,
308}
309
310pub struct InstanceInputUniformBuffer<BDI>
316where
317 BDI: AtomicPod,
318{
319 buffer: AtomicSparseBufferVec<BDI>,
321
322 free_uniform_indices: Vec<u32>,
327}
328
329impl<BDI> InstanceInputUniformBuffer<BDI>
330where
331 BDI: AtomicPod,
332{
333 pub fn new() -> InstanceInputUniformBuffer<BDI> {
335 InstanceInputUniformBuffer {
336 buffer: AtomicSparseBufferVec::new(
337 BufferUsages::STORAGE,
338 Arc::from("instance input uniform buffer"),
339 ),
340 free_uniform_indices: vec![],
341 }
342 }
343
344 pub fn clear(&mut self) {
346 self.buffer.clear();
347 self.free_uniform_indices.clear();
348 }
349
350 #[inline]
353 pub fn buffer(&self) -> &AtomicSparseBufferVec<BDI> {
354 &self.buffer
355 }
356
357 pub fn add(&mut self, element: BDI) -> u32 {
360 match self.free_uniform_indices.pop() {
361 Some(uniform_index) => {
362 self.buffer.set(uniform_index, element);
363 uniform_index
364 }
365 None => self.buffer.push(element),
366 }
367 }
368
369 pub fn remove(&mut self, uniform_index: u32) {
373 self.free_uniform_indices.push(uniform_index);
374 }
375
376 pub fn get(&self, uniform_index: u32) -> Option<BDI> {
380 if uniform_index >= self.buffer.len() || self.free_uniform_indices.contains(&uniform_index)
381 {
382 None
383 } else {
384 Some(self.get_unchecked(uniform_index))
385 }
386 }
387
388 pub fn get_unchecked(&self, uniform_index: u32) -> BDI {
394 self.buffer.get(uniform_index)
395 }
396
397 pub fn set(&self, uniform_index: u32, element: BDI) {
402 self.buffer.set(uniform_index, element);
403 }
404
405 pub fn ensure_nonempty(&mut self) {
408 if self.buffer.is_empty() {
409 self.buffer.push(default());
410 }
411 }
412
413 pub fn len(&self) -> usize {
415 self.buffer.len() as usize
416 }
417
418 pub fn is_empty(&self) -> bool {
421 self.buffer.is_empty()
422 }
423
424 pub fn into_buffer(self) -> AtomicSparseBufferVec<BDI> {
427 self.buffer
428 }
429}
430
431impl<BDI> Default for InstanceInputUniformBuffer<BDI>
432where
433 BDI: AtomicPod,
434{
435 fn default() -> Self {
436 Self::new()
437 }
438}
439
440pub struct PreviousInstanceInputUniformBuffer<BPDI>
451where
452 BPDI: AtomicPod,
453{
454 buffer: AtomicRawBufferVec<BPDI>,
456
457 atomic_len: AtomicU32,
459}
460
461impl<BPDI> PreviousInstanceInputUniformBuffer<BPDI>
462where
463 BPDI: AtomicPod,
464{
465 pub fn new() -> PreviousInstanceInputUniformBuffer<BPDI> {
467 PreviousInstanceInputUniformBuffer {
468 buffer: AtomicRawBufferVec::with_label(
469 BufferUsages::STORAGE,
470 "previous instance input uniform buffer",
471 ),
472 atomic_len: AtomicU32::new(0),
473 }
474 }
475
476 fn write_buffer(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
478 debug_assert!(!self.buffer.is_empty());
480 self.buffer.write_buffer_range(
483 0..(self.atomic_len.load(Ordering::Relaxed) as usize).max(1),
484 render_device,
485 render_queue,
486 );
487 }
488
489 pub fn clear(&mut self) {
491 self.atomic_len.store(0, Ordering::Relaxed);
494 }
495
496 pub fn reserve(&mut self, capacity: u32) {
498 self.buffer.grow(capacity);
499 *self.atomic_len.get_mut() = 0;
500 }
501
502 pub fn push(&self, value: BPDI) -> u32 {
506 let index = self.atomic_len.fetch_add(1, Ordering::Relaxed);
507 debug_assert!(
508 (index as usize) < self.buffer.len() as usize,
509 "push exceeded pre-allocated capacity"
510 );
511 self.buffer.set(index, value);
512 index
513 }
514
515 pub fn ensure_nonempty(&mut self) {
518 if self.buffer.is_empty() {
519 self.buffer.push(default());
520 }
521 }
522
523 pub fn buffer(&self) -> Option<&Buffer> {
525 self.buffer.buffer()
526 }
527}
528
529impl<BPDI> Default for PreviousInstanceInputUniformBuffer<BPDI>
530where
531 BPDI: AtomicPod,
532{
533 fn default() -> Self {
534 Self::new()
535 }
536}
537
538#[cfg_attr(
540 not(target_arch = "wasm32"),
541 expect(
542 clippy::large_enum_variant,
543 reason = "See https://github.com/bevyengine/bevy/issues/19220"
544 )
545)]
546pub enum PreprocessWorkItemBuffers {
547 Direct(RawBufferVec<PreprocessWorkItem>),
552
553 Indirect {
559 indexed: PartialBufferVec<PreprocessWorkItem>,
561 non_indexed: PartialBufferVec<PreprocessWorkItem>,
563 gpu_occlusion_culling: Option<GpuOcclusionCullingWorkItemBuffers>,
565 },
566}
567
568pub struct GpuOcclusionCullingWorkItemBuffers {
570 pub late_indexed: UninitBufferVec<PreprocessWorkItem>,
572 pub late_non_indexed: UninitBufferVec<PreprocessWorkItem>,
574 pub late_indirect_parameters_indexed_offset: u32,
578 pub late_indirect_parameters_non_indexed_offset: u32,
582}
583
584#[derive(Clone, Copy, ShaderType, Pod, Zeroable)]
590#[repr(C)]
591pub struct LatePreprocessWorkItemIndirectParameters {
592 dispatch_x: u32,
596 dispatch_y: u32,
599 dispatch_z: u32,
602 work_item_count: u32,
608 pad: UVec4,
610}
611
612impl Default for LatePreprocessWorkItemIndirectParameters {
613 fn default() -> LatePreprocessWorkItemIndirectParameters {
614 LatePreprocessWorkItemIndirectParameters {
615 dispatch_x: 0,
616 dispatch_y: 1,
617 dispatch_z: 1,
618 work_item_count: 0,
619 pad: default(),
620 }
621 }
622}
623
624pub fn get_or_create_work_item_buffer<'a, I>(
633 work_item_buffers: &'a mut HashMap<RetainedViewEntity, PreprocessWorkItemBuffers>,
634 view: RetainedViewEntity,
635 no_indirect_drawing: bool,
636 enable_gpu_occlusion_culling: bool,
637) -> &'a mut PreprocessWorkItemBuffers
638where
639 I: 'static,
640{
641 let preprocess_work_item_buffers = match work_item_buffers.entry(view) {
642 Entry::Occupied(occupied_entry) => occupied_entry.into_mut(),
643 Entry::Vacant(vacant_entry) => {
644 if no_indirect_drawing {
645 vacant_entry.insert(PreprocessWorkItemBuffers::Direct(RawBufferVec::new(
646 BufferUsages::STORAGE,
647 )))
648 } else {
649 vacant_entry.insert(PreprocessWorkItemBuffers::Indirect {
650 indexed: PartialBufferVec::new(
651 BufferUsages::STORAGE,
652 "indexed preprocess work item buffer".to_owned(),
653 ),
654 non_indexed: PartialBufferVec::new(
655 BufferUsages::STORAGE,
656 "non-indexed preprocess work item buffer".to_owned(),
657 ),
658 gpu_occlusion_culling: None,
661 })
662 }
663 }
664 };
665
666 if let PreprocessWorkItemBuffers::Indirect {
668 ref mut gpu_occlusion_culling,
669 ..
670 } = *preprocess_work_item_buffers
671 {
672 match (
673 enable_gpu_occlusion_culling,
674 gpu_occlusion_culling.is_some(),
675 ) {
676 (false, false) | (true, true) => {}
677 (false, true) => {
678 *gpu_occlusion_culling = None;
679 }
680 (true, false) => {
681 *gpu_occlusion_culling = Some(GpuOcclusionCullingWorkItemBuffers {
682 late_indexed: UninitBufferVec::new(BufferUsages::STORAGE),
683 late_non_indexed: UninitBufferVec::new(BufferUsages::STORAGE),
684 late_indirect_parameters_indexed_offset: 0,
685 late_indirect_parameters_non_indexed_offset: 0,
686 });
687 }
688 }
689 }
690
691 preprocess_work_item_buffers
692}
693
694pub fn init_work_item_buffers(
696 work_item_buffers: &mut PreprocessWorkItemBuffers,
697 late_indexed_indirect_parameters_buffer: &'_ mut RawBufferVec<
698 LatePreprocessWorkItemIndirectParameters,
699 >,
700 late_non_indexed_indirect_parameters_buffer: &'_ mut RawBufferVec<
701 LatePreprocessWorkItemIndirectParameters,
702 >,
703) {
704 if let PreprocessWorkItemBuffers::Indirect {
707 gpu_occlusion_culling:
708 Some(GpuOcclusionCullingWorkItemBuffers {
709 ref mut late_indirect_parameters_indexed_offset,
710 ref mut late_indirect_parameters_non_indexed_offset,
711 ..
712 }),
713 ..
714 } = *work_item_buffers
715 {
716 *late_indirect_parameters_indexed_offset = late_indexed_indirect_parameters_buffer
717 .push(LatePreprocessWorkItemIndirectParameters::default())
718 as u32;
719 *late_indirect_parameters_non_indexed_offset = late_non_indexed_indirect_parameters_buffer
720 .push(LatePreprocessWorkItemIndirectParameters::default())
721 as u32;
722 }
723}
724
725impl PreprocessWorkItemBuffers {
726 pub fn push(&mut self, indexed: bool, preprocess_work_item: PreprocessWorkItem) {
731 match *self {
732 PreprocessWorkItemBuffers::Direct(ref mut buffer) => {
733 buffer.push(preprocess_work_item);
734 }
735 PreprocessWorkItemBuffers::Indirect {
736 indexed: ref mut indexed_buffer,
737 non_indexed: ref mut non_indexed_buffer,
738 ref mut gpu_occlusion_culling,
739 } => {
740 if indexed {
741 indexed_buffer.push_init(preprocess_work_item);
742 } else {
743 non_indexed_buffer.push_init(preprocess_work_item);
744 }
745
746 if let Some(ref mut gpu_occlusion_culling) = *gpu_occlusion_culling {
747 if indexed {
748 gpu_occlusion_culling.late_indexed.add();
749 } else {
750 gpu_occlusion_culling.late_non_indexed.add();
751 }
752 }
753 }
754 }
755 }
756
757 pub fn clear(&mut self) {
759 match *self {
760 PreprocessWorkItemBuffers::Direct(ref mut buffer) => {
761 buffer.clear();
762 }
763 PreprocessWorkItemBuffers::Indirect {
764 indexed: ref mut indexed_buffer,
765 non_indexed: ref mut non_indexed_buffer,
766 ref mut gpu_occlusion_culling,
767 } => {
768 indexed_buffer.clear();
769 non_indexed_buffer.clear();
770
771 if let Some(ref mut gpu_occlusion_culling) = *gpu_occlusion_culling {
772 gpu_occlusion_culling.late_indexed.clear();
773 gpu_occlusion_culling.late_non_indexed.clear();
774 gpu_occlusion_culling.late_indirect_parameters_indexed_offset = 0;
775 gpu_occlusion_culling.late_indirect_parameters_non_indexed_offset = 0;
776 }
777 }
778 }
779 }
780}
781
782#[derive(Clone, Copy, Default, Pod, Zeroable, ShaderType)]
785#[repr(C)]
786pub struct PreprocessWorkItem {
787 pub input_index: u32,
790
791 pub output_or_indirect_parameters_index: u32,
799}
800
801#[derive(Clone, Copy, Debug, Pod, Zeroable, ShaderType)]
806#[repr(C)]
807pub struct IndirectParametersIndexed {
808 pub index_count: u32,
810 pub instance_count: u32,
812 pub first_index: u32,
814 pub base_vertex: u32,
816 pub first_instance: u32,
818}
819
820#[derive(Clone, Copy, Debug, Pod, Zeroable, ShaderType)]
825#[repr(C)]
826pub struct IndirectParametersNonIndexed {
827 pub vertex_count: u32,
829 pub instance_count: u32,
831 pub base_vertex: u32,
833 pub first_instance: u32,
835}
836
837impl MeshClassIndirectParameters for IndirectParametersIndexed {
838 fn debug_label() -> &'static str {
839 "indexed"
840 }
841}
842
843impl MeshClassIndirectParameters for IndirectParametersNonIndexed {
844 fn debug_label() -> &'static str {
845 "non-indexed"
846 }
847}
848
849#[derive(Clone, Copy, Default, Debug, Pod, Zeroable, ShaderType)]
854#[repr(C)]
855pub struct IndirectParametersBuildJob {
856 pub first_batch_index: u32,
859 pub last_batch_index: u32,
862 pad_a: u32,
865 pad_b: u32,
866 pad_c: [UVec4; 15],
867}
868
869assert_eq_size!(IndirectParametersBuildJob, [u8; 256]);
872
873impl IndirectParametersBuildJob {
874 pub fn new(batch_range: Range<u32>) -> Self {
877 Self {
878 first_batch_index: batch_range.start,
879 last_batch_index: batch_range.end,
880 pad_a: default(),
881 pad_b: default(),
882 pad_c: default(),
883 }
884 }
885}
886
887#[derive(Clone, Copy, Default, Pod, Zeroable, ShaderType)]
897#[repr(C)]
898pub struct IndirectParametersMetadata {
899 pub base_output_index: u32,
907
908 pub batch_set_index: u32,
916
917 pub mesh_index: u32,
920
921 pub early_instance_count: u32,
926
927 pub late_instance_count: u32,
933}
934
935#[derive(Clone, Copy, Default, Pod, Zeroable, ShaderType)]
947#[repr(C)]
948pub struct IndirectBatchSet {
949 pub indirect_parameters_count: u32,
958
959 pub indirect_parameters_base: u32,
965}
966
967#[derive(Resource, Deref, DerefMut, Default)]
977pub struct IndirectParametersBuffers {
978 #[deref]
983 pub buffers: TypeIdHashMap<UntypedPhaseIndirectParametersBuffers>,
984}
985
986#[derive(Resource)]
988pub struct IndirectParametersBuffersSettings {
989 pub allow_copies_from_indirect_parameter_buffers: bool,
995}
996
997#[derive(Resource)]
1006pub struct IndirectParametersBuildJobs {
1007 buffer: RawBufferVec<IndirectParametersBuildJob>,
1012}
1013
1014impl FromWorld for IndirectParametersBuildJobs {
1015 fn from_world(world: &mut World) -> IndirectParametersBuildJobs {
1016 debug_assert!(size_of::<IndirectParametersBuildJob>().is_multiple_of(
1017 world
1018 .resource::<RenderDevice>()
1019 .limits()
1020 .min_uniform_buffer_offset_alignment as usize
1021 ));
1022 IndirectParametersBuildJobs {
1023 buffer: RawBufferVec::new(
1024 BufferUsages::UNIFORM | BufferUsages::COPY_DST | BufferUsages::COPY_SRC,
1025 ),
1026 }
1027 }
1028}
1029
1030impl IndirectParametersBuildJobs {
1031 fn push(&mut self, job: IndirectParametersBuildJob) -> u32 {
1034 let index = self.buffer.push(job);
1035 (index * size_of::<IndirectParametersBuildJob>()) as u32
1036 }
1037
1038 fn clear(&mut self) {
1040 self.buffer.clear();
1041 }
1042
1043 pub fn buffer(&self) -> Option<&Buffer> {
1045 self.buffer.buffer()
1046 }
1047}
1048
1049#[derive(Clone, Copy, Pod, Zeroable, ShaderType)]
1051#[repr(C)]
1052pub struct GpuBinUnpackingMetadata {
1053 base_output_work_item_index: u32,
1056 base_indirect_parameters_index: u32,
1059 binned_mesh_instance_count: u32,
1062 pad: [u32; 61],
1064}
1065
1066impl Default for GpuBinUnpackingMetadata {
1067 fn default() -> GpuBinUnpackingMetadata {
1068 GpuBinUnpackingMetadata {
1069 base_output_work_item_index: 0,
1070 base_indirect_parameters_index: 0,
1071 binned_mesh_instance_count: 0,
1072 pad: [0; _],
1073 }
1074 }
1075}
1076
1077#[derive(Clone, Copy, Pod, Zeroable, ShaderType)]
1082#[repr(C)]
1083pub struct GpuBinMetadata {
1084 pub indirect_parameters_offset: u32,
1091
1092 pub bin_index: u32,
1099
1100 pub instance_count: u32,
1102}
1103
1104#[derive(Clone, Copy, Pod, Zeroable, ShaderType)]
1106#[repr(C)]
1107pub struct GpuUniformAllocationMetadata {
1108 pub batch_set_index: u32,
1112
1113 pub bin_count: u32,
1115
1116 pub first_indirect_parameters_index: u32,
1121
1122 pub first_output_mesh_uniform_index: u32,
1124
1125 pub pad: [u32; 60],
1127}
1128
1129pub struct SceneUnpackingJob {
1136 pub render_binned_mesh_instance_buffer: Buffer,
1141 pub bin_metadata_buffer: Buffer,
1146 pub fan_buffer: Buffer,
1152 pub bin_index_to_bin_metadata_index_buffer: Buffer,
1155 pub bin_unpacking_metadata_index: BinUnpackingMetadataIndex,
1158 pub uniform_allocation_metadata_index: UniformAllocationMetadataIndex,
1161 pub mesh_instance_count: u32,
1163 pub bin_count: u32,
1166}
1167
1168#[derive(Resource)]
1176pub struct PhaseIndirectParametersBuffers<PI>
1177where
1178 PI: PhaseItem,
1179{
1180 pub buffers: UntypedPhaseIndirectParametersBuffers,
1182 phantom: PhantomData<PI>,
1183}
1184
1185impl<PI> FromWorld for PhaseIndirectParametersBuffers<PI>
1186where
1187 PI: PhaseItem,
1188{
1189 fn from_world(world: &mut World) -> Self {
1190 let settings = world.resource::<IndirectParametersBuffersSettings>();
1191 PhaseIndirectParametersBuffers {
1192 buffers: UntypedPhaseIndirectParametersBuffers::new(
1193 settings.allow_copies_from_indirect_parameter_buffers,
1194 ),
1195 phantom: PhantomData,
1196 }
1197 }
1198}
1199
1200impl<PI> PhaseIndirectParametersBuffers<PI>
1201where
1202 PI: PhaseItem,
1203{
1204 fn allocate(&mut self, no_indirect_drawing: bool, item_is_indexed: bool) -> Option<u32> {
1206 if no_indirect_drawing {
1207 None
1208 } else if item_is_indexed {
1209 Some(self.buffers.indexed.allocate(1))
1210 } else {
1211 Some(self.buffers.non_indexed.allocate(1))
1212 }
1213 }
1214}
1215
1216pub struct UntypedPhaseIndirectParametersBuffers {
1224 pub indexed: MeshClassIndirectParametersBuffers<IndirectParametersIndexed>,
1227 pub non_indexed: MeshClassIndirectParametersBuffers<IndirectParametersNonIndexed>,
1230}
1231
1232impl UntypedPhaseIndirectParametersBuffers {
1233 pub fn new(
1235 allow_copies_from_indirect_parameter_buffers: bool,
1236 ) -> UntypedPhaseIndirectParametersBuffers {
1237 UntypedPhaseIndirectParametersBuffers {
1238 non_indexed: MeshClassIndirectParametersBuffers::new(
1239 allow_copies_from_indirect_parameter_buffers,
1240 ),
1241 indexed: MeshClassIndirectParametersBuffers::new(
1242 allow_copies_from_indirect_parameter_buffers,
1243 ),
1244 }
1245 }
1246
1247 pub fn allocate(&mut self, indexed: bool, count: u32) -> u32 {
1252 if indexed {
1253 self.indexed.allocate(count)
1254 } else {
1255 self.non_indexed.allocate(count)
1256 }
1257 }
1258
1259 fn batch_count(&self, indexed: bool) -> usize {
1264 if indexed {
1265 self.indexed.batch_count()
1266 } else {
1267 self.non_indexed.batch_count()
1268 }
1269 }
1270
1271 pub fn batch_set_count(&self, indexed: bool) -> usize {
1276 if indexed {
1277 self.indexed.batch_sets.len()
1278 } else {
1279 self.non_indexed.batch_sets.len()
1280 }
1281 }
1282
1283 #[inline]
1293 pub fn add_batch_set(
1294 &mut self,
1295 indexed: bool,
1296 retained_view_entity: &RetainedViewEntity,
1297 indirect_parameters_range: Range<u32>,
1298 ) {
1299 if indexed {
1300 self.indexed.batch_sets.push(IndirectBatchSet {
1301 indirect_parameters_base: indirect_parameters_range.start,
1302 indirect_parameters_count: 0,
1303 });
1304 self.indexed
1305 .view_to_indirect_parameters_batch_ranges
1306 .entry(*retained_view_entity)
1307 .or_default()
1308 .push(indirect_parameters_range.clone());
1309 } else {
1310 self.non_indexed.batch_sets.push(IndirectBatchSet {
1311 indirect_parameters_base: indirect_parameters_range.start,
1312 indirect_parameters_count: 0,
1313 });
1314 self.non_indexed
1315 .view_to_indirect_parameters_batch_ranges
1316 .entry(*retained_view_entity)
1317 .or_default()
1318 .push(indirect_parameters_range.clone());
1319 }
1320 }
1321
1322 pub fn get_next_batch_set_index(&self, indexed: bool) -> Option<NonMaxU32> {
1327 NonMaxU32::new(self.batch_set_count(indexed) as u32)
1328 }
1329
1330 pub fn clear(&mut self) {
1332 self.indexed.clear();
1333 self.non_indexed.clear();
1334 }
1335}
1336
1337#[derive(Resource)]
1340pub struct SceneUnpackingBuffers {
1341 pub uniform_allocation_metadata: RawBufferVec<GpuUniformAllocationMetadata>,
1344 pub bin_unpacking_metadata: RawBufferVec<GpuBinUnpackingMetadata>,
1347 pub view_phase_buffers: HashMap<SceneUnpackingBuffersKey, ViewPhaseSceneUnpackingBuffers>,
1349}
1350
1351impl Default for SceneUnpackingBuffers {
1352 fn default() -> Self {
1353 let mut uniform_allocation_metadata = RawBufferVec::new(BufferUsages::UNIFORM);
1354 uniform_allocation_metadata.set_label(Some("uniform allocation metadata buffer"));
1355 let mut bin_unpacking_metadata = RawBufferVec::new(BufferUsages::UNIFORM);
1356 bin_unpacking_metadata.set_label(Some("bin unpacking metadata buffer"));
1357 SceneUnpackingBuffers {
1358 uniform_allocation_metadata,
1359 bin_unpacking_metadata,
1360 view_phase_buffers: HashMap::default(),
1361 }
1362 }
1363}
1364
1365#[derive(Default)]
1368pub struct ViewPhaseSceneUnpackingBuffers {
1369 pub indexed_unpacking_jobs: Vec<SceneUnpackingJob>,
1371 pub non_indexed_unpacking_jobs: Vec<SceneUnpackingJob>,
1374}
1375
1376#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
1379pub struct SceneUnpackingBuffersKey {
1380 pub phase: TypeId,
1382 pub view: RetainedViewEntity,
1384}
1385
1386#[derive(Clone, Copy, Debug, Deref, DerefMut)]
1389pub struct BinUnpackingMetadataIndex(pub NonMaxU32);
1390
1391impl BinUnpackingMetadataIndex {
1392 pub fn uniform_offset(&self) -> u32 {
1396 self.get() * size_of::<GpuBinUnpackingMetadata>() as u32
1397 }
1398}
1399
1400#[derive(Clone, Copy, Deref, DerefMut)]
1403pub struct UniformAllocationMetadataIndex(pub NonMaxU32);
1404
1405impl UniformAllocationMetadataIndex {
1406 pub fn uniform_offset(&self) -> u32 {
1410 self.get() * size_of::<GpuUniformAllocationMetadata>() as u32
1411 }
1412}
1413
1414pub struct MeshClassIndirectParametersBuffers<IP>
1418where
1419 IP: Clone + ShaderSize + WriteInto,
1420{
1421 indirect_draw_parameters: UninitBufferVec<IP>,
1427
1428 metadata: PartialBufferVec<IndirectParametersMetadata>,
1435
1436 batch_sets: RawBufferVec<IndirectBatchSet>,
1443
1444 view_to_indirect_parameters_batch_ranges: HashMap<RetainedViewEntity, Vec<Range<u32>>>,
1447}
1448
1449pub trait MeshClassIndirectParameters: Clone + ShaderSize + WriteInto {
1451 fn debug_label() -> &'static str;
1453}
1454
1455impl<IP> MeshClassIndirectParametersBuffers<IP>
1456where
1457 IP: MeshClassIndirectParameters,
1458{
1459 fn new(
1460 allow_copies_from_indirect_parameter_buffers: bool,
1461 ) -> MeshClassIndirectParametersBuffers<IP> {
1462 let mut indirect_parameter_buffer_usages = BufferUsages::STORAGE | BufferUsages::INDIRECT;
1463 if allow_copies_from_indirect_parameter_buffers {
1464 indirect_parameter_buffer_usages |= BufferUsages::COPY_SRC;
1465 }
1466
1467 MeshClassIndirectParametersBuffers {
1468 indirect_draw_parameters: UninitBufferVec::new(indirect_parameter_buffer_usages),
1469 metadata: PartialBufferVec::new(
1470 BufferUsages::STORAGE,
1471 format!("{} indirect parameters metadata buffer", IP::debug_label()),
1472 ),
1473 batch_sets: RawBufferVec::new(indirect_parameter_buffer_usages),
1474 view_to_indirect_parameters_batch_ranges: HashMap::new(),
1475 }
1476 }
1477
1478 #[inline]
1485 pub fn data_buffer(&self) -> Option<&Buffer> {
1486 self.indirect_draw_parameters.buffer()
1487 }
1488
1489 #[inline]
1496 pub fn metadata_buffer(&self) -> Option<&Buffer> {
1497 self.metadata.buffer()
1498 }
1499
1500 #[inline]
1507 pub fn batch_sets_buffer(&self) -> Option<&Buffer> {
1508 self.batch_sets.buffer()
1509 }
1510
1511 fn allocate(&mut self, count: u32) -> u32 {
1516 let length = self.indirect_draw_parameters.len();
1517 self.metadata.push_multiple_init(count as usize);
1518 for _ in 0..count {
1519 self.indirect_draw_parameters.add();
1520 }
1521 length as u32
1522 }
1523
1524 pub fn set(&mut self, index: u32, value: IndirectParametersMetadata) {
1526 self.metadata.set(index as usize, value);
1527 }
1528
1529 #[inline]
1532 pub fn batch_count(&self) -> usize {
1533 self.indirect_draw_parameters.len()
1534 }
1535
1536 pub fn clear(&mut self) {
1538 self.indirect_draw_parameters.clear();
1539 self.metadata.clear();
1540 self.batch_sets.clear();
1541 self.view_to_indirect_parameters_batch_ranges.clear();
1542 }
1543}
1544
1545impl FromWorld for GpuPreprocessingSupport {
1546 fn from_world(world: &mut World) -> Self {
1547 let adapter = world.resource::<RenderAdapter>();
1548 let device = world.resource::<RenderDevice>();
1549
1550 fn is_non_supported_android_device(adapter_info: &RenderAdapterInfo) -> bool {
1556 crate::get_adreno_model(adapter_info).is_some_and(|model| model != 720 && model <= 730)
1557 || crate::get_mali_driver_version(adapter_info).is_some_and(|version| version < 48)
1558 }
1559 fn is_preprocessing_only_android_device(adapter_info: &RenderAdapterInfo) -> bool {
1560 crate::get_pixel10_driver_version(adapter_info).is_some()
1561 }
1562
1563 let culling_feature_support = device
1564 .features()
1565 .contains(Features::INDIRECT_FIRST_INSTANCE | Features::IMMEDIATES);
1566 let limit_support = device.limits().max_storage_textures_per_shader_stage >= 12 &&
1569 device.limits().max_storage_buffers_per_shader_stage >= 10 &&
1570 device.limits().max_compute_workgroup_storage_size != 0;
1575
1576 let downlevel_support = adapter
1577 .get_downlevel_capabilities()
1578 .flags
1579 .contains(DownlevelFlags::COMPUTE_SHADERS);
1580
1581 let adapter_info = RenderAdapterInfo::new(adapter.get_info());
1582
1583 let max_supported_mode = if device.limits().max_compute_workgroup_size_x == 0
1584 || is_non_supported_android_device(&adapter_info)
1585 || adapter_info.backend == wgpu::Backend::Gl
1586 {
1587 info_once!(
1588 "GPU preprocessing is not supported on this device. \
1589 Falling back to CPU preprocessing.",
1590 );
1591 GpuPreprocessingMode::None
1592 } else if !(culling_feature_support && limit_support && downlevel_support)
1593 || is_preprocessing_only_android_device(&adapter_info)
1594 {
1595 info_once!("Some GPU preprocessing are limited on this device.");
1596 GpuPreprocessingMode::PreprocessingOnly
1597 } else {
1598 info_once!("GPU preprocessing is fully supported on this device.");
1599 GpuPreprocessingMode::Culling
1600 };
1601
1602 GpuPreprocessingSupport { max_supported_mode }
1603 }
1604}
1605
1606impl<BD, BDI> BatchedInstanceBuffers<BD, BDI>
1607where
1608 BD: GpuArrayBufferable + Sync + Send + 'static,
1609 BDI: BufferDataInput,
1610{
1611 pub fn new() -> Self {
1613 Self::default()
1614 }
1615
1616 pub fn clear(&mut self) {
1618 for phase_instance_buffer in self.phase_instance_buffers.values_mut() {
1619 phase_instance_buffer.clear();
1620 }
1621 }
1622}
1623
1624impl<BD> UntypedPhaseBatchedInstanceBuffers<BD>
1625where
1626 BD: GpuArrayBufferable + Sync + Send + 'static,
1627{
1628 pub fn new() -> Self {
1629 UntypedPhaseBatchedInstanceBuffers {
1630 data_buffer: UninitBufferVec::new(BufferUsages::STORAGE),
1631 work_item_buffers: HashMap::default(),
1632 late_indexed_indirect_parameters_buffer: RawBufferVec::new(
1633 BufferUsages::STORAGE | BufferUsages::INDIRECT,
1634 ),
1635 late_non_indexed_indirect_parameters_buffer: RawBufferVec::new(
1636 BufferUsages::STORAGE | BufferUsages::INDIRECT,
1637 ),
1638 }
1639 }
1640
1641 pub fn instance_data_binding(&self) -> Option<BindingResource<'_>> {
1645 self.data_buffer
1646 .buffer()
1647 .map(|buffer| buffer.as_entire_binding())
1648 }
1649
1650 pub fn clear(&mut self) {
1652 self.data_buffer.clear();
1653 self.late_indexed_indirect_parameters_buffer.clear();
1654 self.late_non_indexed_indirect_parameters_buffer.clear();
1655
1656 for view_work_item_buffers in self.work_item_buffers.values_mut() {
1659 view_work_item_buffers.clear();
1660 }
1661 }
1662}
1663
1664impl<BD> Default for UntypedPhaseBatchedInstanceBuffers<BD>
1665where
1666 BD: GpuArrayBufferable + Sync + Send + 'static,
1667{
1668 fn default() -> Self {
1669 Self::new()
1670 }
1671}
1672
1673#[derive(Resource, Default, Debug, Deref, DerefMut)]
1676pub struct BuildIndirectParametersMetadata(
1677 pub HashMap<RetainedViewEntity, ViewBuildIndirectParametersMetadata>,
1678);
1679
1680#[derive(Default, Debug, Deref, DerefMut)]
1686pub struct ViewBuildIndirectParametersMetadata(
1687 pub TypeIdHashMap<PhaseBuildIndirectParametersMetadata>,
1688);
1689
1690#[derive(Clone, Default, Debug)]
1693pub struct PhaseBuildIndirectParametersMetadata {
1694 pub indexed: Vec<MeshClassBuildIndirectParametersMetadata>,
1699
1700 pub non_indexed: Vec<MeshClassBuildIndirectParametersMetadata>,
1705}
1706
1707#[derive(Clone, Copy, Default, Debug)]
1711pub struct MeshClassBuildIndirectParametersMetadata {
1712 pub uniform_offset: u32,
1718
1719 pub batch_count: u32,
1724}
1725
1726struct SortedRenderBatchSet<F>
1729where
1730 F: GetBatchData,
1731{
1732 phase_item_start_index: u32,
1735
1736 instance_start_index: u32,
1738
1739 indexed: bool,
1741
1742 retained_view_entity: RetainedViewEntity,
1744
1745 indirect_parameters_index_range: Option<Range<u32>>,
1750
1751 meta: Option<(BatchSetMeta<F::BatchSetCompareData>, F::BatchCompareData)>,
1756}
1757
1758impl<F> SortedRenderBatchSet<F>
1759where
1760 F: GetBatchData,
1761{
1762 fn flush<I>(
1768 self,
1769 instance_end_index: u32,
1770 phase: &mut SortedRenderPhase<I>,
1771 phase_indirect_parameters_buffers: &mut UntypedPhaseIndirectParametersBuffers,
1772 ) where
1773 I: CachedRenderPipelinePhaseItem + SortedPhaseItem,
1774 {
1775 let (batch_range, batch_extra_index) =
1776 phase.items[self.phase_item_start_index as usize].batch_range_and_extra_index_mut();
1777 *batch_range = self.instance_start_index..instance_end_index;
1778 *batch_extra_index = match self.indirect_parameters_index_range {
1779 Some(ref indirect_parameters_index_range) => {
1780 PhaseItemExtraIndex::IndirectParametersIndex {
1781 range: (*indirect_parameters_index_range).clone(),
1782 batch_set_index: None,
1783 }
1784 }
1785 None => PhaseItemExtraIndex::None,
1786 };
1787 if let Some(ref indirect_parameters_index_range) = self.indirect_parameters_index_range {
1788 phase_indirect_parameters_buffers.add_batch_set(
1789 self.indexed,
1790 &self.retained_view_entity,
1791 indirect_parameters_index_range.clone(),
1792 );
1793 }
1794 }
1795}
1796
1797pub fn clear_batched_gpu_instance_buffers<GFBD>(
1804 gpu_batched_instance_buffers: Option<
1805 ResMut<BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>>,
1806 >,
1807) where
1808 GFBD: GetFullBatchData,
1809{
1810 if let Some(mut gpu_batched_instance_buffers) = gpu_batched_instance_buffers {
1813 gpu_batched_instance_buffers.clear();
1814 }
1815}
1816
1817pub fn delete_old_work_item_buffers<GFBD>(
1824 mut gpu_batched_instance_buffers: ResMut<
1825 BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1826 >,
1827 extracted_views: Query<&ExtractedView>,
1828) where
1829 GFBD: GetFullBatchData,
1830{
1831 let retained_view_entities: HashSet<_> = extracted_views
1832 .iter()
1833 .map(|extracted_view| extracted_view.retained_view_entity)
1834 .collect();
1835 for phase_instance_buffers in gpu_batched_instance_buffers
1836 .phase_instance_buffers
1837 .values_mut()
1838 {
1839 phase_instance_buffers
1840 .work_item_buffers
1841 .retain(|retained_view_entity, _| {
1842 retained_view_entities.contains(retained_view_entity)
1843 });
1844 }
1845}
1846
1847pub fn batch_and_prepare_sorted_render_phase<I, GFBD>(
1851 mut phase_batched_instance_buffers: ResMut<PhaseBatchedInstanceBuffers<I, GFBD::BufferData>>,
1852 mut phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<I>>,
1853 mut sorted_render_phases: ResMut<ViewSortedRenderPhases<I>>,
1854 mut views: Query<(
1855 &ExtractedView,
1856 Has<NoIndirectDrawing>,
1857 Has<OcclusionCulling>,
1858 )>,
1859 system_param_item: StaticSystemParam<GFBD::Param>,
1860) where
1861 I: CachedRenderPipelinePhaseItem + SortedPhaseItem,
1862 GFBD: GetFullBatchData,
1863{
1864 let UntypedPhaseBatchedInstanceBuffers {
1866 ref mut data_buffer,
1867 ref mut work_item_buffers,
1868 ref mut late_indexed_indirect_parameters_buffer,
1869 ref mut late_non_indexed_indirect_parameters_buffer,
1870 } = phase_batched_instance_buffers.buffers;
1871
1872 for (extracted_view, no_indirect_drawing, gpu_occlusion_culling) in &mut views {
1873 let Some(phase) = sorted_render_phases.get_mut(&extracted_view.retained_view_entity) else {
1874 continue;
1875 };
1876
1877 let work_item_buffer = get_or_create_work_item_buffer::<I>(
1879 work_item_buffers,
1880 extracted_view.retained_view_entity,
1881 no_indirect_drawing,
1882 gpu_occlusion_culling,
1883 );
1884
1885 init_work_item_buffers(
1887 work_item_buffer,
1888 late_indexed_indirect_parameters_buffer,
1889 late_non_indexed_indirect_parameters_buffer,
1890 );
1891
1892 let mut batch_set: Option<SortedRenderBatchSet<GFBD>> = None;
1894
1895 for current_index in 0..phase.items.len() {
1896 let item = &phase.items[current_index];
1899 let entity = item.main_entity();
1900 let item_is_indexed = item.indexed();
1901 let current_batch_input_index =
1902 GFBD::get_index_and_compare_data(&system_param_item, entity);
1903
1904 let Some((current_input_index, current_meta)) = current_batch_input_index else {
1909 if let Some(batch_set) = batch_set.take() {
1911 batch_set.flush(
1912 data_buffer.len() as u32,
1913 phase,
1914 &mut phase_indirect_parameters_buffers.buffers,
1915 );
1916 }
1917
1918 continue;
1919 };
1920 let current_meta = if I::AUTOMATIC_BATCHING {
1921 current_meta.map(|(batch_set_meta, batch_meta)| {
1922 (
1923 BatchSetMeta::new(&phase.items[current_index], batch_set_meta),
1924 batch_meta,
1925 )
1926 })
1927 } else {
1928 None
1929 };
1930
1931 let can_batch = match batch_set.as_ref() {
1934 None => SortedPhaseItemBatchability::BreakBatchSet,
1935 Some(batch_set) => match (¤t_meta, &batch_set.meta) {
1936 (
1937 &Some((ref current_batch_set_key, ref current_bin_key)),
1938 &Some((ref batch_set_key, ref bin_key)),
1939 ) => {
1940 if *current_batch_set_key == *batch_set_key {
1941 if *current_bin_key == *bin_key {
1942 SortedPhaseItemBatchability::BatchOk
1943 } else if no_indirect_drawing {
1944 SortedPhaseItemBatchability::BreakBatchSet
1946 } else {
1947 SortedPhaseItemBatchability::BreakBatch
1948 }
1949 } else {
1950 SortedPhaseItemBatchability::BreakBatchSet
1951 }
1952 }
1953 _ => SortedPhaseItemBatchability::BreakBatchSet,
1954 },
1955 };
1956
1957 let output_index = data_buffer.add() as u32;
1959
1960 match can_batch {
1963 SortedPhaseItemBatchability::BreakBatchSet => {
1964 if let Some(batch_set) = batch_set.take() {
1966 batch_set.flush(
1967 output_index,
1968 phase,
1969 &mut phase_indirect_parameters_buffers.buffers,
1970 );
1971 }
1972
1973 let indirect_parameters_index = phase_indirect_parameters_buffers
1974 .allocate(no_indirect_drawing, item_is_indexed);
1975
1976 if let Some(indirect_parameters_index) = indirect_parameters_index {
1978 GFBD::write_batch_indirect_parameters_metadata(
1979 item_is_indexed,
1980 output_index,
1981 None,
1982 &mut phase_indirect_parameters_buffers.buffers,
1983 indirect_parameters_index,
1984 );
1985 }
1986
1987 batch_set = Some(SortedRenderBatchSet {
1988 phase_item_start_index: current_index as u32,
1989 instance_start_index: output_index,
1990 retained_view_entity: extracted_view.retained_view_entity,
1991 indexed: item_is_indexed,
1992 indirect_parameters_index_range: indirect_parameters_index
1993 .map(|i| i..(i + 1)),
1994 meta: current_meta,
1995 });
1996 }
1997
1998 SortedPhaseItemBatchability::BreakBatch => {
1999 let maybe_indirect_parameters_index = phase_indirect_parameters_buffers
2001 .allocate(no_indirect_drawing, item_is_indexed);
2002
2003 if let (&mut Some(ref mut batch_set), Some(indirect_parameters_index)) =
2004 (&mut batch_set, maybe_indirect_parameters_index)
2005 {
2006 GFBD::write_batch_indirect_parameters_metadata(
2007 item_is_indexed,
2008 output_index,
2009 None,
2010 &mut phase_indirect_parameters_buffers.buffers,
2011 indirect_parameters_index,
2012 );
2013
2014 batch_set.meta = current_meta;
2015
2016 let indirect_parameters_index_range = batch_set
2017 .indirect_parameters_index_range
2018 .as_mut()
2019 .expect("Can't allocate in a multidraw set if we aren't multidrawing");
2020 debug_assert_eq!(
2021 indirect_parameters_index,
2022 indirect_parameters_index_range.end
2023 );
2024 indirect_parameters_index_range.end += 1;
2025 }
2026 }
2027
2028 SortedPhaseItemBatchability::BatchOk => {}
2029 };
2030
2031 if let Some(batch_set) = batch_set.as_ref() {
2034 work_item_buffer.push(
2035 item_is_indexed,
2036 PreprocessWorkItem {
2037 input_index: current_input_index.into(),
2038 output_or_indirect_parameters_index: match (
2039 no_indirect_drawing,
2040 &batch_set.indirect_parameters_index_range,
2041 ) {
2042 (true, _) => output_index,
2043 (false, Some(indirect_parameters_index_range)) => {
2044 indirect_parameters_index_range.end - 1
2045 }
2046 (false, None) => 0,
2047 },
2048 },
2049 );
2050 }
2051 }
2052
2053 if let Some(batch_set) = batch_set.take() {
2055 batch_set.flush(
2056 data_buffer.len() as u32,
2057 phase,
2058 &mut phase_indirect_parameters_buffers.buffers,
2059 );
2060 }
2061 }
2062}
2063
2064#[derive(Clone, Copy, PartialEq)]
2066enum SortedPhaseItemBatchability {
2067 BatchOk,
2069 BreakBatch,
2074 BreakBatchSet,
2076}
2077
2078pub fn batch_and_prepare_binned_render_phase<BPI, GFBD>(
2080 mut phase_batched_instance_buffers: ResMut<PhaseBatchedInstanceBuffers<BPI, GFBD::BufferData>>,
2081 phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<BPI>>,
2082 mut binned_render_phases: ResMut<ViewBinnedRenderPhases<BPI>>,
2083 mut views: Query<
2084 (
2085 &ExtractedView,
2086 Has<NoIndirectDrawing>,
2087 Has<OcclusionCulling>,
2088 ),
2089 With<ExtractedView>,
2090 >,
2091 param: StaticSystemParam<GFBD::Param>,
2092) where
2093 BPI: BinnedPhaseItem,
2094 GFBD: GetFullBatchData,
2095{
2096 let system_param_item = param.into_inner();
2097
2098 let phase_indirect_parameters_buffers = phase_indirect_parameters_buffers.into_inner();
2099
2100 let UntypedPhaseBatchedInstanceBuffers {
2101 ref mut data_buffer,
2102 ref mut work_item_buffers,
2103 ref mut late_indexed_indirect_parameters_buffer,
2104 ref mut late_non_indexed_indirect_parameters_buffer,
2105 } = phase_batched_instance_buffers.buffers;
2106
2107 for (extracted_view, no_indirect_drawing, gpu_occlusion_culling) in &mut views {
2127 let Some(phase) = binned_render_phases.get_mut(&extracted_view.retained_view_entity) else {
2128 continue;
2129 };
2130
2131 let work_item_buffer = get_or_create_work_item_buffer::<BPI>(
2134 work_item_buffers,
2135 extracted_view.retained_view_entity,
2136 no_indirect_drawing,
2137 gpu_occlusion_culling,
2138 );
2139
2140 init_work_item_buffers(
2142 work_item_buffer,
2143 late_indexed_indirect_parameters_buffer,
2144 late_non_indexed_indirect_parameters_buffer,
2145 );
2146
2147 for (key, unbatchables) in &mut phase.unbatchable_meshes {
2150 let mut indirect_parameters_offset = if no_indirect_drawing {
2152 None
2153 } else if key.0.indexed() {
2154 Some(
2155 phase_indirect_parameters_buffers
2156 .buffers
2157 .indexed
2158 .allocate(unbatchables.entities.len() as u32),
2159 )
2160 } else {
2161 Some(
2162 phase_indirect_parameters_buffers
2163 .buffers
2164 .non_indexed
2165 .allocate(unbatchables.entities.len() as u32),
2166 )
2167 };
2168
2169 for main_entity in unbatchables.entities.keys() {
2170 let Some(input_index) = GFBD::get_binned_index(&system_param_item, *main_entity)
2171 else {
2172 continue;
2173 };
2174 let output_index = data_buffer.add() as u32;
2175
2176 if let Some(ref mut indirect_parameters_index) = indirect_parameters_offset {
2177 GFBD::write_batch_indirect_parameters_metadata(
2180 key.0.indexed(),
2181 output_index,
2182 None,
2183 &mut phase_indirect_parameters_buffers.buffers,
2184 *indirect_parameters_index,
2185 );
2186 work_item_buffer.push(
2187 key.0.indexed(),
2188 PreprocessWorkItem {
2189 input_index: input_index.into(),
2190 output_or_indirect_parameters_index: *indirect_parameters_index,
2191 },
2192 );
2193 unbatchables
2194 .buffer_indices
2195 .add(UnbatchableBinnedEntityIndices {
2196 instance_index: *indirect_parameters_index,
2197 extra_index: PhaseItemExtraIndex::IndirectParametersIndex {
2198 range: *indirect_parameters_index..(*indirect_parameters_index + 1),
2199 batch_set_index: None,
2200 },
2201 });
2202 phase_indirect_parameters_buffers.buffers.add_batch_set(
2203 key.0.indexed(),
2204 &extracted_view.retained_view_entity,
2205 *indirect_parameters_index..(*indirect_parameters_index + 1),
2206 );
2207 *indirect_parameters_index += 1;
2208 } else {
2209 work_item_buffer.push(
2210 key.0.indexed(),
2211 PreprocessWorkItem {
2212 input_index: input_index.into(),
2213 output_or_indirect_parameters_index: output_index,
2214 },
2215 );
2216 unbatchables
2217 .buffer_indices
2218 .add(UnbatchableBinnedEntityIndices {
2219 instance_index: output_index,
2220 extra_index: PhaseItemExtraIndex::None,
2221 });
2222 }
2223 }
2224 }
2225
2226 for (key, bin) in &phase.batchable_meshes {
2229 let mut batch: Option<BinnedRenderPhaseBatch> = None;
2230 for (&main_entity, &input_index) in bin.entities() {
2231 let output_index = data_buffer.add() as u32;
2232
2233 match batch {
2234 Some(ref mut batch) => {
2235 batch.instance_range.end = output_index + 1;
2236
2237 work_item_buffer.push(
2245 key.0.indexed(),
2246 PreprocessWorkItem {
2247 input_index: *input_index,
2248 output_or_indirect_parameters_index: match (
2249 no_indirect_drawing,
2250 &batch.extra_index,
2251 ) {
2252 (true, _) => output_index,
2253 (
2254 false,
2255 PhaseItemExtraIndex::IndirectParametersIndex {
2256 range: indirect_parameters_range,
2257 ..
2258 },
2259 ) => indirect_parameters_range.start,
2260 (false, &PhaseItemExtraIndex::DynamicOffset(_))
2261 | (false, &PhaseItemExtraIndex::None) => 0,
2262 },
2263 },
2264 );
2265 }
2266
2267 None if !no_indirect_drawing => {
2268 let indirect_parameters_index = phase_indirect_parameters_buffers
2270 .buffers
2271 .allocate(key.0.indexed(), 1);
2272 let batch_set_index = phase_indirect_parameters_buffers
2273 .buffers
2274 .get_next_batch_set_index(key.0.indexed());
2275
2276 GFBD::write_batch_indirect_parameters_metadata(
2277 key.0.indexed(),
2278 output_index,
2279 batch_set_index,
2280 &mut phase_indirect_parameters_buffers.buffers,
2281 indirect_parameters_index,
2282 );
2283 phase_indirect_parameters_buffers.buffers.add_batch_set(
2284 key.0.indexed(),
2285 &extracted_view.retained_view_entity,
2286 indirect_parameters_index..(indirect_parameters_index + 1),
2287 );
2288 work_item_buffer.push(
2289 key.0.indexed(),
2290 PreprocessWorkItem {
2291 input_index: *input_index,
2292 output_or_indirect_parameters_index: indirect_parameters_index,
2293 },
2294 );
2295 batch = Some(BinnedRenderPhaseBatch {
2296 representative_entity: (Entity::PLACEHOLDER, main_entity),
2297 instance_range: output_index..output_index + 1,
2298 extra_index: PhaseItemExtraIndex::IndirectParametersIndex {
2299 range: indirect_parameters_index..(indirect_parameters_index + 1),
2300 batch_set_index,
2301 },
2302 });
2303 }
2304
2305 None => {
2306 work_item_buffer.push(
2308 key.0.indexed(),
2309 PreprocessWorkItem {
2310 input_index: *input_index,
2311 output_or_indirect_parameters_index: output_index,
2312 },
2313 );
2314 batch = Some(BinnedRenderPhaseBatch {
2315 representative_entity: (Entity::PLACEHOLDER, main_entity),
2316 instance_range: output_index..output_index + 1,
2317 extra_index: PhaseItemExtraIndex::None,
2318 });
2319 }
2320 }
2321 }
2322
2323 if let Some(batch) = batch {
2324 match phase.batch_sets {
2325 BinnedRenderPhaseBatchSets::DynamicUniforms(_) => {
2326 error!("Dynamic uniform batch sets shouldn't be used here");
2327 }
2328 BinnedRenderPhaseBatchSets::Direct(ref mut vec) => {
2329 vec.push(batch);
2330 }
2331 BinnedRenderPhaseBatchSets::MultidrawIndirect(ref mut vec) => {
2332 let batch_set_index = match batch.extra_index {
2337 PhaseItemExtraIndex::IndirectParametersIndex {
2338 batch_set_index: Some(batch_set_index),
2339 ..
2340 } => u32::from(batch_set_index),
2341 _ => 0,
2342 };
2343 vec.push(BinnedRenderPhaseBatchSet {
2344 first_batch: batch,
2345 batch_count: 1,
2346 bin_key: key.1.clone(),
2347 index: batch_set_index,
2348 first_work_item_index: 0,
2350 first_indirect_parameters_index: 0,
2352 first_output_mesh_uniform_index: 0,
2354 });
2355 }
2356 }
2357 }
2358 }
2359 }
2360
2361 for (extracted_view, _, _) in &mut views {
2364 let (Some(phase), Some(work_item_buffer)) = (
2367 binned_render_phases.get_mut(&extracted_view.retained_view_entity),
2368 work_item_buffers.get_mut(&extracted_view.retained_view_entity),
2369 ) else {
2370 continue;
2371 };
2372
2373 let (
2376 &mut BinnedRenderPhaseBatchSets::MultidrawIndirect(ref mut batch_sets),
2377 &mut PreprocessWorkItemBuffers::Indirect {
2378 indexed: ref mut indexed_work_item_buffer,
2379 non_indexed: ref mut non_indexed_work_item_buffer,
2380 gpu_occlusion_culling: ref mut gpu_occlusion_culling_buffers,
2381 },
2382 ) = (&mut phase.batch_sets, &mut *work_item_buffer)
2383 else {
2384 continue;
2385 };
2386
2387 let mut indexed_preparer: MultidrawableBatchSetPreparer<BPI, GFBD> =
2389 MultidrawableBatchSetPreparer::new(
2390 phase_indirect_parameters_buffers.buffers.batch_count(true) as u32,
2391 phase_indirect_parameters_buffers
2392 .buffers
2393 .indexed
2394 .batch_sets
2395 .len() as u32,
2396 );
2397 let mut non_indexed_preparer: MultidrawableBatchSetPreparer<BPI, GFBD> =
2398 MultidrawableBatchSetPreparer::new(
2399 phase_indirect_parameters_buffers.buffers.batch_count(false) as u32,
2400 phase_indirect_parameters_buffers
2401 .buffers
2402 .non_indexed
2403 .batch_sets
2404 .len() as u32,
2405 );
2406
2407 for (batch_set_key, bins) in &phase.multidrawable_meshes {
2409 if batch_set_key.indexed() {
2410 indexed_preparer.prepare_multidrawable_binned_batch_set(
2411 bins,
2412 data_buffer,
2413 indexed_work_item_buffer,
2414 &mut phase_indirect_parameters_buffers.buffers.indexed,
2415 batch_sets,
2416 );
2417 } else {
2418 non_indexed_preparer.prepare_multidrawable_binned_batch_set(
2419 bins,
2420 data_buffer,
2421 non_indexed_work_item_buffer,
2422 &mut phase_indirect_parameters_buffers.buffers.non_indexed,
2423 batch_sets,
2424 );
2425 }
2426 }
2427
2428 if let Some(gpu_occlusion_culling_buffers) = gpu_occlusion_culling_buffers {
2430 gpu_occlusion_culling_buffers
2431 .late_indexed
2432 .add_multiple(indexed_preparer.work_item_count);
2433 gpu_occlusion_culling_buffers
2434 .late_non_indexed
2435 .add_multiple(non_indexed_preparer.work_item_count);
2436 }
2437
2438 indexed_preparer.flush_batch_range_to(
2441 &mut phase_indirect_parameters_buffers.buffers.indexed,
2442 extracted_view.retained_view_entity,
2443 );
2444 non_indexed_preparer.flush_batch_range_to(
2445 &mut phase_indirect_parameters_buffers.buffers.non_indexed,
2446 extracted_view.retained_view_entity,
2447 );
2448 }
2449}
2450
2451struct MultidrawableBatchSetPreparer<BPI, GFBD>
2457where
2458 BPI: BinnedPhaseItem,
2459 GFBD: GetFullBatchData,
2460{
2461 indirect_parameters_index: u32,
2464 initial_indirect_parameters_index: u32,
2467 batch_set_index: u32,
2469 work_item_count: usize,
2471 phantom: PhantomData<(BPI, GFBD)>,
2472}
2473
2474impl<BPI, GFBD> MultidrawableBatchSetPreparer<BPI, GFBD>
2475where
2476 BPI: BinnedPhaseItem,
2477 GFBD: GetFullBatchData,
2478{
2479 #[inline]
2482 fn new(initial_indirect_parameters_index: u32, initial_batch_set_index: u32) -> Self {
2483 MultidrawableBatchSetPreparer {
2484 indirect_parameters_index: initial_indirect_parameters_index,
2485 initial_indirect_parameters_index,
2486 batch_set_index: initial_batch_set_index,
2487 work_item_count: 0,
2488 phantom: PhantomData,
2489 }
2490 }
2491
2492 #[inline]
2497 fn prepare_multidrawable_binned_batch_set<IP>(
2498 &mut self,
2499 batch_set: &RenderMultidrawableBatchSet<BPI>,
2500 data_buffer: &mut UninitBufferVec<GFBD::BufferData>,
2501 work_item_buffer: &mut PartialBufferVec<PreprocessWorkItem>,
2502 mesh_class_buffers: &mut MeshClassIndirectParametersBuffers<IP>,
2503 batch_sets: &mut Vec<BinnedRenderPhaseBatchSet<BPI::BinKey>>,
2504 ) where
2505 IP: Clone + ShaderSize + WriteInto,
2506 {
2507 let current_indexed_batch_set_index = self.batch_set_index;
2513 let current_output_index = data_buffer.len() as u32;
2514 let first_work_item_index = work_item_buffer.len() as u32;
2515
2516 let indirect_parameters_base = self.indirect_parameters_index;
2517
2518 let Some((first_bin_key, first_bin_index)) = batch_set.bin_key_to_bin_index.iter().next()
2521 else {
2522 return;
2523 };
2524 let first_bin = batch_set
2525 .bin(*first_bin_index)
2526 .expect("At least one bin must be present in each batch set");
2527 let first_bin_len = first_bin.entity_to_binned_mesh_instance_index.len();
2528 let first_bin_entity = batch_set
2529 .representative_entity()
2530 .unwrap_or(MainEntity::from(Entity::PLACEHOLDER));
2531
2532 let bin_count = batch_set.bin_count();
2536 let first_metadata_index =
2537 mesh_class_buffers.metadata.push_multiple_uninit(bin_count) as u32;
2538
2539 let first_output_mesh_uniform_index =
2542 data_buffer.add_multiple(batch_set.instance_count as usize) as u32;
2543 work_item_buffer.push_multiple_uninit(batch_set.instance_count as usize);
2544 self.work_item_count += batch_set.instance_count as usize;
2545
2546 let first_indirect_parameters_index = mesh_class_buffers
2548 .indirect_draw_parameters
2549 .add_multiple(bin_count) as u32;
2550 debug_assert_eq!(first_metadata_index, first_indirect_parameters_index);
2551
2552 mesh_class_buffers.batch_sets.push(IndirectBatchSet {
2554 indirect_parameters_base,
2555 indirect_parameters_count: 0,
2556 });
2557
2558 self.indirect_parameters_index += bin_count as u32;
2559 self.batch_set_index += 1;
2560
2561 batch_sets.push(BinnedRenderPhaseBatchSet {
2564 first_batch: BinnedRenderPhaseBatch {
2565 representative_entity: (Entity::PLACEHOLDER, first_bin_entity),
2566 instance_range: current_output_index..(current_output_index + first_bin_len as u32),
2567 extra_index: PhaseItemExtraIndex::maybe_indirect_parameters_index(NonMaxU32::new(
2568 indirect_parameters_base,
2569 )),
2570 },
2571 bin_key: (*first_bin_key).clone(),
2572 batch_count: self.indirect_parameters_index - indirect_parameters_base,
2573 index: current_indexed_batch_set_index,
2574 first_work_item_index,
2575 first_indirect_parameters_index,
2576 first_output_mesh_uniform_index,
2577 });
2578 }
2579
2580 fn flush_batch_range_to<IP>(
2583 &self,
2584 indirect_parameters_buffers: &mut MeshClassIndirectParametersBuffers<IP>,
2585 retained_view_entity: RetainedViewEntity,
2586 ) where
2587 IP: Clone + ShaderSize + WriteInto,
2588 {
2589 indirect_parameters_buffers
2590 .view_to_indirect_parameters_batch_ranges
2591 .entry(retained_view_entity)
2592 .or_default()
2593 .push(self.initial_indirect_parameters_index..self.indirect_parameters_index);
2594 }
2595}
2596
2597pub fn collect_buffers_for_phase<PI, GFBD>(
2612 mut phase_batched_instance_buffers: ResMut<PhaseBatchedInstanceBuffers<PI, GFBD::BufferData>>,
2613 mut phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<PI>>,
2614 mut batched_instance_buffers: ResMut<
2615 BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
2616 >,
2617 mut indirect_parameters_buffers: ResMut<IndirectParametersBuffers>,
2618 indirect_parameters_buffers_settings: Res<IndirectParametersBuffersSettings>,
2619) where
2620 PI: PhaseItem,
2621 GFBD: GetFullBatchData + Send + Sync + 'static,
2622{
2623 let untyped_phase_batched_instance_buffers =
2627 mem::take(&mut phase_batched_instance_buffers.buffers);
2628 if let Some(mut old_untyped_phase_batched_instance_buffers) = batched_instance_buffers
2629 .phase_instance_buffers
2630 .insert(TypeId::of::<PI>(), untyped_phase_batched_instance_buffers)
2631 {
2632 old_untyped_phase_batched_instance_buffers.clear();
2633 phase_batched_instance_buffers.buffers = old_untyped_phase_batched_instance_buffers;
2634 }
2635
2636 let untyped_phase_indirect_parameters_buffers = mem::replace(
2640 &mut phase_indirect_parameters_buffers.buffers,
2641 UntypedPhaseIndirectParametersBuffers::new(
2642 indirect_parameters_buffers_settings.allow_copies_from_indirect_parameter_buffers,
2643 ),
2644 );
2645 if let Some(mut old_untyped_phase_indirect_parameters_buffers) = indirect_parameters_buffers
2646 .insert(
2647 TypeId::of::<PI>(),
2648 untyped_phase_indirect_parameters_buffers,
2649 )
2650 {
2651 old_untyped_phase_indirect_parameters_buffers.clear();
2652 phase_indirect_parameters_buffers.buffers = old_untyped_phase_indirect_parameters_buffers;
2653 }
2654}
2655
2656pub fn prepare_indirect_parameters_build_jobs(
2661 mut build_indirect_parameters_metadata: ResMut<BuildIndirectParametersMetadata>,
2662 mut indirect_parameters_build_jobs: ResMut<IndirectParametersBuildJobs>,
2663 indirect_parameters_buffers: Res<IndirectParametersBuffers>,
2664) {
2665 indirect_parameters_build_jobs.clear();
2668 build_indirect_parameters_metadata.clear();
2669
2670 for (phase_type_id, phase_indirect_parameters_buffers) in indirect_parameters_buffers.iter() {
2672 for (retained_view_entity, indirect_parameters_ranges) in phase_indirect_parameters_buffers
2674 .indexed
2675 .view_to_indirect_parameters_batch_ranges
2676 .iter()
2677 {
2678 for indirect_parameters_range in indirect_parameters_ranges {
2679 let uniform_offset = indirect_parameters_build_jobs.push(
2680 IndirectParametersBuildJob::new(indirect_parameters_range.clone()),
2681 );
2682 build_indirect_parameters_metadata
2683 .entry(*retained_view_entity)
2684 .or_default()
2685 .entry(*phase_type_id)
2686 .or_default()
2687 .indexed
2688 .push(MeshClassBuildIndirectParametersMetadata {
2689 uniform_offset,
2690 batch_count: indirect_parameters_range.end
2691 - indirect_parameters_range.start,
2692 });
2693 }
2694 }
2695
2696 for (retained_view_entity, indirect_parameters_ranges) in phase_indirect_parameters_buffers
2698 .non_indexed
2699 .view_to_indirect_parameters_batch_ranges
2700 .iter()
2701 {
2702 for indirect_parameters_range in indirect_parameters_ranges {
2703 let uniform_offset = indirect_parameters_build_jobs.push(
2704 IndirectParametersBuildJob::new(indirect_parameters_range.clone()),
2705 );
2706 build_indirect_parameters_metadata
2707 .entry(*retained_view_entity)
2708 .or_default()
2709 .entry(*phase_type_id)
2710 .or_default()
2711 .non_indexed
2712 .push(MeshClassBuildIndirectParametersMetadata {
2713 uniform_offset,
2714 batch_count: indirect_parameters_range.end
2715 - indirect_parameters_range.start,
2716 });
2717 }
2718 }
2719 }
2720}
2721
2722pub fn write_batched_instance_buffers<GFBD>(
2724 render_device: Res<RenderDevice>,
2725 render_queue: Res<RenderQueue>,
2726 gpu_array_buffer: ResMut<BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>>,
2727 pipeline_cache: Res<PipelineCache>,
2728 mut bin_unpacking_buffers: ResMut<SceneUnpackingBuffers>,
2729 mut sparse_buffer_update_jobs: ResMut<SparseBufferUpdateJobs>,
2730 mut sparse_buffer_update_bind_groups: ResMut<SparseBufferUpdateBindGroups>,
2731 sparse_buffer_update_pipelines: Res<SparseBufferUpdatePipelines>,
2732) where
2733 GFBD: GetFullBatchData,
2734{
2735 let BatchedInstanceBuffers {
2736 current_input_buffer,
2737 previous_input_buffer,
2738 phase_instance_buffers,
2739 } = gpu_array_buffer.into_inner();
2740
2741 let render_device = &*render_device;
2742 let render_queue = &*render_queue;
2743
2744 let bin_unpacking_buffers = &mut *bin_unpacking_buffers;
2745
2746 ComputeTaskPool::get().scope(|scope| {
2747 scope.spawn(async {
2748 #[cfg(feature = "trace")]
2749 let _span = bevy_log::info_span!("write_current_input_buffers").entered();
2750 current_input_buffer
2751 .buffer
2752 .write_buffers(render_device, render_queue);
2753 });
2754 scope.spawn(async {
2755 #[cfg(feature = "trace")]
2756 let _span = bevy_log::info_span!("write_previous_input_buffers").entered();
2757 previous_input_buffer.write_buffer(render_device, render_queue);
2758 });
2759
2760 for phase_instance_buffers in phase_instance_buffers.values_mut() {
2761 let UntypedPhaseBatchedInstanceBuffers {
2762 ref mut data_buffer,
2763 ref mut work_item_buffers,
2764 ref mut late_indexed_indirect_parameters_buffer,
2765 ref mut late_non_indexed_indirect_parameters_buffer,
2766 } = *phase_instance_buffers;
2767
2768 scope.spawn(async {
2769 #[cfg(feature = "trace")]
2770 let _span = bevy_log::info_span!("write_phase_instance_buffers").entered();
2771 data_buffer.write_buffer(render_device);
2772 late_indexed_indirect_parameters_buffer.write_buffer(render_device, render_queue);
2773 late_non_indexed_indirect_parameters_buffer
2774 .write_buffer(render_device, render_queue);
2775 });
2776
2777 for phase_work_item_buffers in work_item_buffers.values_mut() {
2778 scope.spawn(async {
2779 #[cfg(feature = "trace")]
2780 let _span = bevy_log::info_span!("write_work_item_buffers").entered();
2781 match *phase_work_item_buffers {
2782 PreprocessWorkItemBuffers::Direct(ref mut buffer_vec) => {
2783 buffer_vec.write_buffer(render_device, render_queue);
2784 }
2785 PreprocessWorkItemBuffers::Indirect {
2786 ref mut indexed,
2787 ref mut non_indexed,
2788 ref mut gpu_occlusion_culling,
2789 } => {
2790 indexed.write_buffer(render_device, render_queue);
2791 non_indexed.write_buffer(render_device, render_queue);
2792
2793 if let Some(GpuOcclusionCullingWorkItemBuffers {
2794 ref mut late_indexed,
2795 ref mut late_non_indexed,
2796 late_indirect_parameters_indexed_offset: _,
2797 late_indirect_parameters_non_indexed_offset: _,
2798 }) = *gpu_occlusion_culling
2799 {
2800 if !late_indexed.is_empty() {
2801 late_indexed.write_buffer(render_device);
2802 }
2803 if !late_non_indexed.is_empty() {
2804 late_non_indexed.write_buffer(render_device);
2805 }
2806 }
2807 }
2808 }
2809 });
2810 }
2811 }
2812
2813 scope.spawn(async {
2814 bin_unpacking_buffers
2815 .bin_unpacking_metadata
2816 .write_buffer(render_device, render_queue);
2817 });
2818 scope.spawn(async {
2819 bin_unpacking_buffers
2820 .uniform_allocation_metadata
2821 .write_buffer(render_device, render_queue);
2822 });
2823 });
2824
2825 current_input_buffer.buffer.prepare_to_populate_buffers(
2828 render_device,
2829 &pipeline_cache,
2830 &mut sparse_buffer_update_jobs,
2831 &mut sparse_buffer_update_bind_groups,
2832 &sparse_buffer_update_pipelines,
2833 );
2834}
2835
2836pub fn write_binned_instance_buffers<BPI, GFBD>(
2841 mut views: Query<&ExtractedView>,
2842 mut view_binned_render_phases: ResMut<ViewBinnedRenderPhases<BPI>>,
2843 bin_unpacking_buffers: ResMut<SceneUnpackingBuffers>,
2844 render_device: Res<RenderDevice>,
2845 render_queue: Res<RenderQueue>,
2846) where
2847 BPI: BinnedPhaseItem,
2848 GFBD: GetFullBatchData,
2849{
2850 let bin_unpacking_buffers = bin_unpacking_buffers.into_inner();
2851
2852 let phase_type_id = TypeId::of::<BPI>();
2853
2854 let mut all_seen_view_entities = HashSet::new();
2857
2858 for extracted_view in &mut views {
2859 all_seen_view_entities.insert(extracted_view.retained_view_entity);
2860
2861 let Some(view_binned_render_phase) =
2862 view_binned_render_phases.get_mut(&extracted_view.retained_view_entity)
2863 else {
2864 continue;
2865 };
2866
2867 let BinnedRenderPhaseBatchSets::MultidrawIndirect(ref batch_sets) =
2870 view_binned_render_phase.batch_sets
2871 else {
2872 continue;
2873 };
2874
2875 let view_phase_bin_unpacking_buffers = bin_unpacking_buffers
2878 .view_phase_buffers
2879 .entry(SceneUnpackingBuffersKey {
2880 phase: phase_type_id,
2881 view: extracted_view.retained_view_entity,
2882 })
2883 .or_default();
2884
2885 view_phase_bin_unpacking_buffers
2887 .indexed_unpacking_jobs
2888 .clear();
2889 view_phase_bin_unpacking_buffers
2890 .non_indexed_unpacking_jobs
2891 .clear();
2892
2893 let mut representative_entity_to_batch_set_scene_unpacking_metadata =
2904 MainEntityHashMap::default();
2905
2906 for batch_set in batch_sets {
2907 let main_entity = batch_set.first_batch.representative_entity.1;
2908 if *main_entity != Entity::PLACEHOLDER
2909 && let PhaseItemExtraIndex::IndirectParametersIndex {
2910 range: ref indirect_parameters_range,
2911 ..
2912 } = batch_set.first_batch.extra_index
2913 {
2914 representative_entity_to_batch_set_scene_unpacking_metadata.insert(
2917 main_entity,
2918 BatchSetSceneUnpackingMetadata {
2919 base_output_work_item_index: batch_set.first_work_item_index,
2920 base_indirect_parameters_index: indirect_parameters_range.start,
2921 batch_set_index: batch_set.index,
2922 first_indirect_parameters_index: batch_set.first_indirect_parameters_index,
2923 first_output_mesh_uniform_index: batch_set.first_output_mesh_uniform_index,
2924 },
2925 );
2926 }
2927 }
2928
2929 for (batch_set_key, batch_set) in view_binned_render_phase.multidrawable_meshes.iter_mut() {
2935 let Some(representative_entity) = batch_set.representative_entity() else {
2936 continue;
2937 };
2938 let Some(scene_unpacking_metadata) =
2939 representative_entity_to_batch_set_scene_unpacking_metadata
2940 .get(&representative_entity)
2941 else {
2942 continue;
2943 };
2944
2945 batch_set
2948 .gpu_buffers
2949 .render_binned_mesh_instance_buffer
2950 .write_buffer(&render_device, &render_queue);
2951 batch_set
2952 .gpu_buffers
2953 .bin_metadata_buffer
2954 .write_buffer(&render_device, &render_queue);
2955 batch_set
2956 .gpu_buffers
2957 .fan_buffer
2958 .write_buffer(&render_device);
2959 batch_set
2960 .gpu_buffers
2961 .bin_index_to_bin_metadata_index_buffer
2962 .write_buffer(&render_device, &render_queue);
2963
2964 let (
2965 Some(render_bin_entry_buffer),
2966 Some(bin_metadata_buffer),
2967 Some(fan_buffer),
2968 Some(bin_index_to_bin_metadata_index_buffer),
2969 ) = (
2970 batch_set
2971 .gpu_buffers
2972 .render_binned_mesh_instance_buffer
2973 .buffer(),
2974 batch_set.gpu_buffers.bin_metadata_buffer.buffer(),
2975 batch_set.gpu_buffers.fan_buffer.buffer(),
2976 batch_set
2977 .gpu_buffers
2978 .bin_index_to_bin_metadata_index_buffer
2979 .buffer(),
2980 )
2981 else {
2982 continue;
2983 };
2984
2985 let binned_mesh_instance_count = batch_set
2986 .gpu_buffers
2987 .render_binned_mesh_instance_buffer
2988 .len() as u32;
2989 let bin_count = batch_set.bin_count() as u32;
2990
2991 let gpu_bin_unpacking_metadata_index = bin_unpacking_buffers
2993 .bin_unpacking_metadata
2994 .push(GpuBinUnpackingMetadata {
2995 base_output_work_item_index: scene_unpacking_metadata
2996 .base_output_work_item_index,
2997 base_indirect_parameters_index: scene_unpacking_metadata
2998 .base_indirect_parameters_index,
2999 binned_mesh_instance_count,
3000 pad: [0; _],
3001 });
3002
3003 let gpu_uniform_allocation_metadata_index = bin_unpacking_buffers
3005 .uniform_allocation_metadata
3006 .push(GpuUniformAllocationMetadata {
3007 batch_set_index: scene_unpacking_metadata.batch_set_index,
3008 bin_count,
3009 first_indirect_parameters_index: scene_unpacking_metadata
3010 .first_indirect_parameters_index,
3011 first_output_mesh_uniform_index: scene_unpacking_metadata
3012 .first_output_mesh_uniform_index,
3013 pad: [0; _],
3014 });
3015
3016 let (
3017 Some(gpu_bin_unpacking_metadata_index),
3018 Some(gpu_uniform_allocation_metadata_index),
3019 ) = (
3020 NonMaxU32::new(gpu_bin_unpacking_metadata_index as u32),
3021 NonMaxU32::new(gpu_uniform_allocation_metadata_index as u32),
3022 )
3023 else {
3024 continue;
3025 };
3026
3027 let job = SceneUnpackingJob {
3029 render_binned_mesh_instance_buffer: render_bin_entry_buffer.clone(),
3030 bin_metadata_buffer: bin_metadata_buffer.clone(),
3031 fan_buffer: fan_buffer.clone(),
3032 bin_index_to_bin_metadata_index_buffer: bin_index_to_bin_metadata_index_buffer
3033 .clone(),
3034 bin_unpacking_metadata_index: BinUnpackingMetadataIndex(
3035 gpu_bin_unpacking_metadata_index,
3036 ),
3037 uniform_allocation_metadata_index: UniformAllocationMetadataIndex(
3038 gpu_uniform_allocation_metadata_index,
3039 ),
3040 mesh_instance_count: binned_mesh_instance_count,
3041 bin_count,
3042 };
3043
3044 if batch_set_key.indexed() {
3045 view_phase_bin_unpacking_buffers
3046 .indexed_unpacking_jobs
3047 .push(job);
3048 } else {
3049 view_phase_bin_unpacking_buffers
3050 .non_indexed_unpacking_jobs
3051 .push(job);
3052 }
3053 }
3054 }
3055
3056 bin_unpacking_buffers
3058 .view_phase_buffers
3059 .retain(|bin_unpacking_buffers_key, _| {
3060 bin_unpacking_buffers_key.phase != phase_type_id
3061 || all_seen_view_entities.contains(&bin_unpacking_buffers_key.view)
3062 });
3063}
3064
3065pub fn clear_scene_unpacking_buffers(mut scene_unpacking_buffers: ResMut<SceneUnpackingBuffers>) {
3067 scene_unpacking_buffers.bin_unpacking_metadata.clear();
3068 scene_unpacking_buffers.uniform_allocation_metadata.clear();
3069}
3070
3071struct BatchSetSceneUnpackingMetadata {
3074 base_output_work_item_index: u32,
3077 base_indirect_parameters_index: u32,
3080 batch_set_index: u32,
3082 first_indirect_parameters_index: u32,
3085 first_output_mesh_uniform_index: u32,
3087}
3088
3089pub fn clear_indirect_parameters_buffers(
3090 mut indirect_parameters_buffers: ResMut<IndirectParametersBuffers>,
3091) {
3092 for phase_indirect_parameters_buffers in indirect_parameters_buffers.values_mut() {
3093 phase_indirect_parameters_buffers.clear();
3094 }
3095}
3096
3097pub fn write_indirect_parameters_buffers(
3098 render_device: Res<RenderDevice>,
3099 render_queue: Res<RenderQueue>,
3100 mut indirect_parameters_buffers: ResMut<IndirectParametersBuffers>,
3101) {
3102 let render_device = &*render_device;
3103 let render_queue = &*render_queue;
3104 ComputeTaskPool::get().scope(|scope| {
3105 for phase_indirect_parameters_buffers in indirect_parameters_buffers.values_mut() {
3106 scope.spawn(async {
3107 #[cfg(feature = "trace")]
3108 let _span = bevy_log::info_span!("indexed_data").entered();
3109 phase_indirect_parameters_buffers
3110 .indexed
3111 .indirect_draw_parameters
3112 .write_buffer(render_device);
3113 });
3114 scope.spawn(async {
3115 #[cfg(feature = "trace")]
3116 let _span = bevy_log::info_span!("non_indexed_data").entered();
3117 phase_indirect_parameters_buffers
3118 .non_indexed
3119 .indirect_draw_parameters
3120 .write_buffer(render_device);
3121 });
3122
3123 scope.spawn(async {
3124 #[cfg(feature = "trace")]
3125 let _span = bevy_log::info_span!("indexed_cpu_metadata").entered();
3126 phase_indirect_parameters_buffers
3127 .indexed
3128 .metadata
3129 .write_buffer(render_device, render_queue);
3130 });
3131 scope.spawn(async {
3132 #[cfg(feature = "trace")]
3133 let _span = bevy_log::info_span!("non_indexed_cpu_metadata").entered();
3134 phase_indirect_parameters_buffers
3135 .non_indexed
3136 .metadata
3137 .write_buffer(render_device, render_queue);
3138 });
3139
3140 scope.spawn(async {
3141 #[cfg(feature = "trace")]
3142 let _span = bevy_log::info_span!("indexed_batch_sets").entered();
3143 phase_indirect_parameters_buffers
3144 .indexed
3145 .batch_sets
3146 .write_buffer(render_device, render_queue);
3147 });
3148 scope.spawn(async {
3149 #[cfg(feature = "trace")]
3150 let _span = bevy_log::info_span!("non_indexed_batch_sets").entered();
3151 phase_indirect_parameters_buffers
3152 .non_indexed
3153 .batch_sets
3154 .write_buffer(render_device, render_queue);
3155 });
3156 }
3157 });
3158}
3159
3160pub fn write_indirect_parameters_build_jobs(
3163 render_device: Res<RenderDevice>,
3164 render_queue: Res<RenderQueue>,
3165 mut indirect_parameters_build_jobs: ResMut<IndirectParametersBuildJobs>,
3166) {
3167 indirect_parameters_build_jobs
3168 .buffer
3169 .write_buffer(&render_device, &render_queue);
3170}
3171
3172#[cfg(test)]
3173mod tests {
3174 use bytemuck::{Pod, Zeroable};
3175
3176 use crate::impl_atomic_pod;
3177
3178 use super::*;
3179
3180 #[derive(Clone, Copy, Default, PartialEq, Debug, Pod, Zeroable)]
3181 #[repr(C)]
3182 struct TestData(u32);
3183
3184 impl_atomic_pod!(TestData, TestDataBlob);
3185
3186 #[test]
3187 fn instance_buffer_correct_behavior() {
3188 let mut instance_buffer = InstanceInputUniformBuffer::new();
3189
3190 let index = instance_buffer.add(TestData(2));
3191 instance_buffer.remove(index);
3192 assert_eq!(instance_buffer.get_unchecked(index), TestData(2));
3193 assert_eq!(instance_buffer.get(index), None);
3194
3195 instance_buffer.add(TestData(5));
3196 assert_eq!(instance_buffer.buffer().len(), 1);
3197 }
3198}