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(), item.main_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, main_entity) in &phase.batchable_mesh_values[key] {
110 let Some(buffer_data) =
111 GFBD::get_binned_batch_data(&system_param_item, (entity, main_entity))
112 else {
113 continue;
114 };
115 let instance = gpu_array_buffer.push(buffer_data);
116
117 if !batch_set.last().is_some_and(|batch| {
123 batch.instance_range.end == instance.index
124 && batch.extra_index
125 == PhaseItemExtraIndex::maybe_dynamic_offset(instance.dynamic_offset)
126 }) {
127 batch_set.push(BinnedRenderPhaseBatch {
128 representative_entity: (entity, main_entity),
129 instance_range: instance.index..instance.index,
130 extra_index: PhaseItemExtraIndex::maybe_dynamic_offset(
131 instance.dynamic_offset,
132 ),
133 });
134 }
135
136 if let Some(batch) = batch_set.last_mut() {
137 batch.instance_range.end = instance.index + 1;
138 }
139 }
140
141 phase.batch_sets.push(batch_set);
142 }
143
144 for key in &phase.unbatchable_mesh_keys {
146 let unbatchables = phase.unbatchable_mesh_values.get_mut(key).unwrap();
147 for &entity in &unbatchables.entities {
148 let Some(buffer_data) = GFBD::get_binned_batch_data(&system_param_item, entity)
149 else {
150 continue;
151 };
152 let instance = gpu_array_buffer.push(buffer_data);
153 unbatchables.buffer_indices.add(instance.into());
154 }
155 }
156 }
157}
158
159pub fn write_batched_instance_buffer<GBD>(
161 render_device: Res<RenderDevice>,
162 render_queue: Res<RenderQueue>,
163 mut cpu_batched_instance_buffer: ResMut<BatchedInstanceBuffer<GBD::BufferData>>,
164) where
165 GBD: GetBatchData,
166{
167 cpu_batched_instance_buffer.write_buffer(&render_device, &render_queue);
168}