bevy_render/batching/
no_gpu_preprocessing.rs1use bevy_derive::{Deref, DerefMut};
4use bevy_ecs::entity::Entity;
5use bevy_ecs::resource::Resource;
6use bevy_ecs::system::{Res, ResMut, StaticSystemParam};
7use bevy_ecs::world::{FromWorld, World};
8use bevy_log::error;
9use smallvec::{smallvec, SmallVec};
10use wgpu::{BindingResource, Limits};
11
12use crate::{
13 render_phase::{
14 BinnedPhaseItem, BinnedRenderPhaseBatch, BinnedRenderPhaseBatchSets,
15 CachedRenderPipelinePhaseItem, PhaseItemExtraIndex, SortedPhaseItem,
16 ViewBinnedRenderPhases, ViewSortedRenderPhases,
17 },
18 render_resource::{GpuArrayBuffer, GpuArrayBufferable},
19 renderer::{RenderDevice, RenderQueue},
20};
21
22use super::{GetBatchData, GetFullBatchData};
23
24#[derive(Resource, Deref, DerefMut)]
29pub struct BatchedInstanceBuffer<BD>(pub GpuArrayBuffer<BD>)
30where
31 BD: GpuArrayBufferable + Sync + Send + 'static;
32
33impl<BD> FromWorld for BatchedInstanceBuffer<BD>
34where
35 BD: GpuArrayBufferable + Sync + Send + 'static,
36{
37 fn from_world(world: &mut World) -> Self {
38 let render_device = world.resource::<RenderDevice>();
39 BatchedInstanceBuffer(GpuArrayBuffer::new(&render_device.limits()))
40 }
41}
42
43impl<BD> BatchedInstanceBuffer<BD>
44where
45 BD: GpuArrayBufferable + Sync + Send + 'static,
46{
47 pub fn new(limits: &Limits) -> Self {
49 BatchedInstanceBuffer(GpuArrayBuffer::new(limits))
50 }
51
52 pub fn instance_data_binding(&self) -> Option<BindingResource<'_>> {
57 self.binding()
58 }
59}
60
61pub fn clear_batched_cpu_instance_buffers<GBD>(
65 cpu_batched_instance_buffer: Option<ResMut<BatchedInstanceBuffer<GBD::BufferData>>>,
66) where
67 GBD: GetBatchData,
68{
69 if let Some(mut cpu_batched_instance_buffer) = cpu_batched_instance_buffer {
70 cpu_batched_instance_buffer.clear();
71 }
72}
73
74pub fn batch_and_prepare_sorted_render_phase<I, GBD>(
78 batched_instance_buffer: ResMut<BatchedInstanceBuffer<GBD::BufferData>>,
79 mut phases: ResMut<ViewSortedRenderPhases<I>>,
80 param: StaticSystemParam<GBD::Param>,
81) where
82 I: CachedRenderPipelinePhaseItem + SortedPhaseItem,
83 GBD: GetBatchData,
84{
85 let system_param_item = param.into_inner();
86
87 let batched_instance_buffer = batched_instance_buffer.into_inner();
89
90 for phase in phases.values_mut() {
91 super::batch_and_prepare_sorted_render_phase::<I, GBD>(phase, |item| {
92 let (buffer_data, compare_data) =
93 GBD::get_batch_data(&system_param_item, (item.entity(), item.main_entity()))?;
94 let buffer_index = batched_instance_buffer.push(buffer_data);
95
96 let index = buffer_index.index;
97 let (batch_range, extra_index) = item.batch_range_and_extra_index_mut();
98 *batch_range = index..index + 1;
99 *extra_index = PhaseItemExtraIndex::maybe_dynamic_offset(buffer_index.dynamic_offset);
100
101 compare_data
102 });
103 }
104}
105
106pub fn batch_and_prepare_binned_render_phase<BPI, GFBD>(
109 gpu_array_buffer: ResMut<BatchedInstanceBuffer<GFBD::BufferData>>,
110 mut phases: ResMut<ViewBinnedRenderPhases<BPI>>,
111 param: StaticSystemParam<GFBD::Param>,
112) where
113 BPI: BinnedPhaseItem,
114 GFBD: GetFullBatchData,
115{
116 let gpu_array_buffer = gpu_array_buffer.into_inner();
117 let system_param_item = param.into_inner();
118
119 for phase in phases.values_mut() {
120 for bin in phase.batchable_meshes.values_mut() {
123 let mut batch_set: SmallVec<[BinnedRenderPhaseBatch; 1]> = smallvec![];
124 for main_entity in bin.entities().keys() {
125 let Some(buffer_data) =
126 GFBD::get_binned_batch_data(&system_param_item, *main_entity)
127 else {
128 continue;
129 };
130 let instance = gpu_array_buffer.push(buffer_data);
131
132 if !batch_set.last().is_some_and(|batch| {
138 batch.instance_range.end == instance.index
139 && batch.extra_index
140 == PhaseItemExtraIndex::maybe_dynamic_offset(instance.dynamic_offset)
141 }) {
142 batch_set.push(BinnedRenderPhaseBatch {
143 representative_entity: (Entity::PLACEHOLDER, *main_entity),
144 instance_range: instance.index..instance.index,
145 extra_index: PhaseItemExtraIndex::maybe_dynamic_offset(
146 instance.dynamic_offset,
147 ),
148 });
149 }
150
151 if let Some(batch) = batch_set.last_mut() {
152 batch.instance_range.end = instance.index + 1;
153 }
154 }
155
156 match phase.batch_sets {
157 BinnedRenderPhaseBatchSets::DynamicUniforms(ref mut batch_sets) => {
158 batch_sets.push(batch_set);
159 }
160 BinnedRenderPhaseBatchSets::Direct(_)
161 | BinnedRenderPhaseBatchSets::MultidrawIndirect { .. } => {
162 error!(
163 "Dynamic uniform batch sets should be used when GPU preprocessing is off"
164 );
165 }
166 }
167 }
168
169 for unbatchables in phase.unbatchable_meshes.values_mut() {
171 for main_entity in unbatchables.entities.keys() {
172 let Some(buffer_data) =
173 GFBD::get_binned_batch_data(&system_param_item, *main_entity)
174 else {
175 continue;
176 };
177 let instance = gpu_array_buffer.push(buffer_data);
178 unbatchables.buffer_indices.add(instance.into());
179 }
180 }
181 }
182}
183
184pub fn write_batched_instance_buffer<GBD>(
186 render_device: Res<RenderDevice>,
187 render_queue: Res<RenderQueue>,
188 mut cpu_batched_instance_buffer: ResMut<BatchedInstanceBuffer<GBD::BufferData>>,
189) where
190 GBD: GetBatchData,
191{
192 cpu_batched_instance_buffer.write_buffer(&render_device, &render_queue);
193}