Skip to content

Commit

Permalink
Flatten render commands (bevyengine#6885)
Browse files Browse the repository at this point in the history
# 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
  • Loading branch information
james7132 authored and alradish committed Jan 22, 2023
1 parent f666112 commit 411c1de
Show file tree
Hide file tree
Showing 12 changed files with 242 additions and 217 deletions.
14 changes: 6 additions & 8 deletions crates/bevy_core_pipeline/src/core_2d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ use bevy_render::{
render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
render_phase::{
batch_phase_system, sort_phase_system, BatchedPhaseItem, CachedRenderPipelinePhaseItem,
DrawFunctionId, DrawFunctions, EntityPhaseItem, PhaseItem, RenderPhase,
DrawFunctionId, DrawFunctions, PhaseItem, RenderPhase,
},
render_resource::CachedRenderPipelineId,
Extract, RenderApp, RenderStage,
Expand Down Expand Up @@ -115,6 +115,11 @@ pub struct Transparent2d {
impl PhaseItem for Transparent2d {
type SortKey = FloatOrd;

#[inline]
fn entity(&self) -> Entity {
self.entity
}

#[inline]
fn sort_key(&self) -> Self::SortKey {
self.sort_key
Expand All @@ -131,13 +136,6 @@ impl PhaseItem for Transparent2d {
}
}

impl EntityPhaseItem for Transparent2d {
#[inline]
fn entity(&self) -> Entity {
self.entity
}
}

impl CachedRenderPipelinePhaseItem for Transparent2d {
#[inline]
fn cached_pipeline(&self) -> CachedRenderPipelineId {
Expand Down
40 changes: 17 additions & 23 deletions crates/bevy_core_pipeline/src/core_3d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ use bevy_render::{
prelude::Msaa,
render_graph::{EmptyNode, RenderGraph, SlotInfo, SlotType},
render_phase::{
sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions,
EntityPhaseItem, PhaseItem, RenderPhase,
sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem,
RenderPhase,
},
render_resource::{
CachedRenderPipelineId, Extent3d, TextureDescriptor, TextureDimension, TextureFormat,
Expand Down Expand Up @@ -124,6 +124,11 @@ impl PhaseItem for Opaque3d {
// NOTE: Values increase towards the camera. Front-to-back ordering for opaque means we need a descending sort.
type SortKey = Reverse<FloatOrd>;

#[inline]
fn entity(&self) -> Entity {
self.entity
}

#[inline]
fn sort_key(&self) -> Self::SortKey {
Reverse(FloatOrd(self.distance))
Expand All @@ -141,13 +146,6 @@ impl PhaseItem for Opaque3d {
}
}

impl EntityPhaseItem for Opaque3d {
#[inline]
fn entity(&self) -> Entity {
self.entity
}
}

impl CachedRenderPipelinePhaseItem for Opaque3d {
#[inline]
fn cached_pipeline(&self) -> CachedRenderPipelineId {
Expand All @@ -166,6 +164,11 @@ impl PhaseItem for AlphaMask3d {
// NOTE: Values increase towards the camera. Front-to-back ordering for alpha mask means we need a descending sort.
type SortKey = Reverse<FloatOrd>;

#[inline]
fn entity(&self) -> Entity {
self.entity
}

#[inline]
fn sort_key(&self) -> Self::SortKey {
Reverse(FloatOrd(self.distance))
Expand All @@ -183,13 +186,6 @@ impl PhaseItem for AlphaMask3d {
}
}

impl EntityPhaseItem for AlphaMask3d {
#[inline]
fn entity(&self) -> Entity {
self.entity
}
}

impl CachedRenderPipelinePhaseItem for AlphaMask3d {
#[inline]
fn cached_pipeline(&self) -> CachedRenderPipelineId {
Expand All @@ -208,6 +204,11 @@ impl PhaseItem for Transparent3d {
// NOTE: Values increase towards the camera. Back-to-front ordering for transparent means we need an ascending sort.
type SortKey = FloatOrd;

#[inline]
fn entity(&self) -> Entity {
self.entity
}

#[inline]
fn sort_key(&self) -> Self::SortKey {
FloatOrd(self.distance)
Expand All @@ -224,13 +225,6 @@ impl PhaseItem for Transparent3d {
}
}

impl EntityPhaseItem for Transparent3d {
#[inline]
fn entity(&self) -> Entity {
self.entity
}
}

impl CachedRenderPipelinePhaseItem for Transparent3d {
#[inline]
fn cached_pipeline(&self) -> CachedRenderPipelineId {
Expand Down
23 changes: 13 additions & 10 deletions crates/bevy_pbr/src/material.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,11 @@ use bevy_core_pipeline::{
};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
entity::Entity,
event::EventReader,
prelude::World,
schedule::IntoSystemDescriptor,
system::{
lifetimeless::{Read, SQuery, SRes},
lifetimeless::{Read, SRes},
Commands, Local, Query, Res, ResMut, Resource, SystemParamItem,
},
world::FromWorld,
Expand All @@ -27,8 +26,8 @@ use bevy_render::{
prelude::Image,
render_asset::{PrepareAssetLabel, RenderAssets},
render_phase::{
AddRenderCommand, DrawFunctions, EntityRenderCommand, RenderCommandResult, RenderPhase,
SetItemPipeline, TrackedRenderPass,
AddRenderCommand, DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult,
RenderPhase, SetItemPipeline, TrackedRenderPass,
},
render_resource::{
AsBindGroup, AsBindGroupError, BindGroup, BindGroupLayout, OwnedBindingResource,
Expand Down Expand Up @@ -296,15 +295,19 @@ type DrawMaterial<M> = (

/// Sets the bind group for a given [`Material`] at the configured `I` index.
pub struct SetMaterialBindGroup<M: Material, const I: usize>(PhantomData<M>);
impl<M: Material, const I: usize> EntityRenderCommand for SetMaterialBindGroup<M, I> {
type Param = (SRes<RenderMaterials<M>>, SQuery<Read<Handle<M>>>);
impl<P: PhaseItem, M: Material, const I: usize> RenderCommand<P> for SetMaterialBindGroup<M, I> {
type Param = SRes<RenderMaterials<M>>;
type ViewWorldQuery = ();
type ItemWorldQuery = Read<Handle<M>>;

#[inline]
fn render<'w>(
_view: Entity,
item: Entity,
(materials, query): SystemParamItem<'w, '_, Self::Param>,
_item: &P,
_view: (),
material_handle: &'_ Handle<M>,
materials: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let material_handle = query.get(item).unwrap();
let material = materials.into_inner().get(material_handle).unwrap();
pass.set_bind_group(I, &material.bind_group, &[]);
RenderCommandResult::Success
Expand Down
31 changes: 16 additions & 15 deletions crates/bevy_pbr/src/render/light.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,8 @@ use bevy_render::{
render_asset::RenderAssets,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_phase::{
CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, EntityPhaseItem,
EntityRenderCommand, PhaseItem, RenderCommandResult, RenderPhase, SetItemPipeline,
TrackedRenderPass,
CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem, RenderCommand,
RenderCommandResult, RenderPhase, SetItemPipeline, TrackedRenderPass,
},
render_resource::*,
renderer::{RenderContext, RenderDevice, RenderQueue},
Expand Down Expand Up @@ -1699,6 +1698,11 @@ pub struct Shadow {
impl PhaseItem for Shadow {
type SortKey = FloatOrd;

#[inline]
fn entity(&self) -> Entity {
self.entity
}

#[inline]
fn sort_key(&self) -> Self::SortKey {
FloatOrd(self.distance)
Expand All @@ -1715,12 +1719,6 @@ impl PhaseItem for Shadow {
}
}

impl EntityPhaseItem for Shadow {
fn entity(&self) -> Entity {
self.entity
}
}

impl CachedRenderPipelinePhaseItem for Shadow {
#[inline]
fn cached_pipeline(&self) -> CachedRenderPipelineId {
Expand Down Expand Up @@ -1806,16 +1804,19 @@ pub type DrawShadowMesh = (
);

pub struct SetShadowViewBindGroup<const I: usize>;
impl<const I: usize> EntityRenderCommand for SetShadowViewBindGroup<I> {
type Param = (SRes<LightMeta>, SQuery<Read<ViewUniformOffset>>);
impl<const I: usize> RenderCommand<Shadow> for SetShadowViewBindGroup<I> {
type Param = SRes<LightMeta>;
type ViewWorldQuery = Read<ViewUniformOffset>;
type ItemWorldQuery = ();

#[inline]
fn render<'w>(
view: Entity,
_item: Entity,
(light_meta, view_query): SystemParamItem<'w, '_, Self::Param>,
_item: &Shadow,
view_uniform_offset: &'_ ViewUniformOffset,
_entity: (),
light_meta: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let view_uniform_offset = view_query.get(view).unwrap();
pass.set_bind_group(
I,
light_meta
Expand Down
55 changes: 30 additions & 25 deletions crates/bevy_pbr/src/render/mesh.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use bevy_app::Plugin;
use bevy_asset::{load_internal_asset, Assets, Handle, HandleUntyped};
use bevy_ecs::{
prelude::*,
query::ROQueryItem,
system::{lifetimeless::*, SystemParamItem, SystemState},
};
use bevy_math::{Mat3A, Mat4, Vec2};
Expand All @@ -19,7 +20,7 @@ use bevy_render::{
GpuBufferInfo, Mesh, MeshVertexBufferLayout,
},
render_asset::RenderAssets,
render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass},
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
render_resource::*,
renderer::{RenderDevice, RenderQueue},
texture::{
Expand Down Expand Up @@ -873,20 +874,23 @@ pub fn queue_mesh_view_bind_groups(
}

pub struct SetMeshViewBindGroup<const I: usize>;
impl<const I: usize> EntityRenderCommand for SetMeshViewBindGroup<I> {
type Param = SQuery<(
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshViewBindGroup<I> {
type Param = ();
type ViewWorldQuery = (
Read<ViewUniformOffset>,
Read<ViewLightsUniformOffset>,
Read<MeshViewBindGroup>,
)>;
);
type ItemWorldQuery = ();

#[inline]
fn render<'w>(
view: Entity,
_item: Entity,
view_query: SystemParamItem<'w, '_, Self::Param>,
_item: &P,
(view_uniform, view_lights, mesh_view_bind_group): ROQueryItem<'w, Self::ViewWorldQuery>,
_entity: (),
_: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let (view_uniform, view_lights, mesh_view_bind_group) = view_query.get_inner(view).unwrap();
pass.set_bind_group(
I,
&mesh_view_bind_group.value,
Expand All @@ -898,22 +902,21 @@ impl<const I: usize> EntityRenderCommand for SetMeshViewBindGroup<I> {
}

pub struct SetMeshBindGroup<const I: usize>;
impl<const I: usize> EntityRenderCommand for SetMeshBindGroup<I> {
type Param = (
SRes<MeshBindGroup>,
SQuery<(
Read<DynamicUniformIndex<MeshUniform>>,
Option<Read<SkinnedMeshJoints>>,
)>,
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMeshBindGroup<I> {
type Param = SRes<MeshBindGroup>;
type ViewWorldQuery = ();
type ItemWorldQuery = (
Read<DynamicUniformIndex<MeshUniform>>,
Option<Read<SkinnedMeshJoints>>,
);
#[inline]
fn render<'w>(
_view: Entity,
item: Entity,
(mesh_bind_group, mesh_query): SystemParamItem<'w, '_, Self::Param>,
_item: &P,
_view: (),
(mesh_index, skinned_mesh_joints): ROQueryItem<'_, Self::ItemWorldQuery>,
mesh_bind_group: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let (mesh_index, skinned_mesh_joints) = mesh_query.get(item).unwrap();
if let Some(joints) = skinned_mesh_joints {
pass.set_bind_group(
I,
Expand All @@ -932,16 +935,18 @@ impl<const I: usize> EntityRenderCommand for SetMeshBindGroup<I> {
}

pub struct DrawMesh;
impl EntityRenderCommand for DrawMesh {
type Param = (SRes<RenderAssets<Mesh>>, SQuery<Read<Handle<Mesh>>>);
impl<P: PhaseItem> RenderCommand<P> for DrawMesh {
type Param = SRes<RenderAssets<Mesh>>;
type ViewWorldQuery = ();
type ItemWorldQuery = Read<Handle<Mesh>>;
#[inline]
fn render<'w>(
_view: Entity,
item: Entity,
(meshes, mesh_query): SystemParamItem<'w, '_, Self::Param>,
_item: &P,
_view: (),
mesh_handle: ROQueryItem<'_, Self::ItemWorldQuery>,
meshes: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let mesh_handle = mesh_query.get(item).unwrap();
if let Some(gpu_mesh) = meshes.into_inner().get(mesh_handle) {
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
match &gpu_mesh.buffer_info {
Expand Down
Loading

0 comments on commit 411c1de

Please sign in to comment.