1use core::{any::TypeId, marker::PhantomData, mem};
4
5use bevy_app::{App, Plugin};
6use bevy_derive::{Deref, DerefMut};
7use bevy_ecs::{
8 prelude::Entity,
9 query::{Has, With},
10 resource::Resource,
11 schedule::IntoScheduleConfigs as _,
12 system::{Query, Res, ResMut, StaticSystemParam},
13 world::{FromWorld, World},
14};
15use bevy_encase_derive::ShaderType;
16use bevy_math::UVec4;
17use bevy_platform::collections::{hash_map::Entry, HashMap, HashSet};
18use bevy_utils::{default, TypeIdMap};
19use bytemuck::{Pod, Zeroable};
20use encase::{internal::WriteInto, ShaderSize};
21use indexmap::IndexMap;
22use nonmax::NonMaxU32;
23use tracing::{error, info};
24use wgpu::{BindingResource, BufferUsages, DownlevelFlags, Features};
25
26use crate::{
27 experimental::occlusion_culling::OcclusionCulling,
28 render_phase::{
29 BinnedPhaseItem, BinnedRenderPhaseBatch, BinnedRenderPhaseBatchSet,
30 BinnedRenderPhaseBatchSets, CachedRenderPipelinePhaseItem, PhaseItem,
31 PhaseItemBatchSetKey as _, PhaseItemExtraIndex, RenderBin, SortedPhaseItem,
32 SortedRenderPhase, UnbatchableBinnedEntityIndices, ViewBinnedRenderPhases,
33 ViewSortedRenderPhases,
34 },
35 render_resource::{Buffer, GpuArrayBufferable, RawBufferVec, UninitBufferVec},
36 renderer::{RenderAdapter, RenderDevice, RenderQueue},
37 sync_world::MainEntity,
38 view::{ExtractedView, NoIndirectDrawing, RetainedViewEntity},
39 Render, RenderApp, RenderDebugFlags, RenderSet,
40};
41
42use super::{BatchMeta, GetBatchData, GetFullBatchData};
43
44#[derive(Default)]
45pub struct BatchingPlugin {
46 pub debug_flags: RenderDebugFlags,
48}
49
50impl Plugin for BatchingPlugin {
51 fn build(&self, app: &mut App) {
52 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
53 return;
54 };
55
56 render_app
57 .insert_resource(IndirectParametersBuffers::new(
58 self.debug_flags
59 .contains(RenderDebugFlags::ALLOW_COPIES_FROM_INDIRECT_PARAMETERS),
60 ))
61 .add_systems(
62 Render,
63 write_indirect_parameters_buffers.in_set(RenderSet::PrepareResourcesFlush),
64 )
65 .add_systems(
66 Render,
67 clear_indirect_parameters_buffers.in_set(RenderSet::ManageViews),
68 );
69 }
70
71 fn finish(&self, app: &mut App) {
72 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
73 return;
74 };
75
76 render_app.init_resource::<GpuPreprocessingSupport>();
77 }
78}
79
80#[derive(Clone, Copy, PartialEq, Resource)]
89pub struct GpuPreprocessingSupport {
90 pub max_supported_mode: GpuPreprocessingMode,
92}
93
94impl GpuPreprocessingSupport {
95 #[inline]
97 pub fn is_available(&self) -> bool {
98 self.max_supported_mode != GpuPreprocessingMode::None
99 }
100
101 pub fn min(&self, mode: GpuPreprocessingMode) -> GpuPreprocessingMode {
104 match (self.max_supported_mode, mode) {
105 (GpuPreprocessingMode::None, _) | (_, GpuPreprocessingMode::None) => {
106 GpuPreprocessingMode::None
107 }
108 (mode, GpuPreprocessingMode::Culling) | (GpuPreprocessingMode::Culling, mode) => mode,
109 (GpuPreprocessingMode::PreprocessingOnly, GpuPreprocessingMode::PreprocessingOnly) => {
110 GpuPreprocessingMode::PreprocessingOnly
111 }
112 }
113 }
114
115 pub fn is_culling_supported(&self) -> bool {
117 self.max_supported_mode == GpuPreprocessingMode::Culling
118 }
119}
120
121#[derive(Clone, Copy, PartialEq)]
123pub enum GpuPreprocessingMode {
124 None,
128
129 PreprocessingOnly,
134
135 Culling,
139}
140
141#[derive(Resource)]
152pub struct BatchedInstanceBuffers<BD, BDI>
153where
154 BD: GpuArrayBufferable + Sync + Send + 'static,
155 BDI: Pod + Default,
156{
157 pub current_input_buffer: InstanceInputUniformBuffer<BDI>,
161
162 pub previous_input_buffer: InstanceInputUniformBuffer<BDI>,
170
171 pub phase_instance_buffers: TypeIdMap<UntypedPhaseBatchedInstanceBuffers<BD>>,
176}
177
178impl<BD, BDI> Default for BatchedInstanceBuffers<BD, BDI>
179where
180 BD: GpuArrayBufferable + Sync + Send + 'static,
181 BDI: Pod + Sync + Send + Default + 'static,
182{
183 fn default() -> Self {
184 BatchedInstanceBuffers {
185 current_input_buffer: InstanceInputUniformBuffer::new(),
186 previous_input_buffer: InstanceInputUniformBuffer::new(),
187 phase_instance_buffers: HashMap::default(),
188 }
189 }
190}
191
192#[derive(Resource)]
202pub struct PhaseBatchedInstanceBuffers<PI, BD>
203where
204 PI: PhaseItem,
205 BD: GpuArrayBufferable + Sync + Send + 'static,
206{
207 pub buffers: UntypedPhaseBatchedInstanceBuffers<BD>,
209 phantom: PhantomData<PI>,
210}
211
212impl<PI, BD> Default for PhaseBatchedInstanceBuffers<PI, BD>
213where
214 PI: PhaseItem,
215 BD: GpuArrayBufferable + Sync + Send + 'static,
216{
217 fn default() -> Self {
218 PhaseBatchedInstanceBuffers {
219 buffers: UntypedPhaseBatchedInstanceBuffers::default(),
220 phantom: PhantomData,
221 }
222 }
223}
224
225pub struct UntypedPhaseBatchedInstanceBuffers<BD>
231where
232 BD: GpuArrayBufferable + Sync + Send + 'static,
233{
234 pub data_buffer: UninitBufferVec<BD>,
239
240 pub work_item_buffers: HashMap<RetainedViewEntity, PreprocessWorkItemBuffers>,
245
246 pub late_indexed_indirect_parameters_buffer:
254 RawBufferVec<LatePreprocessWorkItemIndirectParameters>,
255
256 pub late_non_indexed_indirect_parameters_buffer:
264 RawBufferVec<LatePreprocessWorkItemIndirectParameters>,
265}
266
267pub struct InstanceInputUniformBuffer<BDI>
273where
274 BDI: Pod + Default,
275{
276 buffer: RawBufferVec<BDI>,
278
279 free_uniform_indices: Vec<u32>,
284}
285
286impl<BDI> InstanceInputUniformBuffer<BDI>
287where
288 BDI: Pod + Default,
289{
290 pub fn new() -> InstanceInputUniformBuffer<BDI> {
292 InstanceInputUniformBuffer {
293 buffer: RawBufferVec::new(BufferUsages::STORAGE),
294 free_uniform_indices: vec![],
295 }
296 }
297
298 pub fn clear(&mut self) {
300 self.buffer.clear();
301 self.free_uniform_indices.clear();
302 }
303
304 #[inline]
306 pub fn buffer(&self) -> &RawBufferVec<BDI> {
307 &self.buffer
308 }
309
310 pub fn add(&mut self, element: BDI) -> u32 {
313 match self.free_uniform_indices.pop() {
314 Some(uniform_index) => {
315 self.buffer.values_mut()[uniform_index as usize] = element;
316 uniform_index
317 }
318 None => self.buffer.push(element) as u32,
319 }
320 }
321
322 pub fn remove(&mut self, uniform_index: u32) {
326 self.free_uniform_indices.push(uniform_index);
327 }
328
329 pub fn get(&self, uniform_index: u32) -> Option<BDI> {
333 if (uniform_index as usize) >= self.buffer.len()
334 || self.free_uniform_indices.contains(&uniform_index)
335 {
336 None
337 } else {
338 Some(self.get_unchecked(uniform_index))
339 }
340 }
341
342 pub fn get_unchecked(&self, uniform_index: u32) -> BDI {
348 self.buffer.values()[uniform_index as usize]
349 }
350
351 pub fn set(&mut self, uniform_index: u32, element: BDI) {
356 self.buffer.values_mut()[uniform_index as usize] = element;
357 }
358
359 pub fn ensure_nonempty(&mut self) {
362 if self.buffer.is_empty() {
363 self.buffer.push(default());
364 }
365 }
366
367 pub fn len(&self) -> usize {
369 self.buffer.len()
370 }
371
372 pub fn is_empty(&self) -> bool {
375 self.buffer.is_empty()
376 }
377
378 pub fn into_buffer(self) -> RawBufferVec<BDI> {
381 self.buffer
382 }
383}
384
385impl<BDI> Default for InstanceInputUniformBuffer<BDI>
386where
387 BDI: Pod + Default,
388{
389 fn default() -> Self {
390 Self::new()
391 }
392}
393
394pub enum PreprocessWorkItemBuffers {
396 Direct(RawBufferVec<PreprocessWorkItem>),
401
402 Indirect {
408 indexed: RawBufferVec<PreprocessWorkItem>,
410 non_indexed: RawBufferVec<PreprocessWorkItem>,
412 gpu_occlusion_culling: Option<GpuOcclusionCullingWorkItemBuffers>,
414 },
415}
416
417pub struct GpuOcclusionCullingWorkItemBuffers {
419 pub late_indexed: UninitBufferVec<PreprocessWorkItem>,
421 pub late_non_indexed: UninitBufferVec<PreprocessWorkItem>,
423 pub late_indirect_parameters_indexed_offset: u32,
427 pub late_indirect_parameters_non_indexed_offset: u32,
431}
432
433#[derive(Clone, Copy, ShaderType, Pod, Zeroable)]
439#[repr(C)]
440pub struct LatePreprocessWorkItemIndirectParameters {
441 dispatch_x: u32,
445 dispatch_y: u32,
448 dispatch_z: u32,
451 work_item_count: u32,
457 pad: UVec4,
459}
460
461impl Default for LatePreprocessWorkItemIndirectParameters {
462 fn default() -> LatePreprocessWorkItemIndirectParameters {
463 LatePreprocessWorkItemIndirectParameters {
464 dispatch_x: 0,
465 dispatch_y: 1,
466 dispatch_z: 1,
467 work_item_count: 0,
468 pad: default(),
469 }
470 }
471}
472
473pub fn get_or_create_work_item_buffer<'a, I>(
482 work_item_buffers: &'a mut HashMap<RetainedViewEntity, PreprocessWorkItemBuffers>,
483 view: RetainedViewEntity,
484 no_indirect_drawing: bool,
485 enable_gpu_occlusion_culling: bool,
486) -> &'a mut PreprocessWorkItemBuffers
487where
488 I: 'static,
489{
490 let preprocess_work_item_buffers = match work_item_buffers.entry(view) {
491 Entry::Occupied(occupied_entry) => occupied_entry.into_mut(),
492 Entry::Vacant(vacant_entry) => {
493 if no_indirect_drawing {
494 vacant_entry.insert(PreprocessWorkItemBuffers::Direct(RawBufferVec::new(
495 BufferUsages::STORAGE,
496 )))
497 } else {
498 vacant_entry.insert(PreprocessWorkItemBuffers::Indirect {
499 indexed: RawBufferVec::new(BufferUsages::STORAGE),
500 non_indexed: RawBufferVec::new(BufferUsages::STORAGE),
501 gpu_occlusion_culling: None,
504 })
505 }
506 }
507 };
508
509 if let PreprocessWorkItemBuffers::Indirect {
511 ref mut gpu_occlusion_culling,
512 ..
513 } = *preprocess_work_item_buffers
514 {
515 match (
516 enable_gpu_occlusion_culling,
517 gpu_occlusion_culling.is_some(),
518 ) {
519 (false, false) | (true, true) => {}
520 (false, true) => {
521 *gpu_occlusion_culling = None;
522 }
523 (true, false) => {
524 *gpu_occlusion_culling = Some(GpuOcclusionCullingWorkItemBuffers {
525 late_indexed: UninitBufferVec::new(BufferUsages::STORAGE),
526 late_non_indexed: UninitBufferVec::new(BufferUsages::STORAGE),
527 late_indirect_parameters_indexed_offset: 0,
528 late_indirect_parameters_non_indexed_offset: 0,
529 });
530 }
531 }
532 }
533
534 preprocess_work_item_buffers
535}
536
537pub fn init_work_item_buffers(
539 work_item_buffers: &mut PreprocessWorkItemBuffers,
540 late_indexed_indirect_parameters_buffer: &'_ mut RawBufferVec<
541 LatePreprocessWorkItemIndirectParameters,
542 >,
543 late_non_indexed_indirect_parameters_buffer: &'_ mut RawBufferVec<
544 LatePreprocessWorkItemIndirectParameters,
545 >,
546) {
547 if let PreprocessWorkItemBuffers::Indirect {
550 gpu_occlusion_culling:
551 Some(GpuOcclusionCullingWorkItemBuffers {
552 ref mut late_indirect_parameters_indexed_offset,
553 ref mut late_indirect_parameters_non_indexed_offset,
554 ..
555 }),
556 ..
557 } = *work_item_buffers
558 {
559 *late_indirect_parameters_indexed_offset = late_indexed_indirect_parameters_buffer
560 .push(LatePreprocessWorkItemIndirectParameters::default())
561 as u32;
562 *late_indirect_parameters_non_indexed_offset = late_non_indexed_indirect_parameters_buffer
563 .push(LatePreprocessWorkItemIndirectParameters::default())
564 as u32;
565 }
566}
567
568impl PreprocessWorkItemBuffers {
569 pub fn push(&mut self, indexed: bool, preprocess_work_item: PreprocessWorkItem) {
574 match *self {
575 PreprocessWorkItemBuffers::Direct(ref mut buffer) => {
576 buffer.push(preprocess_work_item);
577 }
578 PreprocessWorkItemBuffers::Indirect {
579 indexed: ref mut indexed_buffer,
580 non_indexed: ref mut non_indexed_buffer,
581 ref mut gpu_occlusion_culling,
582 } => {
583 if indexed {
584 indexed_buffer.push(preprocess_work_item);
585 } else {
586 non_indexed_buffer.push(preprocess_work_item);
587 }
588
589 if let Some(ref mut gpu_occlusion_culling) = *gpu_occlusion_culling {
590 if indexed {
591 gpu_occlusion_culling.late_indexed.add();
592 } else {
593 gpu_occlusion_culling.late_non_indexed.add();
594 }
595 }
596 }
597 }
598 }
599
600 pub fn clear(&mut self) {
602 match *self {
603 PreprocessWorkItemBuffers::Direct(ref mut buffer) => {
604 buffer.clear();
605 }
606 PreprocessWorkItemBuffers::Indirect {
607 indexed: ref mut indexed_buffer,
608 non_indexed: ref mut non_indexed_buffer,
609 ref mut gpu_occlusion_culling,
610 } => {
611 indexed_buffer.clear();
612 non_indexed_buffer.clear();
613
614 if let Some(ref mut gpu_occlusion_culling) = *gpu_occlusion_culling {
615 gpu_occlusion_culling.late_indexed.clear();
616 gpu_occlusion_culling.late_non_indexed.clear();
617 gpu_occlusion_culling.late_indirect_parameters_indexed_offset = 0;
618 gpu_occlusion_culling.late_indirect_parameters_non_indexed_offset = 0;
619 }
620 }
621 }
622 }
623}
624
625#[derive(Clone, Copy, Default, Pod, Zeroable, ShaderType)]
628#[repr(C)]
629pub struct PreprocessWorkItem {
630 pub input_index: u32,
633
634 pub output_or_indirect_parameters_index: u32,
642}
643
644#[derive(Clone, Copy, Debug, Pod, Zeroable, ShaderType)]
649#[repr(C)]
650pub struct IndirectParametersIndexed {
651 pub index_count: u32,
653 pub instance_count: u32,
655 pub first_index: u32,
657 pub base_vertex: u32,
659 pub first_instance: u32,
661}
662
663#[derive(Clone, Copy, Debug, Pod, Zeroable, ShaderType)]
668#[repr(C)]
669pub struct IndirectParametersNonIndexed {
670 pub vertex_count: u32,
672 pub instance_count: u32,
674 pub base_vertex: u32,
676 pub first_instance: u32,
678}
679
680#[derive(Clone, Copy, Default, Pod, Zeroable, ShaderType)]
685#[repr(C)]
686pub struct IndirectParametersCpuMetadata {
687 pub base_output_index: u32,
695
696 pub batch_set_index: u32,
704}
705
706#[derive(Clone, Copy, Default, Pod, Zeroable, ShaderType)]
716#[repr(C)]
717pub struct IndirectParametersGpuMetadata {
718 pub mesh_index: u32,
721
722 pub early_instance_count: u32,
727
728 pub late_instance_count: u32,
734}
735
736#[derive(Clone, Copy, Default, Pod, Zeroable, ShaderType)]
748#[repr(C)]
749pub struct IndirectBatchSet {
750 pub indirect_parameters_count: u32,
759
760 pub indirect_parameters_base: u32,
766}
767
768#[derive(Resource, Deref, DerefMut)]
778pub struct IndirectParametersBuffers {
779 #[deref]
784 pub buffers: TypeIdMap<UntypedPhaseIndirectParametersBuffers>,
785 pub allow_copies_from_indirect_parameter_buffers: bool,
791}
792
793impl IndirectParametersBuffers {
794 pub fn new(allow_copies_from_indirect_parameter_buffers: bool) -> IndirectParametersBuffers {
796 IndirectParametersBuffers {
797 buffers: TypeIdMap::default(),
798 allow_copies_from_indirect_parameter_buffers,
799 }
800 }
801}
802
803#[derive(Resource)]
811pub struct PhaseIndirectParametersBuffers<PI>
812where
813 PI: PhaseItem,
814{
815 pub buffers: UntypedPhaseIndirectParametersBuffers,
817 phantom: PhantomData<PI>,
818}
819
820impl<PI> PhaseIndirectParametersBuffers<PI>
821where
822 PI: PhaseItem,
823{
824 pub fn new(allow_copies_from_indirect_parameter_buffers: bool) -> Self {
825 PhaseIndirectParametersBuffers {
826 buffers: UntypedPhaseIndirectParametersBuffers::new(
827 allow_copies_from_indirect_parameter_buffers,
828 ),
829 phantom: PhantomData,
830 }
831 }
832}
833
834pub struct UntypedPhaseIndirectParametersBuffers {
842 pub indexed: MeshClassIndirectParametersBuffers<IndirectParametersIndexed>,
845 pub non_indexed: MeshClassIndirectParametersBuffers<IndirectParametersNonIndexed>,
848}
849
850impl UntypedPhaseIndirectParametersBuffers {
851 pub fn new(
853 allow_copies_from_indirect_parameter_buffers: bool,
854 ) -> UntypedPhaseIndirectParametersBuffers {
855 let mut indirect_parameter_buffer_usages = BufferUsages::STORAGE | BufferUsages::INDIRECT;
856 if allow_copies_from_indirect_parameter_buffers {
857 indirect_parameter_buffer_usages |= BufferUsages::COPY_SRC;
858 }
859
860 UntypedPhaseIndirectParametersBuffers {
861 non_indexed: MeshClassIndirectParametersBuffers::new(
862 allow_copies_from_indirect_parameter_buffers,
863 ),
864 indexed: MeshClassIndirectParametersBuffers::new(
865 allow_copies_from_indirect_parameter_buffers,
866 ),
867 }
868 }
869
870 pub fn allocate(&mut self, indexed: bool, count: u32) -> u32 {
875 if indexed {
876 self.indexed.allocate(count)
877 } else {
878 self.non_indexed.allocate(count)
879 }
880 }
881
882 fn batch_count(&self, indexed: bool) -> usize {
887 if indexed {
888 self.indexed.batch_count()
889 } else {
890 self.non_indexed.batch_count()
891 }
892 }
893
894 pub fn batch_set_count(&self, indexed: bool) -> usize {
899 if indexed {
900 self.indexed.batch_sets.len()
901 } else {
902 self.non_indexed.batch_sets.len()
903 }
904 }
905
906 #[inline]
914 pub fn add_batch_set(&mut self, indexed: bool, indirect_parameters_base: u32) {
915 if indexed {
916 self.indexed.batch_sets.push(IndirectBatchSet {
917 indirect_parameters_base,
918 indirect_parameters_count: 0,
919 });
920 } else {
921 self.non_indexed.batch_sets.push(IndirectBatchSet {
922 indirect_parameters_base,
923 indirect_parameters_count: 0,
924 });
925 }
926 }
927
928 pub fn get_next_batch_set_index(&self, indexed: bool) -> Option<NonMaxU32> {
933 NonMaxU32::new(self.batch_set_count(indexed) as u32)
934 }
935
936 pub fn clear(&mut self) {
938 self.indexed.clear();
939 self.non_indexed.clear();
940 }
941}
942
943pub struct MeshClassIndirectParametersBuffers<IP>
947where
948 IP: Clone + ShaderSize + WriteInto,
949{
950 data: UninitBufferVec<IP>,
956
957 cpu_metadata: RawBufferVec<IndirectParametersCpuMetadata>,
964
965 gpu_metadata: UninitBufferVec<IndirectParametersGpuMetadata>,
972
973 batch_sets: RawBufferVec<IndirectBatchSet>,
980}
981
982impl<IP> MeshClassIndirectParametersBuffers<IP>
983where
984 IP: Clone + ShaderSize + WriteInto,
985{
986 fn new(
987 allow_copies_from_indirect_parameter_buffers: bool,
988 ) -> MeshClassIndirectParametersBuffers<IP> {
989 let mut indirect_parameter_buffer_usages = BufferUsages::STORAGE | BufferUsages::INDIRECT;
990 if allow_copies_from_indirect_parameter_buffers {
991 indirect_parameter_buffer_usages |= BufferUsages::COPY_SRC;
992 }
993
994 MeshClassIndirectParametersBuffers {
995 data: UninitBufferVec::new(indirect_parameter_buffer_usages),
996 cpu_metadata: RawBufferVec::new(BufferUsages::STORAGE),
997 gpu_metadata: UninitBufferVec::new(BufferUsages::STORAGE),
998 batch_sets: RawBufferVec::new(indirect_parameter_buffer_usages),
999 }
1000 }
1001
1002 #[inline]
1009 pub fn data_buffer(&self) -> Option<&Buffer> {
1010 self.data.buffer()
1011 }
1012
1013 #[inline]
1019 pub fn cpu_metadata_buffer(&self) -> Option<&Buffer> {
1020 self.cpu_metadata.buffer()
1021 }
1022
1023 #[inline]
1030 pub fn gpu_metadata_buffer(&self) -> Option<&Buffer> {
1031 self.gpu_metadata.buffer()
1032 }
1033
1034 #[inline]
1041 pub fn batch_sets_buffer(&self) -> Option<&Buffer> {
1042 self.batch_sets.buffer()
1043 }
1044
1045 fn allocate(&mut self, count: u32) -> u32 {
1050 let length = self.data.len();
1051 self.cpu_metadata.reserve_internal(count as usize);
1052 self.gpu_metadata.add_multiple(count as usize);
1053 for _ in 0..count {
1054 self.data.add();
1055 self.cpu_metadata
1056 .push(IndirectParametersCpuMetadata::default());
1057 }
1058 length as u32
1059 }
1060
1061 pub fn set(&mut self, index: u32, value: IndirectParametersCpuMetadata) {
1064 self.cpu_metadata.set(index, value);
1065 }
1066
1067 #[inline]
1070 pub fn batch_count(&self) -> usize {
1071 self.data.len()
1072 }
1073
1074 pub fn clear(&mut self) {
1076 self.data.clear();
1077 self.cpu_metadata.clear();
1078 self.gpu_metadata.clear();
1079 self.batch_sets.clear();
1080 }
1081}
1082
1083impl Default for IndirectParametersBuffers {
1084 fn default() -> Self {
1085 Self::new(false)
1088 }
1089}
1090
1091impl FromWorld for GpuPreprocessingSupport {
1092 fn from_world(world: &mut World) -> Self {
1093 let adapter = world.resource::<RenderAdapter>();
1094 let device = world.resource::<RenderDevice>();
1095
1096 fn is_non_supported_android_device(adapter: &RenderAdapter) -> bool {
1101 crate::get_adreno_model(adapter).is_some_and(|model| model != 720 && model <= 730)
1102 || crate::get_mali_driver_version(adapter).is_some_and(|version| version < 48)
1103 }
1104
1105 let culling_feature_support = device.features().contains(
1106 Features::INDIRECT_FIRST_INSTANCE
1107 | Features::MULTI_DRAW_INDIRECT
1108 | Features::PUSH_CONSTANTS,
1109 );
1110 let limit_support = device.limits().max_storage_textures_per_shader_stage >= 12 &&
1112 device.limits().max_compute_workgroup_storage_size != 0;
1117
1118 let downlevel_support = adapter.get_downlevel_capabilities().flags.contains(
1119 DownlevelFlags::COMPUTE_SHADERS |
1120 DownlevelFlags::VERTEX_AND_INSTANCE_INDEX_RESPECTS_RESPECTIVE_FIRST_VALUE_IN_INDIRECT_DRAW
1121 );
1122
1123 let max_supported_mode = if device.limits().max_compute_workgroup_size_x == 0
1124 || is_non_supported_android_device(adapter)
1125 {
1126 info!(
1127 "GPU preprocessing is not supported on this device. \
1128 Falling back to CPU preprocessing.",
1129 );
1130 GpuPreprocessingMode::None
1131 } else if !(culling_feature_support && limit_support && downlevel_support) {
1132 info!("Some GPU preprocessing are limited on this device.");
1133 GpuPreprocessingMode::PreprocessingOnly
1134 } else {
1135 info!("GPU preprocessing is fully supported on this device.");
1136 GpuPreprocessingMode::Culling
1137 };
1138
1139 GpuPreprocessingSupport { max_supported_mode }
1140 }
1141}
1142
1143impl<BD, BDI> BatchedInstanceBuffers<BD, BDI>
1144where
1145 BD: GpuArrayBufferable + Sync + Send + 'static,
1146 BDI: Pod + Sync + Send + Default + 'static,
1147{
1148 pub fn new() -> Self {
1150 Self::default()
1151 }
1152
1153 pub fn clear(&mut self) {
1155 for phase_instance_buffer in self.phase_instance_buffers.values_mut() {
1156 phase_instance_buffer.clear();
1157 }
1158 }
1159}
1160
1161impl<BD> UntypedPhaseBatchedInstanceBuffers<BD>
1162where
1163 BD: GpuArrayBufferable + Sync + Send + 'static,
1164{
1165 pub fn new() -> Self {
1166 UntypedPhaseBatchedInstanceBuffers {
1167 data_buffer: UninitBufferVec::new(BufferUsages::STORAGE),
1168 work_item_buffers: HashMap::default(),
1169 late_indexed_indirect_parameters_buffer: RawBufferVec::new(
1170 BufferUsages::STORAGE | BufferUsages::INDIRECT,
1171 ),
1172 late_non_indexed_indirect_parameters_buffer: RawBufferVec::new(
1173 BufferUsages::STORAGE | BufferUsages::INDIRECT,
1174 ),
1175 }
1176 }
1177
1178 pub fn instance_data_binding(&self) -> Option<BindingResource> {
1182 self.data_buffer
1183 .buffer()
1184 .map(|buffer| buffer.as_entire_binding())
1185 }
1186
1187 pub fn clear(&mut self) {
1189 self.data_buffer.clear();
1190 self.late_indexed_indirect_parameters_buffer.clear();
1191 self.late_non_indexed_indirect_parameters_buffer.clear();
1192
1193 for view_work_item_buffers in self.work_item_buffers.values_mut() {
1196 view_work_item_buffers.clear();
1197 }
1198 }
1199}
1200
1201impl<BD> Default for UntypedPhaseBatchedInstanceBuffers<BD>
1202where
1203 BD: GpuArrayBufferable + Sync + Send + 'static,
1204{
1205 fn default() -> Self {
1206 Self::new()
1207 }
1208}
1209
1210struct SortedRenderBatch<F>
1213where
1214 F: GetBatchData,
1215{
1216 phase_item_start_index: u32,
1219
1220 instance_start_index: u32,
1222
1223 indexed: bool,
1225
1226 indirect_parameters_index: Option<NonMaxU32>,
1231
1232 meta: Option<BatchMeta<F::CompareData>>,
1237}
1238
1239impl<F> SortedRenderBatch<F>
1240where
1241 F: GetBatchData,
1242{
1243 fn flush<I>(
1249 self,
1250 instance_end_index: u32,
1251 phase: &mut SortedRenderPhase<I>,
1252 phase_indirect_parameters_buffers: &mut UntypedPhaseIndirectParametersBuffers,
1253 ) where
1254 I: CachedRenderPipelinePhaseItem + SortedPhaseItem,
1255 {
1256 let (batch_range, batch_extra_index) =
1257 phase.items[self.phase_item_start_index as usize].batch_range_and_extra_index_mut();
1258 *batch_range = self.instance_start_index..instance_end_index;
1259 *batch_extra_index = match self.indirect_parameters_index {
1260 Some(indirect_parameters_index) => PhaseItemExtraIndex::IndirectParametersIndex {
1261 range: u32::from(indirect_parameters_index)
1262 ..(u32::from(indirect_parameters_index) + 1),
1263 batch_set_index: None,
1264 },
1265 None => PhaseItemExtraIndex::None,
1266 };
1267 if let Some(indirect_parameters_index) = self.indirect_parameters_index {
1268 phase_indirect_parameters_buffers
1269 .add_batch_set(self.indexed, indirect_parameters_index.into());
1270 }
1271 }
1272}
1273
1274pub fn clear_batched_gpu_instance_buffers<GFBD>(
1281 gpu_batched_instance_buffers: Option<
1282 ResMut<BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>>,
1283 >,
1284) where
1285 GFBD: GetFullBatchData,
1286{
1287 if let Some(mut gpu_batched_instance_buffers) = gpu_batched_instance_buffers {
1290 gpu_batched_instance_buffers.clear();
1291 }
1292}
1293
1294pub fn delete_old_work_item_buffers<GFBD>(
1301 mut gpu_batched_instance_buffers: ResMut<
1302 BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1303 >,
1304 extracted_views: Query<&ExtractedView>,
1305) where
1306 GFBD: GetFullBatchData,
1307{
1308 let retained_view_entities: HashSet<_> = extracted_views
1309 .iter()
1310 .map(|extracted_view| extracted_view.retained_view_entity)
1311 .collect();
1312 for phase_instance_buffers in gpu_batched_instance_buffers
1313 .phase_instance_buffers
1314 .values_mut()
1315 {
1316 phase_instance_buffers
1317 .work_item_buffers
1318 .retain(|retained_view_entity, _| {
1319 retained_view_entities.contains(retained_view_entity)
1320 });
1321 }
1322}
1323
1324pub fn batch_and_prepare_sorted_render_phase<I, GFBD>(
1328 mut phase_batched_instance_buffers: ResMut<PhaseBatchedInstanceBuffers<I, GFBD::BufferData>>,
1329 mut phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<I>>,
1330 mut sorted_render_phases: ResMut<ViewSortedRenderPhases<I>>,
1331 mut views: Query<(
1332 &ExtractedView,
1333 Has<NoIndirectDrawing>,
1334 Has<OcclusionCulling>,
1335 )>,
1336 system_param_item: StaticSystemParam<GFBD::Param>,
1337) where
1338 I: CachedRenderPipelinePhaseItem + SortedPhaseItem,
1339 GFBD: GetFullBatchData,
1340{
1341 let UntypedPhaseBatchedInstanceBuffers {
1343 ref mut data_buffer,
1344 ref mut work_item_buffers,
1345 ref mut late_indexed_indirect_parameters_buffer,
1346 ref mut late_non_indexed_indirect_parameters_buffer,
1347 } = phase_batched_instance_buffers.buffers;
1348
1349 for (extracted_view, no_indirect_drawing, gpu_occlusion_culling) in &mut views {
1350 let Some(phase) = sorted_render_phases.get_mut(&extracted_view.retained_view_entity) else {
1351 continue;
1352 };
1353
1354 let work_item_buffer = get_or_create_work_item_buffer::<I>(
1356 work_item_buffers,
1357 extracted_view.retained_view_entity,
1358 no_indirect_drawing,
1359 gpu_occlusion_culling,
1360 );
1361
1362 init_work_item_buffers(
1364 work_item_buffer,
1365 late_indexed_indirect_parameters_buffer,
1366 late_non_indexed_indirect_parameters_buffer,
1367 );
1368
1369 let mut batch: Option<SortedRenderBatch<GFBD>> = None;
1371
1372 for current_index in 0..phase.items.len() {
1373 let item = &phase.items[current_index];
1376 let entity = item.main_entity();
1377 let item_is_indexed = item.indexed();
1378 let current_batch_input_index =
1379 GFBD::get_index_and_compare_data(&system_param_item, entity);
1380
1381 let Some((current_input_index, current_meta)) = current_batch_input_index else {
1386 if let Some(batch) = batch.take() {
1388 batch.flush(
1389 data_buffer.len() as u32,
1390 phase,
1391 &mut phase_indirect_parameters_buffers.buffers,
1392 );
1393 }
1394
1395 continue;
1396 };
1397 let current_meta =
1398 current_meta.map(|meta| BatchMeta::new(&phase.items[current_index], meta));
1399
1400 let can_batch = batch.as_ref().is_some_and(|batch| {
1403 match (¤t_meta, &batch.meta) {
1405 (Some(current_meta), Some(batch_meta)) => current_meta == batch_meta,
1406 (_, _) => false,
1407 }
1408 });
1409
1410 let output_index = data_buffer.add() as u32;
1412
1413 if !can_batch {
1415 if let Some(batch) = batch.take() {
1417 batch.flush(
1418 output_index,
1419 phase,
1420 &mut phase_indirect_parameters_buffers.buffers,
1421 );
1422 }
1423
1424 let indirect_parameters_index = if no_indirect_drawing {
1425 None
1426 } else if item_is_indexed {
1427 Some(
1428 phase_indirect_parameters_buffers
1429 .buffers
1430 .indexed
1431 .allocate(1),
1432 )
1433 } else {
1434 Some(
1435 phase_indirect_parameters_buffers
1436 .buffers
1437 .non_indexed
1438 .allocate(1),
1439 )
1440 };
1441
1442 if let Some(indirect_parameters_index) = indirect_parameters_index {
1444 GFBD::write_batch_indirect_parameters_metadata(
1445 item_is_indexed,
1446 output_index,
1447 None,
1448 &mut phase_indirect_parameters_buffers.buffers,
1449 indirect_parameters_index,
1450 );
1451 };
1452
1453 batch = Some(SortedRenderBatch {
1454 phase_item_start_index: current_index as u32,
1455 instance_start_index: output_index,
1456 indexed: item_is_indexed,
1457 indirect_parameters_index: indirect_parameters_index.and_then(NonMaxU32::new),
1458 meta: current_meta,
1459 });
1460 }
1461
1462 if let Some(batch) = batch.as_ref() {
1465 work_item_buffer.push(
1466 item_is_indexed,
1467 PreprocessWorkItem {
1468 input_index: current_input_index.into(),
1469 output_or_indirect_parameters_index: match (
1470 no_indirect_drawing,
1471 batch.indirect_parameters_index,
1472 ) {
1473 (true, _) => output_index,
1474 (false, Some(indirect_parameters_index)) => {
1475 indirect_parameters_index.into()
1476 }
1477 (false, None) => 0,
1478 },
1479 },
1480 );
1481 }
1482 }
1483
1484 if let Some(batch) = batch.take() {
1486 batch.flush(
1487 data_buffer.len() as u32,
1488 phase,
1489 &mut phase_indirect_parameters_buffers.buffers,
1490 );
1491 }
1492 }
1493}
1494
1495pub fn batch_and_prepare_binned_render_phase<BPI, GFBD>(
1497 mut phase_batched_instance_buffers: ResMut<PhaseBatchedInstanceBuffers<BPI, GFBD::BufferData>>,
1498 phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<BPI>>,
1499 mut binned_render_phases: ResMut<ViewBinnedRenderPhases<BPI>>,
1500 mut views: Query<
1501 (
1502 &ExtractedView,
1503 Has<NoIndirectDrawing>,
1504 Has<OcclusionCulling>,
1505 ),
1506 With<ExtractedView>,
1507 >,
1508 param: StaticSystemParam<GFBD::Param>,
1509) where
1510 BPI: BinnedPhaseItem,
1511 GFBD: GetFullBatchData,
1512{
1513 let system_param_item = param.into_inner();
1514
1515 let phase_indirect_parameters_buffers = phase_indirect_parameters_buffers.into_inner();
1516
1517 let UntypedPhaseBatchedInstanceBuffers {
1518 ref mut data_buffer,
1519 ref mut work_item_buffers,
1520 ref mut late_indexed_indirect_parameters_buffer,
1521 ref mut late_non_indexed_indirect_parameters_buffer,
1522 } = phase_batched_instance_buffers.buffers;
1523
1524 for (extracted_view, no_indirect_drawing, gpu_occlusion_culling) in &mut views {
1525 let Some(phase) = binned_render_phases.get_mut(&extracted_view.retained_view_entity) else {
1526 continue;
1527 };
1528
1529 let work_item_buffer = get_or_create_work_item_buffer::<BPI>(
1532 work_item_buffers,
1533 extracted_view.retained_view_entity,
1534 no_indirect_drawing,
1535 gpu_occlusion_culling,
1536 );
1537
1538 init_work_item_buffers(
1540 work_item_buffer,
1541 late_indexed_indirect_parameters_buffer,
1542 late_non_indexed_indirect_parameters_buffer,
1543 );
1544
1545 if let (
1548 &mut BinnedRenderPhaseBatchSets::MultidrawIndirect(ref mut batch_sets),
1549 &mut PreprocessWorkItemBuffers::Indirect {
1550 indexed: ref mut indexed_work_item_buffer,
1551 non_indexed: ref mut non_indexed_work_item_buffer,
1552 gpu_occlusion_culling: ref mut gpu_occlusion_culling_buffers,
1553 },
1554 ) = (&mut phase.batch_sets, &mut *work_item_buffer)
1555 {
1556 let mut output_index = data_buffer.len() as u32;
1557
1558 let mut indexed_preparer: MultidrawableBatchSetPreparer<BPI, GFBD> =
1560 MultidrawableBatchSetPreparer::new(
1561 phase_indirect_parameters_buffers.buffers.batch_count(true) as u32,
1562 phase_indirect_parameters_buffers
1563 .buffers
1564 .indexed
1565 .batch_sets
1566 .len() as u32,
1567 );
1568 let mut non_indexed_preparer: MultidrawableBatchSetPreparer<BPI, GFBD> =
1569 MultidrawableBatchSetPreparer::new(
1570 phase_indirect_parameters_buffers.buffers.batch_count(false) as u32,
1571 phase_indirect_parameters_buffers
1572 .buffers
1573 .non_indexed
1574 .batch_sets
1575 .len() as u32,
1576 );
1577
1578 for (batch_set_key, bins) in &phase.multidrawable_meshes {
1580 if batch_set_key.indexed() {
1581 indexed_preparer.prepare_multidrawable_binned_batch_set(
1582 bins,
1583 &mut output_index,
1584 data_buffer,
1585 indexed_work_item_buffer,
1586 &mut phase_indirect_parameters_buffers.buffers.indexed,
1587 batch_sets,
1588 );
1589 } else {
1590 non_indexed_preparer.prepare_multidrawable_binned_batch_set(
1591 bins,
1592 &mut output_index,
1593 data_buffer,
1594 non_indexed_work_item_buffer,
1595 &mut phase_indirect_parameters_buffers.buffers.non_indexed,
1596 batch_sets,
1597 );
1598 }
1599 }
1600
1601 if let Some(gpu_occlusion_culling_buffers) = gpu_occlusion_culling_buffers {
1603 gpu_occlusion_culling_buffers
1604 .late_indexed
1605 .add_multiple(indexed_preparer.work_item_count);
1606 gpu_occlusion_culling_buffers
1607 .late_non_indexed
1608 .add_multiple(non_indexed_preparer.work_item_count);
1609 }
1610 }
1611
1612 for (key, bin) in &phase.batchable_meshes {
1615 let mut batch: Option<BinnedRenderPhaseBatch> = None;
1616 for (&main_entity, &input_index) in bin.entities() {
1617 let output_index = data_buffer.add() as u32;
1618
1619 match batch {
1620 Some(ref mut batch) => {
1621 batch.instance_range.end = output_index + 1;
1622
1623 work_item_buffer.push(
1631 key.0.indexed(),
1632 PreprocessWorkItem {
1633 input_index: *input_index,
1634 output_or_indirect_parameters_index: match (
1635 no_indirect_drawing,
1636 &batch.extra_index,
1637 ) {
1638 (true, _) => output_index,
1639 (
1640 false,
1641 PhaseItemExtraIndex::IndirectParametersIndex {
1642 range: indirect_parameters_range,
1643 ..
1644 },
1645 ) => indirect_parameters_range.start,
1646 (false, &PhaseItemExtraIndex::DynamicOffset(_))
1647 | (false, &PhaseItemExtraIndex::None) => 0,
1648 },
1649 },
1650 );
1651 }
1652
1653 None if !no_indirect_drawing => {
1654 let indirect_parameters_index = phase_indirect_parameters_buffers
1656 .buffers
1657 .allocate(key.0.indexed(), 1);
1658 let batch_set_index = phase_indirect_parameters_buffers
1659 .buffers
1660 .get_next_batch_set_index(key.0.indexed());
1661
1662 GFBD::write_batch_indirect_parameters_metadata(
1663 key.0.indexed(),
1664 output_index,
1665 batch_set_index,
1666 &mut phase_indirect_parameters_buffers.buffers,
1667 indirect_parameters_index,
1668 );
1669 work_item_buffer.push(
1670 key.0.indexed(),
1671 PreprocessWorkItem {
1672 input_index: *input_index,
1673 output_or_indirect_parameters_index: indirect_parameters_index,
1674 },
1675 );
1676 batch = Some(BinnedRenderPhaseBatch {
1677 representative_entity: (Entity::PLACEHOLDER, main_entity),
1678 instance_range: output_index..output_index + 1,
1679 extra_index: PhaseItemExtraIndex::IndirectParametersIndex {
1680 range: indirect_parameters_index..(indirect_parameters_index + 1),
1681 batch_set_index: None,
1682 },
1683 });
1684 }
1685
1686 None => {
1687 work_item_buffer.push(
1689 key.0.indexed(),
1690 PreprocessWorkItem {
1691 input_index: *input_index,
1692 output_or_indirect_parameters_index: output_index,
1693 },
1694 );
1695 batch = Some(BinnedRenderPhaseBatch {
1696 representative_entity: (Entity::PLACEHOLDER, main_entity),
1697 instance_range: output_index..output_index + 1,
1698 extra_index: PhaseItemExtraIndex::None,
1699 });
1700 }
1701 }
1702 }
1703
1704 if let Some(batch) = batch {
1705 match phase.batch_sets {
1706 BinnedRenderPhaseBatchSets::DynamicUniforms(_) => {
1707 error!("Dynamic uniform batch sets shouldn't be used here");
1708 }
1709 BinnedRenderPhaseBatchSets::Direct(ref mut vec) => {
1710 vec.push(batch);
1711 }
1712 BinnedRenderPhaseBatchSets::MultidrawIndirect(ref mut vec) => {
1713 vec.push(BinnedRenderPhaseBatchSet {
1718 first_batch: batch,
1719 batch_count: 1,
1720 bin_key: key.1.clone(),
1721 index: phase_indirect_parameters_buffers
1722 .buffers
1723 .batch_set_count(key.0.indexed())
1724 as u32,
1725 });
1726 }
1727 }
1728 }
1729 }
1730
1731 for (key, unbatchables) in &mut phase.unbatchable_meshes {
1733 let mut indirect_parameters_offset = if no_indirect_drawing {
1735 None
1736 } else if key.0.indexed() {
1737 Some(
1738 phase_indirect_parameters_buffers
1739 .buffers
1740 .indexed
1741 .allocate(unbatchables.entities.len() as u32),
1742 )
1743 } else {
1744 Some(
1745 phase_indirect_parameters_buffers
1746 .buffers
1747 .non_indexed
1748 .allocate(unbatchables.entities.len() as u32),
1749 )
1750 };
1751
1752 for main_entity in unbatchables.entities.keys() {
1753 let Some(input_index) = GFBD::get_binned_index(&system_param_item, *main_entity)
1754 else {
1755 continue;
1756 };
1757 let output_index = data_buffer.add() as u32;
1758
1759 if let Some(ref mut indirect_parameters_index) = indirect_parameters_offset {
1760 GFBD::write_batch_indirect_parameters_metadata(
1763 key.0.indexed(),
1764 output_index,
1765 None,
1766 &mut phase_indirect_parameters_buffers.buffers,
1767 *indirect_parameters_index,
1768 );
1769 work_item_buffer.push(
1770 key.0.indexed(),
1771 PreprocessWorkItem {
1772 input_index: input_index.into(),
1773 output_or_indirect_parameters_index: *indirect_parameters_index,
1774 },
1775 );
1776 unbatchables
1777 .buffer_indices
1778 .add(UnbatchableBinnedEntityIndices {
1779 instance_index: *indirect_parameters_index,
1780 extra_index: PhaseItemExtraIndex::IndirectParametersIndex {
1781 range: *indirect_parameters_index..(*indirect_parameters_index + 1),
1782 batch_set_index: None,
1783 },
1784 });
1785 phase_indirect_parameters_buffers
1786 .buffers
1787 .add_batch_set(key.0.indexed(), *indirect_parameters_index);
1788 *indirect_parameters_index += 1;
1789 } else {
1790 work_item_buffer.push(
1791 key.0.indexed(),
1792 PreprocessWorkItem {
1793 input_index: input_index.into(),
1794 output_or_indirect_parameters_index: output_index,
1795 },
1796 );
1797 unbatchables
1798 .buffer_indices
1799 .add(UnbatchableBinnedEntityIndices {
1800 instance_index: output_index,
1801 extra_index: PhaseItemExtraIndex::None,
1802 });
1803 }
1804 }
1805 }
1806 }
1807}
1808
1809struct MultidrawableBatchSetPreparer<BPI, GFBD>
1815where
1816 BPI: BinnedPhaseItem,
1817 GFBD: GetFullBatchData,
1818{
1819 indirect_parameters_index: u32,
1822 batch_set_index: u32,
1824 work_item_count: usize,
1826 phantom: PhantomData<(BPI, GFBD)>,
1827}
1828
1829impl<BPI, GFBD> MultidrawableBatchSetPreparer<BPI, GFBD>
1830where
1831 BPI: BinnedPhaseItem,
1832 GFBD: GetFullBatchData,
1833{
1834 #[inline]
1837 fn new(initial_indirect_parameters_index: u32, initial_batch_set_index: u32) -> Self {
1838 MultidrawableBatchSetPreparer {
1839 indirect_parameters_index: initial_indirect_parameters_index,
1840 batch_set_index: initial_batch_set_index,
1841 work_item_count: 0,
1842 phantom: PhantomData,
1843 }
1844 }
1845
1846 #[inline]
1851 fn prepare_multidrawable_binned_batch_set<IP>(
1852 &mut self,
1853 bins: &IndexMap<BPI::BinKey, RenderBin>,
1854 output_index: &mut u32,
1855 data_buffer: &mut UninitBufferVec<GFBD::BufferData>,
1856 indexed_work_item_buffer: &mut RawBufferVec<PreprocessWorkItem>,
1857 mesh_class_buffers: &mut MeshClassIndirectParametersBuffers<IP>,
1858 batch_sets: &mut Vec<BinnedRenderPhaseBatchSet<BPI::BinKey>>,
1859 ) where
1860 IP: Clone + ShaderSize + WriteInto,
1861 {
1862 let current_indexed_batch_set_index = self.batch_set_index;
1863 let current_output_index = *output_index;
1864
1865 let indirect_parameters_base = self.indirect_parameters_index;
1866
1867 let Some((first_bin_key, first_bin)) = bins.iter().next() else {
1870 return;
1871 };
1872 let first_bin_len = first_bin.entities().len();
1873 let first_bin_entity = first_bin
1874 .entities()
1875 .keys()
1876 .next()
1877 .copied()
1878 .unwrap_or(MainEntity::from(Entity::PLACEHOLDER));
1879
1880 for bin in bins.values() {
1882 mesh_class_buffers
1885 .cpu_metadata
1886 .push(IndirectParametersCpuMetadata {
1887 base_output_index: *output_index,
1888 batch_set_index: self.batch_set_index,
1889 });
1890
1891 for &input_index in bin.entities().values() {
1894 indexed_work_item_buffer.push(PreprocessWorkItem {
1895 input_index: *input_index,
1896 output_or_indirect_parameters_index: self.indirect_parameters_index,
1897 });
1898 }
1899
1900 let bin_entity_count = bin.entities().len();
1903 data_buffer.add_multiple(bin_entity_count);
1904 *output_index += bin_entity_count as u32;
1905 self.work_item_count += bin_entity_count;
1906
1907 self.indirect_parameters_index += 1;
1908 }
1909
1910 let bin_count = bins.len();
1912 mesh_class_buffers.gpu_metadata.add_multiple(bin_count);
1913 mesh_class_buffers.data.add_multiple(bin_count);
1914
1915 mesh_class_buffers.batch_sets.push(IndirectBatchSet {
1917 indirect_parameters_base,
1918 indirect_parameters_count: 0,
1919 });
1920
1921 self.batch_set_index += 1;
1922
1923 batch_sets.push(BinnedRenderPhaseBatchSet {
1926 first_batch: BinnedRenderPhaseBatch {
1927 representative_entity: (Entity::PLACEHOLDER, first_bin_entity),
1928 instance_range: current_output_index..(current_output_index + first_bin_len as u32),
1929 extra_index: PhaseItemExtraIndex::maybe_indirect_parameters_index(NonMaxU32::new(
1930 indirect_parameters_base,
1931 )),
1932 },
1933 bin_key: (*first_bin_key).clone(),
1934 batch_count: self.indirect_parameters_index - indirect_parameters_base,
1935 index: current_indexed_batch_set_index,
1936 });
1937 }
1938}
1939
1940pub fn collect_buffers_for_phase<PI, GFBD>(
1955 mut phase_batched_instance_buffers: ResMut<PhaseBatchedInstanceBuffers<PI, GFBD::BufferData>>,
1956 mut phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<PI>>,
1957 mut batched_instance_buffers: ResMut<
1958 BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1959 >,
1960 mut indirect_parameters_buffers: ResMut<IndirectParametersBuffers>,
1961) where
1962 PI: PhaseItem,
1963 GFBD: GetFullBatchData + Send + Sync + 'static,
1964{
1965 let untyped_phase_batched_instance_buffers =
1969 mem::take(&mut phase_batched_instance_buffers.buffers);
1970 if let Some(mut old_untyped_phase_batched_instance_buffers) = batched_instance_buffers
1971 .phase_instance_buffers
1972 .insert(TypeId::of::<PI>(), untyped_phase_batched_instance_buffers)
1973 {
1974 old_untyped_phase_batched_instance_buffers.clear();
1975 phase_batched_instance_buffers.buffers = old_untyped_phase_batched_instance_buffers;
1976 }
1977
1978 let untyped_phase_indirect_parameters_buffers = mem::replace(
1982 &mut phase_indirect_parameters_buffers.buffers,
1983 UntypedPhaseIndirectParametersBuffers::new(
1984 indirect_parameters_buffers.allow_copies_from_indirect_parameter_buffers,
1985 ),
1986 );
1987 if let Some(mut old_untyped_phase_indirect_parameters_buffers) = indirect_parameters_buffers
1988 .insert(
1989 TypeId::of::<PI>(),
1990 untyped_phase_indirect_parameters_buffers,
1991 )
1992 {
1993 old_untyped_phase_indirect_parameters_buffers.clear();
1994 phase_indirect_parameters_buffers.buffers = old_untyped_phase_indirect_parameters_buffers;
1995 }
1996}
1997
1998pub fn write_batched_instance_buffers<GFBD>(
2000 render_device: Res<RenderDevice>,
2001 render_queue: Res<RenderQueue>,
2002 gpu_array_buffer: ResMut<BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>>,
2003) where
2004 GFBD: GetFullBatchData,
2005{
2006 let BatchedInstanceBuffers {
2007 current_input_buffer,
2008 previous_input_buffer,
2009 phase_instance_buffers,
2010 } = gpu_array_buffer.into_inner();
2011
2012 current_input_buffer
2013 .buffer
2014 .write_buffer(&render_device, &render_queue);
2015 previous_input_buffer
2016 .buffer
2017 .write_buffer(&render_device, &render_queue);
2018
2019 for phase_instance_buffers in phase_instance_buffers.values_mut() {
2020 let UntypedPhaseBatchedInstanceBuffers {
2021 ref mut data_buffer,
2022 ref mut work_item_buffers,
2023 ref mut late_indexed_indirect_parameters_buffer,
2024 ref mut late_non_indexed_indirect_parameters_buffer,
2025 } = *phase_instance_buffers;
2026
2027 data_buffer.write_buffer(&render_device);
2028 late_indexed_indirect_parameters_buffer.write_buffer(&render_device, &render_queue);
2029 late_non_indexed_indirect_parameters_buffer.write_buffer(&render_device, &render_queue);
2030
2031 for phase_work_item_buffers in work_item_buffers.values_mut() {
2032 match *phase_work_item_buffers {
2033 PreprocessWorkItemBuffers::Direct(ref mut buffer_vec) => {
2034 buffer_vec.write_buffer(&render_device, &render_queue);
2035 }
2036 PreprocessWorkItemBuffers::Indirect {
2037 ref mut indexed,
2038 ref mut non_indexed,
2039 ref mut gpu_occlusion_culling,
2040 } => {
2041 indexed.write_buffer(&render_device, &render_queue);
2042 non_indexed.write_buffer(&render_device, &render_queue);
2043
2044 if let Some(GpuOcclusionCullingWorkItemBuffers {
2045 ref mut late_indexed,
2046 ref mut late_non_indexed,
2047 late_indirect_parameters_indexed_offset: _,
2048 late_indirect_parameters_non_indexed_offset: _,
2049 }) = *gpu_occlusion_culling
2050 {
2051 if !late_indexed.is_empty() {
2052 late_indexed.write_buffer(&render_device);
2053 }
2054 if !late_non_indexed.is_empty() {
2055 late_non_indexed.write_buffer(&render_device);
2056 }
2057 }
2058 }
2059 }
2060 }
2061 }
2062}
2063
2064pub fn clear_indirect_parameters_buffers(
2065 mut indirect_parameters_buffers: ResMut<IndirectParametersBuffers>,
2066) {
2067 for phase_indirect_parameters_buffers in indirect_parameters_buffers.values_mut() {
2068 phase_indirect_parameters_buffers.clear();
2069 }
2070}
2071
2072pub fn write_indirect_parameters_buffers(
2073 render_device: Res<RenderDevice>,
2074 render_queue: Res<RenderQueue>,
2075 mut indirect_parameters_buffers: ResMut<IndirectParametersBuffers>,
2076) {
2077 for phase_indirect_parameters_buffers in indirect_parameters_buffers.values_mut() {
2078 phase_indirect_parameters_buffers
2079 .indexed
2080 .data
2081 .write_buffer(&render_device);
2082 phase_indirect_parameters_buffers
2083 .non_indexed
2084 .data
2085 .write_buffer(&render_device);
2086
2087 phase_indirect_parameters_buffers
2088 .indexed
2089 .cpu_metadata
2090 .write_buffer(&render_device, &render_queue);
2091 phase_indirect_parameters_buffers
2092 .non_indexed
2093 .cpu_metadata
2094 .write_buffer(&render_device, &render_queue);
2095
2096 phase_indirect_parameters_buffers
2097 .non_indexed
2098 .gpu_metadata
2099 .write_buffer(&render_device);
2100 phase_indirect_parameters_buffers
2101 .indexed
2102 .gpu_metadata
2103 .write_buffer(&render_device);
2104
2105 phase_indirect_parameters_buffers
2106 .indexed
2107 .batch_sets
2108 .write_buffer(&render_device, &render_queue);
2109 phase_indirect_parameters_buffers
2110 .non_indexed
2111 .batch_sets
2112 .write_buffer(&render_device, &render_queue);
2113 }
2114}
2115
2116#[cfg(test)]
2117mod tests {
2118 use super::*;
2119
2120 #[test]
2121 fn instance_buffer_correct_behavior() {
2122 let mut instance_buffer = InstanceInputUniformBuffer::new();
2123
2124 let index = instance_buffer.add(2);
2125 instance_buffer.remove(index);
2126 assert_eq!(instance_buffer.get_unchecked(index), 2);
2127 assert_eq!(instance_buffer.get(index), None);
2128
2129 instance_buffer.add(5);
2130 assert_eq!(instance_buffer.buffer().len(), 1);
2131 }
2132}