Skip to content

Commit 2d727af

Browse files
committed
Flatten render commands (#6885)
# Objective Speed up the render phase of rendering. Simplify the trait structure for render commands. ## Solution - Merge `EntityPhaseItem` into `PhaseItem` (`EntityPhaseItem::entity` -> `PhaseItem::entity`) - Merge `EntityRenderCommand` into `RenderCommand`. - Add two associated types to `RenderCommand`: `RenderCommand::ViewWorldQuery` and `RenderCommand::WorldQuery`. - Use the new associated types to construct two `QueryStates`s for `RenderCommandState`. - Hoist any `SQuery<T>` fetches in `EntityRenderCommand`s into the aformentioned two queries. Batch fetch them all at once. ## Performance `main_opaque_pass_3d` is slightly faster on `many_foxes` (427.52us -> 401.15us) ![image](https://user-images.githubusercontent.com/3137680/206359804-9928b20a-7d92-41f8-bf7d-6e8c5cc802f0.png) The shadow pass node is also slightly faster (344.52 -> 338.24us) ![image](https://user-images.githubusercontent.com/3137680/206359977-1212198d-f933-49a0-80f1-62ff88eb5727.png) ## Future Work - Can we hoist the view level queries out of the core loop? --- ## Changelog Added: `PhaseItem::entity` Added: `RenderCommand::ViewWorldQuery` associated type. Added: `RenderCommand::ItemorldQuery` associated type. Added: `Draw<T>::prepare` optional trait function. Removed: `EntityPhaseItem` trait ## Migration Guide TODO
1 parent f866d72 commit 2d727af

File tree

12 files changed

+242
-217
lines changed

12 files changed

+242
-217
lines changed

crates/bevy_core_pipeline/src/core_2d/mod.rs

+6-8
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ use bevy_render::{
2727
render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
2828
render_phase::{
2929
batch_phase_system, sort_phase_system, BatchedPhaseItem, CachedRenderPipelinePhaseItem,
30-
DrawFunctionId, DrawFunctions, EntityPhaseItem, PhaseItem, RenderPhase,
30+
DrawFunctionId, DrawFunctions, PhaseItem, RenderPhase,
3131
},
3232
render_resource::CachedRenderPipelineId,
3333
Extract, RenderApp, RenderStage,
@@ -115,6 +115,11 @@ pub struct Transparent2d {
115115
impl PhaseItem for Transparent2d {
116116
type SortKey = FloatOrd;
117117

118+
#[inline]
119+
fn entity(&self) -> Entity {
120+
self.entity
121+
}
122+
118123
#[inline]
119124
fn sort_key(&self) -> Self::SortKey {
120125
self.sort_key
@@ -131,13 +136,6 @@ impl PhaseItem for Transparent2d {
131136
}
132137
}
133138

134-
impl EntityPhaseItem for Transparent2d {
135-
#[inline]
136-
fn entity(&self) -> Entity {
137-
self.entity
138-
}
139-
}
140-
141139
impl CachedRenderPipelinePhaseItem for Transparent2d {
142140
#[inline]
143141
fn cached_pipeline(&self) -> CachedRenderPipelineId {

crates/bevy_core_pipeline/src/core_3d/mod.rs

+17-23
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ use bevy_render::{
2929
prelude::Msaa,
3030
render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
3131
render_phase::{
32-
sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions,
33-
EntityPhaseItem, PhaseItem, RenderPhase,
32+
sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem,
33+
RenderPhase,
3434
},
3535
render_resource::{
3636
CachedRenderPipelineId, Extent3d, TextureDescriptor, TextureDimension, TextureFormat,
@@ -124,6 +124,11 @@ impl PhaseItem for Opaque3d {
124124
// NOTE: Values increase towards the camera. Front-to-back ordering for opaque means we need a descending sort.
125125
type SortKey = Reverse<FloatOrd>;
126126

127+
#[inline]
128+
fn entity(&self) -> Entity {
129+
self.entity
130+
}
131+
127132
#[inline]
128133
fn sort_key(&self) -> Self::SortKey {
129134
Reverse(FloatOrd(self.distance))
@@ -141,13 +146,6 @@ impl PhaseItem for Opaque3d {
141146
}
142147
}
143148

144-
impl EntityPhaseItem for Opaque3d {
145-
#[inline]
146-
fn entity(&self) -> Entity {
147-
self.entity
148-
}
149-
}
150-
151149
impl CachedRenderPipelinePhaseItem for Opaque3d {
152150
#[inline]
153151
fn cached_pipeline(&self) -> CachedRenderPipelineId {
@@ -166,6 +164,11 @@ impl PhaseItem for AlphaMask3d {
166164
// NOTE: Values increase towards the camera. Front-to-back ordering for alpha mask means we need a descending sort.
167165
type SortKey = Reverse<FloatOrd>;
168166

167+
#[inline]
168+
fn entity(&self) -> Entity {
169+
self.entity
170+
}
171+
169172
#[inline]
170173
fn sort_key(&self) -> Self::SortKey {
171174
Reverse(FloatOrd(self.distance))
@@ -183,13 +186,6 @@ impl PhaseItem for AlphaMask3d {
183186
}
184187
}
185188

186-
impl EntityPhaseItem for AlphaMask3d {
187-
#[inline]
188-
fn entity(&self) -> Entity {
189-
self.entity
190-
}
191-
}
192-
193189
impl CachedRenderPipelinePhaseItem for AlphaMask3d {
194190
#[inline]
195191
fn cached_pipeline(&self) -> CachedRenderPipelineId {
@@ -208,6 +204,11 @@ impl PhaseItem for Transparent3d {
208204
// NOTE: Values increase towards the camera. Back-to-front ordering for transparent means we need an ascending sort.
209205
type SortKey = FloatOrd;
210206

207+
#[inline]
208+
fn entity(&self) -> Entity {
209+
self.entity
210+
}
211+
211212
#[inline]
212213
fn sort_key(&self) -> Self::SortKey {
213214
FloatOrd(self.distance)
@@ -224,13 +225,6 @@ impl PhaseItem for Transparent3d {
224225
}
225226
}
226227

227-
impl EntityPhaseItem for Transparent3d {
228-
#[inline]
229-
fn entity(&self) -> Entity {
230-
self.entity
231-
}
232-
}
233-
234228
impl CachedRenderPipelinePhaseItem for Transparent3d {
235229
#[inline]
236230
fn cached_pipeline(&self) -> CachedRenderPipelineId {

crates/bevy_pbr/src/material.rs

+13-10
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@ use bevy_core_pipeline::{
1010
};
1111
use bevy_derive::{Deref, DerefMut};
1212
use bevy_ecs::{
13-
entity::Entity,
1413
event::EventReader,
1514
prelude::World,
1615
schedule::IntoSystemDescriptor,
1716
system::{
18-
lifetimeless::{Read, SQuery, SRes},
17+
lifetimeless::{Read, SRes},
1918
Commands, Local, Query, Res, ResMut, Resource, SystemParamItem,
2019
},
2120
world::FromWorld,
@@ -27,8 +26,8 @@ use bevy_render::{
2726
prelude::Image,
2827
render_asset::{PrepareAssetLabel, RenderAssets},
2928
render_phase::{
30-
AddRenderCommand, DrawFunctions, EntityRenderCommand, RenderCommandResult, RenderPhase,
31-
SetItemPipeline, TrackedRenderPass,
29+
AddRenderCommand, DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult,
30+
RenderPhase, SetItemPipeline, TrackedRenderPass,
3231
},
3332
render_resource::{
3433
AsBindGroup, AsBindGroupError, BindGroup, BindGroupLayout, OwnedBindingResource,
@@ -296,15 +295,19 @@ type DrawMaterial<M> = (
296295

297296
/// Sets the bind group for a given [`Material`] at the configured `I` index.
298297
pub struct SetMaterialBindGroup<M: Material, const I: usize>(PhantomData<M>);
299-
impl<M: Material, const I: usize> EntityRenderCommand for SetMaterialBindGroup<M, I> {
300-
type Param = (SRes<RenderMaterials<M>>, SQuery<Read<Handle<M>>>);
298+
impl<P: PhaseItem, M: Material, const I: usize> RenderCommand<P> for SetMaterialBindGroup<M, I> {
299+
type Param = SRes<RenderMaterials<M>>;
300+
type ViewWorldQuery = ();
301+
type ItemWorldQuery = Read<Handle<M>>;
302+
303+
#[inline]
301304
fn render<'w>(
302-
_view: Entity,
303-
item: Entity,
304-
(materials, query): SystemParamItem<'w, '_, Self::Param>,
305+
_item: &P,
306+
_view: (),
307+
material_handle: &'_ Handle<M>,
308+
materials: SystemParamItem<'w, '_, Self::Param>,
305309
pass: &mut TrackedRenderPass<'w>,
306310
) -> RenderCommandResult {
307-
let material_handle = query.get(item).unwrap();
308311
let material = materials.into_inner().get(material_handle).unwrap();
309312
pass.set_bind_group(I, &material.bind_group, &[]);
310313
RenderCommandResult::Success

crates/bevy_pbr/src/render/light.rs

+16-15
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,8 @@ use bevy_render::{
1818
render_asset::RenderAssets,
1919
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
2020
render_phase::{
21-
CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, EntityPhaseItem,
22-
EntityRenderCommand, PhaseItem, RenderCommandResult, RenderPhase, SetItemPipeline,
23-
TrackedRenderPass,
21+
CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem, RenderCommand,
22+
RenderCommandResult, RenderPhase, SetItemPipeline, TrackedRenderPass,
2423
},
2524
render_resource::*,
2625
renderer::{RenderContext, RenderDevice, RenderQueue},
@@ -1699,6 +1698,11 @@ pub struct Shadow {
16991698
impl PhaseItem for Shadow {
17001699
type SortKey = FloatOrd;
17011700

1701+
#[inline]
1702+
fn entity(&self) -> Entity {
1703+
self.entity
1704+
}
1705+
17021706
#[inline]
17031707
fn sort_key(&self) -> Self::SortKey {
17041708
FloatOrd(self.distance)
@@ -1715,12 +1719,6 @@ impl PhaseItem for Shadow {
17151719
}
17161720
}
17171721

1718-
impl EntityPhaseItem for Shadow {
1719-
fn entity(&self) -> Entity {
1720-
self.entity
1721-
}
1722-
}
1723-
17241722
impl CachedRenderPipelinePhaseItem for Shadow {
17251723
#[inline]
17261724
fn cached_pipeline(&self) -> CachedRenderPipelineId {
@@ -1806,16 +1804,19 @@ pub type DrawShadowMesh = (
18061804
);
18071805

18081806
pub struct SetShadowViewBindGroup<const I: usize>;
1809-
impl<const I: usize> EntityRenderCommand for SetShadowViewBindGroup<I> {
1810-
type Param = (SRes<LightMeta>, SQuery<Read<ViewUniformOffset>>);
1807+
impl<const I: usize> RenderCommand<Shadow> for SetShadowViewBindGroup<I> {
1808+
type Param = SRes<LightMeta>;
1809+
type ViewWorldQuery = Read<ViewUniformOffset>;
1810+
type ItemWorldQuery = ();
1811+
18111812
#[inline]
18121813
fn render<'w>(
1813-
view: Entity,
1814-
_item: Entity,
1815-
(light_meta, view_query): SystemParamItem<'w, '_, Self::Param>,
1814+
_item: &Shadow,
1815+
view_uniform_offset: &'_ ViewUniformOffset,
1816+
_entity: (),
1817+
light_meta: SystemParamItem<'w, '_, Self::Param>,
18161818
pass: &mut TrackedRenderPass<'w>,
18171819
) -> RenderCommandResult {
1818-
let view_uniform_offset = view_query.get(view).unwrap();
18191820
pass.set_bind_group(
18201821
I,
18211822
light_meta

crates/bevy_pbr/src/render/mesh.rs

+30-25
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use bevy_app::Plugin;
77
use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
88
use bevy_ecs::{
99
prelude::*,
10+
query::ROQueryItem,
1011
system::{lifetimeless::*, SystemParamItem, SystemState},
1112
};
1213
use bevy_math::{Mat3A, Mat4, Vec2};
@@ -19,7 +20,7 @@ use bevy_render::{
1920
GpuBufferInfo, Mesh, MeshVertexBufferLayout,
2021
},
2122
render_asset::RenderAssets,
22-
render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass},
23+
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
2324
render_resource::*,
2425
renderer::{RenderDevice, RenderQueue},
2526
texture::{
@@ -873,20 +874,23 @@ pub fn queue_mesh_view_bind_groups(
873874
}
874875

875876
pub struct SetMeshViewBindGroup<const I: usize>;
876-
impl<const I: usize> EntityRenderCommand for SetMeshViewBindGroup<I> {
877-
type Param = SQuery<(
877+
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindGroup<I> {
878+
type Param = ();
879+
type ViewWorldQuery = (
878880
Read<ViewUniformOffset>,
879881
Read<ViewLightsUniformOffset>,
880882
Read<MeshViewBindGroup>,
881-
)>;
883+
);
884+
type ItemWorldQuery = ();
885+
882886
#[inline]
883887
fn render<'w>(
884-
view: Entity,
885-
_item: Entity,
886-
view_query: SystemParamItem<'w, '_, Self::Param>,
888+
_item: &P,
889+
(view_uniform, view_lights, mesh_view_bind_group): ROQueryItem<'w, Self::ViewWorldQuery>,
890+
_entity: (),
891+
_: SystemParamItem<'w, '_, Self::Param>,
887892
pass: &mut TrackedRenderPass<'w>,
888893
) -> RenderCommandResult {
889-
let (view_uniform, view_lights, mesh_view_bind_group) = view_query.get_inner(view).unwrap();
890894
pass.set_bind_group(
891895
I,
892896
&mesh_view_bind_group.value,
@@ -898,22 +902,21 @@ impl<const I: usize> EntityRenderCommand for SetMeshViewBindGroup<I> {
898902
}
899903

900904
pub struct SetMeshBindGroup<const I: usize>;
901-
impl<const I: usize> EntityRenderCommand for SetMeshBindGroup<I> {
902-
type Param = (
903-
SRes<MeshBindGroup>,
904-
SQuery<(
905-
Read<DynamicUniformIndex<MeshUniform>>,
906-
Option<Read<SkinnedMeshJoints>>,
907-
)>,
905+
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshBindGroup<I> {
906+
type Param = SRes<MeshBindGroup>;
907+
type ViewWorldQuery = ();
908+
type ItemWorldQuery = (
909+
Read<DynamicUniformIndex<MeshUniform>>,
910+
Option<Read<SkinnedMeshJoints>>,
908911
);
909912
#[inline]
910913
fn render<'w>(
911-
_view: Entity,
912-
item: Entity,
913-
(mesh_bind_group, mesh_query): SystemParamItem<'w, '_, Self::Param>,
914+
_item: &P,
915+
_view: (),
916+
(mesh_index, skinned_mesh_joints): ROQueryItem<'_, Self::ItemWorldQuery>,
917+
mesh_bind_group: SystemParamItem<'w, '_, Self::Param>,
914918
pass: &mut TrackedRenderPass<'w>,
915919
) -> RenderCommandResult {
916-
let (mesh_index, skinned_mesh_joints) = mesh_query.get(item).unwrap();
917920
if let Some(joints) = skinned_mesh_joints {
918921
pass.set_bind_group(
919922
I,
@@ -932,16 +935,18 @@ impl<const I: usize> EntityRenderCommand for SetMeshBindGroup<I> {
932935
}
933936

934937
pub struct DrawMesh;
935-
impl EntityRenderCommand for DrawMesh {
936-
type Param = (SRes<RenderAssets<Mesh>>, SQuery<Read<Handle<Mesh>>>);
938+
impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
939+
type Param = SRes<RenderAssets<Mesh>>;
940+
type ViewWorldQuery = ();
941+
type ItemWorldQuery = Read<Handle<Mesh>>;
937942
#[inline]
938943
fn render<'w>(
939-
_view: Entity,
940-
item: Entity,
941-
(meshes, mesh_query): SystemParamItem<'w, '_, Self::Param>,
944+
_item: &P,
945+
_view: (),
946+
mesh_handle: ROQueryItem<'_, Self::ItemWorldQuery>,
947+
meshes: SystemParamItem<'w, '_, Self::Param>,
942948
pass: &mut TrackedRenderPass<'w>,
943949
) -> RenderCommandResult {
944-
let mesh_handle = mesh_query.get(item).unwrap();
945950
if let Some(gpu_mesh) = meshes.into_inner().get(mesh_handle) {
946951
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
947952
match &gpu_mesh.buffer_info {

0 commit comments

Comments
 (0)