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_tasks::ComputeTaskPool;
19use bevy_utils::{default, TypeIdMap};
20use bytemuck::{Pod, Zeroable};
21use encase::{internal::WriteInto, ShaderSize};
22use indexmap::IndexMap;
23use nonmax::NonMaxU32;
24use tracing::{error, info};
25use wgpu::{BindingResource, BufferUsages, DownlevelFlags, Features};
26
27use crate::{
28 experimental::occlusion_culling::OcclusionCulling,
29 render_phase::{
30 BinnedPhaseItem, BinnedRenderPhaseBatch, BinnedRenderPhaseBatchSet,
31 BinnedRenderPhaseBatchSets, CachedRenderPipelinePhaseItem, PhaseItem,
32 PhaseItemBatchSetKey as _, PhaseItemExtraIndex, RenderBin, SortedPhaseItem,
33 SortedRenderPhase, UnbatchableBinnedEntityIndices, ViewBinnedRenderPhases,
34 ViewSortedRenderPhases,
35 },
36 render_resource::{Buffer, GpuArrayBufferable, RawBufferVec, UninitBufferVec},
37 renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue, WgpuWrapper},
38 sync_world::MainEntity,
39 view::{ExtractedView, NoIndirectDrawing, RetainedViewEntity},
40 Render, RenderApp, RenderDebugFlags, RenderSystems,
41};
42
43use super::{BatchMeta, GetBatchData, GetFullBatchData};
44
45#[derive(Default)]
46pub struct BatchingPlugin {
47 pub debug_flags: RenderDebugFlags,
49}
50
51impl Plugin for BatchingPlugin {
52 fn build(&self, app: &mut App) {
53 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
54 return;
55 };
56
57 render_app
58 .insert_resource(IndirectParametersBuffers::new(
59 self.debug_flags
60 .contains(RenderDebugFlags::ALLOW_COPIES_FROM_INDIRECT_PARAMETERS),
61 ))
62 .add_systems(
63 Render,
64 write_indirect_parameters_buffers.in_set(RenderSystems::PrepareResourcesFlush),
65 )
66 .add_systems(
67 Render,
68 clear_indirect_parameters_buffers.in_set(RenderSystems::ManageViews),
69 );
70 }
71
72 fn finish(&self, app: &mut App) {
73 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
74 return;
75 };
76
77 render_app.init_resource::<GpuPreprocessingSupport>();
78 }
79}
80
81#[derive(Clone, Copy, PartialEq, Resource)]
90pub struct GpuPreprocessingSupport {
91 pub max_supported_mode: GpuPreprocessingMode,
93}
94
95impl GpuPreprocessingSupport {
96 #[inline]
98 pub fn is_available(&self) -> bool {
99 self.max_supported_mode != GpuPreprocessingMode::None
100 }
101
102 pub fn min(&self, mode: GpuPreprocessingMode) -> GpuPreprocessingMode {
105 match (self.max_supported_mode, mode) {
106 (GpuPreprocessingMode::None, _) | (_, GpuPreprocessingMode::None) => {
107 GpuPreprocessingMode::None
108 }
109 (mode, GpuPreprocessingMode::Culling) | (GpuPreprocessingMode::Culling, mode) => mode,
110 (GpuPreprocessingMode::PreprocessingOnly, GpuPreprocessingMode::PreprocessingOnly) => {
111 GpuPreprocessingMode::PreprocessingOnly
112 }
113 }
114 }
115
116 pub fn is_culling_supported(&self) -> bool {
118 self.max_supported_mode == GpuPreprocessingMode::Culling
119 }
120}
121
122#[derive(Clone, Copy, PartialEq)]
124pub enum GpuPreprocessingMode {
125 None,
129
130 PreprocessingOnly,
135
136 Culling,
140}
141
142#[derive(Resource)]
153pub struct BatchedInstanceBuffers<BD, BDI>
154where
155 BD: GpuArrayBufferable + Sync + Send + 'static,
156 BDI: Pod + Default,
157{
158 pub current_input_buffer: InstanceInputUniformBuffer<BDI>,
162
163 pub previous_input_buffer: InstanceInputUniformBuffer<BDI>,
171
172 pub phase_instance_buffers: TypeIdMap<UntypedPhaseBatchedInstanceBuffers<BD>>,
177}
178
179impl<BD, BDI> Default for BatchedInstanceBuffers<BD, BDI>
180where
181 BD: GpuArrayBufferable + Sync + Send + 'static,
182 BDI: Pod + Sync + Send + Default + 'static,
183{
184 fn default() -> Self {
185 BatchedInstanceBuffers {
186 current_input_buffer: InstanceInputUniformBuffer::new(),
187 previous_input_buffer: InstanceInputUniformBuffer::new(),
188 phase_instance_buffers: HashMap::default(),
189 }
190 }
191}
192
193#[derive(Resource)]
203pub struct PhaseBatchedInstanceBuffers<PI, BD>
204where
205 PI: PhaseItem,
206 BD: GpuArrayBufferable + Sync + Send + 'static,
207{
208 pub buffers: UntypedPhaseBatchedInstanceBuffers<BD>,
210 phantom: PhantomData<PI>,
211}
212
213impl<PI, BD> Default for PhaseBatchedInstanceBuffers<PI, BD>
214where
215 PI: PhaseItem,
216 BD: GpuArrayBufferable + Sync + Send + 'static,
217{
218 fn default() -> Self {
219 PhaseBatchedInstanceBuffers {
220 buffers: UntypedPhaseBatchedInstanceBuffers::default(),
221 phantom: PhantomData,
222 }
223 }
224}
225
226pub struct UntypedPhaseBatchedInstanceBuffers<BD>
232where
233 BD: GpuArrayBufferable + Sync + Send + 'static,
234{
235 pub data_buffer: UninitBufferVec<BD>,
240
241 pub work_item_buffers: HashMap<RetainedViewEntity, PreprocessWorkItemBuffers>,
246
247 pub late_indexed_indirect_parameters_buffer:
255 RawBufferVec<LatePreprocessWorkItemIndirectParameters>,
256
257 pub late_non_indexed_indirect_parameters_buffer:
265 RawBufferVec<LatePreprocessWorkItemIndirectParameters>,
266}
267
268pub struct InstanceInputUniformBuffer<BDI>
274where
275 BDI: Pod + Default,
276{
277 buffer: RawBufferVec<BDI>,
279
280 free_uniform_indices: Vec<u32>,
285}
286
287impl<BDI> InstanceInputUniformBuffer<BDI>
288where
289 BDI: Pod + Default,
290{
291 pub fn new() -> InstanceInputUniformBuffer<BDI> {
293 InstanceInputUniformBuffer {
294 buffer: RawBufferVec::new(BufferUsages::STORAGE),
295 free_uniform_indices: vec![],
296 }
297 }
298
299 pub fn clear(&mut self) {
301 self.buffer.clear();
302 self.free_uniform_indices.clear();
303 }
304
305 #[inline]
307 pub fn buffer(&self) -> &RawBufferVec<BDI> {
308 &self.buffer
309 }
310
311 pub fn add(&mut self, element: BDI) -> u32 {
314 match self.free_uniform_indices.pop() {
315 Some(uniform_index) => {
316 self.buffer.values_mut()[uniform_index as usize] = element;
317 uniform_index
318 }
319 None => self.buffer.push(element) as u32,
320 }
321 }
322
323 pub fn remove(&mut self, uniform_index: u32) {
327 self.free_uniform_indices.push(uniform_index);
328 }
329
330 pub fn get(&self, uniform_index: u32) -> Option<BDI> {
334 if (uniform_index as usize) >= self.buffer.len()
335 || self.free_uniform_indices.contains(&uniform_index)
336 {
337 None
338 } else {
339 Some(self.get_unchecked(uniform_index))
340 }
341 }
342
343 pub fn get_unchecked(&self, uniform_index: u32) -> BDI {
349 self.buffer.values()[uniform_index as usize]
350 }
351
352 pub fn set(&mut self, uniform_index: u32, element: BDI) {
357 self.buffer.values_mut()[uniform_index as usize] = element;
358 }
359
360 pub fn ensure_nonempty(&mut self) {
363 if self.buffer.is_empty() {
364 self.buffer.push(default());
365 }
366 }
367
368 pub fn len(&self) -> usize {
370 self.buffer.len()
371 }
372
373 pub fn is_empty(&self) -> bool {
376 self.buffer.is_empty()
377 }
378
379 pub fn into_buffer(self) -> RawBufferVec<BDI> {
382 self.buffer
383 }
384}
385
386impl<BDI> Default for InstanceInputUniformBuffer<BDI>
387where
388 BDI: Pod + Default,
389{
390 fn default() -> Self {
391 Self::new()
392 }
393}
394
395#[cfg_attr(
397 not(target_arch = "wasm32"),
398 expect(
399 clippy::large_enum_variant,
400 reason = "See https://github.com/bevyengine/bevy/issues/19220"
401 )
402)]
403pub enum PreprocessWorkItemBuffers {
404 Direct(RawBufferVec<PreprocessWorkItem>),
409
410 Indirect {
416 indexed: RawBufferVec<PreprocessWorkItem>,
418 non_indexed: RawBufferVec<PreprocessWorkItem>,
420 gpu_occlusion_culling: Option<GpuOcclusionCullingWorkItemBuffers>,
422 },
423}
424
425pub struct GpuOcclusionCullingWorkItemBuffers {
427 pub late_indexed: UninitBufferVec<PreprocessWorkItem>,
429 pub late_non_indexed: UninitBufferVec<PreprocessWorkItem>,
431 pub late_indirect_parameters_indexed_offset: u32,
435 pub late_indirect_parameters_non_indexed_offset: u32,
439}
440
441#[derive(Clone, Copy, ShaderType, Pod, Zeroable)]
447#[repr(C)]
448pub struct LatePreprocessWorkItemIndirectParameters {
449 dispatch_x: u32,
453 dispatch_y: u32,
456 dispatch_z: u32,
459 work_item_count: u32,
465 pad: UVec4,
467}
468
469impl Default for LatePreprocessWorkItemIndirectParameters {
470 fn default() -> LatePreprocessWorkItemIndirectParameters {
471 LatePreprocessWorkItemIndirectParameters {
472 dispatch_x: 0,
473 dispatch_y: 1,
474 dispatch_z: 1,
475 work_item_count: 0,
476 pad: default(),
477 }
478 }
479}
480
481pub fn get_or_create_work_item_buffer<'a, I>(
490 work_item_buffers: &'a mut HashMap<RetainedViewEntity, PreprocessWorkItemBuffers>,
491 view: RetainedViewEntity,
492 no_indirect_drawing: bool,
493 enable_gpu_occlusion_culling: bool,
494) -> &'a mut PreprocessWorkItemBuffers
495where
496 I: 'static,
497{
498 let preprocess_work_item_buffers = match work_item_buffers.entry(view) {
499 Entry::Occupied(occupied_entry) => occupied_entry.into_mut(),
500 Entry::Vacant(vacant_entry) => {
501 if no_indirect_drawing {
502 vacant_entry.insert(PreprocessWorkItemBuffers::Direct(RawBufferVec::new(
503 BufferUsages::STORAGE,
504 )))
505 } else {
506 vacant_entry.insert(PreprocessWorkItemBuffers::Indirect {
507 indexed: RawBufferVec::new(BufferUsages::STORAGE),
508 non_indexed: RawBufferVec::new(BufferUsages::STORAGE),
509 gpu_occlusion_culling: None,
512 })
513 }
514 }
515 };
516
517 if let PreprocessWorkItemBuffers::Indirect {
519 ref mut gpu_occlusion_culling,
520 ..
521 } = *preprocess_work_item_buffers
522 {
523 match (
524 enable_gpu_occlusion_culling,
525 gpu_occlusion_culling.is_some(),
526 ) {
527 (false, false) | (true, true) => {}
528 (false, true) => {
529 *gpu_occlusion_culling = None;
530 }
531 (true, false) => {
532 *gpu_occlusion_culling = Some(GpuOcclusionCullingWorkItemBuffers {
533 late_indexed: UninitBufferVec::new(BufferUsages::STORAGE),
534 late_non_indexed: UninitBufferVec::new(BufferUsages::STORAGE),
535 late_indirect_parameters_indexed_offset: 0,
536 late_indirect_parameters_non_indexed_offset: 0,
537 });
538 }
539 }
540 }
541
542 preprocess_work_item_buffers
543}
544
545pub fn init_work_item_buffers(
547 work_item_buffers: &mut PreprocessWorkItemBuffers,
548 late_indexed_indirect_parameters_buffer: &'_ mut RawBufferVec<
549 LatePreprocessWorkItemIndirectParameters,
550 >,
551 late_non_indexed_indirect_parameters_buffer: &'_ mut RawBufferVec<
552 LatePreprocessWorkItemIndirectParameters,
553 >,
554) {
555 if let PreprocessWorkItemBuffers::Indirect {
558 gpu_occlusion_culling:
559 Some(GpuOcclusionCullingWorkItemBuffers {
560 ref mut late_indirect_parameters_indexed_offset,
561 ref mut late_indirect_parameters_non_indexed_offset,
562 ..
563 }),
564 ..
565 } = *work_item_buffers
566 {
567 *late_indirect_parameters_indexed_offset = late_indexed_indirect_parameters_buffer
568 .push(LatePreprocessWorkItemIndirectParameters::default())
569 as u32;
570 *late_indirect_parameters_non_indexed_offset = late_non_indexed_indirect_parameters_buffer
571 .push(LatePreprocessWorkItemIndirectParameters::default())
572 as u32;
573 }
574}
575
576impl PreprocessWorkItemBuffers {
577 pub fn push(&mut self, indexed: bool, preprocess_work_item: PreprocessWorkItem) {
582 match *self {
583 PreprocessWorkItemBuffers::Direct(ref mut buffer) => {
584 buffer.push(preprocess_work_item);
585 }
586 PreprocessWorkItemBuffers::Indirect {
587 indexed: ref mut indexed_buffer,
588 non_indexed: ref mut non_indexed_buffer,
589 ref mut gpu_occlusion_culling,
590 } => {
591 if indexed {
592 indexed_buffer.push(preprocess_work_item);
593 } else {
594 non_indexed_buffer.push(preprocess_work_item);
595 }
596
597 if let Some(ref mut gpu_occlusion_culling) = *gpu_occlusion_culling {
598 if indexed {
599 gpu_occlusion_culling.late_indexed.add();
600 } else {
601 gpu_occlusion_culling.late_non_indexed.add();
602 }
603 }
604 }
605 }
606 }
607
608 pub fn clear(&mut self) {
610 match *self {
611 PreprocessWorkItemBuffers::Direct(ref mut buffer) => {
612 buffer.clear();
613 }
614 PreprocessWorkItemBuffers::Indirect {
615 indexed: ref mut indexed_buffer,
616 non_indexed: ref mut non_indexed_buffer,
617 ref mut gpu_occlusion_culling,
618 } => {
619 indexed_buffer.clear();
620 non_indexed_buffer.clear();
621
622 if let Some(ref mut gpu_occlusion_culling) = *gpu_occlusion_culling {
623 gpu_occlusion_culling.late_indexed.clear();
624 gpu_occlusion_culling.late_non_indexed.clear();
625 gpu_occlusion_culling.late_indirect_parameters_indexed_offset = 0;
626 gpu_occlusion_culling.late_indirect_parameters_non_indexed_offset = 0;
627 }
628 }
629 }
630 }
631}
632
633#[derive(Clone, Copy, Default, Pod, Zeroable, ShaderType)]
636#[repr(C)]
637pub struct PreprocessWorkItem {
638 pub input_index: u32,
641
642 pub output_or_indirect_parameters_index: u32,
650}
651
652#[derive(Clone, Copy, Debug, Pod, Zeroable, ShaderType)]
657#[repr(C)]
658pub struct IndirectParametersIndexed {
659 pub index_count: u32,
661 pub instance_count: u32,
663 pub first_index: u32,
665 pub base_vertex: u32,
667 pub first_instance: u32,
669}
670
671#[derive(Clone, Copy, Debug, Pod, Zeroable, ShaderType)]
676#[repr(C)]
677pub struct IndirectParametersNonIndexed {
678 pub vertex_count: u32,
680 pub instance_count: u32,
682 pub base_vertex: u32,
684 pub first_instance: u32,
686}
687
688#[derive(Clone, Copy, Default, Pod, Zeroable, ShaderType)]
693#[repr(C)]
694pub struct IndirectParametersCpuMetadata {
695 pub base_output_index: u32,
703
704 pub batch_set_index: u32,
712}
713
714#[derive(Clone, Copy, Default, Pod, Zeroable, ShaderType)]
724#[repr(C)]
725pub struct IndirectParametersGpuMetadata {
726 pub mesh_index: u32,
729
730 pub early_instance_count: u32,
735
736 pub late_instance_count: u32,
742}
743
744#[derive(Clone, Copy, Default, Pod, Zeroable, ShaderType)]
756#[repr(C)]
757pub struct IndirectBatchSet {
758 pub indirect_parameters_count: u32,
767
768 pub indirect_parameters_base: u32,
774}
775
776#[derive(Resource, Deref, DerefMut)]
786pub struct IndirectParametersBuffers {
787 #[deref]
792 pub buffers: TypeIdMap<UntypedPhaseIndirectParametersBuffers>,
793 pub allow_copies_from_indirect_parameter_buffers: bool,
799}
800
801impl IndirectParametersBuffers {
802 pub fn new(allow_copies_from_indirect_parameter_buffers: bool) -> IndirectParametersBuffers {
804 IndirectParametersBuffers {
805 buffers: TypeIdMap::default(),
806 allow_copies_from_indirect_parameter_buffers,
807 }
808 }
809}
810
811#[derive(Resource)]
819pub struct PhaseIndirectParametersBuffers<PI>
820where
821 PI: PhaseItem,
822{
823 pub buffers: UntypedPhaseIndirectParametersBuffers,
825 phantom: PhantomData<PI>,
826}
827
828impl<PI> PhaseIndirectParametersBuffers<PI>
829where
830 PI: PhaseItem,
831{
832 pub fn new(allow_copies_from_indirect_parameter_buffers: bool) -> Self {
833 PhaseIndirectParametersBuffers {
834 buffers: UntypedPhaseIndirectParametersBuffers::new(
835 allow_copies_from_indirect_parameter_buffers,
836 ),
837 phantom: PhantomData,
838 }
839 }
840}
841
842pub struct UntypedPhaseIndirectParametersBuffers {
850 pub indexed: MeshClassIndirectParametersBuffers<IndirectParametersIndexed>,
853 pub non_indexed: MeshClassIndirectParametersBuffers<IndirectParametersNonIndexed>,
856}
857
858impl UntypedPhaseIndirectParametersBuffers {
859 pub fn new(
861 allow_copies_from_indirect_parameter_buffers: bool,
862 ) -> UntypedPhaseIndirectParametersBuffers {
863 let mut indirect_parameter_buffer_usages = BufferUsages::STORAGE | BufferUsages::INDIRECT;
864 if allow_copies_from_indirect_parameter_buffers {
865 indirect_parameter_buffer_usages |= BufferUsages::COPY_SRC;
866 }
867
868 UntypedPhaseIndirectParametersBuffers {
869 non_indexed: MeshClassIndirectParametersBuffers::new(
870 allow_copies_from_indirect_parameter_buffers,
871 ),
872 indexed: MeshClassIndirectParametersBuffers::new(
873 allow_copies_from_indirect_parameter_buffers,
874 ),
875 }
876 }
877
878 pub fn allocate(&mut self, indexed: bool, count: u32) -> u32 {
883 if indexed {
884 self.indexed.allocate(count)
885 } else {
886 self.non_indexed.allocate(count)
887 }
888 }
889
890 fn batch_count(&self, indexed: bool) -> usize {
895 if indexed {
896 self.indexed.batch_count()
897 } else {
898 self.non_indexed.batch_count()
899 }
900 }
901
902 pub fn batch_set_count(&self, indexed: bool) -> usize {
907 if indexed {
908 self.indexed.batch_sets.len()
909 } else {
910 self.non_indexed.batch_sets.len()
911 }
912 }
913
914 #[inline]
922 pub fn add_batch_set(&mut self, indexed: bool, indirect_parameters_base: u32) {
923 if indexed {
924 self.indexed.batch_sets.push(IndirectBatchSet {
925 indirect_parameters_base,
926 indirect_parameters_count: 0,
927 });
928 } else {
929 self.non_indexed.batch_sets.push(IndirectBatchSet {
930 indirect_parameters_base,
931 indirect_parameters_count: 0,
932 });
933 }
934 }
935
936 pub fn get_next_batch_set_index(&self, indexed: bool) -> Option<NonMaxU32> {
941 NonMaxU32::new(self.batch_set_count(indexed) as u32)
942 }
943
944 pub fn clear(&mut self) {
946 self.indexed.clear();
947 self.non_indexed.clear();
948 }
949}
950
951pub struct MeshClassIndirectParametersBuffers<IP>
955where
956 IP: Clone + ShaderSize + WriteInto,
957{
958 data: UninitBufferVec<IP>,
964
965 cpu_metadata: RawBufferVec<IndirectParametersCpuMetadata>,
972
973 gpu_metadata: UninitBufferVec<IndirectParametersGpuMetadata>,
980
981 batch_sets: RawBufferVec<IndirectBatchSet>,
988}
989
990impl<IP> MeshClassIndirectParametersBuffers<IP>
991where
992 IP: Clone + ShaderSize + WriteInto,
993{
994 fn new(
995 allow_copies_from_indirect_parameter_buffers: bool,
996 ) -> MeshClassIndirectParametersBuffers<IP> {
997 let mut indirect_parameter_buffer_usages = BufferUsages::STORAGE | BufferUsages::INDIRECT;
998 if allow_copies_from_indirect_parameter_buffers {
999 indirect_parameter_buffer_usages |= BufferUsages::COPY_SRC;
1000 }
1001
1002 MeshClassIndirectParametersBuffers {
1003 data: UninitBufferVec::new(indirect_parameter_buffer_usages),
1004 cpu_metadata: RawBufferVec::new(BufferUsages::STORAGE),
1005 gpu_metadata: UninitBufferVec::new(BufferUsages::STORAGE),
1006 batch_sets: RawBufferVec::new(indirect_parameter_buffer_usages),
1007 }
1008 }
1009
1010 #[inline]
1017 pub fn data_buffer(&self) -> Option<&Buffer> {
1018 self.data.buffer()
1019 }
1020
1021 #[inline]
1027 pub fn cpu_metadata_buffer(&self) -> Option<&Buffer> {
1028 self.cpu_metadata.buffer()
1029 }
1030
1031 #[inline]
1038 pub fn gpu_metadata_buffer(&self) -> Option<&Buffer> {
1039 self.gpu_metadata.buffer()
1040 }
1041
1042 #[inline]
1049 pub fn batch_sets_buffer(&self) -> Option<&Buffer> {
1050 self.batch_sets.buffer()
1051 }
1052
1053 fn allocate(&mut self, count: u32) -> u32 {
1058 let length = self.data.len();
1059 self.cpu_metadata.reserve_internal(count as usize);
1060 self.gpu_metadata.add_multiple(count as usize);
1061 for _ in 0..count {
1062 self.data.add();
1063 self.cpu_metadata
1064 .push(IndirectParametersCpuMetadata::default());
1065 }
1066 length as u32
1067 }
1068
1069 pub fn set(&mut self, index: u32, value: IndirectParametersCpuMetadata) {
1072 self.cpu_metadata.set(index, value);
1073 }
1074
1075 #[inline]
1078 pub fn batch_count(&self) -> usize {
1079 self.data.len()
1080 }
1081
1082 pub fn clear(&mut self) {
1084 self.data.clear();
1085 self.cpu_metadata.clear();
1086 self.gpu_metadata.clear();
1087 self.batch_sets.clear();
1088 }
1089}
1090
1091impl Default for IndirectParametersBuffers {
1092 fn default() -> Self {
1093 Self::new(false)
1096 }
1097}
1098
1099impl FromWorld for GpuPreprocessingSupport {
1100 fn from_world(world: &mut World) -> Self {
1101 let adapter = world.resource::<RenderAdapter>();
1102 let device = world.resource::<RenderDevice>();
1103
1104 fn is_non_supported_android_device(adapter_info: &RenderAdapterInfo) -> bool {
1109 crate::get_adreno_model(adapter_info).is_some_and(|model| model != 720 && model <= 730)
1110 || crate::get_mali_driver_version(adapter_info).is_some_and(|version| version < 48)
1111 }
1112
1113 let culling_feature_support = device
1114 .features()
1115 .contains(Features::INDIRECT_FIRST_INSTANCE | Features::PUSH_CONSTANTS);
1116 let limit_support = device.limits().max_storage_textures_per_shader_stage >= 12 &&
1118 device.limits().max_compute_workgroup_storage_size != 0;
1123
1124 let downlevel_support = adapter
1125 .get_downlevel_capabilities()
1126 .flags
1127 .contains(DownlevelFlags::COMPUTE_SHADERS);
1128
1129 let adapter_info = RenderAdapterInfo(WgpuWrapper::new(adapter.get_info()));
1130
1131 let max_supported_mode = if device.limits().max_compute_workgroup_size_x == 0
1132 || is_non_supported_android_device(&adapter_info)
1133 || adapter_info.backend == wgpu::Backend::Gl
1134 {
1135 info!(
1136 "GPU preprocessing is not supported on this device. \
1137 Falling back to CPU preprocessing.",
1138 );
1139 GpuPreprocessingMode::None
1140 } else if !(culling_feature_support && limit_support && downlevel_support) {
1141 info!("Some GPU preprocessing are limited on this device.");
1142 GpuPreprocessingMode::PreprocessingOnly
1143 } else {
1144 info!("GPU preprocessing is fully supported on this device.");
1145 GpuPreprocessingMode::Culling
1146 };
1147
1148 GpuPreprocessingSupport { max_supported_mode }
1149 }
1150}
1151
1152impl<BD, BDI> BatchedInstanceBuffers<BD, BDI>
1153where
1154 BD: GpuArrayBufferable + Sync + Send + 'static,
1155 BDI: Pod + Sync + Send + Default + 'static,
1156{
1157 pub fn new() -> Self {
1159 Self::default()
1160 }
1161
1162 pub fn clear(&mut self) {
1164 for phase_instance_buffer in self.phase_instance_buffers.values_mut() {
1165 phase_instance_buffer.clear();
1166 }
1167 }
1168}
1169
1170impl<BD> UntypedPhaseBatchedInstanceBuffers<BD>
1171where
1172 BD: GpuArrayBufferable + Sync + Send + 'static,
1173{
1174 pub fn new() -> Self {
1175 UntypedPhaseBatchedInstanceBuffers {
1176 data_buffer: UninitBufferVec::new(BufferUsages::STORAGE),
1177 work_item_buffers: HashMap::default(),
1178 late_indexed_indirect_parameters_buffer: RawBufferVec::new(
1179 BufferUsages::STORAGE | BufferUsages::INDIRECT,
1180 ),
1181 late_non_indexed_indirect_parameters_buffer: RawBufferVec::new(
1182 BufferUsages::STORAGE | BufferUsages::INDIRECT,
1183 ),
1184 }
1185 }
1186
1187 pub fn instance_data_binding(&self) -> Option<BindingResource<'_>> {
1191 self.data_buffer
1192 .buffer()
1193 .map(|buffer| buffer.as_entire_binding())
1194 }
1195
1196 pub fn clear(&mut self) {
1198 self.data_buffer.clear();
1199 self.late_indexed_indirect_parameters_buffer.clear();
1200 self.late_non_indexed_indirect_parameters_buffer.clear();
1201
1202 for view_work_item_buffers in self.work_item_buffers.values_mut() {
1205 view_work_item_buffers.clear();
1206 }
1207 }
1208}
1209
1210impl<BD> Default for UntypedPhaseBatchedInstanceBuffers<BD>
1211where
1212 BD: GpuArrayBufferable + Sync + Send + 'static,
1213{
1214 fn default() -> Self {
1215 Self::new()
1216 }
1217}
1218
1219struct SortedRenderBatch<F>
1222where
1223 F: GetBatchData,
1224{
1225 phase_item_start_index: u32,
1228
1229 instance_start_index: u32,
1231
1232 indexed: bool,
1234
1235 indirect_parameters_index: Option<NonMaxU32>,
1240
1241 meta: Option<BatchMeta<F::CompareData>>,
1246}
1247
1248impl<F> SortedRenderBatch<F>
1249where
1250 F: GetBatchData,
1251{
1252 fn flush<I>(
1258 self,
1259 instance_end_index: u32,
1260 phase: &mut SortedRenderPhase<I>,
1261 phase_indirect_parameters_buffers: &mut UntypedPhaseIndirectParametersBuffers,
1262 ) where
1263 I: CachedRenderPipelinePhaseItem + SortedPhaseItem,
1264 {
1265 let (batch_range, batch_extra_index) =
1266 phase.items[self.phase_item_start_index as usize].batch_range_and_extra_index_mut();
1267 *batch_range = self.instance_start_index..instance_end_index;
1268 *batch_extra_index = match self.indirect_parameters_index {
1269 Some(indirect_parameters_index) => PhaseItemExtraIndex::IndirectParametersIndex {
1270 range: u32::from(indirect_parameters_index)
1271 ..(u32::from(indirect_parameters_index) + 1),
1272 batch_set_index: None,
1273 },
1274 None => PhaseItemExtraIndex::None,
1275 };
1276 if let Some(indirect_parameters_index) = self.indirect_parameters_index {
1277 phase_indirect_parameters_buffers
1278 .add_batch_set(self.indexed, indirect_parameters_index.into());
1279 }
1280 }
1281}
1282
1283pub fn clear_batched_gpu_instance_buffers<GFBD>(
1290 gpu_batched_instance_buffers: Option<
1291 ResMut<BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>>,
1292 >,
1293) where
1294 GFBD: GetFullBatchData,
1295{
1296 if let Some(mut gpu_batched_instance_buffers) = gpu_batched_instance_buffers {
1299 gpu_batched_instance_buffers.clear();
1300 }
1301}
1302
1303pub fn delete_old_work_item_buffers<GFBD>(
1310 mut gpu_batched_instance_buffers: ResMut<
1311 BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1312 >,
1313 extracted_views: Query<&ExtractedView>,
1314) where
1315 GFBD: GetFullBatchData,
1316{
1317 let retained_view_entities: HashSet<_> = extracted_views
1318 .iter()
1319 .map(|extracted_view| extracted_view.retained_view_entity)
1320 .collect();
1321 for phase_instance_buffers in gpu_batched_instance_buffers
1322 .phase_instance_buffers
1323 .values_mut()
1324 {
1325 phase_instance_buffers
1326 .work_item_buffers
1327 .retain(|retained_view_entity, _| {
1328 retained_view_entities.contains(retained_view_entity)
1329 });
1330 }
1331}
1332
1333pub fn batch_and_prepare_sorted_render_phase<I, GFBD>(
1337 mut phase_batched_instance_buffers: ResMut<PhaseBatchedInstanceBuffers<I, GFBD::BufferData>>,
1338 mut phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<I>>,
1339 mut sorted_render_phases: ResMut<ViewSortedRenderPhases<I>>,
1340 mut views: Query<(
1341 &ExtractedView,
1342 Has<NoIndirectDrawing>,
1343 Has<OcclusionCulling>,
1344 )>,
1345 system_param_item: StaticSystemParam<GFBD::Param>,
1346) where
1347 I: CachedRenderPipelinePhaseItem + SortedPhaseItem,
1348 GFBD: GetFullBatchData,
1349{
1350 let UntypedPhaseBatchedInstanceBuffers {
1352 ref mut data_buffer,
1353 ref mut work_item_buffers,
1354 ref mut late_indexed_indirect_parameters_buffer,
1355 ref mut late_non_indexed_indirect_parameters_buffer,
1356 } = phase_batched_instance_buffers.buffers;
1357
1358 for (extracted_view, no_indirect_drawing, gpu_occlusion_culling) in &mut views {
1359 let Some(phase) = sorted_render_phases.get_mut(&extracted_view.retained_view_entity) else {
1360 continue;
1361 };
1362
1363 let work_item_buffer = get_or_create_work_item_buffer::<I>(
1365 work_item_buffers,
1366 extracted_view.retained_view_entity,
1367 no_indirect_drawing,
1368 gpu_occlusion_culling,
1369 );
1370
1371 init_work_item_buffers(
1373 work_item_buffer,
1374 late_indexed_indirect_parameters_buffer,
1375 late_non_indexed_indirect_parameters_buffer,
1376 );
1377
1378 let mut batch: Option<SortedRenderBatch<GFBD>> = None;
1380
1381 for current_index in 0..phase.items.len() {
1382 let item = &phase.items[current_index];
1385 let entity = item.main_entity();
1386 let item_is_indexed = item.indexed();
1387 let current_batch_input_index =
1388 GFBD::get_index_and_compare_data(&system_param_item, entity);
1389
1390 let Some((current_input_index, current_meta)) = current_batch_input_index else {
1395 if let Some(batch) = batch.take() {
1397 batch.flush(
1398 data_buffer.len() as u32,
1399 phase,
1400 &mut phase_indirect_parameters_buffers.buffers,
1401 );
1402 }
1403
1404 continue;
1405 };
1406 let current_meta =
1407 current_meta.map(|meta| BatchMeta::new(&phase.items[current_index], meta));
1408
1409 let can_batch = batch.as_ref().is_some_and(|batch| {
1412 match (¤t_meta, &batch.meta) {
1414 (Some(current_meta), Some(batch_meta)) => current_meta == batch_meta,
1415 (_, _) => false,
1416 }
1417 });
1418
1419 let output_index = data_buffer.add() as u32;
1421
1422 if !can_batch {
1424 if let Some(batch) = batch.take() {
1426 batch.flush(
1427 output_index,
1428 phase,
1429 &mut phase_indirect_parameters_buffers.buffers,
1430 );
1431 }
1432
1433 let indirect_parameters_index = if no_indirect_drawing {
1434 None
1435 } else if item_is_indexed {
1436 Some(
1437 phase_indirect_parameters_buffers
1438 .buffers
1439 .indexed
1440 .allocate(1),
1441 )
1442 } else {
1443 Some(
1444 phase_indirect_parameters_buffers
1445 .buffers
1446 .non_indexed
1447 .allocate(1),
1448 )
1449 };
1450
1451 if let Some(indirect_parameters_index) = indirect_parameters_index {
1453 GFBD::write_batch_indirect_parameters_metadata(
1454 item_is_indexed,
1455 output_index,
1456 None,
1457 &mut phase_indirect_parameters_buffers.buffers,
1458 indirect_parameters_index,
1459 );
1460 };
1461
1462 batch = Some(SortedRenderBatch {
1463 phase_item_start_index: current_index as u32,
1464 instance_start_index: output_index,
1465 indexed: item_is_indexed,
1466 indirect_parameters_index: indirect_parameters_index.and_then(NonMaxU32::new),
1467 meta: current_meta,
1468 });
1469 }
1470
1471 if let Some(batch) = batch.as_ref() {
1474 work_item_buffer.push(
1475 item_is_indexed,
1476 PreprocessWorkItem {
1477 input_index: current_input_index.into(),
1478 output_or_indirect_parameters_index: match (
1479 no_indirect_drawing,
1480 batch.indirect_parameters_index,
1481 ) {
1482 (true, _) => output_index,
1483 (false, Some(indirect_parameters_index)) => {
1484 indirect_parameters_index.into()
1485 }
1486 (false, None) => 0,
1487 },
1488 },
1489 );
1490 }
1491 }
1492
1493 if let Some(batch) = batch.take() {
1495 batch.flush(
1496 data_buffer.len() as u32,
1497 phase,
1498 &mut phase_indirect_parameters_buffers.buffers,
1499 );
1500 }
1501 }
1502}
1503
1504pub fn batch_and_prepare_binned_render_phase<BPI, GFBD>(
1506 mut phase_batched_instance_buffers: ResMut<PhaseBatchedInstanceBuffers<BPI, GFBD::BufferData>>,
1507 phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<BPI>>,
1508 mut binned_render_phases: ResMut<ViewBinnedRenderPhases<BPI>>,
1509 mut views: Query<
1510 (
1511 &ExtractedView,
1512 Has<NoIndirectDrawing>,
1513 Has<OcclusionCulling>,
1514 ),
1515 With<ExtractedView>,
1516 >,
1517 param: StaticSystemParam<GFBD::Param>,
1518) where
1519 BPI: BinnedPhaseItem,
1520 GFBD: GetFullBatchData,
1521{
1522 let system_param_item = param.into_inner();
1523
1524 let phase_indirect_parameters_buffers = phase_indirect_parameters_buffers.into_inner();
1525
1526 let UntypedPhaseBatchedInstanceBuffers {
1527 ref mut data_buffer,
1528 ref mut work_item_buffers,
1529 ref mut late_indexed_indirect_parameters_buffer,
1530 ref mut late_non_indexed_indirect_parameters_buffer,
1531 } = phase_batched_instance_buffers.buffers;
1532
1533 for (extracted_view, no_indirect_drawing, gpu_occlusion_culling) in &mut views {
1534 let Some(phase) = binned_render_phases.get_mut(&extracted_view.retained_view_entity) else {
1535 continue;
1536 };
1537
1538 let work_item_buffer = get_or_create_work_item_buffer::<BPI>(
1541 work_item_buffers,
1542 extracted_view.retained_view_entity,
1543 no_indirect_drawing,
1544 gpu_occlusion_culling,
1545 );
1546
1547 init_work_item_buffers(
1549 work_item_buffer,
1550 late_indexed_indirect_parameters_buffer,
1551 late_non_indexed_indirect_parameters_buffer,
1552 );
1553
1554 if let (
1557 &mut BinnedRenderPhaseBatchSets::MultidrawIndirect(ref mut batch_sets),
1558 &mut PreprocessWorkItemBuffers::Indirect {
1559 indexed: ref mut indexed_work_item_buffer,
1560 non_indexed: ref mut non_indexed_work_item_buffer,
1561 gpu_occlusion_culling: ref mut gpu_occlusion_culling_buffers,
1562 },
1563 ) = (&mut phase.batch_sets, &mut *work_item_buffer)
1564 {
1565 let mut output_index = data_buffer.len() as u32;
1566
1567 let mut indexed_preparer: MultidrawableBatchSetPreparer<BPI, GFBD> =
1569 MultidrawableBatchSetPreparer::new(
1570 phase_indirect_parameters_buffers.buffers.batch_count(true) as u32,
1571 phase_indirect_parameters_buffers
1572 .buffers
1573 .indexed
1574 .batch_sets
1575 .len() as u32,
1576 );
1577 let mut non_indexed_preparer: MultidrawableBatchSetPreparer<BPI, GFBD> =
1578 MultidrawableBatchSetPreparer::new(
1579 phase_indirect_parameters_buffers.buffers.batch_count(false) as u32,
1580 phase_indirect_parameters_buffers
1581 .buffers
1582 .non_indexed
1583 .batch_sets
1584 .len() as u32,
1585 );
1586
1587 for (batch_set_key, bins) in &phase.multidrawable_meshes {
1589 if batch_set_key.indexed() {
1590 indexed_preparer.prepare_multidrawable_binned_batch_set(
1591 bins,
1592 &mut output_index,
1593 data_buffer,
1594 indexed_work_item_buffer,
1595 &mut phase_indirect_parameters_buffers.buffers.indexed,
1596 batch_sets,
1597 );
1598 } else {
1599 non_indexed_preparer.prepare_multidrawable_binned_batch_set(
1600 bins,
1601 &mut output_index,
1602 data_buffer,
1603 non_indexed_work_item_buffer,
1604 &mut phase_indirect_parameters_buffers.buffers.non_indexed,
1605 batch_sets,
1606 );
1607 }
1608 }
1609
1610 if let Some(gpu_occlusion_culling_buffers) = gpu_occlusion_culling_buffers {
1612 gpu_occlusion_culling_buffers
1613 .late_indexed
1614 .add_multiple(indexed_preparer.work_item_count);
1615 gpu_occlusion_culling_buffers
1616 .late_non_indexed
1617 .add_multiple(non_indexed_preparer.work_item_count);
1618 }
1619 }
1620
1621 for (key, bin) in &phase.batchable_meshes {
1624 let mut batch: Option<BinnedRenderPhaseBatch> = None;
1625 for (&main_entity, &input_index) in bin.entities() {
1626 let output_index = data_buffer.add() as u32;
1627
1628 match batch {
1629 Some(ref mut batch) => {
1630 batch.instance_range.end = output_index + 1;
1631
1632 work_item_buffer.push(
1640 key.0.indexed(),
1641 PreprocessWorkItem {
1642 input_index: *input_index,
1643 output_or_indirect_parameters_index: match (
1644 no_indirect_drawing,
1645 &batch.extra_index,
1646 ) {
1647 (true, _) => output_index,
1648 (
1649 false,
1650 PhaseItemExtraIndex::IndirectParametersIndex {
1651 range: indirect_parameters_range,
1652 ..
1653 },
1654 ) => indirect_parameters_range.start,
1655 (false, &PhaseItemExtraIndex::DynamicOffset(_))
1656 | (false, &PhaseItemExtraIndex::None) => 0,
1657 },
1658 },
1659 );
1660 }
1661
1662 None if !no_indirect_drawing => {
1663 let indirect_parameters_index = phase_indirect_parameters_buffers
1665 .buffers
1666 .allocate(key.0.indexed(), 1);
1667 let batch_set_index = phase_indirect_parameters_buffers
1668 .buffers
1669 .get_next_batch_set_index(key.0.indexed());
1670
1671 GFBD::write_batch_indirect_parameters_metadata(
1672 key.0.indexed(),
1673 output_index,
1674 batch_set_index,
1675 &mut phase_indirect_parameters_buffers.buffers,
1676 indirect_parameters_index,
1677 );
1678 work_item_buffer.push(
1679 key.0.indexed(),
1680 PreprocessWorkItem {
1681 input_index: *input_index,
1682 output_or_indirect_parameters_index: indirect_parameters_index,
1683 },
1684 );
1685 batch = Some(BinnedRenderPhaseBatch {
1686 representative_entity: (Entity::PLACEHOLDER, main_entity),
1687 instance_range: output_index..output_index + 1,
1688 extra_index: PhaseItemExtraIndex::IndirectParametersIndex {
1689 range: indirect_parameters_index..(indirect_parameters_index + 1),
1690 batch_set_index: None,
1691 },
1692 });
1693 }
1694
1695 None => {
1696 work_item_buffer.push(
1698 key.0.indexed(),
1699 PreprocessWorkItem {
1700 input_index: *input_index,
1701 output_or_indirect_parameters_index: output_index,
1702 },
1703 );
1704 batch = Some(BinnedRenderPhaseBatch {
1705 representative_entity: (Entity::PLACEHOLDER, main_entity),
1706 instance_range: output_index..output_index + 1,
1707 extra_index: PhaseItemExtraIndex::None,
1708 });
1709 }
1710 }
1711 }
1712
1713 if let Some(batch) = batch {
1714 match phase.batch_sets {
1715 BinnedRenderPhaseBatchSets::DynamicUniforms(_) => {
1716 error!("Dynamic uniform batch sets shouldn't be used here");
1717 }
1718 BinnedRenderPhaseBatchSets::Direct(ref mut vec) => {
1719 vec.push(batch);
1720 }
1721 BinnedRenderPhaseBatchSets::MultidrawIndirect(ref mut vec) => {
1722 vec.push(BinnedRenderPhaseBatchSet {
1727 first_batch: batch,
1728 batch_count: 1,
1729 bin_key: key.1.clone(),
1730 index: phase_indirect_parameters_buffers
1731 .buffers
1732 .batch_set_count(key.0.indexed())
1733 as u32,
1734 });
1735 }
1736 }
1737 }
1738 }
1739
1740 for (key, unbatchables) in &mut phase.unbatchable_meshes {
1742 let mut indirect_parameters_offset = if no_indirect_drawing {
1744 None
1745 } else if key.0.indexed() {
1746 Some(
1747 phase_indirect_parameters_buffers
1748 .buffers
1749 .indexed
1750 .allocate(unbatchables.entities.len() as u32),
1751 )
1752 } else {
1753 Some(
1754 phase_indirect_parameters_buffers
1755 .buffers
1756 .non_indexed
1757 .allocate(unbatchables.entities.len() as u32),
1758 )
1759 };
1760
1761 for main_entity in unbatchables.entities.keys() {
1762 let Some(input_index) = GFBD::get_binned_index(&system_param_item, *main_entity)
1763 else {
1764 continue;
1765 };
1766 let output_index = data_buffer.add() as u32;
1767
1768 if let Some(ref mut indirect_parameters_index) = indirect_parameters_offset {
1769 GFBD::write_batch_indirect_parameters_metadata(
1772 key.0.indexed(),
1773 output_index,
1774 None,
1775 &mut phase_indirect_parameters_buffers.buffers,
1776 *indirect_parameters_index,
1777 );
1778 work_item_buffer.push(
1779 key.0.indexed(),
1780 PreprocessWorkItem {
1781 input_index: input_index.into(),
1782 output_or_indirect_parameters_index: *indirect_parameters_index,
1783 },
1784 );
1785 unbatchables
1786 .buffer_indices
1787 .add(UnbatchableBinnedEntityIndices {
1788 instance_index: *indirect_parameters_index,
1789 extra_index: PhaseItemExtraIndex::IndirectParametersIndex {
1790 range: *indirect_parameters_index..(*indirect_parameters_index + 1),
1791 batch_set_index: None,
1792 },
1793 });
1794 phase_indirect_parameters_buffers
1795 .buffers
1796 .add_batch_set(key.0.indexed(), *indirect_parameters_index);
1797 *indirect_parameters_index += 1;
1798 } else {
1799 work_item_buffer.push(
1800 key.0.indexed(),
1801 PreprocessWorkItem {
1802 input_index: input_index.into(),
1803 output_or_indirect_parameters_index: output_index,
1804 },
1805 );
1806 unbatchables
1807 .buffer_indices
1808 .add(UnbatchableBinnedEntityIndices {
1809 instance_index: output_index,
1810 extra_index: PhaseItemExtraIndex::None,
1811 });
1812 }
1813 }
1814 }
1815 }
1816}
1817
1818struct MultidrawableBatchSetPreparer<BPI, GFBD>
1824where
1825 BPI: BinnedPhaseItem,
1826 GFBD: GetFullBatchData,
1827{
1828 indirect_parameters_index: u32,
1831 batch_set_index: u32,
1833 work_item_count: usize,
1835 phantom: PhantomData<(BPI, GFBD)>,
1836}
1837
1838impl<BPI, GFBD> MultidrawableBatchSetPreparer<BPI, GFBD>
1839where
1840 BPI: BinnedPhaseItem,
1841 GFBD: GetFullBatchData,
1842{
1843 #[inline]
1846 fn new(initial_indirect_parameters_index: u32, initial_batch_set_index: u32) -> Self {
1847 MultidrawableBatchSetPreparer {
1848 indirect_parameters_index: initial_indirect_parameters_index,
1849 batch_set_index: initial_batch_set_index,
1850 work_item_count: 0,
1851 phantom: PhantomData,
1852 }
1853 }
1854
1855 #[inline]
1860 fn prepare_multidrawable_binned_batch_set<IP>(
1861 &mut self,
1862 bins: &IndexMap<BPI::BinKey, RenderBin>,
1863 output_index: &mut u32,
1864 data_buffer: &mut UninitBufferVec<GFBD::BufferData>,
1865 indexed_work_item_buffer: &mut RawBufferVec<PreprocessWorkItem>,
1866 mesh_class_buffers: &mut MeshClassIndirectParametersBuffers<IP>,
1867 batch_sets: &mut Vec<BinnedRenderPhaseBatchSet<BPI::BinKey>>,
1868 ) where
1869 IP: Clone + ShaderSize + WriteInto,
1870 {
1871 let current_indexed_batch_set_index = self.batch_set_index;
1872 let current_output_index = *output_index;
1873
1874 let indirect_parameters_base = self.indirect_parameters_index;
1875
1876 let Some((first_bin_key, first_bin)) = bins.iter().next() else {
1879 return;
1880 };
1881 let first_bin_len = first_bin.entities().len();
1882 let first_bin_entity = first_bin
1883 .entities()
1884 .keys()
1885 .next()
1886 .copied()
1887 .unwrap_or(MainEntity::from(Entity::PLACEHOLDER));
1888
1889 for bin in bins.values() {
1891 mesh_class_buffers
1894 .cpu_metadata
1895 .push(IndirectParametersCpuMetadata {
1896 base_output_index: *output_index,
1897 batch_set_index: self.batch_set_index,
1898 });
1899
1900 for &input_index in bin.entities().values() {
1903 indexed_work_item_buffer.push(PreprocessWorkItem {
1904 input_index: *input_index,
1905 output_or_indirect_parameters_index: self.indirect_parameters_index,
1906 });
1907 }
1908
1909 let bin_entity_count = bin.entities().len();
1912 data_buffer.add_multiple(bin_entity_count);
1913 *output_index += bin_entity_count as u32;
1914 self.work_item_count += bin_entity_count;
1915
1916 self.indirect_parameters_index += 1;
1917 }
1918
1919 let bin_count = bins.len();
1921 mesh_class_buffers.gpu_metadata.add_multiple(bin_count);
1922 mesh_class_buffers.data.add_multiple(bin_count);
1923
1924 mesh_class_buffers.batch_sets.push(IndirectBatchSet {
1926 indirect_parameters_base,
1927 indirect_parameters_count: 0,
1928 });
1929
1930 self.batch_set_index += 1;
1931
1932 batch_sets.push(BinnedRenderPhaseBatchSet {
1935 first_batch: BinnedRenderPhaseBatch {
1936 representative_entity: (Entity::PLACEHOLDER, first_bin_entity),
1937 instance_range: current_output_index..(current_output_index + first_bin_len as u32),
1938 extra_index: PhaseItemExtraIndex::maybe_indirect_parameters_index(NonMaxU32::new(
1939 indirect_parameters_base,
1940 )),
1941 },
1942 bin_key: (*first_bin_key).clone(),
1943 batch_count: self.indirect_parameters_index - indirect_parameters_base,
1944 index: current_indexed_batch_set_index,
1945 });
1946 }
1947}
1948
1949pub fn collect_buffers_for_phase<PI, GFBD>(
1964 mut phase_batched_instance_buffers: ResMut<PhaseBatchedInstanceBuffers<PI, GFBD::BufferData>>,
1965 mut phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<PI>>,
1966 mut batched_instance_buffers: ResMut<
1967 BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1968 >,
1969 mut indirect_parameters_buffers: ResMut<IndirectParametersBuffers>,
1970) where
1971 PI: PhaseItem,
1972 GFBD: GetFullBatchData + Send + Sync + 'static,
1973{
1974 let untyped_phase_batched_instance_buffers =
1978 mem::take(&mut phase_batched_instance_buffers.buffers);
1979 if let Some(mut old_untyped_phase_batched_instance_buffers) = batched_instance_buffers
1980 .phase_instance_buffers
1981 .insert(TypeId::of::<PI>(), untyped_phase_batched_instance_buffers)
1982 {
1983 old_untyped_phase_batched_instance_buffers.clear();
1984 phase_batched_instance_buffers.buffers = old_untyped_phase_batched_instance_buffers;
1985 }
1986
1987 let untyped_phase_indirect_parameters_buffers = mem::replace(
1991 &mut phase_indirect_parameters_buffers.buffers,
1992 UntypedPhaseIndirectParametersBuffers::new(
1993 indirect_parameters_buffers.allow_copies_from_indirect_parameter_buffers,
1994 ),
1995 );
1996 if let Some(mut old_untyped_phase_indirect_parameters_buffers) = indirect_parameters_buffers
1997 .insert(
1998 TypeId::of::<PI>(),
1999 untyped_phase_indirect_parameters_buffers,
2000 )
2001 {
2002 old_untyped_phase_indirect_parameters_buffers.clear();
2003 phase_indirect_parameters_buffers.buffers = old_untyped_phase_indirect_parameters_buffers;
2004 }
2005}
2006
2007pub fn write_batched_instance_buffers<GFBD>(
2009 render_device: Res<RenderDevice>,
2010 render_queue: Res<RenderQueue>,
2011 gpu_array_buffer: ResMut<BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>>,
2012) where
2013 GFBD: GetFullBatchData,
2014{
2015 let BatchedInstanceBuffers {
2016 current_input_buffer,
2017 previous_input_buffer,
2018 phase_instance_buffers,
2019 } = gpu_array_buffer.into_inner();
2020
2021 let render_device = &*render_device;
2022 let render_queue = &*render_queue;
2023
2024 ComputeTaskPool::get().scope(|scope| {
2025 scope.spawn(async {
2026 let _span = tracing::info_span!("write_current_input_buffers").entered();
2027 current_input_buffer
2028 .buffer
2029 .write_buffer(render_device, render_queue);
2030 });
2031 scope.spawn(async {
2032 let _span = tracing::info_span!("write_previous_input_buffers").entered();
2033 previous_input_buffer
2034 .buffer
2035 .write_buffer(render_device, render_queue);
2036 });
2037
2038 for phase_instance_buffers in phase_instance_buffers.values_mut() {
2039 let UntypedPhaseBatchedInstanceBuffers {
2040 ref mut data_buffer,
2041 ref mut work_item_buffers,
2042 ref mut late_indexed_indirect_parameters_buffer,
2043 ref mut late_non_indexed_indirect_parameters_buffer,
2044 } = *phase_instance_buffers;
2045
2046 scope.spawn(async {
2047 let _span = tracing::info_span!("write_phase_instance_buffers").entered();
2048 data_buffer.write_buffer(render_device);
2049 late_indexed_indirect_parameters_buffer.write_buffer(render_device, render_queue);
2050 late_non_indexed_indirect_parameters_buffer
2051 .write_buffer(render_device, render_queue);
2052 });
2053
2054 for phase_work_item_buffers in work_item_buffers.values_mut() {
2055 scope.spawn(async {
2056 let _span = tracing::info_span!("write_work_item_buffers").entered();
2057 match *phase_work_item_buffers {
2058 PreprocessWorkItemBuffers::Direct(ref mut buffer_vec) => {
2059 buffer_vec.write_buffer(render_device, render_queue);
2060 }
2061 PreprocessWorkItemBuffers::Indirect {
2062 ref mut indexed,
2063 ref mut non_indexed,
2064 ref mut gpu_occlusion_culling,
2065 } => {
2066 indexed.write_buffer(render_device, render_queue);
2067 non_indexed.write_buffer(render_device, render_queue);
2068
2069 if let Some(GpuOcclusionCullingWorkItemBuffers {
2070 ref mut late_indexed,
2071 ref mut late_non_indexed,
2072 late_indirect_parameters_indexed_offset: _,
2073 late_indirect_parameters_non_indexed_offset: _,
2074 }) = *gpu_occlusion_culling
2075 {
2076 if !late_indexed.is_empty() {
2077 late_indexed.write_buffer(render_device);
2078 }
2079 if !late_non_indexed.is_empty() {
2080 late_non_indexed.write_buffer(render_device);
2081 }
2082 }
2083 }
2084 }
2085 });
2086 }
2087 }
2088 });
2089}
2090
2091pub fn clear_indirect_parameters_buffers(
2092 mut indirect_parameters_buffers: ResMut<IndirectParametersBuffers>,
2093) {
2094 for phase_indirect_parameters_buffers in indirect_parameters_buffers.values_mut() {
2095 phase_indirect_parameters_buffers.clear();
2096 }
2097}
2098
2099pub fn write_indirect_parameters_buffers(
2100 render_device: Res<RenderDevice>,
2101 render_queue: Res<RenderQueue>,
2102 mut indirect_parameters_buffers: ResMut<IndirectParametersBuffers>,
2103) {
2104 let render_device = &*render_device;
2105 let render_queue = &*render_queue;
2106 ComputeTaskPool::get().scope(|scope| {
2107 for phase_indirect_parameters_buffers in indirect_parameters_buffers.values_mut() {
2108 scope.spawn(async {
2109 let _span = tracing::info_span!("indexed_data").entered();
2110 phase_indirect_parameters_buffers
2111 .indexed
2112 .data
2113 .write_buffer(render_device);
2114 });
2115 scope.spawn(async {
2116 let _span = tracing::info_span!("non_indexed_data").entered();
2117 phase_indirect_parameters_buffers
2118 .non_indexed
2119 .data
2120 .write_buffer(render_device);
2121 });
2122
2123 scope.spawn(async {
2124 let _span = tracing::info_span!("indexed_cpu_metadata").entered();
2125 phase_indirect_parameters_buffers
2126 .indexed
2127 .cpu_metadata
2128 .write_buffer(render_device, render_queue);
2129 });
2130 scope.spawn(async {
2131 let _span = tracing::info_span!("non_indexed_cpu_metadata").entered();
2132 phase_indirect_parameters_buffers
2133 .non_indexed
2134 .cpu_metadata
2135 .write_buffer(render_device, render_queue);
2136 });
2137
2138 scope.spawn(async {
2139 let _span = tracing::info_span!("non_indexed_gpu_metadata").entered();
2140 phase_indirect_parameters_buffers
2141 .non_indexed
2142 .gpu_metadata
2143 .write_buffer(render_device);
2144 });
2145 scope.spawn(async {
2146 let _span = tracing::info_span!("indexed_gpu_metadata").entered();
2147 phase_indirect_parameters_buffers
2148 .indexed
2149 .gpu_metadata
2150 .write_buffer(render_device);
2151 });
2152
2153 scope.spawn(async {
2154 let _span = tracing::info_span!("indexed_batch_sets").entered();
2155 phase_indirect_parameters_buffers
2156 .indexed
2157 .batch_sets
2158 .write_buffer(render_device, render_queue);
2159 });
2160 scope.spawn(async {
2161 let _span = tracing::info_span!("non_indexed_batch_sets").entered();
2162 phase_indirect_parameters_buffers
2163 .non_indexed
2164 .batch_sets
2165 .write_buffer(render_device, render_queue);
2166 });
2167 }
2168 });
2169}
2170
2171#[cfg(test)]
2172mod tests {
2173 use super::*;
2174
2175 #[test]
2176 fn instance_buffer_correct_behavior() {
2177 let mut instance_buffer = InstanceInputUniformBuffer::new();
2178
2179 let index = instance_buffer.add(2);
2180 instance_buffer.remove(index);
2181 assert_eq!(instance_buffer.get_unchecked(index), 2);
2182 assert_eq!(instance_buffer.get(index), None);
2183
2184 instance_buffer.add(5);
2185 assert_eq!(instance_buffer.buffer().len(), 1);
2186 }
2187}