Skip to content

Commit

Permalink
Reorder render sets, refactor bevy_sprite to take advantage (#9236)
Browse files Browse the repository at this point in the history
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 <[email protected]>
Co-authored-by: robtfm <[email protected]>
  • Loading branch information
3 people authored Aug 27, 2023
1 parent e8b3892 commit 4f1d9a6
Show file tree
Hide file tree
Showing 42 changed files with 989 additions and 1,136 deletions.
6 changes: 3 additions & 3 deletions crates/bevy_core_pipeline/src/bloom/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ impl Plugin for BloomPlugin {
.add_systems(
Render,
(
prepare_bloom_textures.in_set(RenderSet::Prepare),
prepare_downsampling_pipeline.in_set(RenderSet::Prepare),
prepare_upsampling_pipeline.in_set(RenderSet::Prepare),
queue_bloom_bind_groups.in_set(RenderSet::Queue),
prepare_bloom_textures.in_set(RenderSet::PrepareResources),
prepare_bloom_bind_groups.in_set(RenderSet::PrepareBindGroups),
),
)
// Add bloom to the 3d render graph
Expand Down Expand Up @@ -403,7 +403,7 @@ struct BloomBindGroups {
sampler: Sampler,
}

fn queue_bloom_bind_groups(
fn prepare_bloom_bind_groups(
mut commands: Commands,
render_device: Res<RenderDevice>,
downsampling_pipeline: Res<BloomDownsamplingPipeline>,
Expand Down
30 changes: 9 additions & 21 deletions crates/bevy_core_pipeline/src/core_2d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,13 @@ use bevy_render::{
extract_component::ExtractComponentPlugin,
render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner},
render_phase::{
batch_phase_system, sort_phase_system, BatchedPhaseItem, CachedRenderPipelinePhaseItem,
DrawFunctionId, DrawFunctions, PhaseItem, RenderPhase,
sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem,
RenderPhase,
},
render_resource::CachedRenderPipelineId,
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
};
use bevy_utils::FloatOrd;
use std::ops::Range;

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

Expand All @@ -57,12 +56,7 @@ impl Plugin for Core2dPlugin {
.add_systems(ExtractSchedule, extract_core_2d_camera_phases)
.add_systems(
Render,
(
sort_phase_system::<Transparent2d>.in_set(RenderSet::PhaseSort),
batch_phase_system::<Transparent2d>
.after(sort_phase_system::<Transparent2d>)
.in_set(RenderSet::PhaseSort),
),
sort_phase_system::<Transparent2d>.in_set(RenderSet::PhaseSort),
);

use graph::node::*;
Expand All @@ -89,8 +83,7 @@ pub struct Transparent2d {
pub entity: Entity,
pub pipeline: CachedRenderPipelineId,
pub draw_function: DrawFunctionId,
/// Range in the vertex buffer of this item
pub batch_range: Option<Range<u32>>,
pub batch_size: usize,
}

impl PhaseItem for Transparent2d {
Expand All @@ -115,6 +108,11 @@ impl PhaseItem for Transparent2d {
fn sort(items: &mut [Self]) {
items.sort_by_key(|item| item.sort_key());
}

#[inline]
fn batch_size(&self) -> usize {
self.batch_size
}
}

impl CachedRenderPipelinePhaseItem for Transparent2d {
Expand All @@ -124,16 +122,6 @@ impl CachedRenderPipelinePhaseItem for Transparent2d {
}
}

impl BatchedPhaseItem for Transparent2d {
fn batch_range(&self) -> &Option<Range<u32>> {
&self.batch_range
}

fn batch_range_mut(&mut self) -> &mut Option<Range<u32>> {
&mut self.batch_range
}
}

pub fn extract_core_2d_camera_phases(
mut commands: Commands,
cameras_2d: Extract<Query<(Entity, &Camera), With<Camera2d>>>,
Expand Down
56 changes: 26 additions & 30 deletions crates/bevy_core_pipeline/src/core_3d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,17 +87,13 @@ impl Plugin for Core3dPlugin {
.add_systems(
Render,
(
prepare_core_3d_depth_textures
.in_set(RenderSet::Prepare)
.after(bevy_render::view::prepare_windows),
prepare_prepass_textures
.in_set(RenderSet::Prepare)
.after(bevy_render::view::prepare_windows),
sort_phase_system::<Opaque3d>.in_set(RenderSet::PhaseSort),
sort_phase_system::<AlphaMask3d>.in_set(RenderSet::PhaseSort),
sort_phase_system::<Transparent3d>.in_set(RenderSet::PhaseSort),
sort_phase_system::<Opaque3dPrepass>.in_set(RenderSet::PhaseSort),
sort_phase_system::<AlphaMask3dPrepass>.in_set(RenderSet::PhaseSort),
prepare_core_3d_depth_textures.in_set(RenderSet::PrepareResources),
prepare_prepass_textures.in_set(RenderSet::PrepareResources),
),
);

Expand Down Expand Up @@ -136,18 +132,15 @@ impl Plugin for Core3dPlugin {

pub struct Opaque3d {
pub distance: f32,
// Per-object data may be bound at different dynamic offsets within a buffer. If it is, then
// each batch of per-object data starts at the same dynamic offset.
pub per_object_binding_dynamic_offset: u32,
pub pipeline: CachedRenderPipelineId,
pub entity: Entity,
pub draw_function: DrawFunctionId,
pub batch_size: usize,
}

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

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

#[inline]
fn sort_key(&self) -> Self::SortKey {
(
self.per_object_binding_dynamic_offset,
Reverse(FloatOrd(self.distance)),
)
Reverse(FloatOrd(self.distance))
}

#[inline]
Expand All @@ -170,9 +160,12 @@ impl PhaseItem for Opaque3d {
#[inline]
fn sort(items: &mut [Self]) {
// Key negated to match reversed SortKey ordering
radsort::sort_by_key(items, |item| {
(item.per_object_binding_dynamic_offset, -item.distance)
});
radsort::sort_by_key(items, |item| -item.distance);
}

#[inline]
fn batch_size(&self) -> usize {
self.batch_size
}
}

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

pub struct AlphaMask3d {
pub distance: f32,
// Per-object data may be bound at different dynamic offsets within a buffer. If it is, then
// each batch of per-object data starts at the same dynamic offset.
pub per_object_binding_dynamic_offset: u32,
pub pipeline: CachedRenderPipelineId,
pub entity: Entity,
pub draw_function: DrawFunctionId,
pub batch_size: usize,
}

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

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

#[inline]
fn sort_key(&self) -> Self::SortKey {
(
self.per_object_binding_dynamic_offset,
Reverse(FloatOrd(self.distance)),
)
Reverse(FloatOrd(self.distance))
}

#[inline]
Expand All @@ -219,9 +206,12 @@ impl PhaseItem for AlphaMask3d {
#[inline]
fn sort(items: &mut [Self]) {
// Key negated to match reversed SortKey ordering
radsort::sort_by_key(items, |item| {
(item.per_object_binding_dynamic_offset, -item.distance)
});
radsort::sort_by_key(items, |item| -item.distance);
}

#[inline]
fn batch_size(&self) -> usize {
self.batch_size
}
}

Expand All @@ -237,6 +227,7 @@ pub struct Transparent3d {
pub pipeline: CachedRenderPipelineId,
pub entity: Entity,
pub draw_function: DrawFunctionId,
pub batch_size: usize,
}

impl PhaseItem for Transparent3d {
Expand All @@ -262,6 +253,11 @@ impl PhaseItem for Transparent3d {
fn sort(items: &mut [Self]) {
radsort::sort_by_key(items, |item| item.distance);
}

#[inline]
fn batch_size(&self) -> usize {
self.batch_size
}
}

impl CachedRenderPipelinePhaseItem for Transparent3d {
Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_core_pipeline/src/msaa_writeback.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ impl Plugin for MsaaWritebackPlugin {

render_app.add_systems(
Render,
queue_msaa_writeback_pipelines.in_set(RenderSet::Queue),
prepare_msaa_writeback_pipelines.in_set(RenderSet::Prepare),
);
{
use core_2d::graph::node::*;
Expand Down Expand Up @@ -123,7 +123,7 @@ impl Node for MsaaWritebackNode {
#[derive(Component)]
pub struct MsaaWritebackBlitPipeline(CachedRenderPipelineId);

fn queue_msaa_writeback_pipelines(
fn prepare_msaa_writeback_pipelines(
mut commands: Commands,
pipeline_cache: Res<PipelineCache>,
mut pipelines: ResMut<SpecializedRenderPipelines<BlitPipeline>>,
Expand Down
30 changes: 6 additions & 24 deletions crates/bevy_core_pipeline/src/prepass/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -80,18 +80,14 @@ pub struct ViewPrepassTextures {
/// Used to render all 3D meshes with materials that have no transparency.
pub struct Opaque3dPrepass {
pub distance: f32,
// Per-object data may be bound at different dynamic offsets within a buffer. If it is, then
// each batch of per-object data starts at the same dynamic offset.
pub per_object_binding_dynamic_offset: u32,
pub entity: Entity,
pub pipeline_id: CachedRenderPipelineId,
pub draw_function: DrawFunctionId,
}

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

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

#[inline]
fn sort_key(&self) -> Self::SortKey {
(
self.per_object_binding_dynamic_offset,
Reverse(FloatOrd(self.distance)),
)
Reverse(FloatOrd(self.distance))
}

#[inline]
Expand All @@ -114,9 +107,7 @@ impl PhaseItem for Opaque3dPrepass {
#[inline]
fn sort(items: &mut [Self]) {
// Key negated to match reversed SortKey ordering
radsort::sort_by_key(items, |item| {
(item.per_object_binding_dynamic_offset, -item.distance)
});
radsort::sort_by_key(items, |item| -item.distance);
}
}

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

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

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

#[inline]
fn sort_key(&self) -> Self::SortKey {
(
self.per_object_binding_dynamic_offset,
Reverse(FloatOrd(self.distance)),
)
Reverse(FloatOrd(self.distance))
}

#[inline]
Expand All @@ -168,9 +152,7 @@ impl PhaseItem for AlphaMask3dPrepass {
#[inline]
fn sort(items: &mut [Self]) {
// Key negated to match reversed SortKey ordering
radsort::sort_by_key(items, |item| {
(item.per_object_binding_dynamic_offset, -item.distance)
});
radsort::sort_by_key(items, |item| -item.distance);
}
}

Expand Down
4 changes: 2 additions & 2 deletions crates/bevy_core_pipeline/src/skybox/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ impl Plugin for SkyboxPlugin {
Render,
(
prepare_skybox_pipelines.in_set(RenderSet::Prepare),
queue_skybox_bind_groups.in_set(RenderSet::Queue),
prepare_skybox_bind_groups.in_set(RenderSet::PrepareBindGroups),
),
);
}
Expand Down Expand Up @@ -209,7 +209,7 @@ fn prepare_skybox_pipelines(
#[derive(Component)]
pub struct SkyboxBindGroup(pub BindGroup);

fn queue_skybox_bind_groups(
fn prepare_skybox_bind_groups(
mut commands: Commands,
pipeline: Res<SkyboxPipeline>,
view_uniforms: Res<ViewUniforms>,
Expand Down
11 changes: 4 additions & 7 deletions crates/bevy_core_pipeline/src/taa/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use bevy_core::FrameCount;
use bevy_ecs::{
prelude::{Bundle, Component, Entity},
query::{QueryItem, With},
schedule::{apply_deferred, IntoSystemConfigs},
schedule::IntoSystemConfigs,
system::{Commands, Query, Res, ResMut, Resource},
world::{FromWorld, World},
};
Expand All @@ -31,7 +31,7 @@ use bevy_render::{
},
renderer::{RenderContext, RenderDevice},
texture::{BevyDefault, CachedTexture, TextureCache},
view::{prepare_view_uniforms, ExtractedView, Msaa, ViewTarget},
view::{ExtractedView, Msaa, ViewTarget},
ExtractSchedule, MainWorld, Render, RenderApp, RenderSet,
};

Expand Down Expand Up @@ -67,12 +67,9 @@ impl Plugin for TemporalAntiAliasPlugin {
.add_systems(
Render,
(
(prepare_taa_jitter_and_mip_bias, apply_deferred)
.chain()
.before(prepare_view_uniforms)
.in_set(RenderSet::Prepare),
prepare_taa_history_textures.in_set(RenderSet::Prepare),
prepare_taa_jitter_and_mip_bias.in_set(RenderSet::ManageViews),
prepare_taa_pipelines.in_set(RenderSet::Prepare),
prepare_taa_history_textures.in_set(RenderSet::PrepareResources),
),
)
.add_render_graph_node::<ViewNodeRunner<TAANode>>(CORE_3D, draw_3d_graph::node::TAA)
Expand Down
Loading

0 comments on commit 4f1d9a6

Please sign in to comment.