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 {
1662 SortedPhaseItemBatchability::BreakBatch
1663 }
1664 } else {
1665 SortedPhaseItemBatchability::BreakBatchSet
1666 }
1667 }
1668 _ => SortedPhaseItemBatchability::BreakBatchSet,
1669 },
1670 };
1671
1672 let output_index = data_buffer.add() as u32;
1674
1675 match can_batch {
1678 SortedPhaseItemBatchability::BreakBatchSet => {
1679 if let Some(batch_set) = batch_set.take() {
1681 batch_set.flush(
1682 output_index,
1683 phase,
1684 &mut phase_indirect_parameters_buffers.buffers,
1685 );
1686 }
1687
1688 let indirect_parameters_index = phase_indirect_parameters_buffers
1689 .allocate(no_indirect_drawing, item_is_indexed);
1690
1691 if let Some(indirect_parameters_index) = indirect_parameters_index {
1693 GFBD::write_batch_indirect_parameters_metadata(
1694 item_is_indexed,
1695 output_index,
1696 None,
1697 &mut phase_indirect_parameters_buffers.buffers,
1698 indirect_parameters_index,
1699 );
1700 }
1701
1702 batch_set = Some(SortedRenderBatchSet {
1703 phase_item_start_index: current_index as u32,
1704 instance_start_index: output_index,
1705 indexed: item_is_indexed,
1706 indirect_parameters_index_range: indirect_parameters_index
1707 .map(|i| i..(i + 1)),
1708 meta: current_meta,
1709 });
1710 }
1711
1712 SortedPhaseItemBatchability::BreakBatch => {
1713 let maybe_indirect_parameters_index = phase_indirect_parameters_buffers
1715 .allocate(no_indirect_drawing, item_is_indexed);
1716
1717 if let (&mut Some(ref mut batch_set), Some(indirect_parameters_index)) =
1718 (&mut batch_set, maybe_indirect_parameters_index)
1719 {
1720 GFBD::write_batch_indirect_parameters_metadata(
1721 item_is_indexed,
1722 output_index,
1723 None,
1724 &mut phase_indirect_parameters_buffers.buffers,
1725 indirect_parameters_index,
1726 );
1727
1728 batch_set.meta = current_meta;
1729
1730 let indirect_parameters_index_range = batch_set
1731 .indirect_parameters_index_range
1732 .as_mut()
1733 .expect("Can't allocate in a multidraw set if we aren't multidrawing");
1734 debug_assert_eq!(
1735 indirect_parameters_index,
1736 indirect_parameters_index_range.end
1737 );
1738 indirect_parameters_index_range.end += 1;
1739 }
1740 }
1741
1742 SortedPhaseItemBatchability::BatchOk => {}
1743 };
1744
1745 if let Some(batch_set) = batch_set.as_ref() {
1748 work_item_buffer.push(
1749 item_is_indexed,
1750 PreprocessWorkItem {
1751 input_index: current_input_index.into(),
1752 output_or_indirect_parameters_index: match (
1753 no_indirect_drawing,
1754 &batch_set.indirect_parameters_index_range,
1755 ) {
1756 (true, _) => output_index,
1757 (false, Some(indirect_parameters_index_range)) => {
1758 indirect_parameters_index_range.end - 1
1759 }
1760 (false, None) => 0,
1761 },
1762 },
1763 );
1764 }
1765 }
1766
1767 if let Some(batch_set) = batch_set.take() {
1769 batch_set.flush(
1770 data_buffer.len() as u32,
1771 phase,
1772 &mut phase_indirect_parameters_buffers.buffers,
1773 );
1774 }
1775 }
1776}
1777
1778#[derive(Clone, Copy, PartialEq)]
1780enum SortedPhaseItemBatchability {
1781 BatchOk,
1783 BreakBatch,
1788 BreakBatchSet,
1790}
1791
1792pub fn batch_and_prepare_binned_render_phase<BPI, GFBD>(
1794 mut phase_batched_instance_buffers: ResMut<PhaseBatchedInstanceBuffers<BPI, GFBD::BufferData>>,
1795 phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<BPI>>,
1796 mut binned_render_phases: ResMut<ViewBinnedRenderPhases<BPI>>,
1797 mut views: Query<
1798 (
1799 &ExtractedView,
1800 Has<NoIndirectDrawing>,
1801 Has<OcclusionCulling>,
1802 ),
1803 With<ExtractedView>,
1804 >,
1805 param: StaticSystemParam<GFBD::Param>,
1806) where
1807 BPI: BinnedPhaseItem,
1808 GFBD: GetFullBatchData,
1809{
1810 let system_param_item = param.into_inner();
1811
1812 let phase_indirect_parameters_buffers = phase_indirect_parameters_buffers.into_inner();
1813
1814 let UntypedPhaseBatchedInstanceBuffers {
1815 ref mut data_buffer,
1816 ref mut work_item_buffers,
1817 ref mut late_indexed_indirect_parameters_buffer,
1818 ref mut late_non_indexed_indirect_parameters_buffer,
1819 } = phase_batched_instance_buffers.buffers;
1820
1821 for (extracted_view, no_indirect_drawing, gpu_occlusion_culling) in &mut views {
1822 let Some(phase) = binned_render_phases.get_mut(&extracted_view.retained_view_entity) else {
1823 continue;
1824 };
1825
1826 let work_item_buffer = get_or_create_work_item_buffer::<BPI>(
1829 work_item_buffers,
1830 extracted_view.retained_view_entity,
1831 no_indirect_drawing,
1832 gpu_occlusion_culling,
1833 );
1834
1835 init_work_item_buffers(
1837 work_item_buffer,
1838 late_indexed_indirect_parameters_buffer,
1839 late_non_indexed_indirect_parameters_buffer,
1840 );
1841
1842 for (key, unbatchables) in &mut phase.unbatchable_meshes {
1861 let mut indirect_parameters_offset = if no_indirect_drawing {
1863 None
1864 } else if key.0.indexed() {
1865 Some(
1866 phase_indirect_parameters_buffers
1867 .buffers
1868 .indexed
1869 .allocate(unbatchables.entities.len() as u32),
1870 )
1871 } else {
1872 Some(
1873 phase_indirect_parameters_buffers
1874 .buffers
1875 .non_indexed
1876 .allocate(unbatchables.entities.len() as u32),
1877 )
1878 };
1879
1880 for main_entity in unbatchables.entities.keys() {
1881 let Some(input_index) = GFBD::get_binned_index(&system_param_item, *main_entity)
1882 else {
1883 continue;
1884 };
1885 let output_index = data_buffer.add() as u32;
1886
1887 if let Some(ref mut indirect_parameters_index) = indirect_parameters_offset {
1888 GFBD::write_batch_indirect_parameters_metadata(
1891 key.0.indexed(),
1892 output_index,
1893 None,
1894 &mut phase_indirect_parameters_buffers.buffers,
1895 *indirect_parameters_index,
1896 );
1897 work_item_buffer.push(
1898 key.0.indexed(),
1899 PreprocessWorkItem {
1900 input_index: input_index.into(),
1901 output_or_indirect_parameters_index: *indirect_parameters_index,
1902 },
1903 );
1904 unbatchables
1905 .buffer_indices
1906 .add(UnbatchableBinnedEntityIndices {
1907 instance_index: *indirect_parameters_index,
1908 extra_index: PhaseItemExtraIndex::IndirectParametersIndex {
1909 range: *indirect_parameters_index..(*indirect_parameters_index + 1),
1910 batch_set_index: None,
1911 },
1912 });
1913 phase_indirect_parameters_buffers
1914 .buffers
1915 .add_batch_set(key.0.indexed(), *indirect_parameters_index);
1916 *indirect_parameters_index += 1;
1917 } else {
1918 work_item_buffer.push(
1919 key.0.indexed(),
1920 PreprocessWorkItem {
1921 input_index: input_index.into(),
1922 output_or_indirect_parameters_index: output_index,
1923 },
1924 );
1925 unbatchables
1926 .buffer_indices
1927 .add(UnbatchableBinnedEntityIndices {
1928 instance_index: output_index,
1929 extra_index: PhaseItemExtraIndex::None,
1930 });
1931 }
1932 }
1933 }
1934
1935 for (key, bin) in &phase.batchable_meshes {
1938 let mut batch: Option<BinnedRenderPhaseBatch> = None;
1939 for (&main_entity, &input_index) in bin.entities() {
1940 let output_index = data_buffer.add() as u32;
1941
1942 match batch {
1943 Some(ref mut batch) => {
1944 batch.instance_range.end = output_index + 1;
1945
1946 work_item_buffer.push(
1954 key.0.indexed(),
1955 PreprocessWorkItem {
1956 input_index: *input_index,
1957 output_or_indirect_parameters_index: match (
1958 no_indirect_drawing,
1959 &batch.extra_index,
1960 ) {
1961 (true, _) => output_index,
1962 (
1963 false,
1964 PhaseItemExtraIndex::IndirectParametersIndex {
1965 range: indirect_parameters_range,
1966 ..
1967 },
1968 ) => indirect_parameters_range.start,
1969 (false, &PhaseItemExtraIndex::DynamicOffset(_))
1970 | (false, &PhaseItemExtraIndex::None) => 0,
1971 },
1972 },
1973 );
1974 }
1975
1976 None if !no_indirect_drawing => {
1977 let indirect_parameters_index = phase_indirect_parameters_buffers
1979 .buffers
1980 .allocate(key.0.indexed(), 1);
1981 let batch_set_index = phase_indirect_parameters_buffers
1982 .buffers
1983 .get_next_batch_set_index(key.0.indexed());
1984
1985 GFBD::write_batch_indirect_parameters_metadata(
1986 key.0.indexed(),
1987 output_index,
1988 batch_set_index,
1989 &mut phase_indirect_parameters_buffers.buffers,
1990 indirect_parameters_index,
1991 );
1992 work_item_buffer.push(
1993 key.0.indexed(),
1994 PreprocessWorkItem {
1995 input_index: *input_index,
1996 output_or_indirect_parameters_index: indirect_parameters_index,
1997 },
1998 );
1999 batch = Some(BinnedRenderPhaseBatch {
2000 representative_entity: (Entity::PLACEHOLDER, main_entity),
2001 instance_range: output_index..output_index + 1,
2002 extra_index: PhaseItemExtraIndex::IndirectParametersIndex {
2003 range: indirect_parameters_index..(indirect_parameters_index + 1),
2004 batch_set_index: None,
2005 },
2006 });
2007 }
2008
2009 None => {
2010 work_item_buffer.push(
2012 key.0.indexed(),
2013 PreprocessWorkItem {
2014 input_index: *input_index,
2015 output_or_indirect_parameters_index: output_index,
2016 },
2017 );
2018 batch = Some(BinnedRenderPhaseBatch {
2019 representative_entity: (Entity::PLACEHOLDER, main_entity),
2020 instance_range: output_index..output_index + 1,
2021 extra_index: PhaseItemExtraIndex::None,
2022 });
2023 }
2024 }
2025 }
2026
2027 if let Some(batch) = batch {
2028 match phase.batch_sets {
2029 BinnedRenderPhaseBatchSets::DynamicUniforms(_) => {
2030 error!("Dynamic uniform batch sets shouldn't be used here");
2031 }
2032 BinnedRenderPhaseBatchSets::Direct(ref mut vec) => {
2033 vec.push(batch);
2034 }
2035 BinnedRenderPhaseBatchSets::MultidrawIndirect(ref mut vec) => {
2036 vec.push(BinnedRenderPhaseBatchSet {
2041 first_batch: batch,
2042 batch_count: 1,
2043 bin_key: key.1.clone(),
2044 index: phase_indirect_parameters_buffers
2045 .buffers
2046 .batch_set_count(key.0.indexed())
2047 as u32,
2048 first_work_item_index: 0,
2050 });
2051 }
2052 }
2053 }
2054 }
2055
2056 if let (
2059 &mut BinnedRenderPhaseBatchSets::MultidrawIndirect(ref mut batch_sets),
2060 &mut PreprocessWorkItemBuffers::Indirect {
2061 indexed: ref mut indexed_work_item_buffer,
2062 non_indexed: ref mut non_indexed_work_item_buffer,
2063 gpu_occlusion_culling: ref mut gpu_occlusion_culling_buffers,
2064 },
2065 ) = (&mut phase.batch_sets, &mut *work_item_buffer)
2066 {
2067 let mut indexed_preparer: MultidrawableBatchSetPreparer<BPI, GFBD> =
2069 MultidrawableBatchSetPreparer::new(
2070 phase_indirect_parameters_buffers.buffers.batch_count(true) as u32,
2071 phase_indirect_parameters_buffers
2072 .buffers
2073 .indexed
2074 .batch_sets
2075 .len() as u32,
2076 );
2077 let mut non_indexed_preparer: MultidrawableBatchSetPreparer<BPI, GFBD> =
2078 MultidrawableBatchSetPreparer::new(
2079 phase_indirect_parameters_buffers.buffers.batch_count(false) as u32,
2080 phase_indirect_parameters_buffers
2081 .buffers
2082 .non_indexed
2083 .batch_sets
2084 .len() as u32,
2085 );
2086
2087 for (batch_set_key, bins) in &phase.multidrawable_meshes {
2089 if batch_set_key.indexed() {
2090 indexed_preparer.prepare_multidrawable_binned_batch_set(
2091 bins,
2092 data_buffer,
2093 indexed_work_item_buffer,
2094 &mut phase_indirect_parameters_buffers.buffers.indexed,
2095 batch_sets,
2096 );
2097 } else {
2098 non_indexed_preparer.prepare_multidrawable_binned_batch_set(
2099 bins,
2100 data_buffer,
2101 non_indexed_work_item_buffer,
2102 &mut phase_indirect_parameters_buffers.buffers.non_indexed,
2103 batch_sets,
2104 );
2105 }
2106 }
2107
2108 if let Some(gpu_occlusion_culling_buffers) = gpu_occlusion_culling_buffers {
2110 gpu_occlusion_culling_buffers
2111 .late_indexed
2112 .add_multiple(indexed_preparer.work_item_count);
2113 gpu_occlusion_culling_buffers
2114 .late_non_indexed
2115 .add_multiple(non_indexed_preparer.work_item_count);
2116 }
2117 }
2118 }
2119}
2120
2121struct MultidrawableBatchSetPreparer<BPI, GFBD>
2127where
2128 BPI: BinnedPhaseItem,
2129 GFBD: GetFullBatchData,
2130{
2131 indirect_parameters_index: u32,
2134 batch_set_index: u32,
2136 work_item_count: usize,
2138 phantom: PhantomData<(BPI, GFBD)>,
2139}
2140
2141impl<BPI, GFBD> MultidrawableBatchSetPreparer<BPI, GFBD>
2142where
2143 BPI: BinnedPhaseItem,
2144 GFBD: GetFullBatchData,
2145{
2146 #[inline]
2149 fn new(initial_indirect_parameters_index: u32, initial_batch_set_index: u32) -> Self {
2150 MultidrawableBatchSetPreparer {
2151 indirect_parameters_index: initial_indirect_parameters_index,
2152 batch_set_index: initial_batch_set_index,
2153 work_item_count: 0,
2154 phantom: PhantomData,
2155 }
2156 }
2157
2158 #[inline]
2163 fn prepare_multidrawable_binned_batch_set<IP>(
2164 &mut self,
2165 batch_set: &RenderMultidrawableBatchSet<BPI>,
2166 data_buffer: &mut UninitBufferVec<GFBD::BufferData>,
2167 work_item_buffer: &mut PartialBufferVec<PreprocessWorkItem>,
2168 mesh_class_buffers: &mut MeshClassIndirectParametersBuffers<IP>,
2169 batch_sets: &mut Vec<BinnedRenderPhaseBatchSet<BPI::BinKey>>,
2170 ) where
2171 IP: Clone + ShaderSize + WriteInto,
2172 {
2173 let current_indexed_batch_set_index = self.batch_set_index;
2174 let current_output_index = data_buffer.len() as u32;
2175 let first_work_item_index = work_item_buffer.len() as u32;
2176
2177 let indirect_parameters_base = self.indirect_parameters_index;
2178
2179 let Some((first_bin_key, first_bin_index)) = batch_set.bin_key_to_bin_index.iter().next()
2182 else {
2183 return;
2184 };
2185 let first_bin = batch_set
2186 .bin(*first_bin_index)
2187 .expect("At least one bin must be present in each batch set");
2188 let first_bin_len = first_bin.entity_to_binned_mesh_instance_index.len();
2189 let first_bin_entity = batch_set
2190 .representative_entity()
2191 .unwrap_or(MainEntity::from(Entity::PLACEHOLDER));
2192
2193 let cpu_metadata_offset = mesh_class_buffers.cpu_metadata.len() as u32;
2202 for _ in 0..batch_set.bin_count() {
2203 mesh_class_buffers
2204 .cpu_metadata
2205 .push(IndirectParametersCpuMetadata {
2206 base_output_index: 0,
2208 batch_set_index: self.batch_set_index,
2209 });
2210 }
2211
2212 for bin_index in batch_set.bin_key_to_bin_index.values() {
2216 let bin = batch_set.bin(*bin_index).expect("Bin not present");
2217
2218 let indirect_parameters_offset = *batch_set
2220 .gpu_buffers
2221 .bin_index_to_indirect_parameters_offset_buffer
2222 .get(bin_index.0)
2223 .unwrap();
2224 mesh_class_buffers.cpu_metadata.values_mut()
2225 [cpu_metadata_offset as usize + indirect_parameters_offset as usize]
2226 .base_output_index = data_buffer.len() as u32;
2227
2228 let bin_entity_count = bin.entity_to_binned_mesh_instance_index.len();
2232 work_item_buffer.push_multiple_uninit(bin_entity_count);
2233 data_buffer.add_multiple(bin_entity_count);
2234 self.work_item_count += bin_entity_count;
2235 }
2236
2237 let bin_count = batch_set.bin_count();
2239 mesh_class_buffers.gpu_metadata.add_multiple(bin_count);
2240 mesh_class_buffers
2241 .indirect_draw_parameters
2242 .add_multiple(bin_count);
2243
2244 mesh_class_buffers.batch_sets.push(IndirectBatchSet {
2246 indirect_parameters_base,
2247 indirect_parameters_count: 0,
2248 });
2249
2250 self.indirect_parameters_index += bin_count as u32;
2251 self.batch_set_index += 1;
2252
2253 batch_sets.push(BinnedRenderPhaseBatchSet {
2256 first_batch: BinnedRenderPhaseBatch {
2257 representative_entity: (Entity::PLACEHOLDER, first_bin_entity),
2258 instance_range: current_output_index..(current_output_index + first_bin_len as u32),
2259 extra_index: PhaseItemExtraIndex::maybe_indirect_parameters_index(NonMaxU32::new(
2260 indirect_parameters_base,
2261 )),
2262 },
2263 bin_key: (*first_bin_key).clone(),
2264 batch_count: self.indirect_parameters_index - indirect_parameters_base,
2265 index: current_indexed_batch_set_index,
2266 first_work_item_index,
2267 });
2268 }
2269}
2270
2271pub fn collect_buffers_for_phase<PI, GFBD>(
2286 mut phase_batched_instance_buffers: ResMut<PhaseBatchedInstanceBuffers<PI, GFBD::BufferData>>,
2287 mut phase_indirect_parameters_buffers: ResMut<PhaseIndirectParametersBuffers<PI>>,
2288 mut batched_instance_buffers: ResMut<
2289 BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
2290 >,
2291 mut indirect_parameters_buffers: ResMut<IndirectParametersBuffers>,
2292 indirect_parameters_buffers_settings: Res<IndirectParametersBuffersSettings>,
2293) where
2294 PI: PhaseItem,
2295 GFBD: GetFullBatchData + Send + Sync + 'static,
2296{
2297 let untyped_phase_batched_instance_buffers =
2301 mem::take(&mut phase_batched_instance_buffers.buffers);
2302 if let Some(mut old_untyped_phase_batched_instance_buffers) = batched_instance_buffers
2303 .phase_instance_buffers
2304 .insert(TypeId::of::<PI>(), untyped_phase_batched_instance_buffers)
2305 {
2306 old_untyped_phase_batched_instance_buffers.clear();
2307 phase_batched_instance_buffers.buffers = old_untyped_phase_batched_instance_buffers;
2308 }
2309
2310 let untyped_phase_indirect_parameters_buffers = mem::replace(
2314 &mut phase_indirect_parameters_buffers.buffers,
2315 UntypedPhaseIndirectParametersBuffers::new(
2316 indirect_parameters_buffers_settings.allow_copies_from_indirect_parameter_buffers,
2317 ),
2318 );
2319 if let Some(mut old_untyped_phase_indirect_parameters_buffers) = indirect_parameters_buffers
2320 .insert(
2321 TypeId::of::<PI>(),
2322 untyped_phase_indirect_parameters_buffers,
2323 )
2324 {
2325 old_untyped_phase_indirect_parameters_buffers.clear();
2326 phase_indirect_parameters_buffers.buffers = old_untyped_phase_indirect_parameters_buffers;
2327 }
2328}
2329
2330pub fn write_batched_instance_buffers<GFBD>(
2332 render_device: Res<RenderDevice>,
2333 render_queue: Res<RenderQueue>,
2334 gpu_array_buffer: ResMut<BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>>,
2335 pipeline_cache: Res<PipelineCache>,
2336 mut bin_unpacking_buffers: ResMut<BinUnpackingBuffers>,
2337 mut sparse_buffer_update_jobs: ResMut<SparseBufferUpdateJobs>,
2338 mut sparse_buffer_update_bind_groups: ResMut<SparseBufferUpdateBindGroups>,
2339 sparse_buffer_update_pipelines: Res<SparseBufferUpdatePipelines>,
2340) where
2341 GFBD: GetFullBatchData,
2342{
2343 let BatchedInstanceBuffers {
2344 current_input_buffer,
2345 previous_input_buffer,
2346 phase_instance_buffers,
2347 } = gpu_array_buffer.into_inner();
2348
2349 let render_device = &*render_device;
2350 let render_queue = &*render_queue;
2351
2352 ComputeTaskPool::get().scope(|scope| {
2353 scope.spawn(async {
2354 #[cfg(feature = "trace")]
2355 let _span = bevy_log::info_span!("write_current_input_buffers").entered();
2356 current_input_buffer
2357 .buffer
2358 .write_buffers(render_device, render_queue);
2359 });
2360 scope.spawn(async {
2361 #[cfg(feature = "trace")]
2362 let _span = bevy_log::info_span!("write_previous_input_buffers").entered();
2363 previous_input_buffer.write_buffer(render_device, render_queue);
2364 });
2365
2366 for phase_instance_buffers in phase_instance_buffers.values_mut() {
2367 let UntypedPhaseBatchedInstanceBuffers {
2368 ref mut data_buffer,
2369 ref mut work_item_buffers,
2370 ref mut late_indexed_indirect_parameters_buffer,
2371 ref mut late_non_indexed_indirect_parameters_buffer,
2372 } = *phase_instance_buffers;
2373
2374 scope.spawn(async {
2375 #[cfg(feature = "trace")]
2376 let _span = bevy_log::info_span!("write_phase_instance_buffers").entered();
2377 data_buffer.write_buffer(render_device);
2378 late_indexed_indirect_parameters_buffer.write_buffer(render_device, render_queue);
2379 late_non_indexed_indirect_parameters_buffer
2380 .write_buffer(render_device, render_queue);
2381 });
2382
2383 for phase_work_item_buffers in work_item_buffers.values_mut() {
2384 scope.spawn(async {
2385 #[cfg(feature = "trace")]
2386 let _span = bevy_log::info_span!("write_work_item_buffers").entered();
2387 match *phase_work_item_buffers {
2388 PreprocessWorkItemBuffers::Direct(ref mut buffer_vec) => {
2389 buffer_vec.write_buffer(render_device, render_queue);
2390 }
2391 PreprocessWorkItemBuffers::Indirect {
2392 ref mut indexed,
2393 ref mut non_indexed,
2394 ref mut gpu_occlusion_culling,
2395 } => {
2396 indexed.write_buffer(render_device, render_queue);
2397 non_indexed.write_buffer(render_device, render_queue);
2398
2399 if let Some(GpuOcclusionCullingWorkItemBuffers {
2400 ref mut late_indexed,
2401 ref mut late_non_indexed,
2402 late_indirect_parameters_indexed_offset: _,
2403 late_indirect_parameters_non_indexed_offset: _,
2404 }) = *gpu_occlusion_culling
2405 {
2406 if !late_indexed.is_empty() {
2407 late_indexed.write_buffer(render_device);
2408 }
2409 if !late_non_indexed.is_empty() {
2410 late_non_indexed.write_buffer(render_device);
2411 }
2412 }
2413 }
2414 }
2415 });
2416 }
2417 }
2418 });
2419
2420 current_input_buffer.buffer.prepare_to_populate_buffers(
2423 render_device,
2424 &pipeline_cache,
2425 &mut sparse_buffer_update_jobs,
2426 &mut sparse_buffer_update_bind_groups,
2427 &sparse_buffer_update_pipelines,
2428 );
2429
2430 bin_unpacking_buffers
2431 .bin_unpacking_metadata
2432 .write_buffer(render_device, render_queue);
2433}
2434
2435pub fn write_binned_instance_buffers<BPI, GFBD>(
2440 mut views: Query<&ExtractedView>,
2441 mut view_binned_render_phases: ResMut<ViewBinnedRenderPhases<BPI>>,
2442 bin_unpacking_buffers: ResMut<BinUnpackingBuffers>,
2443 render_device: Res<RenderDevice>,
2444 render_queue: Res<RenderQueue>,
2445) where
2446 BPI: BinnedPhaseItem,
2447 GFBD: GetFullBatchData,
2448{
2449 let bin_unpacking_buffers = bin_unpacking_buffers.into_inner();
2450
2451 let phase_type_id = TypeId::of::<BPI>();
2452
2453 let mut all_seen_view_entities = HashSet::new();
2456
2457 for extracted_view in &mut views {
2458 all_seen_view_entities.insert(extracted_view.retained_view_entity);
2459
2460 let Some(view_binned_render_phase) =
2461 view_binned_render_phases.get_mut(&extracted_view.retained_view_entity)
2462 else {
2463 continue;
2464 };
2465
2466 let BinnedRenderPhaseBatchSets::MultidrawIndirect(ref batch_sets) =
2469 view_binned_render_phase.batch_sets
2470 else {
2471 continue;
2472 };
2473
2474 let view_phase_bin_unpacking_buffers = bin_unpacking_buffers
2477 .view_phase_buffers
2478 .entry(BinUnpackingBuffersKey {
2479 phase: phase_type_id,
2480 view: extracted_view.retained_view_entity,
2481 })
2482 .or_default();
2483
2484 view_phase_bin_unpacking_buffers
2486 .indexed_unpacking_jobs
2487 .clear();
2488 view_phase_bin_unpacking_buffers
2489 .non_indexed_unpacking_jobs
2490 .clear();
2491
2492 let mut representative_entity_to_batch_set_bin_unpacking_metadata =
2503 MainEntityHashMap::default();
2504
2505 for batch_set in batch_sets {
2506 let main_entity = batch_set.first_batch.representative_entity.1;
2507 if *main_entity != Entity::PLACEHOLDER
2508 && let PhaseItemExtraIndex::IndirectParametersIndex {
2509 range: ref indirect_parameters_range,
2510 ..
2511 } = batch_set.first_batch.extra_index
2512 {
2513 representative_entity_to_batch_set_bin_unpacking_metadata.insert(
2516 main_entity,
2517 BatchSetBinUnpackingMetadata {
2518 base_output_work_item_index: batch_set.first_work_item_index,
2519 base_indirect_parameters_index: indirect_parameters_range.start,
2520 },
2521 );
2522 }
2523 }
2524
2525 for (batch_set_key, batch_set) in view_binned_render_phase.multidrawable_meshes.iter_mut() {
2531 let Some(representative_entity) = batch_set.representative_entity() else {
2532 continue;
2533 };
2534 let Some(bin_unpacking_metadata) =
2535 representative_entity_to_batch_set_bin_unpacking_metadata
2536 .get(&representative_entity)
2537 else {
2538 continue;
2539 };
2540
2541 batch_set
2544 .gpu_buffers
2545 .render_binned_mesh_instance_buffer
2546 .write_buffer(&render_device, &render_queue);
2547 batch_set
2548 .gpu_buffers
2549 .bin_index_to_indirect_parameters_offset_buffer
2550 .write_buffer(&render_device, &render_queue);
2551
2552 let (
2553 Some(render_bin_entry_buffer),
2554 Some(bin_index_to_indirect_parameters_offset_buffer),
2555 ) = (
2556 batch_set
2557 .gpu_buffers
2558 .render_binned_mesh_instance_buffer
2559 .buffer(),
2560 batch_set
2561 .gpu_buffers
2562 .bin_index_to_indirect_parameters_offset_buffer
2563 .buffer(),
2564 )
2565 else {
2566 continue;
2567 };
2568
2569 let binned_mesh_instance_count = batch_set
2570 .gpu_buffers
2571 .render_binned_mesh_instance_buffer
2572 .len() as u32;
2573
2574 let gpu_bin_unpacking_metadata_index = bin_unpacking_buffers
2576 .bin_unpacking_metadata
2577 .push(GpuBinUnpackingMetadata {
2578 base_output_work_item_index: bin_unpacking_metadata.base_output_work_item_index,
2579 base_indirect_parameters_index: bin_unpacking_metadata
2580 .base_indirect_parameters_index,
2581 binned_mesh_instance_count,
2582 pad: [0; _],
2583 });
2584
2585 let Some(gpu_bin_unpacking_metadata_index) =
2586 NonMaxU32::new(gpu_bin_unpacking_metadata_index as u32)
2587 else {
2588 continue;
2589 };
2590
2591 let job = BinUnpackingJob {
2593 render_binned_mesh_instance_buffer: render_bin_entry_buffer.clone(),
2594 bin_index_to_indirect_parameters_offset_buffer:
2595 bin_index_to_indirect_parameters_offset_buffer.clone(),
2596 bin_unpacking_metadata_index: BinUnpackingMetadataIndex(
2597 gpu_bin_unpacking_metadata_index,
2598 ),
2599 mesh_instance_count: binned_mesh_instance_count,
2600 };
2601
2602 if batch_set_key.indexed() {
2603 view_phase_bin_unpacking_buffers
2604 .indexed_unpacking_jobs
2605 .push(job);
2606 } else {
2607 view_phase_bin_unpacking_buffers
2608 .non_indexed_unpacking_jobs
2609 .push(job);
2610 }
2611 }
2612 }
2613
2614 bin_unpacking_buffers
2616 .view_phase_buffers
2617 .retain(|bin_unpacking_buffers_key, _| {
2618 bin_unpacking_buffers_key.phase != phase_type_id
2619 || all_seen_view_entities.contains(&bin_unpacking_buffers_key.view)
2620 });
2621}
2622
2623pub fn clear_bin_unpacking_buffers(mut bin_unpacking_buffers: ResMut<BinUnpackingBuffers>) {
2625 bin_unpacking_buffers.bin_unpacking_metadata.clear();
2626}
2627
2628struct BatchSetBinUnpackingMetadata {
2631 base_output_work_item_index: u32,
2634 base_indirect_parameters_index: u32,
2637}
2638
2639pub fn clear_indirect_parameters_buffers(
2640 mut indirect_parameters_buffers: ResMut<IndirectParametersBuffers>,
2641) {
2642 for phase_indirect_parameters_buffers in indirect_parameters_buffers.values_mut() {
2643 phase_indirect_parameters_buffers.clear();
2644 }
2645}
2646
2647pub fn write_indirect_parameters_buffers(
2648 render_device: Res<RenderDevice>,
2649 render_queue: Res<RenderQueue>,
2650 mut indirect_parameters_buffers: ResMut<IndirectParametersBuffers>,
2651) {
2652 let render_device = &*render_device;
2653 let render_queue = &*render_queue;
2654 ComputeTaskPool::get().scope(|scope| {
2655 for phase_indirect_parameters_buffers in indirect_parameters_buffers.values_mut() {
2656 scope.spawn(async {
2657 #[cfg(feature = "trace")]
2658 let _span = bevy_log::info_span!("indexed_data").entered();
2659 phase_indirect_parameters_buffers
2660 .indexed
2661 .indirect_draw_parameters
2662 .write_buffer(render_device);
2663 });
2664 scope.spawn(async {
2665 #[cfg(feature = "trace")]
2666 let _span = bevy_log::info_span!("non_indexed_data").entered();
2667 phase_indirect_parameters_buffers
2668 .non_indexed
2669 .indirect_draw_parameters
2670 .write_buffer(render_device);
2671 });
2672
2673 scope.spawn(async {
2674 #[cfg(feature = "trace")]
2675 let _span = bevy_log::info_span!("indexed_cpu_metadata").entered();
2676 phase_indirect_parameters_buffers
2677 .indexed
2678 .cpu_metadata
2679 .write_buffer(render_device, render_queue);
2680 });
2681 scope.spawn(async {
2682 #[cfg(feature = "trace")]
2683 let _span = bevy_log::info_span!("non_indexed_cpu_metadata").entered();
2684 phase_indirect_parameters_buffers
2685 .non_indexed
2686 .cpu_metadata
2687 .write_buffer(render_device, render_queue);
2688 });
2689
2690 scope.spawn(async {
2691 #[cfg(feature = "trace")]
2692 let _span = bevy_log::info_span!("non_indexed_gpu_metadata").entered();
2693 phase_indirect_parameters_buffers
2694 .non_indexed
2695 .gpu_metadata
2696 .write_buffer(render_device);
2697 });
2698 scope.spawn(async {
2699 #[cfg(feature = "trace")]
2700 let _span = bevy_log::info_span!("indexed_gpu_metadata").entered();
2701 phase_indirect_parameters_buffers
2702 .indexed
2703 .gpu_metadata
2704 .write_buffer(render_device);
2705 });
2706
2707 scope.spawn(async {
2708 #[cfg(feature = "trace")]
2709 let _span = bevy_log::info_span!("indexed_batch_sets").entered();
2710 phase_indirect_parameters_buffers
2711 .indexed
2712 .batch_sets
2713 .write_buffer(render_device, render_queue);
2714 });
2715 scope.spawn(async {
2716 #[cfg(feature = "trace")]
2717 let _span = bevy_log::info_span!("non_indexed_batch_sets").entered();
2718 phase_indirect_parameters_buffers
2719 .non_indexed
2720 .batch_sets
2721 .write_buffer(render_device, render_queue);
2722 });
2723 }
2724 });
2725}
2726
2727#[cfg(test)]
2728mod tests {
2729 use bytemuck::{Pod, Zeroable};
2730
2731 use crate::impl_atomic_pod;
2732
2733 use super::*;
2734
2735 #[derive(Clone, Copy, Default, PartialEq, Debug, Pod, Zeroable)]
2736 #[repr(C)]
2737 struct TestData(u32);
2738
2739 impl_atomic_pod!(TestData, TestDataBlob);
2740
2741 #[test]
2742 fn instance_buffer_correct_behavior() {
2743 let mut instance_buffer = InstanceInputUniformBuffer::new();
2744
2745 let index = instance_buffer.add(TestData(2));
2746 instance_buffer.remove(index);
2747 assert_eq!(instance_buffer.get_unchecked(index), TestData(2));
2748 assert_eq!(instance_buffer.get(index), None);
2749
2750 instance_buffer.add(TestData(5));
2751 assert_eq!(instance_buffer.buffer().len(), 1);
2752 }
2753}