Skip to content

Commit 4f1d9a6

Browse files
james-j-obriensuperdumprobtfm
authored
Reorder render sets, refactor bevy_sprite to take advantage (#9236)
This is a continuation of this PR: #8062 # Objective - Reorder render schedule sets to allow data preparation when phase item order is known to support improved batching - Part of the batching/instancing etc plan from here: #89 (comment) - The original idea came from @inodentry and proved to be a good one. Thanks! - Refactor `bevy_sprite` and `bevy_ui` to take advantage of the new ordering ## Solution - Move `Prepare` and `PrepareFlush` after `PhaseSortFlush` - Add a `PrepareAssets` set that runs in parallel with other systems and sets in the render schedule. - Put prepare_assets systems in the `PrepareAssets` set - If explicit dependencies are needed on Mesh or Material RenderAssets then depend on the appropriate system. - Add `ManageViews` and `ManageViewsFlush` sets between `ExtractCommands` and Queue - Move `queue_mesh*_bind_group` to the Prepare stage - Rename them to `prepare_` - Put systems that prepare resources (buffers, textures, etc.) into a `PrepareResources` set inside `Prepare` - Put the `prepare_..._bind_group` systems into a `PrepareBindGroup` set after `PrepareResources` - Move `prepare_lights` to the `ManageViews` set - `prepare_lights` creates views and this must happen before `Queue` - This system needs refactoring to stop handling all responsibilities - Gather lights, sort, and create shadow map views. Store sorted light entities in a resource - Remove `BatchedPhaseItem` - Replace `batch_range` with `batch_size` representing how many items to skip after rendering the item or to skip the item entirely if `batch_size` is 0. - `queue_sprites` has been split into `queue_sprites` for queueing phase items and `prepare_sprites` for batching after the `PhaseSort` - `PhaseItem`s are still inserted in `queue_sprites` - After sorting adjacent compatible sprite phase items are accumulated into `SpriteBatch` components on the first entity of each batch, containing a range of vertex indices. The associated `PhaseItem`'s `batch_size` is updated appropriately. - `SpriteBatch` items are then drawn skipping over the other items in the batch based on the value in `batch_size` - A very similar refactor was performed on `bevy_ui` --- ## Changelog Changed: - Reordered and reworked render app schedule sets. The main change is that data is extracted, queued, sorted, and then prepared when the order of data is known. - Refactor `bevy_sprite` and `bevy_ui` to take advantage of the reordering. ## Migration Guide - Assets such as materials and meshes should now be created in `PrepareAssets` e.g. `prepare_assets<Mesh>` - Queueing entities to `RenderPhase`s continues to be done in `Queue` e.g. `queue_sprites` - Preparing resources (textures, buffers, etc.) should now be done in `PrepareResources`, e.g. `prepare_prepass_textures`, `prepare_mesh_uniforms` - Prepare bind groups should now be done in `PrepareBindGroups` e.g. `prepare_mesh_bind_group` - Any batching or instancing can now be done in `Prepare` where the order of the phase items is known e.g. `prepare_sprites` ## Next Steps - Introduce some generic mechanism to ensure items that can be batched are grouped in the phase item order, currently you could easily have `[sprite at z 0, mesh at z 0, sprite at z 0]` preventing batching. - Investigate improved orderings for building the MeshUniform buffer - Implementing batching across the rest of bevy --------- Co-authored-by: Robert Swain <robert.swain@gmail.com> Co-authored-by: robtfm <50659922+robtfm@users.noreply.github.com>
1 parent e8b3892 commit 4f1d9a6

File tree

42 files changed

+989
-1136
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+989
-1136
lines changed

crates/bevy_core_pipeline/src/bloom/mod.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,10 @@ impl Plugin for BloomPlugin {
6868
.add_systems(
6969
Render,
7070
(
71-
prepare_bloom_textures.in_set(RenderSet::Prepare),
7271
prepare_downsampling_pipeline.in_set(RenderSet::Prepare),
7372
prepare_upsampling_pipeline.in_set(RenderSet::Prepare),
74-
queue_bloom_bind_groups.in_set(RenderSet::Queue),
73+
prepare_bloom_textures.in_set(RenderSet::PrepareResources),
74+
prepare_bloom_bind_groups.in_set(RenderSet::PrepareBindGroups),
7575
),
7676
)
7777
// Add bloom to the 3d render graph
@@ -403,7 +403,7 @@ struct BloomBindGroups {
403403
sampler: Sampler,
404404
}
405405

406-
fn queue_bloom_bind_groups(
406+
fn prepare_bloom_bind_groups(
407407
mut commands: Commands,
408408
render_device: Res<RenderDevice>,
409409
downsampling_pipeline: Res<BloomDownsamplingPipeline>,

crates/bevy_core_pipeline/src/core_2d/mod.rs

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,13 @@ use bevy_render::{
2929
extract_component::ExtractComponentPlugin,
3030
render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner},
3131
render_phase::{
32-
batch_phase_system, sort_phase_system, BatchedPhaseItem, CachedRenderPipelinePhaseItem,
33-
DrawFunctionId, DrawFunctions, PhaseItem, RenderPhase,
32+
sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem,
33+
RenderPhase,
3434
},
3535
render_resource::CachedRenderPipelineId,
3636
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
3737
};
3838
use bevy_utils::FloatOrd;
39-
use std::ops::Range;
4039

4140
use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode};
4241

@@ -57,12 +56,7 @@ impl Plugin for Core2dPlugin {
5756
.add_systems(ExtractSchedule, extract_core_2d_camera_phases)
5857
.add_systems(
5958
Render,
60-
(
61-
sort_phase_system::<Transparent2d>.in_set(RenderSet::PhaseSort),
62-
batch_phase_system::<Transparent2d>
63-
.after(sort_phase_system::<Transparent2d>)
64-
.in_set(RenderSet::PhaseSort),
65-
),
59+
sort_phase_system::<Transparent2d>.in_set(RenderSet::PhaseSort),
6660
);
6761

6862
use graph::node::*;
@@ -89,8 +83,7 @@ pub struct Transparent2d {
8983
pub entity: Entity,
9084
pub pipeline: CachedRenderPipelineId,
9185
pub draw_function: DrawFunctionId,
92-
/// Range in the vertex buffer of this item
93-
pub batch_range: Option<Range<u32>>,
86+
pub batch_size: usize,
9487
}
9588

9689
impl PhaseItem for Transparent2d {
@@ -115,6 +108,11 @@ impl PhaseItem for Transparent2d {
115108
fn sort(items: &mut [Self]) {
116109
items.sort_by_key(|item| item.sort_key());
117110
}
111+
112+
#[inline]
113+
fn batch_size(&self) -> usize {
114+
self.batch_size
115+
}
118116
}
119117

120118
impl CachedRenderPipelinePhaseItem for Transparent2d {
@@ -124,16 +122,6 @@ impl CachedRenderPipelinePhaseItem for Transparent2d {
124122
}
125123
}
126124

127-
impl BatchedPhaseItem for Transparent2d {
128-
fn batch_range(&self) -> &Option<Range<u32>> {
129-
&self.batch_range
130-
}
131-
132-
fn batch_range_mut(&mut self) -> &mut Option<Range<u32>> {
133-
&mut self.batch_range
134-
}
135-
}
136-
137125
pub fn extract_core_2d_camera_phases(
138126
mut commands: Commands,
139127
cameras_2d: Extract<Query<(Entity, &Camera), With<Camera2d>>>,

crates/bevy_core_pipeline/src/core_3d/mod.rs

Lines changed: 26 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -87,17 +87,13 @@ impl Plugin for Core3dPlugin {
8787
.add_systems(
8888
Render,
8989
(
90-
prepare_core_3d_depth_textures
91-
.in_set(RenderSet::Prepare)
92-
.after(bevy_render::view::prepare_windows),
93-
prepare_prepass_textures
94-
.in_set(RenderSet::Prepare)
95-
.after(bevy_render::view::prepare_windows),
9690
sort_phase_system::<Opaque3d>.in_set(RenderSet::PhaseSort),
9791
sort_phase_system::<AlphaMask3d>.in_set(RenderSet::PhaseSort),
9892
sort_phase_system::<Transparent3d>.in_set(RenderSet::PhaseSort),
9993
sort_phase_system::<Opaque3dPrepass>.in_set(RenderSet::PhaseSort),
10094
sort_phase_system::<AlphaMask3dPrepass>.in_set(RenderSet::PhaseSort),
95+
prepare_core_3d_depth_textures.in_set(RenderSet::PrepareResources),
96+
prepare_prepass_textures.in_set(RenderSet::PrepareResources),
10197
),
10298
);
10399

@@ -136,18 +132,15 @@ impl Plugin for Core3dPlugin {
136132

137133
pub struct Opaque3d {
138134
pub distance: f32,
139-
// Per-object data may be bound at different dynamic offsets within a buffer. If it is, then
140-
// each batch of per-object data starts at the same dynamic offset.
141-
pub per_object_binding_dynamic_offset: u32,
142135
pub pipeline: CachedRenderPipelineId,
143136
pub entity: Entity,
144137
pub draw_function: DrawFunctionId,
138+
pub batch_size: usize,
145139
}
146140

147141
impl PhaseItem for Opaque3d {
148-
// NOTE: (dynamic offset, -distance)
149142
// NOTE: Values increase towards the camera. Front-to-back ordering for opaque means we need a descending sort.
150-
type SortKey = (u32, Reverse<FloatOrd>);
143+
type SortKey = Reverse<FloatOrd>;
151144

152145
#[inline]
153146
fn entity(&self) -> Entity {
@@ -156,10 +149,7 @@ impl PhaseItem for Opaque3d {
156149

157150
#[inline]
158151
fn sort_key(&self) -> Self::SortKey {
159-
(
160-
self.per_object_binding_dynamic_offset,
161-
Reverse(FloatOrd(self.distance)),
162-
)
152+
Reverse(FloatOrd(self.distance))
163153
}
164154

165155
#[inline]
@@ -170,9 +160,12 @@ impl PhaseItem for Opaque3d {
170160
#[inline]
171161
fn sort(items: &mut [Self]) {
172162
// Key negated to match reversed SortKey ordering
173-
radsort::sort_by_key(items, |item| {
174-
(item.per_object_binding_dynamic_offset, -item.distance)
175-
});
163+
radsort::sort_by_key(items, |item| -item.distance);
164+
}
165+
166+
#[inline]
167+
fn batch_size(&self) -> usize {
168+
self.batch_size
176169
}
177170
}
178171

@@ -185,18 +178,15 @@ impl CachedRenderPipelinePhaseItem for Opaque3d {
185178

186179
pub struct AlphaMask3d {
187180
pub distance: f32,
188-
// Per-object data may be bound at different dynamic offsets within a buffer. If it is, then
189-
// each batch of per-object data starts at the same dynamic offset.
190-
pub per_object_binding_dynamic_offset: u32,
191181
pub pipeline: CachedRenderPipelineId,
192182
pub entity: Entity,
193183
pub draw_function: DrawFunctionId,
184+
pub batch_size: usize,
194185
}
195186

196187
impl PhaseItem for AlphaMask3d {
197-
// NOTE: (dynamic offset, -distance)
198188
// NOTE: Values increase towards the camera. Front-to-back ordering for alpha mask means we need a descending sort.
199-
type SortKey = (u32, Reverse<FloatOrd>);
189+
type SortKey = Reverse<FloatOrd>;
200190

201191
#[inline]
202192
fn entity(&self) -> Entity {
@@ -205,10 +195,7 @@ impl PhaseItem for AlphaMask3d {
205195

206196
#[inline]
207197
fn sort_key(&self) -> Self::SortKey {
208-
(
209-
self.per_object_binding_dynamic_offset,
210-
Reverse(FloatOrd(self.distance)),
211-
)
198+
Reverse(FloatOrd(self.distance))
212199
}
213200

214201
#[inline]
@@ -219,9 +206,12 @@ impl PhaseItem for AlphaMask3d {
219206
#[inline]
220207
fn sort(items: &mut [Self]) {
221208
// Key negated to match reversed SortKey ordering
222-
radsort::sort_by_key(items, |item| {
223-
(item.per_object_binding_dynamic_offset, -item.distance)
224-
});
209+
radsort::sort_by_key(items, |item| -item.distance);
210+
}
211+
212+
#[inline]
213+
fn batch_size(&self) -> usize {
214+
self.batch_size
225215
}
226216
}
227217

@@ -237,6 +227,7 @@ pub struct Transparent3d {
237227
pub pipeline: CachedRenderPipelineId,
238228
pub entity: Entity,
239229
pub draw_function: DrawFunctionId,
230+
pub batch_size: usize,
240231
}
241232

242233
impl PhaseItem for Transparent3d {
@@ -262,6 +253,11 @@ impl PhaseItem for Transparent3d {
262253
fn sort(items: &mut [Self]) {
263254
radsort::sort_by_key(items, |item| item.distance);
264255
}
256+
257+
#[inline]
258+
fn batch_size(&self) -> usize {
259+
self.batch_size
260+
}
265261
}
266262

267263
impl CachedRenderPipelinePhaseItem for Transparent3d {

crates/bevy_core_pipeline/src/msaa_writeback.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ impl Plugin for MsaaWritebackPlugin {
2626

2727
render_app.add_systems(
2828
Render,
29-
queue_msaa_writeback_pipelines.in_set(RenderSet::Queue),
29+
prepare_msaa_writeback_pipelines.in_set(RenderSet::Prepare),
3030
);
3131
{
3232
use core_2d::graph::node::*;
@@ -123,7 +123,7 @@ impl Node for MsaaWritebackNode {
123123
#[derive(Component)]
124124
pub struct MsaaWritebackBlitPipeline(CachedRenderPipelineId);
125125

126-
fn queue_msaa_writeback_pipelines(
126+
fn prepare_msaa_writeback_pipelines(
127127
mut commands: Commands,
128128
pipeline_cache: Res<PipelineCache>,
129129
mut pipelines: ResMut<SpecializedRenderPipelines<BlitPipeline>>,

crates/bevy_core_pipeline/src/prepass/mod.rs

Lines changed: 6 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -80,18 +80,14 @@ pub struct ViewPrepassTextures {
8080
/// Used to render all 3D meshes with materials that have no transparency.
8181
pub struct Opaque3dPrepass {
8282
pub distance: f32,
83-
// Per-object data may be bound at different dynamic offsets within a buffer. If it is, then
84-
// each batch of per-object data starts at the same dynamic offset.
85-
pub per_object_binding_dynamic_offset: u32,
8683
pub entity: Entity,
8784
pub pipeline_id: CachedRenderPipelineId,
8885
pub draw_function: DrawFunctionId,
8986
}
9087

9188
impl PhaseItem for Opaque3dPrepass {
92-
// NOTE: (dynamic offset, -distance)
9389
// NOTE: Values increase towards the camera. Front-to-back ordering for opaque means we need a descending sort.
94-
type SortKey = (u32, Reverse<FloatOrd>);
90+
type SortKey = Reverse<FloatOrd>;
9591

9692
#[inline]
9793
fn entity(&self) -> Entity {
@@ -100,10 +96,7 @@ impl PhaseItem for Opaque3dPrepass {
10096

10197
#[inline]
10298
fn sort_key(&self) -> Self::SortKey {
103-
(
104-
self.per_object_binding_dynamic_offset,
105-
Reverse(FloatOrd(self.distance)),
106-
)
99+
Reverse(FloatOrd(self.distance))
107100
}
108101

109102
#[inline]
@@ -114,9 +107,7 @@ impl PhaseItem for Opaque3dPrepass {
114107
#[inline]
115108
fn sort(items: &mut [Self]) {
116109
// Key negated to match reversed SortKey ordering
117-
radsort::sort_by_key(items, |item| {
118-
(item.per_object_binding_dynamic_offset, -item.distance)
119-
});
110+
radsort::sort_by_key(items, |item| -item.distance);
120111
}
121112
}
122113

@@ -134,18 +125,14 @@ impl CachedRenderPipelinePhaseItem for Opaque3dPrepass {
134125
/// Used to render all meshes with a material with an alpha mask.
135126
pub struct AlphaMask3dPrepass {
136127
pub distance: f32,
137-
// Per-object data may be bound at different dynamic offsets within a buffer. If it is, then
138-
// each batch of per-object data starts at the same dynamic offset.
139-
pub per_object_binding_dynamic_offset: u32,
140128
pub entity: Entity,
141129
pub pipeline_id: CachedRenderPipelineId,
142130
pub draw_function: DrawFunctionId,
143131
}
144132

145133
impl PhaseItem for AlphaMask3dPrepass {
146-
// NOTE: (dynamic offset, -distance)
147134
// NOTE: Values increase towards the camera. Front-to-back ordering for opaque means we need a descending sort.
148-
type SortKey = (u32, Reverse<FloatOrd>);
135+
type SortKey = Reverse<FloatOrd>;
149136

150137
#[inline]
151138
fn entity(&self) -> Entity {
@@ -154,10 +141,7 @@ impl PhaseItem for AlphaMask3dPrepass {
154141

155142
#[inline]
156143
fn sort_key(&self) -> Self::SortKey {
157-
(
158-
self.per_object_binding_dynamic_offset,
159-
Reverse(FloatOrd(self.distance)),
160-
)
144+
Reverse(FloatOrd(self.distance))
161145
}
162146

163147
#[inline]
@@ -168,9 +152,7 @@ impl PhaseItem for AlphaMask3dPrepass {
168152
#[inline]
169153
fn sort(items: &mut [Self]) {
170154
// Key negated to match reversed SortKey ordering
171-
radsort::sort_by_key(items, |item| {
172-
(item.per_object_binding_dynamic_offset, -item.distance)
173-
});
155+
radsort::sort_by_key(items, |item| -item.distance);
174156
}
175157
}
176158

crates/bevy_core_pipeline/src/skybox/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ impl Plugin for SkyboxPlugin {
4747
Render,
4848
(
4949
prepare_skybox_pipelines.in_set(RenderSet::Prepare),
50-
queue_skybox_bind_groups.in_set(RenderSet::Queue),
50+
prepare_skybox_bind_groups.in_set(RenderSet::PrepareBindGroups),
5151
),
5252
);
5353
}
@@ -209,7 +209,7 @@ fn prepare_skybox_pipelines(
209209
#[derive(Component)]
210210
pub struct SkyboxBindGroup(pub BindGroup);
211211

212-
fn queue_skybox_bind_groups(
212+
fn prepare_skybox_bind_groups(
213213
mut commands: Commands,
214214
pipeline: Res<SkyboxPipeline>,
215215
view_uniforms: Res<ViewUniforms>,

crates/bevy_core_pipeline/src/taa/mod.rs

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use bevy_core::FrameCount;
1010
use bevy_ecs::{
1111
prelude::{Bundle, Component, Entity},
1212
query::{QueryItem, With},
13-
schedule::{apply_deferred, IntoSystemConfigs},
13+
schedule::IntoSystemConfigs,
1414
system::{Commands, Query, Res, ResMut, Resource},
1515
world::{FromWorld, World},
1616
};
@@ -31,7 +31,7 @@ use bevy_render::{
3131
},
3232
renderer::{RenderContext, RenderDevice},
3333
texture::{BevyDefault, CachedTexture, TextureCache},
34-
view::{prepare_view_uniforms, ExtractedView, Msaa, ViewTarget},
34+
view::{ExtractedView, Msaa, ViewTarget},
3535
ExtractSchedule, MainWorld, Render, RenderApp, RenderSet,
3636
};
3737

@@ -67,12 +67,9 @@ impl Plugin for TemporalAntiAliasPlugin {
6767
.add_systems(
6868
Render,
6969
(
70-
(prepare_taa_jitter_and_mip_bias, apply_deferred)
71-
.chain()
72-
.before(prepare_view_uniforms)
73-
.in_set(RenderSet::Prepare),
74-
prepare_taa_history_textures.in_set(RenderSet::Prepare),
70+
prepare_taa_jitter_and_mip_bias.in_set(RenderSet::ManageViews),
7571
prepare_taa_pipelines.in_set(RenderSet::Prepare),
72+
prepare_taa_history_textures.in_set(RenderSet::PrepareResources),
7673
),
7774
)
7875
.add_render_graph_node::<ViewNodeRunner<TAANode>>(CORE_3D, draw_3d_graph::node::TAA)

0 commit comments

Comments
 (0)