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
This commit is contained in:
James Liu 2023-01-04 01:13:30 +00:00
parent f866d72f15
commit 2d727afaf7
12 changed files with 242 additions and 217 deletions

View file

@ -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,
@ -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
@ -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 {

View file

@ -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,
@ -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))
@ -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 {
@ -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))
@ -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 {
@ -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)
@ -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 {

View file

@ -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,
@ -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,
@ -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

View file

@ -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},
@ -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)
@ -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 {
@ -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

View file

@ -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};
@ -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::{
@ -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,
@ -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,
@ -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 {

View file

@ -6,6 +6,7 @@ use bevy_app::App;
use bevy_ecs::{
all_tuples,
entity::Entity,
query::{QueryState, ROQueryItem, ReadOnlyWorldQuery},
system::{
lifetimeless::SRes, ReadOnlySystemParam, Resource, SystemParam, SystemParamItem,
SystemState,
@ -21,6 +22,12 @@ use std::{any::TypeId, fmt::Debug, hash::Hash, ops::Range};
/// They are the general form of drawing items, whereas [`RenderCommands`](RenderCommand)
/// are more modular.
pub trait Draw<P: PhaseItem>: Send + Sync + 'static {
/// Prepares the draw function to be used. This is called once and only once before the phase
/// begins. There may be zero or more `draw` calls following a call to this function..
/// Implementing this is optional.
#[allow(unused_variables)]
fn prepare(&mut self, world: &'_ World) {}
/// Draws the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`].
fn draw<'w>(
&mut self,
@ -39,6 +46,8 @@ pub trait Draw<P: PhaseItem>: Send + Sync + 'static {
pub trait PhaseItem: Sized + Send + Sync + 'static {
/// The type used for ordering the items. The smallest values are drawn first.
type SortKey: Ord;
fn entity(&self) -> Entity;
/// Determines the order in which the items are drawn during the corresponding [`RenderPhase`](super::RenderPhase).
fn sort_key(&self) -> Self::SortKey;
/// Specifies the [`Draw`] function used to render the item.
@ -75,6 +84,12 @@ pub struct DrawFunctionsInternal<P: PhaseItem> {
}
impl<P: PhaseItem> DrawFunctionsInternal<P> {
pub fn prepare(&mut self, world: &World) {
for function in &mut self.draw_functions {
function.prepare(world);
}
}
/// Adds the [`Draw`] function and associates it to its own type.
pub fn add<T: Draw<P>>(&mut self, draw_function: T) -> DrawFunctionId {
self.add_with::<T, T>(draw_function)
@ -168,11 +183,14 @@ pub trait RenderCommand<P: PhaseItem> {
/// Specifies all ECS data required by [`RenderCommand::render`].
/// All parameters have to be read only.
type Param: SystemParam + 'static;
type ViewWorldQuery: ReadOnlyWorldQuery;
type ItemWorldQuery: ReadOnlyWorldQuery;
/// Renders the [`PhaseItem`] by issuing draw calls via the [`TrackedRenderPass`].
fn render<'w>(
view: Entity,
item: &P,
view: ROQueryItem<'w, Self::ViewWorldQuery>,
entity: ROQueryItem<'w, Self::ItemWorldQuery>,
param: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult;
@ -183,20 +201,6 @@ pub enum RenderCommandResult {
Failure,
}
pub trait EntityRenderCommand {
type Param: SystemParam + 'static;
fn render<'w>(
view: Entity,
item: Entity,
param: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult;
}
pub trait EntityPhaseItem: PhaseItem {
fn entity(&self) -> Entity;
}
pub trait CachedRenderPipelinePhaseItem: PhaseItem {
fn cached_pipeline(&self) -> CachedRenderPipelineId;
}
@ -208,7 +212,7 @@ pub trait CachedRenderPipelinePhaseItem: PhaseItem {
///
/// If this is implemented on a type, the implementation of [`PhaseItem::sort`] should
/// be changed to implement a stable sort, or incorrect/suboptimal batching may result.
pub trait BatchedPhaseItem: EntityPhaseItem {
pub trait BatchedPhaseItem: PhaseItem {
/// Range in the vertex buffer of this item
fn batch_range(&self) -> &Option<Range<u32>>;
@ -247,30 +251,16 @@ pub enum BatchResult {
IncompatibleItems,
}
impl<P: EntityPhaseItem, E: EntityRenderCommand> RenderCommand<P> for E
where
E::Param: 'static,
{
type Param = E::Param;
#[inline]
fn render<'w>(
view: Entity,
item: &P,
param: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
<E as EntityRenderCommand>::render(view, item.entity(), param, pass)
}
}
pub struct SetItemPipeline;
impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline {
type Param = SRes<PipelineCache>;
type ViewWorldQuery = ();
type ItemWorldQuery = ();
#[inline]
fn render<'w>(
_view: Entity,
item: &P,
_view: (),
_entity: (),
pipeline_cache: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
@ -287,18 +277,21 @@ impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline {
}
macro_rules! render_command_tuple_impl {
($($name: ident),*) => {
($(($name: ident, $view: ident, $entity: ident)),*) => {
impl<P: PhaseItem, $($name: RenderCommand<P>),*> RenderCommand<P> for ($($name,)*) {
type Param = ($($name::Param,)*);
type ViewWorldQuery = ($($name::ViewWorldQuery,)*);
type ItemWorldQuery = ($($name::ItemWorldQuery,)*);
#[allow(non_snake_case)]
fn render<'w>(
_view: Entity,
_item: &P,
($($view,)*): ROQueryItem<'w, Self::ViewWorldQuery>,
($($entity,)*): ROQueryItem<'w, Self::ItemWorldQuery>,
($($name,)*): SystemParamItem<'w, '_, Self::Param>,
_pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult{
$(if let RenderCommandResult::Failure = $name::render(_view, _item, $name, _pass) {
) -> RenderCommandResult {
$(if let RenderCommandResult::Failure = $name::render(_item, $view, $entity, $name, _pass) {
return RenderCommandResult::Failure;
})*
RenderCommandResult::Success
@ -307,18 +300,22 @@ macro_rules! render_command_tuple_impl {
};
}
all_tuples!(render_command_tuple_impl, 0, 15, C);
all_tuples!(render_command_tuple_impl, 0, 15, C, V, E);
/// Wraps a [`RenderCommand`] into a state so that it can be used as a [`Draw`] function.
/// Therefore the [`RenderCommand::Param`] is queried from the ECS and passed to the command.
pub struct RenderCommandState<P: PhaseItem + 'static, C: RenderCommand<P>> {
state: SystemState<C::Param>,
view: QueryState<C::ViewWorldQuery>,
entity: QueryState<C::ItemWorldQuery>,
}
impl<P: PhaseItem, C: RenderCommand<P>> RenderCommandState<P, C> {
pub fn new(world: &mut World) -> Self {
Self {
state: SystemState::new(world),
view: world.query(),
entity: world.query(),
}
}
}
@ -327,6 +324,11 @@ impl<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static> Draw<P> for Rend
where
C::Param: ReadOnlySystemParam,
{
fn prepare(&mut self, world: &'_ World) {
self.view.update_archetypes(world);
self.entity.update_archetypes(world);
}
/// Prepares the ECS parameters for the wrapped [`RenderCommand`] and then renders it.
fn draw<'w>(
&mut self,
@ -336,7 +338,9 @@ where
item: &P,
) {
let param = self.state.get(world);
C::render(view, item, param, pass);
let view = self.view.get_manual(world, view).unwrap();
let entity = self.entity.get_manual(world, item.entity()).unwrap();
C::render(item, view, entity, param, pass);
}
}

View file

@ -40,6 +40,7 @@ impl<I: PhaseItem> RenderPhase<I> {
) {
let draw_functions = world.resource::<DrawFunctions<I>>();
let mut draw_functions = draw_functions.write();
draw_functions.prepare(world);
for item in &self.items {
let draw_function = draw_functions.get_mut(item.draw_function()).unwrap();
@ -107,17 +108,16 @@ mod tests {
impl PhaseItem for TestPhaseItem {
type SortKey = ();
fn entity(&self) -> bevy_ecs::entity::Entity {
self.entity
}
fn sort_key(&self) -> Self::SortKey {}
fn draw_function(&self) -> DrawFunctionId {
unimplemented!();
}
}
impl EntityPhaseItem for TestPhaseItem {
fn entity(&self) -> bevy_ecs::entity::Entity {
self.entity
}
}
impl BatchedPhaseItem for TestPhaseItem {
fn batch_range(&self) -> &Option<std::ops::Range<u32>> {
&self.batch_range

View file

@ -3,12 +3,12 @@ use bevy_asset::{AddAsset, AssetEvent, AssetServer, Assets, Handle};
use bevy_core_pipeline::{core_2d::Transparent2d, tonemapping::Tonemapping};
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::{
entity::Entity,
event::EventReader,
prelude::{Bundle, World},
query::ROQueryItem,
schedule::IntoSystemDescriptor,
system::{
lifetimeless::{Read, SQuery, SRes},
lifetimeless::{Read, SRes},
Commands, Local, Query, Res, ResMut, Resource, SystemParamItem,
},
world::FromWorld,
@ -21,8 +21,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,
@ -281,15 +281,21 @@ type DrawMaterial2d<M> = (
);
pub struct SetMaterial2dBindGroup<M: Material2d, const I: usize>(PhantomData<M>);
impl<M: Material2d, const I: usize> EntityRenderCommand for SetMaterial2dBindGroup<M, I> {
type Param = (SRes<RenderMaterials2d<M>>, SQuery<Read<Handle<M>>>);
impl<P: PhaseItem, M: Material2d, const I: usize> RenderCommand<P>
for SetMaterial2dBindGroup<M, I>
{
type Param = SRes<RenderMaterials2d<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: (),
material2d_handle: ROQueryItem<'_, Self::ItemWorldQuery>,
materials: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let material2d_handle = query.get(item).unwrap();
let material2d = materials.into_inner().get(material2d_handle).unwrap();
pass.set_bind_group(I, &material2d.bind_group, &[]);
RenderCommandResult::Success

View file

@ -2,6 +2,7 @@ use bevy_app::Plugin;
use bevy_asset::{load_internal_asset, Handle, HandleUntyped};
use bevy_ecs::{
prelude::*,
query::ROQueryItem,
system::{lifetimeless::*, SystemParamItem, SystemState},
};
use bevy_math::{Mat4, Vec2};
@ -11,7 +12,7 @@ use bevy_render::{
globals::{GlobalsBuffer, GlobalsUniform},
mesh::{GpuBufferInfo, Mesh, MeshVertexBufferLayout},
render_asset::RenderAssets,
render_phase::{EntityRenderCommand, RenderCommandResult, TrackedRenderPass},
render_phase::{PhaseItem, RenderCommand, RenderCommandResult, TrackedRenderPass},
render_resource::*,
renderer::{RenderDevice, RenderQueue},
texture::{
@ -495,16 +496,19 @@ pub fn queue_mesh2d_view_bind_groups(
}
pub struct SetMesh2dViewBindGroup<const I: usize>;
impl<const I: usize> EntityRenderCommand for SetMesh2dViewBindGroup<I> {
type Param = SQuery<(Read<ViewUniformOffset>, Read<Mesh2dViewBindGroup>)>;
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMesh2dViewBindGroup<I> {
type Param = ();
type ViewWorldQuery = (Read<ViewUniformOffset>, Read<Mesh2dViewBindGroup>);
type ItemWorldQuery = ();
#[inline]
fn render<'w>(
view: Entity,
_item: Entity,
view_query: SystemParamItem<'w, '_, Self::Param>,
_item: &P,
(view_uniform, mesh2d_view_bind_group): ROQueryItem<'w, Self::ViewWorldQuery>,
_view: (),
_param: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let (view_uniform, mesh2d_view_bind_group) = view_query.get_inner(view).unwrap();
pass.set_bind_group(I, &mesh2d_view_bind_group.value, &[view_uniform.offset]);
RenderCommandResult::Success
@ -512,19 +516,19 @@ impl<const I: usize> EntityRenderCommand for SetMesh2dViewBindGroup<I> {
}
pub struct SetMesh2dBindGroup<const I: usize>;
impl<const I: usize> EntityRenderCommand for SetMesh2dBindGroup<I> {
type Param = (
SRes<Mesh2dBindGroup>,
SQuery<Read<DynamicUniformIndex<Mesh2dUniform>>>,
);
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetMesh2dBindGroup<I> {
type Param = SRes<Mesh2dBindGroup>;
type ViewWorldQuery = ();
type ItemWorldQuery = Read<DynamicUniformIndex<Mesh2dUniform>>;
#[inline]
fn render<'w>(
_view: Entity,
item: Entity,
(mesh2d_bind_group, mesh2d_query): SystemParamItem<'w, '_, Self::Param>,
_item: &P,
_view: (),
mesh2d_index: &'_ DynamicUniformIndex<Mesh2dUniform>,
mesh2d_bind_group: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let mesh2d_index = mesh2d_query.get(item).unwrap();
pass.set_bind_group(
I,
&mesh2d_bind_group.into_inner().value,
@ -535,17 +539,20 @@ impl<const I: usize> EntityRenderCommand for SetMesh2dBindGroup<I> {
}
pub struct DrawMesh2d;
impl EntityRenderCommand for DrawMesh2d {
type Param = (SRes<RenderAssets<Mesh>>, SQuery<Read<Mesh2dHandle>>);
impl<P: PhaseItem> RenderCommand<P> for DrawMesh2d {
type Param = SRes<RenderAssets<Mesh>>;
type ViewWorldQuery = ();
type ItemWorldQuery = Read<Mesh2dHandle>;
#[inline]
fn render<'w>(
_view: Entity,
item: Entity,
(meshes, mesh2d_query): SystemParamItem<'w, '_, Self::Param>,
_item: &P,
_view: (),
mesh_handle: ROQueryItem<'w, Self::ItemWorldQuery>,
meshes: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let mesh_handle = &mesh2d_query.get(item).unwrap().0;
if let Some(gpu_mesh) = meshes.into_inner().get(mesh_handle) {
if let Some(gpu_mesh) = meshes.into_inner().get(&mesh_handle.0) {
pass.set_vertex_buffer(0, gpu_mesh.vertex_buffer.slice(..));
match &gpu_mesh.buffer_info {
GpuBufferInfo::Indexed {

View file

@ -16,7 +16,7 @@ use bevy_render::{
color::Color,
render_asset::RenderAssets,
render_phase::{
BatchedPhaseItem, DrawFunctions, EntityRenderCommand, RenderCommand, RenderCommandResult,
BatchedPhaseItem, DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult,
RenderPhase, SetItemPipeline, TrackedRenderPass,
},
render_resource::*,
@ -696,16 +696,18 @@ pub type DrawSprite = (
);
pub struct SetSpriteViewBindGroup<const I: usize>;
impl<const I: usize> EntityRenderCommand for SetSpriteViewBindGroup<I> {
type Param = (SRes<SpriteMeta>, SQuery<Read<ViewUniformOffset>>);
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetSpriteViewBindGroup<I> {
type Param = SRes<SpriteMeta>;
type ViewWorldQuery = Read<ViewUniformOffset>;
type ItemWorldQuery = ();
fn render<'w>(
view: Entity,
_item: Entity,
(sprite_meta, view_query): SystemParamItem<'w, '_, Self::Param>,
_item: &P,
view_uniform: &'_ ViewUniformOffset,
_entity: (),
sprite_meta: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let view_uniform = view_query.get(view).unwrap();
pass.set_bind_group(
I,
sprite_meta.into_inner().view_bind_group.as_ref().unwrap(),
@ -715,16 +717,18 @@ impl<const I: usize> EntityRenderCommand for SetSpriteViewBindGroup<I> {
}
}
pub struct SetSpriteTextureBindGroup<const I: usize>;
impl<const I: usize> EntityRenderCommand for SetSpriteTextureBindGroup<I> {
type Param = (SRes<ImageBindGroups>, SQuery<Read<SpriteBatch>>);
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetSpriteTextureBindGroup<I> {
type Param = SRes<ImageBindGroups>;
type ViewWorldQuery = ();
type ItemWorldQuery = Read<SpriteBatch>;
fn render<'w>(
_view: Entity,
item: Entity,
(image_bind_groups, query_batch): SystemParamItem<'w, '_, Self::Param>,
_item: &P,
_view: (),
sprite_batch: &'_ SpriteBatch,
image_bind_groups: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let sprite_batch = query_batch.get(item).unwrap();
let image_bind_groups = image_bind_groups.into_inner();
pass.set_bind_group(
@ -741,15 +745,17 @@ impl<const I: usize> EntityRenderCommand for SetSpriteTextureBindGroup<I> {
pub struct DrawSpriteBatch;
impl<P: BatchedPhaseItem> RenderCommand<P> for DrawSpriteBatch {
type Param = (SRes<SpriteMeta>, SQuery<Read<SpriteBatch>>);
type Param = SRes<SpriteMeta>;
type ViewWorldQuery = ();
type ItemWorldQuery = Read<SpriteBatch>;
fn render<'w>(
_view: Entity,
item: &P,
(sprite_meta, query_batch): SystemParamItem<'w, '_, Self::Param>,
_view: (),
sprite_batch: &'_ SpriteBatch,
sprite_meta: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let sprite_batch = query_batch.get(item.entity()).unwrap();
let sprite_meta = sprite_meta.into_inner();
if sprite_batch.colored {
pass.set_vertex_buffer(0, sprite_meta.colored_vertices.buffer().unwrap().slice(..));

View file

@ -106,6 +106,11 @@ pub struct TransparentUi {
impl PhaseItem for TransparentUi {
type SortKey = FloatOrd;
#[inline]
fn entity(&self) -> Entity {
self.entity
}
#[inline]
fn sort_key(&self) -> Self::SortKey {
self.sort_key
@ -117,13 +122,6 @@ impl PhaseItem for TransparentUi {
}
}
impl EntityPhaseItem for TransparentUi {
#[inline]
fn entity(&self) -> Entity {
self.entity
}
}
impl CachedRenderPipelinePhaseItem for TransparentUi {
#[inline]
fn cached_pipeline(&self) -> CachedRenderPipelineId {
@ -139,16 +137,18 @@ pub type DrawUi = (
);
pub struct SetUiViewBindGroup<const I: usize>;
impl<const I: usize> EntityRenderCommand for SetUiViewBindGroup<I> {
type Param = (SRes<UiMeta>, SQuery<Read<ViewUniformOffset>>);
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetUiViewBindGroup<I> {
type Param = SRes<UiMeta>;
type ViewWorldQuery = Read<ViewUniformOffset>;
type ItemWorldQuery = ();
fn render<'w>(
view: Entity,
_item: Entity,
(ui_meta, view_query): SystemParamItem<'w, '_, Self::Param>,
_item: &P,
view_uniform: &'w ViewUniformOffset,
_entity: (),
ui_meta: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let view_uniform = view_query.get(view).unwrap();
pass.set_bind_group(
I,
ui_meta.into_inner().view_bind_group.as_ref().unwrap(),
@ -158,34 +158,38 @@ impl<const I: usize> EntityRenderCommand for SetUiViewBindGroup<I> {
}
}
pub struct SetUiTextureBindGroup<const I: usize>;
impl<const I: usize> EntityRenderCommand for SetUiTextureBindGroup<I> {
type Param = (SRes<UiImageBindGroups>, SQuery<Read<UiBatch>>);
impl<P: PhaseItem, const I: usize> RenderCommand<P> for SetUiTextureBindGroup<I> {
type Param = SRes<UiImageBindGroups>;
type ViewWorldQuery = ();
type ItemWorldQuery = Read<UiBatch>;
#[inline]
fn render<'w>(
_view: Entity,
item: Entity,
(image_bind_groups, query_batch): SystemParamItem<'w, '_, Self::Param>,
_item: &P,
_view: (),
batch: &'w UiBatch,
image_bind_groups: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let batch = query_batch.get(item).unwrap();
let image_bind_groups = image_bind_groups.into_inner();
pass.set_bind_group(I, image_bind_groups.values.get(&batch.image).unwrap(), &[]);
RenderCommandResult::Success
}
}
pub struct DrawUiNode;
impl EntityRenderCommand for DrawUiNode {
type Param = (SRes<UiMeta>, SQuery<Read<UiBatch>>);
impl<P: PhaseItem> RenderCommand<P> for DrawUiNode {
type Param = SRes<UiMeta>;
type ViewWorldQuery = ();
type ItemWorldQuery = Read<UiBatch>;
#[inline]
fn render<'w>(
_view: Entity,
item: Entity,
(ui_meta, query_batch): SystemParamItem<'w, '_, Self::Param>,
_item: &P,
_view: (),
batch: &'w UiBatch,
ui_meta: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let batch = query_batch.get(item).unwrap();
pass.set_vertex_buffer(0, ui_meta.into_inner().vertices.buffer().unwrap().slice(..));
pass.draw(batch.range.clone(), 0..1);
RenderCommandResult::Success

View file

@ -13,8 +13,8 @@ use bevy::{
mesh::{GpuBufferInfo, MeshVertexBufferLayout},
render_asset::RenderAssets,
render_phase::{
AddRenderCommand, DrawFunctions, EntityRenderCommand, RenderCommandResult, RenderPhase,
SetItemPipeline, TrackedRenderPass,
AddRenderCommand, DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult,
RenderPhase, SetItemPipeline, TrackedRenderPass,
},
render_resource::*,
renderer::RenderDevice,
@ -223,22 +223,19 @@ type DrawCustom = (
pub struct DrawMeshInstanced;
impl EntityRenderCommand for DrawMeshInstanced {
type Param = (
SRes<RenderAssets<Mesh>>,
SQuery<Read<Handle<Mesh>>>,
SQuery<Read<InstanceBuffer>>,
);
impl<P: PhaseItem> RenderCommand<P> for DrawMeshInstanced {
type Param = SRes<RenderAssets<Mesh>>;
type ViewWorldQuery = ();
type ItemWorldQuery = (Read<Handle<Mesh>>, Read<InstanceBuffer>);
#[inline]
fn render<'w>(
_view: Entity,
item: Entity,
(meshes, mesh_query, instance_buffer_query): SystemParamItem<'w, '_, Self::Param>,
_item: &P,
_view: (),
(mesh_handle, instance_buffer): (&'w Handle<Mesh>, &'w InstanceBuffer),
meshes: SystemParamItem<'w, '_, Self::Param>,
pass: &mut TrackedRenderPass<'w>,
) -> RenderCommandResult {
let mesh_handle = mesh_query.get(item).unwrap();
let instance_buffer = instance_buffer_query.get_inner(item).unwrap();
let gpu_mesh = match meshes.into_inner().get(mesh_handle) {
Some(gpu_mesh) => gpu_mesh,
None => return RenderCommandResult::Failure,