bevy_render/batching/
no_gpu_preprocessing.rs1use bevy_derive::{Deref, DerefMut};
4use bevy_ecs::system::{Res, ResMut, Resource, StaticSystemParam};
5use smallvec::{smallvec, SmallVec};
6use wgpu::BindingResource;
7
8use crate::{
9 render_phase::{
10 BinnedPhaseItem, BinnedRenderPhaseBatch, CachedRenderPipelinePhaseItem,
11 PhaseItemExtraIndex, SortedPhaseItem, ViewBinnedRenderPhases, ViewSortedRenderPhases,
12 },
13 render_resource::{GpuArrayBuffer, GpuArrayBufferable},
14 renderer::{RenderDevice, RenderQueue},
15};
16
17use super::{GetBatchData, GetFullBatchData};
18
19#[derive(Resource, Deref, DerefMut)]
24pub struct BatchedInstanceBuffer<BD>(pub GpuArrayBuffer<BD>)
25where
26 BD: GpuArrayBufferable + Sync + Send + 'static;
27
28impl<BD> BatchedInstanceBuffer<BD>
29where
30 BD: GpuArrayBufferable + Sync + Send + 'static,
31{
32 pub fn new(render_device: &RenderDevice) -> Self {
34 BatchedInstanceBuffer(GpuArrayBuffer::new(render_device))
35 }
36
37 pub fn instance_data_binding(&self) -> Option<BindingResource> {
42 self.binding()
43 }
44}
45
46pub fn clear_batched_cpu_instance_buffers<GBD>(
50 cpu_batched_instance_buffer: Option<ResMut<BatchedInstanceBuffer<GBD::BufferData>>>,
51) where
52 GBD: GetBatchData,
53{
54 if let Some(mut cpu_batched_instance_buffer) = cpu_batched_instance_buffer {
55 cpu_batched_instance_buffer.clear();
56 }
57}
58
59pub fn batch_and_prepare_sorted_render_phase<I, GBD>(
63 batched_instance_buffer: ResMut<BatchedInstanceBuffer<GBD::BufferData>>,
64 mut phases: ResMut<ViewSortedRenderPhases<I>>,
65 param: StaticSystemParam<GBD::Param>,
66) where
67 I: CachedRenderPipelinePhaseItem + SortedPhaseItem,
68 GBD: GetBatchData,
69{
70 let system_param_item = param.into_inner();
71
72 let batched_instance_buffer = batched_instance_buffer.into_inner();
74
75 for phase in phases.values_mut() {
76 super::batch_and_prepare_sorted_render_phase::<I, GBD>(phase, |item| {
77 let (buffer_data, compare_data) =
78 GBD::get_batch_data(&system_param_item, item.entity())?;
79 let buffer_index = batched_instance_buffer.push(buffer_data);
80
81 let index = buffer_index.index;
82 let (batch_range, extra_index) = item.batch_range_and_extra_index_mut();
83 *batch_range = index..index + 1;
84 *extra_index = PhaseItemExtraIndex::maybe_dynamic_offset(buffer_index.dynamic_offset);
85
86 compare_data
87 });
88 }
89}
90
91pub fn batch_and_prepare_binned_render_phase<BPI, GFBD>(
94 gpu_array_buffer: ResMut<BatchedInstanceBuffer<GFBD::BufferData>>,
95 mut phases: ResMut<ViewBinnedRenderPhases<BPI>>,
96 param: StaticSystemParam<GFBD::Param>,
97) where
98 BPI: BinnedPhaseItem,
99 GFBD: GetFullBatchData,
100{
101 let gpu_array_buffer = gpu_array_buffer.into_inner();
102 let system_param_item = param.into_inner();
103
104 for phase in phases.values_mut() {
105 for key in &phase.batchable_mesh_keys {
108 let mut batch_set: SmallVec<[BinnedRenderPhaseBatch; 1]> = smallvec![];
109 for &entity in &phase.batchable_mesh_values[key] {
110 let Some(buffer_data) = GFBD::get_binned_batch_data(&system_param_item, entity)
111 else {
112 continue;
113 };
114 let instance = gpu_array_buffer.push(buffer_data);
115
116 if !batch_set.last().is_some_and(|batch| {
122 batch.instance_range.end == instance.index
123 && batch.extra_index
124 == PhaseItemExtraIndex::maybe_dynamic_offset(instance.dynamic_offset)
125 }) {
126 batch_set.push(BinnedRenderPhaseBatch {
127 representative_entity: entity,
128 instance_range: instance.index..instance.index,
129 extra_index: PhaseItemExtraIndex::maybe_dynamic_offset(
130 instance.dynamic_offset,
131 ),
132 });
133 }
134
135 if let Some(batch) = batch_set.last_mut() {
136 batch.instance_range.end = instance.index + 1;
137 }
138 }
139
140 phase.batch_sets.push(batch_set);
141 }
142
143 for key in &phase.unbatchable_mesh_keys {
145 let unbatchables = phase.unbatchable_mesh_values.get_mut(key).unwrap();
146 for &entity in &unbatchables.entities {
147 let Some(buffer_data) = GFBD::get_binned_batch_data(&system_param_item, entity)
148 else {
149 continue;
150 };
151 let instance = gpu_array_buffer.push(buffer_data);
152 unbatchables.buffer_indices.add(instance.into());
153 }
154 }
155 }
156}
157
158pub fn write_batched_instance_buffer<GBD>(
160 render_device: Res<RenderDevice>,
161 render_queue: Res<RenderQueue>,
162 mut cpu_batched_instance_buffer: ResMut<BatchedInstanceBuffer<GBD::BufferData>>,
163) where
164 GBD: GetBatchData,
165{
166 cpu_batched_instance_buffer.write_buffer(&render_device, &render_queue);
167}