1use alloc::sync::Arc;
4use core::{
5 any::TypeId,
6 marker::PhantomData,
7 mem,
8 ops::Range,
9 sync::atomic::{AtomicU32, Ordering},
10};
11
12use bevy_app::{App, Plugin};
13use bevy_derive::{Deref, DerefMut};
14use bevy_ecs::{
15 prelude::Entity,
16 query::{Has, With},
17 resource::Resource,
18 schedule::IntoScheduleConfigs as _,
19 system::{Query, Res, ResMut, StaticSystemParam},
20 world::{FromWorld, World},
21};
22use bevy_encase_derive::ShaderType;
23use bevy_log::{error, info_once};
24use bevy_math::UVec4;
25use bevy_platform::collections::{hash_map::Entry, HashMap, HashSet};
26use bevy_tasks::ComputeTaskPool;
27use bevy_utils::{default, TypeIdMap};
28use bytemuck::{Pod, Zeroable};
29use encase::{internal::WriteInto, ShaderSize};
30use nonmax::NonMaxU32;
31use wgpu::{BindingResource, BufferUsages, DownlevelFlags, Features};
32
33use crate::{
34 occlusion_culling::OcclusionCulling,
35 render_phase::{
36 BinnedPhaseItem, BinnedRenderPhaseBatch, BinnedRenderPhaseBatchSet,
37 BinnedRenderPhaseBatchSets, CachedRenderPipelinePhaseItem, PhaseItem,
38 PhaseItemBatchSetKey as _, PhaseItemExtraIndex, RenderMultidrawableBatchSet,
39 SortedPhaseItem, SortedRenderPhase, UnbatchableBinnedEntityIndices, ViewBinnedRenderPhases,
40 ViewSortedRenderPhases,
41 },
42 render_resource::{
43 AtomicPod, AtomicRawBufferVec, AtomicSparseBufferVec, Buffer, GpuArrayBufferable,
44 PartialBufferVec, PipelineCache, RawBufferVec, SparseBufferUpdateBindGroups,
45 SparseBufferUpdateJobs, SparseBufferUpdatePipelines, UninitBufferVec,
46 },
47 renderer::{RenderAdapter, RenderAdapterInfo, RenderDevice, RenderQueue, WgpuWrapper},
48 sync_world::{MainEntity, MainEntityHashMap},
49 view::{ExtractedView, NoIndirectDrawing, RetainedViewEntity},
50 GpuResourceAppExt, Render, RenderApp, RenderDebugFlags, RenderSystems,
51};
52
53use super::{BatchSetMeta, GetBatchData, GetFullBatchData};
54
55#[derive(Default)]
56pub struct BatchingPlugin {
57 pub debug_flags: RenderDebugFlags,
59}
60
61impl Plugin for BatchingPlugin {
62 fn build(&self, app: &mut App) {
63 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
64 return;
65 };
66
67 render_app
68 .insert_resource(IndirectParametersBuffersSettings {
69 allow_copies_from_indirect_parameter_buffers: self
70 .debug_flags
71 .contains(RenderDebugFlags::ALLOW_COPIES_FROM_INDIRECT_PARAMETERS),
72 })
73 .init_gpu_resource::<IndirectParametersBuffers>()
74 .allow_ambiguous_resource::<IndirectParametersBuffers>()
75 .init_gpu_resource::<BinUnpackingBuffers>()
76 .add_systems(
77 Render,
78 write_indirect_parameters_buffers.in_set(RenderSystems::PrepareResourcesFlush),
79 )
80 .add_systems(
81 Render,
82 clear_indirect_parameters_buffers.in_set(RenderSystems::PrepareViews),
83 );
84 }
85
86 fn finish(&self, app: &mut App) {
87 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
88 return;
89 };
90
91 render_app.init_gpu_resource::<GpuPreprocessingSupport>();
92 }
93}
94
95#[derive(Clone, Copy, PartialEq, Resource)]
104pub struct GpuPreprocessingSupport {
105 pub max_supported_mode: GpuPreprocessingMode,
107}
108
109impl GpuPreprocessingSupport {
110 #[inline]
112 pub fn is_available(&self) -> bool {
113 self.max_supported_mode != GpuPreprocessingMode::None
114 }
115
116 pub fn min(&self, mode: GpuPreprocessingMode) -> GpuPreprocessingMode {
119 match (self.max_supported_mode, mode) {
120 (GpuPreprocessingMode::None, _) | (_, GpuPreprocessingMode::None) => {
121 GpuPreprocessingMode::None
122 }
123 (mode, GpuPreprocessingMode::Culling) | (GpuPreprocessingMode::Culling, mode) => mode,
124 (GpuPreprocessingMode::PreprocessingOnly, GpuPreprocessingMode::PreprocessingOnly) => {
125 GpuPreprocessingMode::PreprocessingOnly
126 }
127 }
128 }
129
130 pub fn is_culling_supported(&self) -> bool {
132 self.max_supported_mode == GpuPreprocessingMode::Culling
133 }
134}
135
136#[derive(Clone, Copy, PartialEq)]
138pub enum GpuPreprocessingMode {
139 None,
143
144 PreprocessingOnly,
149
150 Culling,
154}
155
156#[derive(Resource)]
167pub struct BatchedInstanceBuffers<BD, BDI>
168where
169 BD: GpuArrayBufferable + Sync + Send + 'static,
170 BDI: AtomicPod,
171{
172 pub current_input_buffer: InstanceInputUniformBuffer<BDI>,
176
177 pub previous_input_buffer: PreviousInstanceInputUniformBuffer<BDI>,
185
186 pub phase_instance_buffers: TypeIdMap<UntypedPhaseBatchedInstanceBuffers<BD>>,
191}
192
193impl<BD, BDI> Default for BatchedInstanceBuffers<BD, BDI>
194where
195 BD: GpuArrayBufferable + Sync + Send + 'static,
196 BDI: AtomicPod,
197{
198 fn default() -> Self {
199 BatchedInstanceBuffers {
200 current_input_buffer: InstanceInputUniformBuffer::new(),
201 previous_input_buffer: PreviousInstanceInputUniformBuffer::new(),
202 phase_instance_buffers: TypeIdMap::default(),
203 }
204 }
205}
206
207#[derive(Resource)]
217pub struct PhaseBatchedInstanceBuffers<PI, BD>
218where
219 PI: PhaseItem,
220 BD: GpuArrayBufferable + Sync + Send + 'static,
221{
222 pub buffers: UntypedPhaseBatchedInstanceBuffers<BD>,
224 phantom: PhantomData<PI>,
225}
226
227impl<PI, BD> Default for PhaseBatchedInstanceBuffers<PI, BD>
228where
229 PI: PhaseItem,
230 BD: GpuArrayBufferable + Sync + Send + 'static,
231{
232 fn default() -> Self {
233 PhaseBatchedInstanceBuffers {
234 buffers: UntypedPhaseBatchedInstanceBuffers::default(),
235 phantom: PhantomData,
236 }
237 }
238}
239
240pub struct UntypedPhaseBatchedInstanceBuffers<BD>
246where
247 BD: GpuArrayBufferable + Sync + Send + 'static,
248{
249 pub data_buffer: UninitBufferVec<BD>,
254
255 pub work_item_buffers: HashMap<RetainedViewEntity, PreprocessWorkItemBuffers>,
260
261 pub late_indexed_indirect_parameters_buffer:
269 RawBufferVec<LatePreprocessWorkItemIndirectParameters>,
270
271 pub late_non_indexed_indirect_parameters_buffer:
279 RawBufferVec<LatePreprocessWorkItemIndirectParameters>,
280}
281
282pub struct InstanceInputUniformBuffer<BDI>
288where
289 BDI: AtomicPod,
290{
291 buffer: AtomicSparseBufferVec<BDI>,
293
294 free_uniform_indices: Vec<u32>,
299}
300
301impl<BDI> InstanceInputUniformBuffer<BDI>
302where
303 BDI: AtomicPod,
304{
305 pub fn new() -> InstanceInputUniformBuffer<BDI> {
307 InstanceInputUniformBuffer {
308 buffer: AtomicSparseBufferVec::new(
309 BufferUsages::STORAGE,
310 8,
311 Arc::from("instance input uniform buffer"),
312 ),
313 free_uniform_indices: vec![],
314 }
315 }
316
317 pub fn clear(&mut self) {
319 self.buffer.clear();
320 self.free_uniform_indices.clear();
321 }
322
323 #[inline]
326 pub fn buffer(&self) -> &AtomicSparseBufferVec<BDI> {
327 &self.buffer
328 }
329
330 pub fn add(&mut self, element: BDI) -> u32 {
333 match self.free_uniform_indices.pop() {
334 Some(uniform_index) => {
335 self.buffer.set(uniform_index, element);
336 uniform_index
337 }
338 None => self.buffer.push(element),
339 }
340 }
341
342 pub fn remove(&mut self, uniform_index: u32) {
346 self.free_uniform_indices.push(uniform_index);
347 }
348
349 pub fn get(&self, uniform_index: u32) -> Option<BDI> {
353 if uniform_index >= self.buffer.len() || self.free_uniform_indices.contains(&uniform_index)
354 {
355 None
356 } else {
357 Some(self.get_unchecked(uniform_index))
358 }
359 }
360
361 pub fn get_unchecked(&self, uniform_index: u32) -> BDI {
367 self.buffer.get(uniform_index)
368 }
369
370 pub fn set(&self, uniform_index: u32, element: BDI) {
375 self.buffer.set(uniform_index, element);
376 }
377
378 pub fn ensure_nonempty(&mut self) {
381 if self.buffer.is_empty() {
382 self.buffer.push(default());
383 }
384 }
385
386 pub fn len(&self) -> usize {
388 self.buffer.len() as usize
389 }
390
391 pub fn is_empty(&self) -> bool {
394 self.buffer.is_empty()
395 }
396
397 pub fn into_buffer(self) -> AtomicSparseBufferVec<BDI> {
400 self.buffer
401 }
402}
403
404impl<BDI> Default for InstanceInputUniformBuffer<BDI>
405where
406 BDI: AtomicPod,
407{
408 fn default() -> Self {
409 Self::new()
410 }
411}
412
413pub struct PreviousInstanceInputUniformBuffer<BDI>
424where
425 BDI: AtomicPod,
426{
427 buffer: AtomicRawBufferVec<BDI>,
429
430 atomic_len: AtomicU32,
432}
433
434impl<BDI> PreviousInstanceInputUniformBuffer<BDI>
435where
436 BDI: AtomicPod,
437{
438 pub fn new() -> PreviousInstanceInputUniformBuffer<BDI> {
440 PreviousInstanceInputUniformBuffer {
441 buffer: AtomicRawBufferVec::with_label(
442 BufferUsages::STORAGE,
443 "previous instance input uniform buffer",
444 ),
445 atomic_len: AtomicU32::new(0),
446 }
447 }
448
449 fn write_buffer(&mut self, render_device: &RenderDevice, render_queue: &RenderQueue) {
451 debug_assert!(!self.buffer.is_empty());
453 self.buffer.write_buffer_range(
456 0..(self.atomic_len.load(Ordering::Relaxed) as usize).max(1),
457 render_device,
458 render_queue,
459 );
460 }
461
462 pub fn clear(&mut self) {
464 self.atomic_len.store(0, Ordering::Relaxed);
467 }
468
469 pub fn reserve(&mut self, capacity: u32) {
471 self.buffer.grow(capacity);
472 *self.atomic_len.get_mut() = 0;
473 }
474
475 pub fn push(&self, value: BDI) -> u32 {
479 let index = self.atomic_len.fetch_add(1, Ordering::Relaxed);
480 debug_assert!(
481 (index as usize) < self.buffer.len() as usize,
482 "push exceeded pre-allocated capacity"
483 );
484 self.buffer.set(index, value);
485 index
486 }
487
488 pub fn ensure_nonempty(&mut self) {
491 if self.buffer.is_empty() {
492 self.buffer.push(default());
493 }
494 }
495
496 pub fn buffer(&self) -> Option<&Buffer> {
498 self.buffer.buffer()
499 }
500}
501
502impl<BDI> Default for PreviousInstanceInputUniformBuffer<BDI>
503where
504 BDI: AtomicPod,
505{
506 fn default() -> Self {
507 Self::new()
508 }
509}
510
511#[cfg_attr(
513 not(target_arch = "wasm32"),
514 expect(
515 clippy::large_enum_variant,
516 reason = "See https://github.com/bevyengine/bevy/issues/19220"
517 )
518)]
519pub enum PreprocessWorkItemBuffers {
520 Direct(RawBufferVec<PreprocessWorkItem>),
525
526 Indirect {
532 indexed: PartialBufferVec<PreprocessWorkItem>,
534 non_indexed: PartialBufferVec<PreprocessWorkItem>,
536 gpu_occlusion_culling: Option<GpuOcclusionCullingWorkItemBuffers>,
538 },
539}
540
541pub struct GpuOcclusionCullingWorkItemBuffers {
543 pub late_indexed: UninitBufferVec<PreprocessWorkItem>,
545 pub late_non_indexed: UninitBufferVec<PreprocessWorkItem>,
547 pub late_indirect_parameters_indexed_offset: u32,
551 pub late_indirect_parameters_non_indexed_offset: u32,
555}
556
557#[derive(Clone, Copy, ShaderType, Pod, Zeroable)]
563#[repr(C)]
564pub struct LatePreprocessWorkItemIndirectParameters {
565 dispatch_x: u32,
569 dispatch_y: u32,
572 dispatch_z: u32,
575 work_item_count: u32,
581 pad: UVec4,
583}
584
585impl Default for LatePreprocessWorkItemIndirectParameters {
586 fn default() -> LatePreprocessWorkItemIndirectParameters {
587 LatePreprocessWorkItemIndirectParameters {
588 dispatch_x: 0,
589 dispatch_y: 1,
590 dispatch_z: 1,
591 work_item_count: 0,
592 pad: default(),
593 }
594 }
595}
596
597pub fn get_or_create_work_item_buffer<'a, I>(
606 work_item_buffers: &'a mut HashMap<RetainedViewEntity, PreprocessWorkItemBuffers>,
607 view: RetainedViewEntity,
608 no_indirect_drawing: bool,
609 enable_gpu_occlusion_culling: bool,
610) -> &'a mut PreprocessWorkItemBuffers
611where
612 I: 'static,
613{
614 let preprocess_work_item_buffers = match work_item_buffers.entry(view) {
615 Entry::Occupied(occupied_entry) => occupied_entry.into_mut(),
616 Entry::Vacant(vacant_entry) => {
617 if no_indirect_drawing {
618 vacant_entry.insert(PreprocessWorkItemBuffers::Direct(RawBufferVec::new(
619 BufferUsages::STORAGE,
620 )))
621 } else {
622 vacant_entry.insert(PreprocessWorkItemBuffers::Indirect {
623 indexed: PartialBufferVec::new(
624 BufferUsages::STORAGE,
625 "indexed preprocess work item buffer".to_owned(),
626 ),
627 non_indexed: PartialBufferVec::new(
628 BufferUsages::STORAGE,
629 "non-indexed preprocess work item buffer".to_owned(),
630 ),
631 gpu_occlusion_culling: None,
634 })
635 }
636 }
637 };
638
639 if let PreprocessWorkItemBuffers::Indirect {
641 ref mut gpu_occlusion_culling,
642 ..
643 } = *preprocess_work_item_buffers
644 {
645 match (
646 enable_gpu_occlusion_culling,
647 gpu_occlusion_culling.is_some(),
648 ) {
649 (false, false) | (true, true) => {}
650 (false, true) => {
651 *gpu_occlusion_culling = None;
652 }
653 (true, false) => {
654 *gpu_occlusion_culling = Some(GpuOcclusionCullingWorkItemBuffers {
655 late_indexed: UninitBufferVec::new(BufferUsages::STORAGE),
656 late_non_indexed: UninitBufferVec::new(BufferUsages::STORAGE),
657 late_indirect_parameters_indexed_offset: 0,
658 late_indirect_parameters_non_indexed_offset: 0,
659 });
660 }
661 }
662 }
663
664 preprocess_work_item_buffers
665}
666
667pub fn init_work_item_buffers(
669 work_item_buffers: &mut PreprocessWorkItemBuffers,
670 late_indexed_indirect_parameters_buffer: &'_ mut RawBufferVec<
671 LatePreprocessWorkItemIndirectParameters,
672 >,
673 late_non_indexed_indirect_parameters_buffer: &'_ mut RawBufferVec<
674 LatePreprocessWorkItemIndirectParameters,
675 >,
676) {
677 if let PreprocessWorkItemBuffers::Indirect {
680 gpu_occlusion_culling:
681 Some(GpuOcclusionCullingWorkItemBuffers {
682 ref mut late_indirect_parameters_indexed_offset,
683 ref mut late_indirect_parameters_non_indexed_offset,
684 ..
685 }),
686 ..
687 } = *work_item_buffers
688 {
689 *late_indirect_parameters_indexed_offset = late_indexed_indirect_parameters_buffer
690 .push(LatePreprocessWorkItemIndirectParameters::default())
691 as u32;
692 *late_indirect_parameters_non_indexed_offset = late_non_indexed_indirect_parameters_buffer
693 .push(LatePreprocessWorkItemIndirectParameters::default())
694 as u32;
695 }
696}
697
698impl PreprocessWorkItemBuffers {
699 pub fn push(&mut self, indexed: bool, preprocess_work_item: PreprocessWorkItem) {
704 match *self {
705 PreprocessWorkItemBuffers::Direct(ref mut buffer) => {
706 buffer.push(preprocess_work_item);
707 }
708 PreprocessWorkItemBuffers::Indirect {
709 indexed: ref mut indexed_buffer,
710 non_indexed: ref mut non_indexed_buffer,
711 ref mut gpu_occlusion_culling,
712 } => {
713 if indexed {
714 indexed_buffer.push_init(preprocess_work_item);
715 } else {
716 non_indexed_buffer.push_init(preprocess_work_item);
717 }
718
719 if let Some(ref mut gpu_occlusion_culling) = *gpu_occlusion_culling {
720 if indexed {
721 gpu_occlusion_culling.late_indexed.add();
722 } else {
723 gpu_occlusion_culling.late_non_indexed.add();
724 }
725 }
726 }
727 }
728 }
729
730 pub fn clear(&mut self) {
732 match *self {
733 PreprocessWorkItemBuffers::Direct(ref mut buffer) => {
734 buffer.clear();
735 }
736 PreprocessWorkItemBuffers::Indirect {
737 indexed: ref mut indexed_buffer,
738 non_indexed: ref mut non_indexed_buffer,
739 ref mut gpu_occlusion_culling,
740 } => {
741 indexed_buffer.clear();
742 non_indexed_buffer.clear();
743
744 if let Some(ref mut gpu_occlusion_culling) = *gpu_occlusion_culling {
745 gpu_occlusion_culling.late_indexed.clear();
746 gpu_occlusion_culling.late_non_indexed.clear();
747 gpu_occlusion_culling.late_indirect_parameters_indexed_offset = 0;
748 gpu_occlusion_culling.late_indirect_parameters_non_indexed_offset = 0;
749 }
750 }
751 }
752 }
753}
754
755#[derive(Clone, Copy, Default, Pod, Zeroable, ShaderType)]
758#[repr(C)]
759pub struct PreprocessWorkItem {
760 pub input_index: u32,
763
764 pub output_or_indirect_parameters_index: u32,
772}
773
774#[derive(Clone, Copy, Debug, Pod, Zeroable, ShaderType)]
779#[repr(C)]
780pub struct IndirectParametersIndexed {
781 pub index_count: u32,
783 pub instance_count: u32,
785 pub first_index: u32,
787 pub base_vertex: u32,
789 pub first_instance: u32,
791}
792
793#[derive(Clone, Copy, Debug, Pod, Zeroable, ShaderType)]
798#[repr(C)]
799pub struct IndirectParametersNonIndexed {
800 pub vertex_count: u32,
802 pub instance_count: u32,
804 pub base_vertex: u32,
806 pub first_instance: u32,
808}
809
810#[derive(Clone, Copy, Default, Pod, Zeroable, ShaderType)]
815#[repr(C)]
816pub struct IndirectParametersCpuMetadata {
817 pub base_output_index: u32,
825
826 pub batch_set_index: u32,
834}
835
836#[derive(Clone, Copy, Default, Pod, Zeroable, ShaderType)]
846#[repr(C)]
847pub struct IndirectParametersGpuMetadata {
848 pub mesh_index: u32,
851
852 pub early_instance_count: u32,
857
858 pub late_instance_count: u32,
864}
865
866#[derive(Clone, Copy, Default, Pod, Zeroable, ShaderType)]
878#[repr(C)]
879pub struct IndirectBatchSet {
880 pub indirect_parameters_count: u32,
889
890 pub indirect_parameters_base: u32,
896}
897
898#[derive(Resource, Deref, DerefMut, Default)]
908pub struct IndirectParametersBuffers {
909 #[deref]
914 pub buffers: TypeIdMap<UntypedPhaseIndirectParametersBuffers>,
915}
916
917#[derive(Resource)]
919pub struct IndirectParametersBuffersSettings {
920 pub allow_copies_from_indirect_parameter_buffers: bool,
926}
927
928#[derive(Clone, Copy, Pod, Zeroable, ShaderType)]
930#[repr(C)]
931pub struct GpuBinUnpackingMetadata {
932 base_output_work_item_index: u32,
935 base_indirect_parameters_index: u32,
938 binned_mesh_instance_count: u32,
941 pad: [u32; 61],
943}
944
945impl Default for GpuBinUnpackingMetadata {
946 fn default() -> GpuBinUnpackingMetadata {
947 GpuBinUnpackingMetadata {
948 base_output_work_item_index: 0,
949 base_indirect_parameters_index: 0,
950 binned_mesh_instance_count: 0,
951 pad: [0; _],
952 }
953 }
954}
955
956pub struct BinUnpackingJob {
959 pub render_binned_mesh_instance_buffer: Buffer,
962 pub bin_index_to_indirect_parameters_offset_buffer: Buffer,
966 pub bin_unpacking_metadata_index: BinUnpackingMetadataIndex,
969 pub mesh_instance_count: u32,
971}
972
973#[derive(Resource)]
981pub struct PhaseIndirectParametersBuffers<PI>
982where
983 PI: PhaseItem,
984{
985 pub buffers: UntypedPhaseIndirectParametersBuffers,
987 phantom: PhantomData<PI>,
988}
989
990impl<PI> FromWorld for PhaseIndirectParametersBuffers<PI>
991where
992 PI: PhaseItem,
993{
994 fn from_world(world: &mut World) -> Self {
995 let settings = world.resource::<IndirectParametersBuffersSettings>();
996 PhaseIndirectParametersBuffers {
997 buffers: UntypedPhaseIndirectParametersBuffers::new(
998 settings.allow_copies_from_indirect_parameter_buffers,
999 ),
1000 phantom: PhantomData,
1001 }
1002 }
1003}
1004
1005impl<PI> PhaseIndirectParametersBuffers<PI>
1006where
1007 PI: PhaseItem,
1008{
1009 fn allocate(&mut self, no_indirect_drawing: bool, item_is_indexed: bool) -> Option<u32> {
1011 if no_indirect_drawing {
1012 None
1013 } else if item_is_indexed {
1014 Some(self.buffers.indexed.allocate(1))
1015 } else {
1016 Some(self.buffers.non_indexed.allocate(1))
1017 }
1018 }
1019}
1020
1021pub struct UntypedPhaseIndirectParametersBuffers {
1029 pub indexed: MeshClassIndirectParametersBuffers<IndirectParametersIndexed>,
1032 pub non_indexed: MeshClassIndirectParametersBuffers<IndirectParametersNonIndexed>,
1035}
1036
1037impl UntypedPhaseIndirectParametersBuffers {
1038 pub fn new(
1040 allow_copies_from_indirect_parameter_buffers: bool,
1041 ) -> UntypedPhaseIndirectParametersBuffers {
1042 UntypedPhaseIndirectParametersBuffers {
1043 non_indexed: MeshClassIndirectParametersBuffers::new(
1044 allow_copies_from_indirect_parameter_buffers,
1045 ),
1046 indexed: MeshClassIndirectParametersBuffers::new(
1047 allow_copies_from_indirect_parameter_buffers,
1048 ),
1049 }
1050 }
1051
1052 pub fn allocate(&mut self, indexed: bool, count: u32) -> u32 {
1057 if indexed {
1058 self.indexed.allocate(count)
1059 } else {
1060 self.non_indexed.allocate(count)
1061 }
1062 }
1063
1064 fn batch_count(&self, indexed: bool) -> usize {
1069 if indexed {
1070 self.indexed.batch_count()
1071 } else {
1072 self.non_indexed.batch_count()
1073 }
1074 }
1075
1076 pub fn batch_set_count(&self, indexed: bool) -> usize {
1081 if indexed {
1082 self.indexed.batch_sets.len()
1083 } else {
1084 self.non_indexed.batch_sets.len()
1085 }
1086 }
1087
1088 #[inline]
1096 pub fn add_batch_set(&mut self, indexed: bool, indirect_parameters_base: u32) {
1097 if indexed {
1098 self.indexed.batch_sets.push(IndirectBatchSet {
1099 indirect_parameters_base,
1100 indirect_parameters_count: 0,
1101 });
1102 } else {
1103 self.non_indexed.batch_sets.push(IndirectBatchSet {
1104 indirect_parameters_base,
1105 indirect_parameters_count: 0,
1106 });
1107 }
1108 }
1109
1110 pub fn get_next_batch_set_index(&self, indexed: bool) -> Option<NonMaxU32> {
1115 NonMaxU32::new(self.batch_set_count(indexed) as u32)
1116 }
1117
1118 pub fn clear(&mut self) {
1120 self.indexed.clear();
1121 self.non_indexed.clear();
1122 }
1123}
1124
1125#[derive(Resource)]
1128pub struct BinUnpackingBuffers {
1129 pub bin_unpacking_metadata: RawBufferVec<GpuBinUnpackingMetadata>,
1132 pub view_phase_buffers: HashMap<BinUnpackingBuffersKey, ViewPhaseBinUnpackingBuffers>,
1134}
1135
1136impl Default for BinUnpackingBuffers {
1137 fn default() -> Self {
1138 let mut bin_unpacking_metadata = RawBufferVec::new(BufferUsages::UNIFORM);
1139 bin_unpacking_metadata.set_label(Some("bin unpacking metadata buffer"));
1140 BinUnpackingBuffers {
1141 bin_unpacking_metadata,
1142 view_phase_buffers: HashMap::default(),
1143 }
1144 }
1145}
1146
1147#[derive(Default)]
1150pub struct ViewPhaseBinUnpackingBuffers {
1151 pub indexed_unpacking_jobs: Vec<BinUnpackingJob>,
1153 pub non_indexed_unpacking_jobs: Vec<BinUnpackingJob>,
1156}
1157
1158#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
1161pub struct BinUnpackingBuffersKey {
1162 pub phase: TypeId,
1164 pub view: RetainedViewEntity,
1166}
1167
1168#[derive(Clone, Copy, Debug, Deref, DerefMut)]
1171pub struct BinUnpackingMetadataIndex(pub NonMaxU32);
1172
1173impl BinUnpackingMetadataIndex {
1174 pub fn uniform_offset(&self) -> u32 {
1178 self.get() * size_of::<GpuBinUnpackingMetadata>() as u32
1179 }
1180}
1181
1182pub struct MeshClassIndirectParametersBuffers<IP>
1186where
1187 IP: Clone + ShaderSize + WriteInto,
1188{
1189 indirect_draw_parameters: UninitBufferVec<IP>,
1195
1196 cpu_metadata: RawBufferVec<IndirectParametersCpuMetadata>,
1203
1204 gpu_metadata: UninitBufferVec<IndirectParametersGpuMetadata>,
1211
1212 batch_sets: RawBufferVec<IndirectBatchSet>,
1219}
1220
1221impl<IP> MeshClassIndirectParametersBuffers<IP>
1222where
1223 IP: Clone + ShaderSize + WriteInto,
1224{
1225 fn new(
1226 allow_copies_from_indirect_parameter_buffers: bool,
1227 ) -> MeshClassIndirectParametersBuffers<IP> {
1228 let mut indirect_parameter_buffer_usages = BufferUsages::STORAGE | BufferUsages::INDIRECT;
1229 if allow_copies_from_indirect_parameter_buffers {
1230 indirect_parameter_buffer_usages |= BufferUsages::COPY_SRC;
1231 }
1232
1233 MeshClassIndirectParametersBuffers {
1234 indirect_draw_parameters: UninitBufferVec::new(indirect_parameter_buffer_usages),
1235 cpu_metadata: RawBufferVec::new(BufferUsages::STORAGE),
1236 gpu_metadata: UninitBufferVec::new(BufferUsages::STORAGE),
1237 batch_sets: RawBufferVec::new(indirect_parameter_buffer_usages),
1238 }
1239 }
1240
1241 #[inline]
1248 pub fn data_buffer(&self) -> Option<&Buffer> {
1249 self.indirect_draw_parameters.buffer()
1250 }
1251
1252 #[inline]
1258 pub fn cpu_metadata_buffer(&self) -> Option<&Buffer> {
1259 self.cpu_metadata.buffer()
1260 }
1261
1262 #[inline]
1269 pub fn gpu_metadata_buffer(&self) -> Option<&Buffer> {
1270 self.gpu_metadata.buffer()
1271 }
1272
1273 #[inline]
1280 pub fn batch_sets_buffer(&self) -> Option<&Buffer> {
1281 self.batch_sets.buffer()
1282 }
1283
1284 fn allocate(&mut self, count: u32) -> u32 {
1289 let length = self.indirect_draw_parameters.len();
1290 self.cpu_metadata.reserve_internal(count as usize);
1291 self.gpu_metadata.add_multiple(count as usize);
1292 for _ in 0..count {
1293 self.indirect_draw_parameters.add();
1294 self.cpu_metadata
1295 .push(IndirectParametersCpuMetadata::default());
1296 }
1297 length as u32
1298 }
1299
1300 pub fn set(&mut self, index: u32, value: IndirectParametersCpuMetadata) {
1303 self.cpu_metadata.set(index, value);
1304 }
1305
1306 #[inline]
1309 pub fn batch_count(&self) -> usize {
1310 self.indirect_draw_parameters.len()
1311 }
1312
1313 pub fn clear(&mut self) {
1315 self.indirect_draw_parameters.clear();
1316 self.cpu_metadata.clear();
1317 self.gpu_metadata.clear();
1318 self.batch_sets.clear();
1319 }
1320}
1321
1322impl FromWorld for GpuPreprocessingSupport {
1323 fn from_world(world: &mut World) -> Self {
1324 let adapter = world.resource::<RenderAdapter>();
1325 let device = world.resource::<RenderDevice>();
1326
1327 fn is_non_supported_android_device(adapter_info: &RenderAdapterInfo) -> bool {
1333 crate::get_adreno_model(adapter_info).is_some_and(|model| model != 720 && model <= 730)
1334 || crate::get_mali_driver_version(adapter_info).is_some_and(|version| version < 48)
1335 }
1336 fn is_preprocessing_only_android_device(adapter_info: &RenderAdapterInfo) -> bool {
1337 crate::get_pixel10_driver_version(adapter_info).is_some()
1338 }
1339
1340 let culling_feature_support = device
1341 .features()
1342 .contains(Features::INDIRECT_FIRST_INSTANCE | Features::IMMEDIATES);
1343 let limit_support = device.limits().max_storage_textures_per_shader_stage >= 12 &&
1346 device.limits().max_storage_buffers_per_shader_stage >= 10 &&
1347 device.limits().max_compute_workgroup_storage_size != 0;
1352
1353 let downlevel_support = adapter
1354 .get_downlevel_capabilities()
1355 .flags
1356 .contains(DownlevelFlags::COMPUTE_SHADERS);
1357
1358 let adapter_info = RenderAdapterInfo(WgpuWrapper::new(adapter.get_info()));
1359
1360 let max_supported_mode = if device.limits().max_compute_workgroup_size_x == 0
1361 || is_non_supported_android_device(&adapter_info)
1362 || adapter_info.backend == wgpu::Backend::Gl
1363 {
1364 info_once!(
1365 "GPU preprocessing is not supported on this device. \
1366 Falling back to CPU preprocessing.",
1367 );
1368 GpuPreprocessingMode::None
1369 } else if !(culling_feature_support && limit_support && downlevel_support)
1370 || is_preprocessing_only_android_device(&adapter_info)
1371 {
1372 info_once!("Some GPU preprocessing are limited on this device.");
1373 GpuPreprocessingMode::PreprocessingOnly
1374 } else {
1375 info_once!("GPU preprocessing is fully supported on this device.");
1376 GpuPreprocessingMode::Culling
1377 };
1378
1379 GpuPreprocessingSupport { max_supported_mode }
1380 }
1381}
1382
1383impl<BD, BDI> BatchedInstanceBuffers<BD, BDI>
1384where
1385 BD: GpuArrayBufferable + Sync + Send + 'static,
1386 BDI: AtomicPod,
1387{
1388 pub fn new() -> Self {
1390 Self::default()
1391 }
1392
1393 pub fn clear(&mut self) {
1395 for phase_instance_buffer in self.phase_instance_buffers.values_mut() {
1396 phase_instance_buffer.clear();
1397 }
1398 }
1399}
1400
1401impl<BD> UntypedPhaseBatchedInstanceBuffers<BD>
1402where
1403 BD: GpuArrayBufferable + Sync + Send + 'static,
1404{
1405 pub fn new() -> Self {
1406 UntypedPhaseBatchedInstanceBuffers {
1407 data_buffer: UninitBufferVec::new(BufferUsages::STORAGE),
1408 work_item_buffers: HashMap::default(),
1409 late_indexed_indirect_parameters_buffer: RawBufferVec::new(
1410 BufferUsages::STORAGE | BufferUsages::INDIRECT,
1411 ),
1412 late_non_indexed_indirect_parameters_buffer: RawBufferVec::new(
1413 BufferUsages::STORAGE | BufferUsages::INDIRECT,
1414 ),
1415 }
1416 }
1417
1418 pub fn instance_data_binding(&self) -> Option<BindingResource<'_>> {
1422 self.data_buffer
1423 .buffer()
1424 .map(|buffer| buffer.as_entire_binding())
1425 }
1426
1427 pub fn clear(&mut self) {
1429 self.data_buffer.clear();
1430 self.late_indexed_indirect_parameters_buffer.clear();
1431 self.late_non_indexed_indirect_parameters_buffer.clear();
1432
1433 for view_work_item_buffers in self.work_item_buffers.values_mut() {
1436 view_work_item_buffers.clear();
1437 }
1438 }
1439}
1440
1441impl<BD> Default for UntypedPhaseBatchedInstanceBuffers<BD>
1442where
1443 BD: GpuArrayBufferable + Sync + Send + 'static,
1444{
1445 fn default() -> Self {
1446 Self::new()
1447 }
1448}
1449
1450struct SortedRenderBatchSet<F>
1453where
1454 F: GetBatchData,
1455{
1456 phase_item_start_index: u32,
1459
1460 instance_start_index: u32,
1462
1463 indexed: bool,
1465
1466 indirect_parameters_index_range: Option<Range<u32>>,
1471
1472 meta: Option<(BatchSetMeta<F::BatchSetCompareData>, F::BatchCompareData)>,
1477}
1478
1479impl<F> SortedRenderBatchSet<F>
1480where
1481 F: GetBatchData,
1482{
1483 fn flush<I>(
1489 self,
1490 instance_end_index: u32,
1491 phase: &mut SortedRenderPhase<I>,
1492 phase_indirect_parameters_buffers: &mut UntypedPhaseIndirectParametersBuffers,
1493 ) where
1494 I: CachedRenderPipelinePhaseItem + SortedPhaseItem,
1495 {
1496 let (batch_range, batch_extra_index) =
1497 phase.items[self.phase_item_start_index as usize].batch_range_and_extra_index_mut();
1498 *batch_range = self.instance_start_index..instance_end_index;
1499 *batch_extra_index = match self.indirect_parameters_index_range {
1500 Some(ref indirect_parameters_index_range) => {
1501 PhaseItemExtraIndex::IndirectParametersIndex {
1502 range: (*indirect_parameters_index_range).clone(),
1503 batch_set_index: None,
1504 }
1505 }
1506 None => PhaseItemExtraIndex::None,
1507 };
1508 if let Some(ref indirect_parameters_index_range) = self.indirect_parameters_index_range {
1509 phase_indirect_parameters_buffers
1510 .add_batch_set(self.indexed, indirect_parameters_index_range.start);
1511 }
1512 }
1513}
1514
1515pub fn clear_batched_gpu_instance_buffers<GFBD>(
1522 gpu_batched_instance_buffers: Option<
1523 ResMut<BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>>,
1524 >,
1525) where
1526 GFBD: GetFullBatchData,
1527{
1528 if let Some(mut gpu_batched_instance_buffers) = gpu_batched_instance_buffers {
1531 gpu_batched_instance_buffers.clear();
1532 }
1533}
1534
1535pub fn delete_old_work_item_buffers<GFBD>(
1542 mut gpu_batched_instance_buffers: ResMut<
1543 BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
1544 >,
1545 extracted_views: Query<&ExtractedView>,
1546) where
1547 GFBD: GetFullBatchData,
1548{
1549 let retained_view_entities: HashSet<_> = extracted_views
1550 .iter()
1551 .map(|extracted_view| extracted_view.retained_view_entity)
1552 .collect();
1553 for phase_instance_buffers in gpu_batched_instance_buffers
1554 .phase_instance_buffers
1555 .values_mut()
1556 {
1557 phase_instance_buffers
1558 .work_item_buffers
1559 .retain(|retained_view_entity, _| {
1560 retained_view_entities.contains(retained_view_entity)
1561 });
1562 }
1563}
1564
1565pub fn batch_and_prepare_sorted_render_phase<I, GFBD>(
1569 mut phase_batched_instance_buffers: ResMut<PhaseBatchedInstanceBuffers<I, GFBD::BufferData>>,
1570 mut phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<I>>,
1571 mut sorted_render_phases: ResMut<ViewSortedRenderPhases<I>>,
1572 mut views: Query<(
1573 &ExtractedView,
1574 Has<NoIndirectDrawing>,
1575 Has<OcclusionCulling>,
1576 )>,
1577 system_param_item: StaticSystemParam<GFBD::Param>,
1578) where
1579 I: CachedRenderPipelinePhaseItem + SortedPhaseItem,
1580 GFBD: GetFullBatchData,
1581{
1582 let UntypedPhaseBatchedInstanceBuffers {
1584 ref mut data_buffer,
1585 ref mut work_item_buffers,
1586 ref mut late_indexed_indirect_parameters_buffer,
1587 ref mut late_non_indexed_indirect_parameters_buffer,
1588 } = phase_batched_instance_buffers.buffers;
1589
1590 for (extracted_view, no_indirect_drawing, gpu_occlusion_culling) in &mut views {
1591 let Some(phase) = sorted_render_phases.get_mut(&extracted_view.retained_view_entity) else {
1592 continue;
1593 };
1594
1595 let work_item_buffer = get_or_create_work_item_buffer::<I>(
1597 work_item_buffers,
1598 extracted_view.retained_view_entity,
1599 no_indirect_drawing,
1600 gpu_occlusion_culling,
1601 );
1602
1603 init_work_item_buffers(
1605 work_item_buffer,
1606 late_indexed_indirect_parameters_buffer,
1607 late_non_indexed_indirect_parameters_buffer,
1608 );
1609
1610 let mut batch_set: Option<SortedRenderBatchSet<GFBD>> = None;
1612
1613 for current_index in 0..phase.items.len() {
1614 let item = &phase.items[current_index];
1617 let entity = item.main_entity();
1618 let item_is_indexed = item.indexed();
1619 let current_batch_input_index =
1620 GFBD::get_index_and_compare_data(&system_param_item, entity);
1621
1622 let Some((current_input_index, current_meta)) = current_batch_input_index else {
1627 if let Some(batch_set) = batch_set.take() {
1629 batch_set.flush(
1630 data_buffer.len() as u32,
1631 phase,
1632 &mut phase_indirect_parameters_buffers.buffers,
1633 );
1634 }
1635
1636 continue;
1637 };
1638 let current_meta = if I::AUTOMATIC_BATCHING {
1639 current_meta.map(|(batch_set_meta, batch_meta)| {
1640 (
1641 BatchSetMeta::new(&phase.items[current_index], batch_set_meta),
1642 batch_meta,
1643 )
1644 })
1645 } else {
1646 None
1647 };
1648
1649 let can_batch = match batch_set.as_ref() {
1652 None => SortedPhaseItemBatchability::BreakBatchSet,
1653 Some(batch_set) => match (¤t_meta, &batch_set.meta) {
1654 (
1655 &Some((ref current_batch_set_key, ref current_bin_key)),
1656 &Some((ref batch_set_key, ref bin_key)),
1657 ) => {
1658 if *current_batch_set_key == *batch_set_key {
1659 if *current_bin_key == *bin_key {
1660 SortedPhaseItemBatchability::BatchOk
1661 } else if no_indirect_drawing {
1662 SortedPhaseItemBatchability::BreakBatchSet
1664 } else {
1665 SortedPhaseItemBatchability::BreakBatch
1666 }
1667 } else {
1668 SortedPhaseItemBatchability::BreakBatchSet
1669 }
1670 }
1671 _ => SortedPhaseItemBatchability::BreakBatchSet,
1672 },
1673 };
1674
1675 let output_index = data_buffer.add() as u32;
1677
1678 match can_batch {
1681 SortedPhaseItemBatchability::BreakBatchSet => {
1682 if let Some(batch_set) = batch_set.take() {
1684 batch_set.flush(
1685 output_index,
1686 phase,
1687 &mut phase_indirect_parameters_buffers.buffers,
1688 );
1689 }
1690
1691 let indirect_parameters_index = phase_indirect_parameters_buffers
1692 .allocate(no_indirect_drawing, item_is_indexed);
1693
1694 if let Some(indirect_parameters_index) = indirect_parameters_index {
1696 GFBD::write_batch_indirect_parameters_metadata(
1697 item_is_indexed,
1698 output_index,
1699 None,
1700 &mut phase_indirect_parameters_buffers.buffers,
1701 indirect_parameters_index,
1702 );
1703 }
1704
1705 batch_set = Some(SortedRenderBatchSet {
1706 phase_item_start_index: current_index as u32,
1707 instance_start_index: output_index,
1708 indexed: item_is_indexed,
1709 indirect_parameters_index_range: indirect_parameters_index
1710 .map(|i| i..(i + 1)),
1711 meta: current_meta,
1712 });
1713 }
1714
1715 SortedPhaseItemBatchability::BreakBatch => {
1716 let maybe_indirect_parameters_index = phase_indirect_parameters_buffers
1718 .allocate(no_indirect_drawing, item_is_indexed);
1719
1720 if let (&mut Some(ref mut batch_set), Some(indirect_parameters_index)) =
1721 (&mut batch_set, maybe_indirect_parameters_index)
1722 {
1723 GFBD::write_batch_indirect_parameters_metadata(
1724 item_is_indexed,
1725 output_index,
1726 None,
1727 &mut phase_indirect_parameters_buffers.buffers,
1728 indirect_parameters_index,
1729 );
1730
1731 batch_set.meta = current_meta;
1732
1733 let indirect_parameters_index_range = batch_set
1734 .indirect_parameters_index_range
1735 .as_mut()
1736 .expect("Can't allocate in a multidraw set if we aren't multidrawing");
1737 debug_assert_eq!(
1738 indirect_parameters_index,
1739 indirect_parameters_index_range.end
1740 );
1741 indirect_parameters_index_range.end += 1;
1742 }
1743 }
1744
1745 SortedPhaseItemBatchability::BatchOk => {}
1746 };
1747
1748 if let Some(batch_set) = batch_set.as_ref() {
1751 work_item_buffer.push(
1752 item_is_indexed,
1753 PreprocessWorkItem {
1754 input_index: current_input_index.into(),
1755 output_or_indirect_parameters_index: match (
1756 no_indirect_drawing,
1757 &batch_set.indirect_parameters_index_range,
1758 ) {
1759 (true, _) => output_index,
1760 (false, Some(indirect_parameters_index_range)) => {
1761 indirect_parameters_index_range.end - 1
1762 }
1763 (false, None) => 0,
1764 },
1765 },
1766 );
1767 }
1768 }
1769
1770 if let Some(batch_set) = batch_set.take() {
1772 batch_set.flush(
1773 data_buffer.len() as u32,
1774 phase,
1775 &mut phase_indirect_parameters_buffers.buffers,
1776 );
1777 }
1778 }
1779}
1780
1781#[derive(Clone, Copy, PartialEq)]
1783enum SortedPhaseItemBatchability {
1784 BatchOk,
1786 BreakBatch,
1791 BreakBatchSet,
1793}
1794
1795pub fn batch_and_prepare_binned_render_phase<BPI, GFBD>(
1797 mut phase_batched_instance_buffers: ResMut<PhaseBatchedInstanceBuffers<BPI, GFBD::BufferData>>,
1798 phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<BPI>>,
1799 mut binned_render_phases: ResMut<ViewBinnedRenderPhases<BPI>>,
1800 mut views: Query<
1801 (
1802 &ExtractedView,
1803 Has<NoIndirectDrawing>,
1804 Has<OcclusionCulling>,
1805 ),
1806 With<ExtractedView>,
1807 >,
1808 param: StaticSystemParam<GFBD::Param>,
1809) where
1810 BPI: BinnedPhaseItem,
1811 GFBD: GetFullBatchData,
1812{
1813 let system_param_item = param.into_inner();
1814
1815 let phase_indirect_parameters_buffers = phase_indirect_parameters_buffers.into_inner();
1816
1817 let UntypedPhaseBatchedInstanceBuffers {
1818 ref mut data_buffer,
1819 ref mut work_item_buffers,
1820 ref mut late_indexed_indirect_parameters_buffer,
1821 ref mut late_non_indexed_indirect_parameters_buffer,
1822 } = phase_batched_instance_buffers.buffers;
1823
1824 for (extracted_view, no_indirect_drawing, gpu_occlusion_culling) in &mut views {
1825 let Some(phase) = binned_render_phases.get_mut(&extracted_view.retained_view_entity) else {
1826 continue;
1827 };
1828
1829 let work_item_buffer = get_or_create_work_item_buffer::<BPI>(
1832 work_item_buffers,
1833 extracted_view.retained_view_entity,
1834 no_indirect_drawing,
1835 gpu_occlusion_culling,
1836 );
1837
1838 init_work_item_buffers(
1840 work_item_buffer,
1841 late_indexed_indirect_parameters_buffer,
1842 late_non_indexed_indirect_parameters_buffer,
1843 );
1844
1845 for (key, unbatchables) in &mut phase.unbatchable_meshes {
1864 let mut indirect_parameters_offset = if no_indirect_drawing {
1866 None
1867 } else if key.0.indexed() {
1868 Some(
1869 phase_indirect_parameters_buffers
1870 .buffers
1871 .indexed
1872 .allocate(unbatchables.entities.len() as u32),
1873 )
1874 } else {
1875 Some(
1876 phase_indirect_parameters_buffers
1877 .buffers
1878 .non_indexed
1879 .allocate(unbatchables.entities.len() as u32),
1880 )
1881 };
1882
1883 for main_entity in unbatchables.entities.keys() {
1884 let Some(input_index) = GFBD::get_binned_index(&system_param_item, *main_entity)
1885 else {
1886 continue;
1887 };
1888 let output_index = data_buffer.add() as u32;
1889
1890 if let Some(ref mut indirect_parameters_index) = indirect_parameters_offset {
1891 GFBD::write_batch_indirect_parameters_metadata(
1894 key.0.indexed(),
1895 output_index,
1896 None,
1897 &mut phase_indirect_parameters_buffers.buffers,
1898 *indirect_parameters_index,
1899 );
1900 work_item_buffer.push(
1901 key.0.indexed(),
1902 PreprocessWorkItem {
1903 input_index: input_index.into(),
1904 output_or_indirect_parameters_index: *indirect_parameters_index,
1905 },
1906 );
1907 unbatchables
1908 .buffer_indices
1909 .add(UnbatchableBinnedEntityIndices {
1910 instance_index: *indirect_parameters_index,
1911 extra_index: PhaseItemExtraIndex::IndirectParametersIndex {
1912 range: *indirect_parameters_index..(*indirect_parameters_index + 1),
1913 batch_set_index: None,
1914 },
1915 });
1916 phase_indirect_parameters_buffers
1917 .buffers
1918 .add_batch_set(key.0.indexed(), *indirect_parameters_index);
1919 *indirect_parameters_index += 1;
1920 } else {
1921 work_item_buffer.push(
1922 key.0.indexed(),
1923 PreprocessWorkItem {
1924 input_index: input_index.into(),
1925 output_or_indirect_parameters_index: output_index,
1926 },
1927 );
1928 unbatchables
1929 .buffer_indices
1930 .add(UnbatchableBinnedEntityIndices {
1931 instance_index: output_index,
1932 extra_index: PhaseItemExtraIndex::None,
1933 });
1934 }
1935 }
1936 }
1937
1938 for (key, bin) in &phase.batchable_meshes {
1941 let mut batch: Option<BinnedRenderPhaseBatch> = None;
1942 for (&main_entity, &input_index) in bin.entities() {
1943 let output_index = data_buffer.add() as u32;
1944
1945 match batch {
1946 Some(ref mut batch) => {
1947 batch.instance_range.end = output_index + 1;
1948
1949 work_item_buffer.push(
1957 key.0.indexed(),
1958 PreprocessWorkItem {
1959 input_index: *input_index,
1960 output_or_indirect_parameters_index: match (
1961 no_indirect_drawing,
1962 &batch.extra_index,
1963 ) {
1964 (true, _) => output_index,
1965 (
1966 false,
1967 PhaseItemExtraIndex::IndirectParametersIndex {
1968 range: indirect_parameters_range,
1969 ..
1970 },
1971 ) => indirect_parameters_range.start,
1972 (false, &PhaseItemExtraIndex::DynamicOffset(_))
1973 | (false, &PhaseItemExtraIndex::None) => 0,
1974 },
1975 },
1976 );
1977 }
1978
1979 None if !no_indirect_drawing => {
1980 let indirect_parameters_index = phase_indirect_parameters_buffers
1982 .buffers
1983 .allocate(key.0.indexed(), 1);
1984 let batch_set_index = phase_indirect_parameters_buffers
1985 .buffers
1986 .get_next_batch_set_index(key.0.indexed());
1987
1988 GFBD::write_batch_indirect_parameters_metadata(
1989 key.0.indexed(),
1990 output_index,
1991 batch_set_index,
1992 &mut phase_indirect_parameters_buffers.buffers,
1993 indirect_parameters_index,
1994 );
1995 work_item_buffer.push(
1996 key.0.indexed(),
1997 PreprocessWorkItem {
1998 input_index: *input_index,
1999 output_or_indirect_parameters_index: indirect_parameters_index,
2000 },
2001 );
2002 batch = Some(BinnedRenderPhaseBatch {
2003 representative_entity: (Entity::PLACEHOLDER, main_entity),
2004 instance_range: output_index..output_index + 1,
2005 extra_index: PhaseItemExtraIndex::IndirectParametersIndex {
2006 range: indirect_parameters_index..(indirect_parameters_index + 1),
2007 batch_set_index: None,
2008 },
2009 });
2010 }
2011
2012 None => {
2013 work_item_buffer.push(
2015 key.0.indexed(),
2016 PreprocessWorkItem {
2017 input_index: *input_index,
2018 output_or_indirect_parameters_index: output_index,
2019 },
2020 );
2021 batch = Some(BinnedRenderPhaseBatch {
2022 representative_entity: (Entity::PLACEHOLDER, main_entity),
2023 instance_range: output_index..output_index + 1,
2024 extra_index: PhaseItemExtraIndex::None,
2025 });
2026 }
2027 }
2028 }
2029
2030 if let Some(batch) = batch {
2031 match phase.batch_sets {
2032 BinnedRenderPhaseBatchSets::DynamicUniforms(_) => {
2033 error!("Dynamic uniform batch sets shouldn't be used here");
2034 }
2035 BinnedRenderPhaseBatchSets::Direct(ref mut vec) => {
2036 vec.push(batch);
2037 }
2038 BinnedRenderPhaseBatchSets::MultidrawIndirect(ref mut vec) => {
2039 vec.push(BinnedRenderPhaseBatchSet {
2044 first_batch: batch,
2045 batch_count: 1,
2046 bin_key: key.1.clone(),
2047 index: phase_indirect_parameters_buffers
2048 .buffers
2049 .batch_set_count(key.0.indexed())
2050 as u32,
2051 first_work_item_index: 0,
2053 });
2054 }
2055 }
2056 }
2057 }
2058
2059 if let (
2062 &mut BinnedRenderPhaseBatchSets::MultidrawIndirect(ref mut batch_sets),
2063 &mut PreprocessWorkItemBuffers::Indirect {
2064 indexed: ref mut indexed_work_item_buffer,
2065 non_indexed: ref mut non_indexed_work_item_buffer,
2066 gpu_occlusion_culling: ref mut gpu_occlusion_culling_buffers,
2067 },
2068 ) = (&mut phase.batch_sets, &mut *work_item_buffer)
2069 {
2070 let mut indexed_preparer: MultidrawableBatchSetPreparer<BPI, GFBD> =
2072 MultidrawableBatchSetPreparer::new(
2073 phase_indirect_parameters_buffers.buffers.batch_count(true) as u32,
2074 phase_indirect_parameters_buffers
2075 .buffers
2076 .indexed
2077 .batch_sets
2078 .len() as u32,
2079 );
2080 let mut non_indexed_preparer: MultidrawableBatchSetPreparer<BPI, GFBD> =
2081 MultidrawableBatchSetPreparer::new(
2082 phase_indirect_parameters_buffers.buffers.batch_count(false) as u32,
2083 phase_indirect_parameters_buffers
2084 .buffers
2085 .non_indexed
2086 .batch_sets
2087 .len() as u32,
2088 );
2089
2090 for (batch_set_key, bins) in &phase.multidrawable_meshes {
2092 if batch_set_key.indexed() {
2093 indexed_preparer.prepare_multidrawable_binned_batch_set(
2094 bins,
2095 data_buffer,
2096 indexed_work_item_buffer,
2097 &mut phase_indirect_parameters_buffers.buffers.indexed,
2098 batch_sets,
2099 );
2100 } else {
2101 non_indexed_preparer.prepare_multidrawable_binned_batch_set(
2102 bins,
2103 data_buffer,
2104 non_indexed_work_item_buffer,
2105 &mut phase_indirect_parameters_buffers.buffers.non_indexed,
2106 batch_sets,
2107 );
2108 }
2109 }
2110
2111 if let Some(gpu_occlusion_culling_buffers) = gpu_occlusion_culling_buffers {
2113 gpu_occlusion_culling_buffers
2114 .late_indexed
2115 .add_multiple(indexed_preparer.work_item_count);
2116 gpu_occlusion_culling_buffers
2117 .late_non_indexed
2118 .add_multiple(non_indexed_preparer.work_item_count);
2119 }
2120 }
2121 }
2122}
2123
2124struct MultidrawableBatchSetPreparer<BPI, GFBD>
2130where
2131 BPI: BinnedPhaseItem,
2132 GFBD: GetFullBatchData,
2133{
2134 indirect_parameters_index: u32,
2137 batch_set_index: u32,
2139 work_item_count: usize,
2141 phantom: PhantomData<(BPI, GFBD)>,
2142}
2143
2144impl<BPI, GFBD> MultidrawableBatchSetPreparer<BPI, GFBD>
2145where
2146 BPI: BinnedPhaseItem,
2147 GFBD: GetFullBatchData,
2148{
2149 #[inline]
2152 fn new(initial_indirect_parameters_index: u32, initial_batch_set_index: u32) -> Self {
2153 MultidrawableBatchSetPreparer {
2154 indirect_parameters_index: initial_indirect_parameters_index,
2155 batch_set_index: initial_batch_set_index,
2156 work_item_count: 0,
2157 phantom: PhantomData,
2158 }
2159 }
2160
2161 #[inline]
2166 fn prepare_multidrawable_binned_batch_set<IP>(
2167 &mut self,
2168 batch_set: &RenderMultidrawableBatchSet<BPI>,
2169 data_buffer: &mut UninitBufferVec<GFBD::BufferData>,
2170 work_item_buffer: &mut PartialBufferVec<PreprocessWorkItem>,
2171 mesh_class_buffers: &mut MeshClassIndirectParametersBuffers<IP>,
2172 batch_sets: &mut Vec<BinnedRenderPhaseBatchSet<BPI::BinKey>>,
2173 ) where
2174 IP: Clone + ShaderSize + WriteInto,
2175 {
2176 let current_indexed_batch_set_index = self.batch_set_index;
2177 let current_output_index = data_buffer.len() as u32;
2178 let first_work_item_index = work_item_buffer.len() as u32;
2179
2180 let indirect_parameters_base = self.indirect_parameters_index;
2181
2182 let Some((first_bin_key, first_bin_index)) = batch_set.bin_key_to_bin_index.iter().next()
2185 else {
2186 return;
2187 };
2188 let first_bin = batch_set
2189 .bin(*first_bin_index)
2190 .expect("At least one bin must be present in each batch set");
2191 let first_bin_len = first_bin.entity_to_binned_mesh_instance_index.len();
2192 let first_bin_entity = batch_set
2193 .representative_entity()
2194 .unwrap_or(MainEntity::from(Entity::PLACEHOLDER));
2195
2196 let cpu_metadata_offset = mesh_class_buffers.cpu_metadata.len() as u32;
2205 for _ in 0..batch_set.bin_count() {
2206 mesh_class_buffers
2207 .cpu_metadata
2208 .push(IndirectParametersCpuMetadata {
2209 base_output_index: 0,
2211 batch_set_index: self.batch_set_index,
2212 });
2213 }
2214
2215 for bin_index in batch_set.bin_key_to_bin_index.values() {
2219 let bin = batch_set.bin(*bin_index).expect("Bin not present");
2220
2221 let indirect_parameters_offset = *batch_set
2223 .gpu_buffers
2224 .bin_index_to_indirect_parameters_offset_buffer
2225 .get(bin_index.0)
2226 .unwrap();
2227 mesh_class_buffers.cpu_metadata.values_mut()
2228 [cpu_metadata_offset as usize + indirect_parameters_offset as usize]
2229 .base_output_index = data_buffer.len() as u32;
2230
2231 let bin_entity_count = bin.entity_to_binned_mesh_instance_index.len();
2235 work_item_buffer.push_multiple_uninit(bin_entity_count);
2236 data_buffer.add_multiple(bin_entity_count);
2237 self.work_item_count += bin_entity_count;
2238 }
2239
2240 let bin_count = batch_set.bin_count();
2242 mesh_class_buffers.gpu_metadata.add_multiple(bin_count);
2243 mesh_class_buffers
2244 .indirect_draw_parameters
2245 .add_multiple(bin_count);
2246
2247 mesh_class_buffers.batch_sets.push(IndirectBatchSet {
2249 indirect_parameters_base,
2250 indirect_parameters_count: 0,
2251 });
2252
2253 self.indirect_parameters_index += bin_count as u32;
2254 self.batch_set_index += 1;
2255
2256 batch_sets.push(BinnedRenderPhaseBatchSet {
2259 first_batch: BinnedRenderPhaseBatch {
2260 representative_entity: (Entity::PLACEHOLDER, first_bin_entity),
2261 instance_range: current_output_index..(current_output_index + first_bin_len as u32),
2262 extra_index: PhaseItemExtraIndex::maybe_indirect_parameters_index(NonMaxU32::new(
2263 indirect_parameters_base,
2264 )),
2265 },
2266 bin_key: (*first_bin_key).clone(),
2267 batch_count: self.indirect_parameters_index - indirect_parameters_base,
2268 index: current_indexed_batch_set_index,
2269 first_work_item_index,
2270 });
2271 }
2272}
2273
2274pub fn collect_buffers_for_phase<PI, GFBD>(
2289 mut phase_batched_instance_buffers: ResMut<PhaseBatchedInstanceBuffers<PI, GFBD::BufferData>>,
2290 mut phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<PI>>,
2291 mut batched_instance_buffers: ResMut<
2292 BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
2293 >,
2294 mut indirect_parameters_buffers: ResMut<IndirectParametersBuffers>,
2295 indirect_parameters_buffers_settings: Res<IndirectParametersBuffersSettings>,
2296) where
2297 PI: PhaseItem,
2298 GFBD: GetFullBatchData + Send + Sync + 'static,
2299{
2300 let untyped_phase_batched_instance_buffers =
2304 mem::take(&mut phase_batched_instance_buffers.buffers);
2305 if let Some(mut old_untyped_phase_batched_instance_buffers) = batched_instance_buffers
2306 .phase_instance_buffers
2307 .insert(TypeId::of::<PI>(), untyped_phase_batched_instance_buffers)
2308 {
2309 old_untyped_phase_batched_instance_buffers.clear();
2310 phase_batched_instance_buffers.buffers = old_untyped_phase_batched_instance_buffers;
2311 }
2312
2313 let untyped_phase_indirect_parameters_buffers = mem::replace(
2317 &mut phase_indirect_parameters_buffers.buffers,
2318 UntypedPhaseIndirectParametersBuffers::new(
2319 indirect_parameters_buffers_settings.allow_copies_from_indirect_parameter_buffers,
2320 ),
2321 );
2322 if let Some(mut old_untyped_phase_indirect_parameters_buffers) = indirect_parameters_buffers
2323 .insert(
2324 TypeId::of::<PI>(),
2325 untyped_phase_indirect_parameters_buffers,
2326 )
2327 {
2328 old_untyped_phase_indirect_parameters_buffers.clear();
2329 phase_indirect_parameters_buffers.buffers = old_untyped_phase_indirect_parameters_buffers;
2330 }
2331}
2332
2333pub fn write_batched_instance_buffers<GFBD>(
2335 render_device: Res<RenderDevice>,
2336 render_queue: Res<RenderQueue>,
2337 gpu_array_buffer: ResMut<BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>>,
2338 pipeline_cache: Res<PipelineCache>,
2339 mut bin_unpacking_buffers: ResMut<BinUnpackingBuffers>,
2340 mut sparse_buffer_update_jobs: ResMut<SparseBufferUpdateJobs>,
2341 mut sparse_buffer_update_bind_groups: ResMut<SparseBufferUpdateBindGroups>,
2342 sparse_buffer_update_pipelines: Res<SparseBufferUpdatePipelines>,
2343) where
2344 GFBD: GetFullBatchData,
2345{
2346 let BatchedInstanceBuffers {
2347 current_input_buffer,
2348 previous_input_buffer,
2349 phase_instance_buffers,
2350 } = gpu_array_buffer.into_inner();
2351
2352 let render_device = &*render_device;
2353 let render_queue = &*render_queue;
2354
2355 ComputeTaskPool::get().scope(|scope| {
2356 scope.spawn(async {
2357 #[cfg(feature = "trace")]
2358 let _span = bevy_log::info_span!("write_current_input_buffers").entered();
2359 current_input_buffer
2360 .buffer
2361 .write_buffers(render_device, render_queue);
2362 });
2363 scope.spawn(async {
2364 #[cfg(feature = "trace")]
2365 let _span = bevy_log::info_span!("write_previous_input_buffers").entered();
2366 previous_input_buffer.write_buffer(render_device, render_queue);
2367 });
2368
2369 for phase_instance_buffers in phase_instance_buffers.values_mut() {
2370 let UntypedPhaseBatchedInstanceBuffers {
2371 ref mut data_buffer,
2372 ref mut work_item_buffers,
2373 ref mut late_indexed_indirect_parameters_buffer,
2374 ref mut late_non_indexed_indirect_parameters_buffer,
2375 } = *phase_instance_buffers;
2376
2377 scope.spawn(async {
2378 #[cfg(feature = "trace")]
2379 let _span = bevy_log::info_span!("write_phase_instance_buffers").entered();
2380 data_buffer.write_buffer(render_device);
2381 late_indexed_indirect_parameters_buffer.write_buffer(render_device, render_queue);
2382 late_non_indexed_indirect_parameters_buffer
2383 .write_buffer(render_device, render_queue);
2384 });
2385
2386 for phase_work_item_buffers in work_item_buffers.values_mut() {
2387 scope.spawn(async {
2388 #[cfg(feature = "trace")]
2389 let _span = bevy_log::info_span!("write_work_item_buffers").entered();
2390 match *phase_work_item_buffers {
2391 PreprocessWorkItemBuffers::Direct(ref mut buffer_vec) => {
2392 buffer_vec.write_buffer(render_device, render_queue);
2393 }
2394 PreprocessWorkItemBuffers::Indirect {
2395 ref mut indexed,
2396 ref mut non_indexed,
2397 ref mut gpu_occlusion_culling,
2398 } => {
2399 indexed.write_buffer(render_device, render_queue);
2400 non_indexed.write_buffer(render_device, render_queue);
2401
2402 if let Some(GpuOcclusionCullingWorkItemBuffers {
2403 ref mut late_indexed,
2404 ref mut late_non_indexed,
2405 late_indirect_parameters_indexed_offset: _,
2406 late_indirect_parameters_non_indexed_offset: _,
2407 }) = *gpu_occlusion_culling
2408 {
2409 if !late_indexed.is_empty() {
2410 late_indexed.write_buffer(render_device);
2411 }
2412 if !late_non_indexed.is_empty() {
2413 late_non_indexed.write_buffer(render_device);
2414 }
2415 }
2416 }
2417 }
2418 });
2419 }
2420 }
2421 });
2422
2423 current_input_buffer.buffer.prepare_to_populate_buffers(
2426 render_device,
2427 &pipeline_cache,
2428 &mut sparse_buffer_update_jobs,
2429 &mut sparse_buffer_update_bind_groups,
2430 &sparse_buffer_update_pipelines,
2431 );
2432
2433 bin_unpacking_buffers
2434 .bin_unpacking_metadata
2435 .write_buffer(render_device, render_queue);
2436}
2437
2438pub fn write_binned_instance_buffers<BPI, GFBD>(
2443 mut views: Query<&ExtractedView>,
2444 mut view_binned_render_phases: ResMut<ViewBinnedRenderPhases<BPI>>,
2445 bin_unpacking_buffers: ResMut<BinUnpackingBuffers>,
2446 render_device: Res<RenderDevice>,
2447 render_queue: Res<RenderQueue>,
2448) where
2449 BPI: BinnedPhaseItem,
2450 GFBD: GetFullBatchData,
2451{
2452 let bin_unpacking_buffers = bin_unpacking_buffers.into_inner();
2453
2454 let phase_type_id = TypeId::of::<BPI>();
2455
2456 let mut all_seen_view_entities = HashSet::new();
2459
2460 for extracted_view in &mut views {
2461 all_seen_view_entities.insert(extracted_view.retained_view_entity);
2462
2463 let Some(view_binned_render_phase) =
2464 view_binned_render_phases.get_mut(&extracted_view.retained_view_entity)
2465 else {
2466 continue;
2467 };
2468
2469 let BinnedRenderPhaseBatchSets::MultidrawIndirect(ref batch_sets) =
2472 view_binned_render_phase.batch_sets
2473 else {
2474 continue;
2475 };
2476
2477 let view_phase_bin_unpacking_buffers = bin_unpacking_buffers
2480 .view_phase_buffers
2481 .entry(BinUnpackingBuffersKey {
2482 phase: phase_type_id,
2483 view: extracted_view.retained_view_entity,
2484 })
2485 .or_default();
2486
2487 view_phase_bin_unpacking_buffers
2489 .indexed_unpacking_jobs
2490 .clear();
2491 view_phase_bin_unpacking_buffers
2492 .non_indexed_unpacking_jobs
2493 .clear();
2494
2495 let mut representative_entity_to_batch_set_bin_unpacking_metadata =
2506 MainEntityHashMap::default();
2507
2508 for batch_set in batch_sets {
2509 let main_entity = batch_set.first_batch.representative_entity.1;
2510 if *main_entity != Entity::PLACEHOLDER
2511 && let PhaseItemExtraIndex::IndirectParametersIndex {
2512 range: ref indirect_parameters_range,
2513 ..
2514 } = batch_set.first_batch.extra_index
2515 {
2516 representative_entity_to_batch_set_bin_unpacking_metadata.insert(
2519 main_entity,
2520 BatchSetBinUnpackingMetadata {
2521 base_output_work_item_index: batch_set.first_work_item_index,
2522 base_indirect_parameters_index: indirect_parameters_range.start,
2523 },
2524 );
2525 }
2526 }
2527
2528 for (batch_set_key, batch_set) in view_binned_render_phase.multidrawable_meshes.iter_mut() {
2534 let Some(representative_entity) = batch_set.representative_entity() else {
2535 continue;
2536 };
2537 let Some(bin_unpacking_metadata) =
2538 representative_entity_to_batch_set_bin_unpacking_metadata
2539 .get(&representative_entity)
2540 else {
2541 continue;
2542 };
2543
2544 batch_set
2547 .gpu_buffers
2548 .render_binned_mesh_instance_buffer
2549 .write_buffer(&render_device, &render_queue);
2550 batch_set
2551 .gpu_buffers
2552 .bin_index_to_indirect_parameters_offset_buffer
2553 .write_buffer(&render_device, &render_queue);
2554
2555 let (
2556 Some(render_bin_entry_buffer),
2557 Some(bin_index_to_indirect_parameters_offset_buffer),
2558 ) = (
2559 batch_set
2560 .gpu_buffers
2561 .render_binned_mesh_instance_buffer
2562 .buffer(),
2563 batch_set
2564 .gpu_buffers
2565 .bin_index_to_indirect_parameters_offset_buffer
2566 .buffer(),
2567 )
2568 else {
2569 continue;
2570 };
2571
2572 let binned_mesh_instance_count = batch_set
2573 .gpu_buffers
2574 .render_binned_mesh_instance_buffer
2575 .len() as u32;
2576
2577 let gpu_bin_unpacking_metadata_index = bin_unpacking_buffers
2579 .bin_unpacking_metadata
2580 .push(GpuBinUnpackingMetadata {
2581 base_output_work_item_index: bin_unpacking_metadata.base_output_work_item_index,
2582 base_indirect_parameters_index: bin_unpacking_metadata
2583 .base_indirect_parameters_index,
2584 binned_mesh_instance_count,
2585 pad: [0; _],
2586 });
2587
2588 let Some(gpu_bin_unpacking_metadata_index) =
2589 NonMaxU32::new(gpu_bin_unpacking_metadata_index as u32)
2590 else {
2591 continue;
2592 };
2593
2594 let job = BinUnpackingJob {
2596 render_binned_mesh_instance_buffer: render_bin_entry_buffer.clone(),
2597 bin_index_to_indirect_parameters_offset_buffer:
2598 bin_index_to_indirect_parameters_offset_buffer.clone(),
2599 bin_unpacking_metadata_index: BinUnpackingMetadataIndex(
2600 gpu_bin_unpacking_metadata_index,
2601 ),
2602 mesh_instance_count: binned_mesh_instance_count,
2603 };
2604
2605 if batch_set_key.indexed() {
2606 view_phase_bin_unpacking_buffers
2607 .indexed_unpacking_jobs
2608 .push(job);
2609 } else {
2610 view_phase_bin_unpacking_buffers
2611 .non_indexed_unpacking_jobs
2612 .push(job);
2613 }
2614 }
2615 }
2616
2617 bin_unpacking_buffers
2619 .view_phase_buffers
2620 .retain(|bin_unpacking_buffers_key, _| {
2621 bin_unpacking_buffers_key.phase != phase_type_id
2622 || all_seen_view_entities.contains(&bin_unpacking_buffers_key.view)
2623 });
2624}
2625
2626pub fn clear_bin_unpacking_buffers(mut bin_unpacking_buffers: ResMut<BinUnpackingBuffers>) {
2628 bin_unpacking_buffers.bin_unpacking_metadata.clear();
2629}
2630
2631struct BatchSetBinUnpackingMetadata {
2634 base_output_work_item_index: u32,
2637 base_indirect_parameters_index: u32,
2640}
2641
2642pub fn clear_indirect_parameters_buffers(
2643 mut indirect_parameters_buffers: ResMut<IndirectParametersBuffers>,
2644) {
2645 for phase_indirect_parameters_buffers in indirect_parameters_buffers.values_mut() {
2646 phase_indirect_parameters_buffers.clear();
2647 }
2648}
2649
2650pub fn write_indirect_parameters_buffers(
2651 render_device: Res<RenderDevice>,
2652 render_queue: Res<RenderQueue>,
2653 mut indirect_parameters_buffers: ResMut<IndirectParametersBuffers>,
2654) {
2655 let render_device = &*render_device;
2656 let render_queue = &*render_queue;
2657 ComputeTaskPool::get().scope(|scope| {
2658 for phase_indirect_parameters_buffers in indirect_parameters_buffers.values_mut() {
2659 scope.spawn(async {
2660 #[cfg(feature = "trace")]
2661 let _span = bevy_log::info_span!("indexed_data").entered();
2662 phase_indirect_parameters_buffers
2663 .indexed
2664 .indirect_draw_parameters
2665 .write_buffer(render_device);
2666 });
2667 scope.spawn(async {
2668 #[cfg(feature = "trace")]
2669 let _span = bevy_log::info_span!("non_indexed_data").entered();
2670 phase_indirect_parameters_buffers
2671 .non_indexed
2672 .indirect_draw_parameters
2673 .write_buffer(render_device);
2674 });
2675
2676 scope.spawn(async {
2677 #[cfg(feature = "trace")]
2678 let _span = bevy_log::info_span!("indexed_cpu_metadata").entered();
2679 phase_indirect_parameters_buffers
2680 .indexed
2681 .cpu_metadata
2682 .write_buffer(render_device, render_queue);
2683 });
2684 scope.spawn(async {
2685 #[cfg(feature = "trace")]
2686 let _span = bevy_log::info_span!("non_indexed_cpu_metadata").entered();
2687 phase_indirect_parameters_buffers
2688 .non_indexed
2689 .cpu_metadata
2690 .write_buffer(render_device, render_queue);
2691 });
2692
2693 scope.spawn(async {
2694 #[cfg(feature = "trace")]
2695 let _span = bevy_log::info_span!("non_indexed_gpu_metadata").entered();
2696 phase_indirect_parameters_buffers
2697 .non_indexed
2698 .gpu_metadata
2699 .write_buffer(render_device);
2700 });
2701 scope.spawn(async {
2702 #[cfg(feature = "trace")]
2703 let _span = bevy_log::info_span!("indexed_gpu_metadata").entered();
2704 phase_indirect_parameters_buffers
2705 .indexed
2706 .gpu_metadata
2707 .write_buffer(render_device);
2708 });
2709
2710 scope.spawn(async {
2711 #[cfg(feature = "trace")]
2712 let _span = bevy_log::info_span!("indexed_batch_sets").entered();
2713 phase_indirect_parameters_buffers
2714 .indexed
2715 .batch_sets
2716 .write_buffer(render_device, render_queue);
2717 });
2718 scope.spawn(async {
2719 #[cfg(feature = "trace")]
2720 let _span = bevy_log::info_span!("non_indexed_batch_sets").entered();
2721 phase_indirect_parameters_buffers
2722 .non_indexed
2723 .batch_sets
2724 .write_buffer(render_device, render_queue);
2725 });
2726 }
2727 });
2728}
2729
2730#[cfg(test)]
2731mod tests {
2732 use bytemuck::{Pod, Zeroable};
2733
2734 use crate::impl_atomic_pod;
2735
2736 use super::*;
2737
2738 #[derive(Clone, Copy, Default, PartialEq, Debug, Pod, Zeroable)]
2739 #[repr(C)]
2740 struct TestData(u32);
2741
2742 impl_atomic_pod!(TestData, TestDataBlob);
2743
2744 #[test]
2745 fn instance_buffer_correct_behavior() {
2746 let mut instance_buffer = InstanceInputUniformBuffer::new();
2747
2748 let index = instance_buffer.add(TestData(2));
2749 instance_buffer.remove(index);
2750 assert_eq!(instance_buffer.get_unchecked(index), TestData(2));
2751 assert_eq!(instance_buffer.get(index), None);
2752
2753 instance_buffer.add(TestData(5));
2754 assert_eq!(instance_buffer.buffer().len(), 1);
2755 }
2756}