Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_render/src/batching/mod.rs
9328 views
1
use bevy_ecs::{
2
component::Component,
3
entity::Entity,
4
system::{ResMut, SystemParam, SystemParamItem},
5
};
6
use bytemuck::Pod;
7
use gpu_preprocessing::UntypedPhaseIndirectParametersBuffers;
8
use nonmax::NonMaxU32;
9
10
use bevy_material::{descriptor::CachedRenderPipelineId, labels::DrawFunctionId};
11
12
use crate::{
13
render_phase::{
14
BinnedPhaseItem, CachedRenderPipelinePhaseItem, PhaseItemExtraIndex, SortedPhaseItem,
15
SortedRenderPhase, ViewBinnedRenderPhases,
16
},
17
render_resource::GpuArrayBufferable,
18
sync_world::MainEntity,
19
};
20
21
pub mod gpu_preprocessing;
22
pub mod no_gpu_preprocessing;
23
24
/// Add this component to mesh entities to disable automatic batching
25
#[derive(Component, Default, Clone, Copy)]
26
pub struct NoAutomaticBatching;
27
28
/// Data necessary to be equal for two draw commands to be mergeable
29
///
30
/// This is based on the following assumptions:
31
/// - Only entities with prepared assets (pipelines, materials, meshes) are
32
/// queued to phases
33
/// - View bindings are constant across a phase for a given draw function as
34
/// phases are per-view
35
/// - `batch_and_prepare_render_phase` is the only system that performs this
36
/// batching and has sole responsibility for preparing the per-object data.
37
/// As such the mesh binding and dynamic offsets are assumed to only be
38
/// variable as a result of the `batch_and_prepare_render_phase` system, e.g.
39
/// due to having to split data across separate uniform bindings within the
40
/// same buffer due to the maximum uniform buffer binding size.
41
#[derive(PartialEq)]
42
struct BatchMeta<T: PartialEq> {
43
/// The pipeline id encompasses all pipeline configuration including vertex
44
/// buffers and layouts, shaders and their specializations, bind group
45
/// layouts, etc.
46
pipeline_id: CachedRenderPipelineId,
47
/// The draw function id defines the `RenderCommands` that are called to
48
/// set the pipeline and bindings, and make the draw command
49
draw_function_id: DrawFunctionId,
50
dynamic_offset: Option<NonMaxU32>,
51
user_data: T,
52
}
53
54
impl<T: PartialEq> BatchMeta<T> {
55
fn new(item: &impl CachedRenderPipelinePhaseItem, user_data: T) -> Self {
56
BatchMeta {
57
pipeline_id: item.cached_pipeline(),
58
draw_function_id: item.draw_function(),
59
dynamic_offset: match item.extra_index() {
60
PhaseItemExtraIndex::DynamicOffset(dynamic_offset) => {
61
NonMaxU32::new(dynamic_offset)
62
}
63
PhaseItemExtraIndex::None | PhaseItemExtraIndex::IndirectParametersIndex { .. } => {
64
None
65
}
66
},
67
user_data,
68
}
69
}
70
}
71
72
/// A trait to support getting data used for batching draw commands via phase
73
/// items.
74
///
75
/// This is a simple version that only allows for sorting, not binning, as well
76
/// as only CPU processing, not GPU preprocessing. For these fancier features,
77
/// see [`GetFullBatchData`].
78
pub trait GetBatchData {
79
/// The system parameters [`GetBatchData::get_batch_data`] needs in
80
/// order to compute the batch data.
81
type Param: SystemParam + 'static;
82
/// Data used for comparison between phase items. If the pipeline id, draw
83
/// function id, per-instance data buffer dynamic offset and this data
84
/// matches, the draws can be batched.
85
type CompareData: PartialEq;
86
/// The per-instance data to be inserted into the
87
/// [`crate::render_resource::GpuArrayBuffer`] containing these data for all
88
/// instances.
89
type BufferData: GpuArrayBufferable + Sync + Send + 'static;
90
/// Get the per-instance data to be inserted into the
91
/// [`crate::render_resource::GpuArrayBuffer`]. If the instance can be
92
/// batched, also return the data used for comparison when deciding whether
93
/// draws can be batched, else return None for the `CompareData`.
94
///
95
/// This is only called when building instance data on CPU. In the GPU
96
/// instance data building path, we use
97
/// [`GetFullBatchData::get_index_and_compare_data`] instead.
98
fn get_batch_data(
99
param: &SystemParamItem<Self::Param>,
100
query_item: (Entity, MainEntity),
101
) -> Option<(Self::BufferData, Option<Self::CompareData>)>;
102
}
103
104
/// A trait to support getting data used for batching draw commands via phase
105
/// items.
106
///
107
/// This version allows for binning and GPU preprocessing.
108
pub trait GetFullBatchData: GetBatchData {
109
/// The per-instance data that was inserted into the
110
/// [`crate::render_resource::BufferVec`] during extraction.
111
type BufferInputData: Pod + Default + Sync + Send;
112
113
/// Get the per-instance data to be inserted into the
114
/// [`crate::render_resource::GpuArrayBuffer`].
115
///
116
/// This is only called when building uniforms on CPU. In the GPU instance
117
/// buffer building path, we use
118
/// [`GetFullBatchData::get_index_and_compare_data`] instead.
119
fn get_binned_batch_data(
120
param: &SystemParamItem<Self::Param>,
121
query_item: MainEntity,
122
) -> Option<Self::BufferData>;
123
124
/// Returns the index of the [`GetFullBatchData::BufferInputData`] that the
125
/// GPU preprocessing phase will use.
126
///
127
/// We already inserted the [`GetFullBatchData::BufferInputData`] during the
128
/// extraction phase before we got here, so this function shouldn't need to
129
/// look up any render data. If CPU instance buffer building is in use, this
130
/// function will never be called.
131
fn get_index_and_compare_data(
132
param: &SystemParamItem<Self::Param>,
133
query_item: MainEntity,
134
) -> Option<(NonMaxU32, Option<Self::CompareData>)>;
135
136
/// Returns the index of the [`GetFullBatchData::BufferInputData`] that the
137
/// GPU preprocessing phase will use.
138
///
139
/// We already inserted the [`GetFullBatchData::BufferInputData`] during the
140
/// extraction phase before we got here, so this function shouldn't need to
141
/// look up any render data.
142
///
143
/// This function is currently only called for unbatchable entities when GPU
144
/// instance buffer building is in use. For batchable entities, the uniform
145
/// index is written during queuing (e.g. in `queue_material_meshes`). In
146
/// the case of CPU instance buffer building, the CPU writes the uniforms,
147
/// so there's no index to return.
148
fn get_binned_index(
149
param: &SystemParamItem<Self::Param>,
150
query_item: MainEntity,
151
) -> Option<NonMaxU32>;
152
153
/// Writes the [`gpu_preprocessing::IndirectParametersGpuMetadata`]
154
/// necessary to draw this batch into the given metadata buffer at the given
155
/// index.
156
///
157
/// This is only used if GPU culling is enabled (which requires GPU
158
/// preprocessing).
159
///
160
/// * `indexed` is true if the mesh is indexed or false if it's non-indexed.
161
///
162
/// * `base_output_index` is the index of the first mesh instance in this
163
/// batch in the `MeshUniform` output buffer.
164
///
165
/// * `batch_set_index` is the index of the batch set in the
166
/// [`gpu_preprocessing::IndirectBatchSet`] buffer, if this batch belongs to
167
/// a batch set.
168
///
169
/// * `indirect_parameters_buffers` is the buffer in which to write the
170
/// metadata.
171
///
172
/// * `indirect_parameters_offset` is the index in that buffer at which to
173
/// write the metadata.
174
fn write_batch_indirect_parameters_metadata(
175
indexed: bool,
176
base_output_index: u32,
177
batch_set_index: Option<NonMaxU32>,
178
indirect_parameters_buffers: &mut UntypedPhaseIndirectParametersBuffers,
179
indirect_parameters_offset: u32,
180
);
181
}
182
183
/// Sorts a render phase that uses bins.
184
pub fn sort_binned_render_phase<BPI>(mut phases: ResMut<ViewBinnedRenderPhases<BPI>>)
185
where
186
BPI: BinnedPhaseItem,
187
{
188
for phase in phases.values_mut() {
189
phase.multidrawable_meshes.sort_unstable_keys();
190
phase.batchable_meshes.sort_unstable_keys();
191
phase.unbatchable_meshes.sort_unstable_keys();
192
phase.non_mesh_items.sort_unstable_keys();
193
}
194
}
195
196
/// Batches the items in a sorted render phase.
197
///
198
/// This means comparing metadata needed to draw each phase item and trying to
199
/// combine the draws into a batch.
200
///
201
/// This is common code factored out from
202
/// [`gpu_preprocessing::batch_and_prepare_sorted_render_phase`] and
203
/// [`no_gpu_preprocessing::batch_and_prepare_sorted_render_phase`].
204
fn batch_and_prepare_sorted_render_phase<I, GBD>(
205
phase: &mut SortedRenderPhase<I>,
206
mut process_item: impl FnMut(&mut I) -> Option<GBD::CompareData>,
207
) where
208
I: CachedRenderPipelinePhaseItem + SortedPhaseItem,
209
GBD: GetBatchData,
210
{
211
let items = phase.items.iter_mut().map(|item| {
212
let batch_data = match process_item(item) {
213
Some(compare_data) if I::AUTOMATIC_BATCHING => Some(BatchMeta::new(item, compare_data)),
214
_ => None,
215
};
216
(item.batch_range_mut(), batch_data)
217
});
218
219
items.reduce(|(start_range, prev_batch_meta), (range, batch_meta)| {
220
if batch_meta.is_some() && prev_batch_meta == batch_meta {
221
start_range.end = range.end;
222
(start_range, prev_batch_meta)
223
} else {
224
(range, batch_meta)
225
}
226
});
227
}
228
229