Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/batching/no_gpu_preprocessing.rs
6596 views
1
//! Batching functionality when GPU preprocessing isn't in use.
2
3
use bevy_derive::{Deref, DerefMut};
4
use bevy_ecs::entity::Entity;
5
use bevy_ecs::resource::Resource;
6
use bevy_ecs::system::{Res, ResMut, StaticSystemParam};
7
use smallvec::{smallvec, SmallVec};
8
use tracing::error;
9
use wgpu::BindingResource;
10
11
use crate::{
12
render_phase::{
13
BinnedPhaseItem, BinnedRenderPhaseBatch, BinnedRenderPhaseBatchSets,
14
CachedRenderPipelinePhaseItem, PhaseItemExtraIndex, SortedPhaseItem,
15
ViewBinnedRenderPhases, ViewSortedRenderPhases,
16
},
17
render_resource::{GpuArrayBuffer, GpuArrayBufferable},
18
renderer::{RenderDevice, RenderQueue},
19
};
20
21
use super::{GetBatchData, GetFullBatchData};
22
23
/// The GPU buffers holding the data needed to render batches.
24
///
25
/// For example, in the 3D PBR pipeline this holds `MeshUniform`s, which are the
26
/// `BD` type parameter in that mode.
27
#[derive(Resource, Deref, DerefMut)]
28
pub struct BatchedInstanceBuffer<BD>(pub GpuArrayBuffer<BD>)
29
where
30
BD: GpuArrayBufferable + Sync + Send + 'static;
31
32
impl<BD> BatchedInstanceBuffer<BD>
33
where
34
BD: GpuArrayBufferable + Sync + Send + 'static,
35
{
36
/// Creates a new buffer.
37
pub fn new(render_device: &RenderDevice) -> Self {
38
BatchedInstanceBuffer(GpuArrayBuffer::new(render_device))
39
}
40
41
/// Returns the binding of the buffer that contains the per-instance data.
42
///
43
/// If we're in the GPU instance buffer building mode, this buffer needs to
44
/// be filled in via a compute shader.
45
pub fn instance_data_binding(&self) -> Option<BindingResource<'_>> {
46
self.binding()
47
}
48
}
49
50
/// A system that clears out the [`BatchedInstanceBuffer`] for the frame.
51
///
52
/// This needs to run before the CPU batched instance buffers are used.
53
pub fn clear_batched_cpu_instance_buffers<GBD>(
54
cpu_batched_instance_buffer: Option<ResMut<BatchedInstanceBuffer<GBD::BufferData>>>,
55
) where
56
GBD: GetBatchData,
57
{
58
if let Some(mut cpu_batched_instance_buffer) = cpu_batched_instance_buffer {
59
cpu_batched_instance_buffer.clear();
60
}
61
}
62
63
/// Batch the items in a sorted render phase, when GPU instance buffer building
64
/// isn't in use. This means comparing metadata needed to draw each phase item
65
/// and trying to combine the draws into a batch.
66
pub fn batch_and_prepare_sorted_render_phase<I, GBD>(
67
batched_instance_buffer: ResMut<BatchedInstanceBuffer<GBD::BufferData>>,
68
mut phases: ResMut<ViewSortedRenderPhases<I>>,
69
param: StaticSystemParam<GBD::Param>,
70
) where
71
I: CachedRenderPipelinePhaseItem + SortedPhaseItem,
72
GBD: GetBatchData,
73
{
74
let system_param_item = param.into_inner();
75
76
// We only process CPU-built batch data in this function.
77
let batched_instance_buffer = batched_instance_buffer.into_inner();
78
79
for phase in phases.values_mut() {
80
super::batch_and_prepare_sorted_render_phase::<I, GBD>(phase, |item| {
81
let (buffer_data, compare_data) =
82
GBD::get_batch_data(&system_param_item, (item.entity(), item.main_entity()))?;
83
let buffer_index = batched_instance_buffer.push(buffer_data);
84
85
let index = buffer_index.index;
86
let (batch_range, extra_index) = item.batch_range_and_extra_index_mut();
87
*batch_range = index..index + 1;
88
*extra_index = PhaseItemExtraIndex::maybe_dynamic_offset(buffer_index.dynamic_offset);
89
90
compare_data
91
});
92
}
93
}
94
95
/// Creates batches for a render phase that uses bins, when GPU batch data
96
/// building isn't in use.
97
pub fn batch_and_prepare_binned_render_phase<BPI, GFBD>(
98
gpu_array_buffer: ResMut<BatchedInstanceBuffer<GFBD::BufferData>>,
99
mut phases: ResMut<ViewBinnedRenderPhases<BPI>>,
100
param: StaticSystemParam<GFBD::Param>,
101
) where
102
BPI: BinnedPhaseItem,
103
GFBD: GetFullBatchData,
104
{
105
let gpu_array_buffer = gpu_array_buffer.into_inner();
106
let system_param_item = param.into_inner();
107
108
for phase in phases.values_mut() {
109
// Prepare batchables.
110
111
for bin in phase.batchable_meshes.values_mut() {
112
let mut batch_set: SmallVec<[BinnedRenderPhaseBatch; 1]> = smallvec![];
113
for main_entity in bin.entities().keys() {
114
let Some(buffer_data) =
115
GFBD::get_binned_batch_data(&system_param_item, *main_entity)
116
else {
117
continue;
118
};
119
let instance = gpu_array_buffer.push(buffer_data);
120
121
// If the dynamic offset has changed, flush the batch.
122
//
123
// This is the only time we ever have more than one batch per
124
// bin. Note that dynamic offsets are only used on platforms
125
// with no storage buffers.
126
if !batch_set.last().is_some_and(|batch| {
127
batch.instance_range.end == instance.index
128
&& batch.extra_index
129
== PhaseItemExtraIndex::maybe_dynamic_offset(instance.dynamic_offset)
130
}) {
131
batch_set.push(BinnedRenderPhaseBatch {
132
representative_entity: (Entity::PLACEHOLDER, *main_entity),
133
instance_range: instance.index..instance.index,
134
extra_index: PhaseItemExtraIndex::maybe_dynamic_offset(
135
instance.dynamic_offset,
136
),
137
});
138
}
139
140
if let Some(batch) = batch_set.last_mut() {
141
batch.instance_range.end = instance.index + 1;
142
}
143
}
144
145
match phase.batch_sets {
146
BinnedRenderPhaseBatchSets::DynamicUniforms(ref mut batch_sets) => {
147
batch_sets.push(batch_set);
148
}
149
BinnedRenderPhaseBatchSets::Direct(_)
150
| BinnedRenderPhaseBatchSets::MultidrawIndirect { .. } => {
151
error!(
152
"Dynamic uniform batch sets should be used when GPU preprocessing is off"
153
);
154
}
155
}
156
}
157
158
// Prepare unbatchables.
159
for unbatchables in phase.unbatchable_meshes.values_mut() {
160
for main_entity in unbatchables.entities.keys() {
161
let Some(buffer_data) =
162
GFBD::get_binned_batch_data(&system_param_item, *main_entity)
163
else {
164
continue;
165
};
166
let instance = gpu_array_buffer.push(buffer_data);
167
unbatchables.buffer_indices.add(instance.into());
168
}
169
}
170
}
171
}
172
173
/// Writes the instance buffer data to the GPU.
174
pub fn write_batched_instance_buffer<GBD>(
175
render_device: Res<RenderDevice>,
176
render_queue: Res<RenderQueue>,
177
mut cpu_batched_instance_buffer: ResMut<BatchedInstanceBuffer<GBD::BufferData>>,
178
) where
179
GBD: GetBatchData,
180
{
181
cpu_batched_instance_buffer.write_buffer(&render_device, &render_queue);
182
}
183
184