mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 12:43:34 +00:00
Improve performance by binning together opaque items instead of sorting them. (#12453)
Today, we sort all entities added to all phases, even the phases that don't strictly need sorting, such as the opaque and shadow phases. This results in a performance loss because our `PhaseItem`s are rather large in memory, so sorting is slow. Additionally, determining the boundaries of batches is an O(n) process. This commit makes Bevy instead applicable place phase items into *bins* keyed by *bin keys*, which have the invariant that everything in the same bin is potentially batchable. This makes determining batch boundaries O(1), because everything in the same bin can be batched. Instead of sorting each entity, we now sort only the bin keys. This drops the sorting time to near-zero on workloads with few bins like `many_cubes --no-frustum-culling`. Memory usage is improved too, with batch boundaries and dynamic indices now implicit instead of explicit. The improved memory usage results in a significant win even on unbatchable workloads like `many_cubes --no-frustum-culling --vary-material-data-per-instance`, presumably due to cache effects. Not all phases can be binned; some, such as transparent and transmissive phases, must still be sorted. To handle this, this commit splits `PhaseItem` into `BinnedPhaseItem` and `SortedPhaseItem`. Most of the logic that today deals with `PhaseItem`s has been moved to `SortedPhaseItem`. `BinnedPhaseItem` has the new logic. Frame time results (in ms/frame) are as follows: | Benchmark | `binning` | `main` | Speedup | | ------------------------ | --------- | ------- | ------- | | `many_cubes -nfc -vpi` | 232.179 | 312.123 | 34.43% | | `many_cubes -nfc` | 25.874 | 30.117 | 16.40% | | `many_foxes` | 3.276 | 3.515 | 7.30% | (`-nfc` is short for `--no-frustum-culling`; `-vpi` is short for `--vary-per-instance`.) --- ## Changelog ### Changed * Render phases have been split into binned and sorted phases. Binned phases, such as the common opaque phase, achieve improved CPU performance by avoiding the sorting step. ## Migration Guide - `PhaseItem` has been split into `BinnedPhaseItem` and `SortedPhaseItem`. If your code has custom `PhaseItem`s, you will need to migrate them to one of these two types. `SortedPhaseItem` requires the fewest code changes, but you may want to pick `BinnedPhaseItem` if your phase doesn't require sorting, as that enables higher performance. ## Tracy graphs `many-cubes --no-frustum-culling`, `main` branch: <img width="1064" alt="Screenshot 2024-03-12 180037" src="https://github.com/bevyengine/bevy/assets/157897/e1180ce8-8e89-46d2-85e3-f59f72109a55"> `many-cubes --no-frustum-culling`, this branch: <img width="1064" alt="Screenshot 2024-03-12 180011" src="https://github.com/bevyengine/bevy/assets/157897/0899f036-6075-44c5-a972-44d95895f46c"> You can see that `batch_and_prepare_binned_render_phase` is a much smaller fraction of the time. Zooming in on that function, with yellow being this branch and red being `main`, we see: <img width="1064" alt="Screenshot 2024-03-12 175832" src="https://github.com/bevyengine/bevy/assets/157897/0dfc8d3f-49f4-496e-8825-a66e64d356d0"> The binning happens in `queue_material_meshes`. Again with yellow being this branch and red being `main`: <img width="1064" alt="Screenshot 2024-03-12 175755" src="https://github.com/bevyengine/bevy/assets/157897/b9b20dc1-11c8-400c-a6cc-1c2e09c1bb96"> We can see that there is a small regression in `queue_material_meshes` performance, but it's not nearly enough to outweigh the large gains in `batch_and_prepare_binned_render_phase`. --------- Co-authored-by: James Liu <contact@jamessliu.com>
This commit is contained in:
parent
df76fd4a1b
commit
4dadebd9c4
31 changed files with 1061 additions and 420 deletions
|
@ -4,7 +4,7 @@ use bevy_render::{
|
|||
camera::ExtractedCamera,
|
||||
diagnostic::RecordDiagnostics,
|
||||
render_graph::{Node, NodeRunError, RenderGraphContext},
|
||||
render_phase::RenderPhase,
|
||||
render_phase::SortedRenderPhase,
|
||||
render_resource::RenderPassDescriptor,
|
||||
renderer::RenderContext,
|
||||
view::{ExtractedView, ViewTarget},
|
||||
|
@ -16,7 +16,7 @@ pub struct MainPass2dNode {
|
|||
query: QueryState<
|
||||
(
|
||||
&'static ExtractedCamera,
|
||||
&'static RenderPhase<Transparent2d>,
|
||||
&'static SortedRenderPhase<Transparent2d>,
|
||||
&'static ViewTarget,
|
||||
),
|
||||
With<ExtractedView>,
|
||||
|
|
|
@ -38,7 +38,7 @@ use bevy_render::{
|
|||
render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner},
|
||||
render_phase::{
|
||||
sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem,
|
||||
RenderPhase,
|
||||
SortedPhaseItem, SortedRenderPhase,
|
||||
},
|
||||
render_resource::CachedRenderPipelineId,
|
||||
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
|
||||
|
@ -96,29 +96,16 @@ 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
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_function(&self) -> DrawFunctionId {
|
||||
self.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
// radsort is a stable radix sort that performed better than `slice::sort_by_key` or `slice::sort_unstable_by_key`.
|
||||
radsort::sort_by_key(items, |item| item.sort_key().0);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn batch_range(&self) -> &Range<u32> {
|
||||
&self.batch_range
|
||||
|
@ -140,6 +127,21 @@ impl PhaseItem for Transparent2d {
|
|||
}
|
||||
}
|
||||
|
||||
impl SortedPhaseItem for Transparent2d {
|
||||
type SortKey = FloatOrd;
|
||||
|
||||
#[inline]
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
self.sort_key
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
// radsort is a stable radix sort that performed better than `slice::sort_by_key` or `slice::sort_unstable_by_key`.
|
||||
radsort::sort_by_key(items, |item| item.sort_key().0);
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedRenderPipelinePhaseItem for Transparent2d {
|
||||
#[inline]
|
||||
fn cached_pipeline(&self) -> CachedRenderPipelineId {
|
||||
|
@ -155,7 +157,7 @@ pub fn extract_core_2d_camera_phases(
|
|||
if camera.is_active {
|
||||
commands
|
||||
.get_or_spawn(entity)
|
||||
.insert(RenderPhase::<Transparent2d>::default());
|
||||
.insert(SortedRenderPhase::<Transparent2d>::default());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ use bevy_render::{
|
|||
camera::ExtractedCamera,
|
||||
diagnostic::RecordDiagnostics,
|
||||
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
|
||||
render_phase::{RenderPhase, TrackedRenderPass},
|
||||
render_phase::{BinnedRenderPhase, TrackedRenderPass},
|
||||
render_resource::{CommandEncoderDescriptor, PipelineCache, RenderPassDescriptor, StoreOp},
|
||||
renderer::RenderContext,
|
||||
view::{ViewDepthTexture, ViewTarget, ViewUniformOffset},
|
||||
|
@ -17,14 +17,16 @@ use bevy_utils::tracing::info_span;
|
|||
|
||||
use super::AlphaMask3d;
|
||||
|
||||
/// A [`bevy_render::render_graph::Node`] that runs the [`Opaque3d`] and [`AlphaMask3d`] [`RenderPhase`].
|
||||
/// A [`bevy_render::render_graph::Node`] that runs the [`Opaque3d`]
|
||||
/// [`BinnedRenderPhase`] and [`AlphaMask3d`]
|
||||
/// [`bevy_render::render_phase::SortedRenderPhase`]s.
|
||||
#[derive(Default)]
|
||||
pub struct MainOpaquePass3dNode;
|
||||
impl ViewNode for MainOpaquePass3dNode {
|
||||
type ViewQuery = (
|
||||
&'static ExtractedCamera,
|
||||
&'static RenderPhase<Opaque3d>,
|
||||
&'static RenderPhase<AlphaMask3d>,
|
||||
&'static BinnedRenderPhase<Opaque3d>,
|
||||
&'static BinnedRenderPhase<AlphaMask3d>,
|
||||
&'static ViewTarget,
|
||||
&'static ViewDepthTexture,
|
||||
Option<&'static SkyboxPipelineId>,
|
||||
|
@ -80,14 +82,14 @@ impl ViewNode for MainOpaquePass3dNode {
|
|||
}
|
||||
|
||||
// Opaque draws
|
||||
if !opaque_phase.items.is_empty() {
|
||||
if !opaque_phase.is_empty() {
|
||||
#[cfg(feature = "trace")]
|
||||
let _opaque_main_pass_3d_span = info_span!("opaque_main_pass_3d").entered();
|
||||
opaque_phase.render(&mut render_pass, world, view_entity);
|
||||
}
|
||||
|
||||
// Alpha draws
|
||||
if !alpha_mask_phase.items.is_empty() {
|
||||
if !alpha_mask_phase.is_empty() {
|
||||
#[cfg(feature = "trace")]
|
||||
let _alpha_mask_main_pass_3d_span = info_span!("alpha_mask_main_pass_3d").entered();
|
||||
alpha_mask_phase.render(&mut render_pass, world, view_entity);
|
||||
|
|
|
@ -4,7 +4,7 @@ use bevy_ecs::{prelude::*, query::QueryItem};
|
|||
use bevy_render::{
|
||||
camera::ExtractedCamera,
|
||||
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
|
||||
render_phase::RenderPhase,
|
||||
render_phase::SortedRenderPhase,
|
||||
render_resource::{Extent3d, RenderPassDescriptor, StoreOp},
|
||||
renderer::RenderContext,
|
||||
view::{ViewDepthTexture, ViewTarget},
|
||||
|
@ -13,7 +13,8 @@ use bevy_render::{
|
|||
use bevy_utils::tracing::info_span;
|
||||
use std::ops::Range;
|
||||
|
||||
/// A [`bevy_render::render_graph::Node`] that runs the [`Transmissive3d`] [`RenderPhase`].
|
||||
/// A [`bevy_render::render_graph::Node`] that runs the [`Transmissive3d`]
|
||||
/// [`SortedRenderPhase`].
|
||||
#[derive(Default)]
|
||||
pub struct MainTransmissivePass3dNode;
|
||||
|
||||
|
@ -21,7 +22,7 @@ impl ViewNode for MainTransmissivePass3dNode {
|
|||
type ViewQuery = (
|
||||
&'static ExtractedCamera,
|
||||
&'static Camera3d,
|
||||
&'static RenderPhase<Transmissive3d>,
|
||||
&'static SortedRenderPhase<Transmissive3d>,
|
||||
&'static ViewTarget,
|
||||
Option<&'static ViewTransmissionTexture>,
|
||||
&'static ViewDepthTexture,
|
||||
|
|
|
@ -4,7 +4,7 @@ use bevy_render::{
|
|||
camera::ExtractedCamera,
|
||||
diagnostic::RecordDiagnostics,
|
||||
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
|
||||
render_phase::RenderPhase,
|
||||
render_phase::SortedRenderPhase,
|
||||
render_resource::{RenderPassDescriptor, StoreOp},
|
||||
renderer::RenderContext,
|
||||
view::{ViewDepthTexture, ViewTarget},
|
||||
|
@ -12,14 +12,15 @@ use bevy_render::{
|
|||
#[cfg(feature = "trace")]
|
||||
use bevy_utils::tracing::info_span;
|
||||
|
||||
/// A [`bevy_render::render_graph::Node`] that runs the [`Transparent3d`] [`RenderPhase`].
|
||||
/// A [`bevy_render::render_graph::Node`] that runs the [`Transparent3d`]
|
||||
/// [`SortedRenderPhase`].
|
||||
#[derive(Default)]
|
||||
pub struct MainTransparentPass3dNode;
|
||||
|
||||
impl ViewNode for MainTransparentPass3dNode {
|
||||
type ViewQuery = (
|
||||
&'static ExtractedCamera,
|
||||
&'static RenderPhase<Transparent3d>,
|
||||
&'static SortedRenderPhase<Transparent3d>,
|
||||
&'static ViewTarget,
|
||||
&'static ViewDepthTexture,
|
||||
);
|
||||
|
|
|
@ -56,15 +56,15 @@ use bevy_render::{
|
|||
prelude::Msaa,
|
||||
render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner},
|
||||
render_phase::{
|
||||
sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem,
|
||||
RenderPhase,
|
||||
sort_phase_system, BinnedPhaseItem, BinnedRenderPhase, CachedRenderPipelinePhaseItem,
|
||||
DrawFunctionId, DrawFunctions, PhaseItem, SortedPhaseItem, SortedRenderPhase,
|
||||
},
|
||||
render_resource::{
|
||||
CachedRenderPipelineId, Extent3d, FilterMode, Sampler, SamplerDescriptor, Texture,
|
||||
TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView,
|
||||
BindGroupId, CachedRenderPipelineId, Extent3d, FilterMode, Sampler, SamplerDescriptor,
|
||||
Texture, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages, TextureView,
|
||||
},
|
||||
renderer::RenderDevice,
|
||||
texture::{BevyDefault, ColorAttachment, TextureCache},
|
||||
texture::{BevyDefault, ColorAttachment, Image, TextureCache},
|
||||
view::{ExtractedView, ViewDepthTexture, ViewTarget},
|
||||
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
|
||||
};
|
||||
|
@ -80,8 +80,8 @@ use crate::{
|
|||
},
|
||||
prepass::{
|
||||
node::PrepassNode, AlphaMask3dPrepass, DeferredPrepass, DepthPrepass, MotionVectorPrepass,
|
||||
NormalPrepass, Opaque3dPrepass, ViewPrepassTextures, MOTION_VECTOR_PREPASS_FORMAT,
|
||||
NORMAL_PREPASS_FORMAT,
|
||||
NormalPrepass, Opaque3dPrepass, OpaqueNoLightmap3dBinKey, ViewPrepassTextures,
|
||||
MOTION_VECTOR_PREPASS_FORMAT, NORMAL_PREPASS_FORMAT,
|
||||
},
|
||||
skybox::SkyboxPlugin,
|
||||
tonemapping::TonemappingNode,
|
||||
|
@ -117,14 +117,8 @@ impl Plugin for Core3dPlugin {
|
|||
.add_systems(
|
||||
Render,
|
||||
(
|
||||
sort_phase_system::<Opaque3d>.in_set(RenderSet::PhaseSort),
|
||||
sort_phase_system::<AlphaMask3d>.in_set(RenderSet::PhaseSort),
|
||||
sort_phase_system::<Transmissive3d>.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),
|
||||
sort_phase_system::<Opaque3dDeferred>.in_set(RenderSet::PhaseSort),
|
||||
sort_phase_system::<AlphaMask3dDeferred>.in_set(RenderSet::PhaseSort),
|
||||
prepare_core_3d_depth_textures.in_set(RenderSet::PrepareResources),
|
||||
prepare_core_3d_transmission_textures.in_set(RenderSet::PrepareResources),
|
||||
prepare_prepass_textures.in_set(RenderSet::PrepareResources),
|
||||
|
@ -180,37 +174,49 @@ impl Plugin for Core3dPlugin {
|
|||
}
|
||||
}
|
||||
|
||||
/// Opaque 3D [`BinnedPhaseItem`]s.
|
||||
pub struct Opaque3d {
|
||||
pub asset_id: AssetId<Mesh>,
|
||||
pub pipeline: CachedRenderPipelineId,
|
||||
pub entity: Entity,
|
||||
pub draw_function: DrawFunctionId,
|
||||
/// The key, which determines which can be batched.
|
||||
pub key: Opaque3dBinKey,
|
||||
/// An entity from which data will be fetched, including the mesh if
|
||||
/// applicable.
|
||||
pub representative_entity: Entity,
|
||||
/// The ranges of instances.
|
||||
pub batch_range: Range<u32>,
|
||||
/// The dynamic offset.
|
||||
pub dynamic_offset: Option<NonMaxU32>,
|
||||
}
|
||||
|
||||
impl PhaseItem for Opaque3d {
|
||||
type SortKey = (usize, AssetId<Mesh>);
|
||||
/// Data that must be identical in order to batch meshes together.
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct Opaque3dBinKey {
|
||||
/// The identifier of the render pipeline.
|
||||
pub pipeline: CachedRenderPipelineId,
|
||||
|
||||
/// The function used to draw.
|
||||
pub draw_function: DrawFunctionId,
|
||||
|
||||
/// The mesh.
|
||||
pub asset_id: AssetId<Mesh>,
|
||||
|
||||
/// The ID of a bind group specific to the material.
|
||||
///
|
||||
/// In the case of PBR, this is the `MaterialBindGroupId`.
|
||||
pub material_bind_group_id: Option<BindGroupId>,
|
||||
|
||||
/// The lightmap, if present.
|
||||
pub lightmap_image: Option<AssetId<Image>>,
|
||||
}
|
||||
|
||||
impl PhaseItem for Opaque3d {
|
||||
#[inline]
|
||||
fn entity(&self) -> Entity {
|
||||
self.entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
// Sort by pipeline, then by mesh to massively decrease drawcall counts in real scenes.
|
||||
(self.pipeline.id(), self.asset_id)
|
||||
self.representative_entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_function(&self) -> DrawFunctionId {
|
||||
self.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
items.sort_unstable_by_key(Self::sort_key);
|
||||
self.key.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -234,44 +240,48 @@ impl PhaseItem for Opaque3d {
|
|||
}
|
||||
}
|
||||
|
||||
impl BinnedPhaseItem for Opaque3d {
|
||||
type BinKey = Opaque3dBinKey;
|
||||
|
||||
#[inline]
|
||||
fn new(
|
||||
key: Self::BinKey,
|
||||
representative_entity: Entity,
|
||||
batch_range: Range<u32>,
|
||||
dynamic_offset: Option<NonMaxU32>,
|
||||
) -> Self {
|
||||
Opaque3d {
|
||||
key,
|
||||
representative_entity,
|
||||
batch_range,
|
||||
dynamic_offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedRenderPipelinePhaseItem for Opaque3d {
|
||||
#[inline]
|
||||
fn cached_pipeline(&self) -> CachedRenderPipelineId {
|
||||
self.pipeline
|
||||
self.key.pipeline
|
||||
}
|
||||
}
|
||||
|
||||
pub struct AlphaMask3d {
|
||||
pub asset_id: AssetId<Mesh>,
|
||||
pub pipeline: CachedRenderPipelineId,
|
||||
pub entity: Entity,
|
||||
pub draw_function: DrawFunctionId,
|
||||
pub key: OpaqueNoLightmap3dBinKey,
|
||||
pub representative_entity: Entity,
|
||||
pub batch_range: Range<u32>,
|
||||
pub dynamic_offset: Option<NonMaxU32>,
|
||||
}
|
||||
|
||||
impl PhaseItem for AlphaMask3d {
|
||||
type SortKey = (usize, AssetId<Mesh>);
|
||||
|
||||
#[inline]
|
||||
fn entity(&self) -> Entity {
|
||||
self.entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
// Sort by pipeline, then by mesh to massively decrease drawcall counts in real scenes.
|
||||
(self.pipeline.id(), self.asset_id)
|
||||
self.representative_entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_function(&self) -> DrawFunctionId {
|
||||
self.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
items.sort_unstable_by_key(Self::sort_key);
|
||||
self.key.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -295,10 +305,29 @@ impl PhaseItem for AlphaMask3d {
|
|||
}
|
||||
}
|
||||
|
||||
impl BinnedPhaseItem for AlphaMask3d {
|
||||
type BinKey = OpaqueNoLightmap3dBinKey;
|
||||
|
||||
#[inline]
|
||||
fn new(
|
||||
key: Self::BinKey,
|
||||
representative_entity: Entity,
|
||||
batch_range: Range<u32>,
|
||||
dynamic_offset: Option<NonMaxU32>,
|
||||
) -> Self {
|
||||
Self {
|
||||
key,
|
||||
representative_entity,
|
||||
batch_range,
|
||||
dynamic_offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedRenderPipelinePhaseItem for AlphaMask3d {
|
||||
#[inline]
|
||||
fn cached_pipeline(&self) -> CachedRenderPipelineId {
|
||||
self.pipeline
|
||||
self.key.pipeline
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -312,9 +341,6 @@ pub struct Transmissive3d {
|
|||
}
|
||||
|
||||
impl PhaseItem for Transmissive3d {
|
||||
// NOTE: Values increase towards the camera. Back-to-front ordering for transmissive means we need an ascending sort.
|
||||
type SortKey = FloatOrd;
|
||||
|
||||
/// For now, automatic batching is disabled for transmissive items because their rendering is
|
||||
/// split into multiple steps depending on [`Camera3d::screen_space_specular_transmission_steps`],
|
||||
/// which the batching system doesn't currently know about.
|
||||
|
@ -331,21 +357,11 @@ impl PhaseItem for Transmissive3d {
|
|||
self.entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
FloatOrd(self.distance)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_function(&self) -> DrawFunctionId {
|
||||
self.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
radsort::sort_by_key(items, |item| item.distance);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn batch_range(&self) -> &Range<u32> {
|
||||
&self.batch_range
|
||||
|
@ -367,6 +383,21 @@ impl PhaseItem for Transmissive3d {
|
|||
}
|
||||
}
|
||||
|
||||
impl SortedPhaseItem for Transmissive3d {
|
||||
// NOTE: Values increase towards the camera. Back-to-front ordering for transmissive means we need an ascending sort.
|
||||
type SortKey = FloatOrd;
|
||||
|
||||
#[inline]
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
FloatOrd(self.distance)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
radsort::sort_by_key(items, |item| item.distance);
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedRenderPipelinePhaseItem for Transmissive3d {
|
||||
#[inline]
|
||||
fn cached_pipeline(&self) -> CachedRenderPipelineId {
|
||||
|
@ -384,29 +415,16 @@ pub struct Transparent3d {
|
|||
}
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_function(&self) -> DrawFunctionId {
|
||||
self.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
radsort::sort_by_key(items, |item| item.distance);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn batch_range(&self) -> &Range<u32> {
|
||||
&self.batch_range
|
||||
|
@ -428,6 +446,21 @@ impl PhaseItem for Transparent3d {
|
|||
}
|
||||
}
|
||||
|
||||
impl SortedPhaseItem 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 sort_key(&self) -> Self::SortKey {
|
||||
FloatOrd(self.distance)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
radsort::sort_by_key(items, |item| item.distance);
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedRenderPipelinePhaseItem for Transparent3d {
|
||||
#[inline]
|
||||
fn cached_pipeline(&self) -> CachedRenderPipelineId {
|
||||
|
@ -442,10 +475,10 @@ pub fn extract_core_3d_camera_phases(
|
|||
for (entity, camera) in &cameras_3d {
|
||||
if camera.is_active {
|
||||
commands.get_or_spawn(entity).insert((
|
||||
RenderPhase::<Opaque3d>::default(),
|
||||
RenderPhase::<AlphaMask3d>::default(),
|
||||
RenderPhase::<Transmissive3d>::default(),
|
||||
RenderPhase::<Transparent3d>::default(),
|
||||
BinnedRenderPhase::<Opaque3d>::default(),
|
||||
BinnedRenderPhase::<AlphaMask3d>::default(),
|
||||
SortedRenderPhase::<Transmissive3d>::default(),
|
||||
SortedRenderPhase::<Transparent3d>::default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -476,15 +509,15 @@ pub fn extract_camera_prepass_phase(
|
|||
|
||||
if depth_prepass || normal_prepass || motion_vector_prepass {
|
||||
entity.insert((
|
||||
RenderPhase::<Opaque3dPrepass>::default(),
|
||||
RenderPhase::<AlphaMask3dPrepass>::default(),
|
||||
BinnedRenderPhase::<Opaque3dPrepass>::default(),
|
||||
BinnedRenderPhase::<AlphaMask3dPrepass>::default(),
|
||||
));
|
||||
}
|
||||
|
||||
if deferred_prepass {
|
||||
entity.insert((
|
||||
RenderPhase::<Opaque3dDeferred>::default(),
|
||||
RenderPhase::<AlphaMask3dDeferred>::default(),
|
||||
BinnedRenderPhase::<Opaque3dDeferred>::default(),
|
||||
BinnedRenderPhase::<AlphaMask3dDeferred>::default(),
|
||||
));
|
||||
}
|
||||
|
||||
|
@ -512,10 +545,10 @@ pub fn prepare_core_3d_depth_textures(
|
|||
views_3d: Query<
|
||||
(Entity, &ExtractedCamera, Option<&DepthPrepass>, &Camera3d),
|
||||
(
|
||||
With<RenderPhase<Opaque3d>>,
|
||||
With<RenderPhase<AlphaMask3d>>,
|
||||
With<RenderPhase<Transmissive3d>>,
|
||||
With<RenderPhase<Transparent3d>>,
|
||||
With<BinnedRenderPhase<Opaque3d>>,
|
||||
With<BinnedRenderPhase<AlphaMask3d>>,
|
||||
With<SortedRenderPhase<Transmissive3d>>,
|
||||
With<SortedRenderPhase<Transparent3d>>,
|
||||
),
|
||||
>,
|
||||
) {
|
||||
|
@ -595,12 +628,12 @@ pub fn prepare_core_3d_transmission_textures(
|
|||
&ExtractedCamera,
|
||||
&Camera3d,
|
||||
&ExtractedView,
|
||||
&RenderPhase<Transmissive3d>,
|
||||
&SortedRenderPhase<Transmissive3d>,
|
||||
),
|
||||
(
|
||||
With<RenderPhase<Opaque3d>>,
|
||||
With<RenderPhase<AlphaMask3d>>,
|
||||
With<RenderPhase<Transparent3d>>,
|
||||
With<BinnedRenderPhase<Opaque3d>>,
|
||||
With<BinnedRenderPhase<AlphaMask3d>>,
|
||||
With<SortedRenderPhase<Transparent3d>>,
|
||||
),
|
||||
>,
|
||||
) {
|
||||
|
@ -700,10 +733,10 @@ pub fn prepare_prepass_textures(
|
|||
Has<DeferredPrepass>,
|
||||
),
|
||||
Or<(
|
||||
With<RenderPhase<Opaque3dPrepass>>,
|
||||
With<RenderPhase<AlphaMask3dPrepass>>,
|
||||
With<RenderPhase<Opaque3dDeferred>>,
|
||||
With<RenderPhase<AlphaMask3dDeferred>>,
|
||||
With<BinnedRenderPhase<Opaque3dPrepass>>,
|
||||
With<BinnedRenderPhase<AlphaMask3dPrepass>>,
|
||||
With<BinnedRenderPhase<Opaque3dDeferred>>,
|
||||
With<BinnedRenderPhase<AlphaMask3dDeferred>>,
|
||||
)>,
|
||||
>,
|
||||
) {
|
||||
|
|
|
@ -3,15 +3,15 @@ pub mod node;
|
|||
|
||||
use std::ops::Range;
|
||||
|
||||
use bevy_asset::AssetId;
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_render::{
|
||||
mesh::Mesh,
|
||||
render_phase::{CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem},
|
||||
render_phase::{BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem},
|
||||
render_resource::{CachedRenderPipelineId, TextureFormat},
|
||||
};
|
||||
use nonmax::NonMaxU32;
|
||||
|
||||
use crate::prepass::OpaqueNoLightmap3dBinKey;
|
||||
|
||||
pub const DEFERRED_PREPASS_FORMAT: TextureFormat = TextureFormat::Rgba32Uint;
|
||||
pub const DEFERRED_LIGHTING_PASS_ID_FORMAT: TextureFormat = TextureFormat::R8Uint;
|
||||
pub const DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT: TextureFormat = TextureFormat::Depth16Unorm;
|
||||
|
@ -21,37 +21,23 @@ pub const DEFERRED_LIGHTING_PASS_ID_DEPTH_FORMAT: TextureFormat = TextureFormat:
|
|||
/// Sorted by pipeline, then by mesh to improve batching.
|
||||
///
|
||||
/// Used to render all 3D meshes with materials that have no transparency.
|
||||
#[derive(PartialEq, Eq, Hash)]
|
||||
pub struct Opaque3dDeferred {
|
||||
pub entity: Entity,
|
||||
pub asset_id: AssetId<Mesh>,
|
||||
pub pipeline_id: CachedRenderPipelineId,
|
||||
pub draw_function: DrawFunctionId,
|
||||
pub key: OpaqueNoLightmap3dBinKey,
|
||||
pub representative_entity: Entity,
|
||||
pub batch_range: Range<u32>,
|
||||
pub dynamic_offset: Option<NonMaxU32>,
|
||||
}
|
||||
|
||||
impl PhaseItem for Opaque3dDeferred {
|
||||
type SortKey = (usize, AssetId<Mesh>);
|
||||
|
||||
#[inline]
|
||||
fn entity(&self) -> Entity {
|
||||
self.entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
// Sort by pipeline, then by mesh to massively decrease drawcall counts in real scenes.
|
||||
(self.pipeline_id.id(), self.asset_id)
|
||||
self.representative_entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_function(&self) -> DrawFunctionId {
|
||||
self.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
items.sort_unstable_by_key(Self::sort_key);
|
||||
self.key.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -75,10 +61,29 @@ impl PhaseItem for Opaque3dDeferred {
|
|||
}
|
||||
}
|
||||
|
||||
impl BinnedPhaseItem for Opaque3dDeferred {
|
||||
type BinKey = OpaqueNoLightmap3dBinKey;
|
||||
|
||||
#[inline]
|
||||
fn new(
|
||||
key: Self::BinKey,
|
||||
representative_entity: Entity,
|
||||
batch_range: Range<u32>,
|
||||
dynamic_offset: Option<NonMaxU32>,
|
||||
) -> Self {
|
||||
Self {
|
||||
key,
|
||||
representative_entity,
|
||||
batch_range,
|
||||
dynamic_offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedRenderPipelinePhaseItem for Opaque3dDeferred {
|
||||
#[inline]
|
||||
fn cached_pipeline(&self) -> CachedRenderPipelineId {
|
||||
self.pipeline_id
|
||||
self.key.pipeline
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -88,36 +93,21 @@ impl CachedRenderPipelinePhaseItem for Opaque3dDeferred {
|
|||
///
|
||||
/// Used to render all meshes with a material with an alpha mask.
|
||||
pub struct AlphaMask3dDeferred {
|
||||
pub asset_id: AssetId<Mesh>,
|
||||
pub entity: Entity,
|
||||
pub pipeline_id: CachedRenderPipelineId,
|
||||
pub draw_function: DrawFunctionId,
|
||||
pub key: OpaqueNoLightmap3dBinKey,
|
||||
pub representative_entity: Entity,
|
||||
pub batch_range: Range<u32>,
|
||||
pub dynamic_offset: Option<NonMaxU32>,
|
||||
}
|
||||
|
||||
impl PhaseItem for AlphaMask3dDeferred {
|
||||
type SortKey = (usize, AssetId<Mesh>);
|
||||
|
||||
#[inline]
|
||||
fn entity(&self) -> Entity {
|
||||
self.entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
// Sort by pipeline, then by mesh to massively decrease drawcall counts in real scenes.
|
||||
(self.pipeline_id.id(), self.asset_id)
|
||||
self.representative_entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_function(&self) -> DrawFunctionId {
|
||||
self.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
items.sort_unstable_by_key(Self::sort_key);
|
||||
self.key.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -141,9 +131,27 @@ impl PhaseItem for AlphaMask3dDeferred {
|
|||
}
|
||||
}
|
||||
|
||||
impl BinnedPhaseItem for AlphaMask3dDeferred {
|
||||
type BinKey = OpaqueNoLightmap3dBinKey;
|
||||
|
||||
fn new(
|
||||
key: Self::BinKey,
|
||||
representative_entity: Entity,
|
||||
batch_range: Range<u32>,
|
||||
dynamic_offset: Option<NonMaxU32>,
|
||||
) -> Self {
|
||||
Self {
|
||||
key,
|
||||
representative_entity,
|
||||
batch_range,
|
||||
dynamic_offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedRenderPipelinePhaseItem for AlphaMask3dDeferred {
|
||||
#[inline]
|
||||
fn cached_pipeline(&self) -> CachedRenderPipelineId {
|
||||
self.pipeline_id
|
||||
self.key.pipeline
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,12 +2,11 @@ use bevy_ecs::prelude::*;
|
|||
use bevy_ecs::query::QueryItem;
|
||||
use bevy_render::render_graph::ViewNode;
|
||||
|
||||
use bevy_render::render_phase::TrackedRenderPass;
|
||||
use bevy_render::render_phase::{BinnedRenderPhase, TrackedRenderPass};
|
||||
use bevy_render::render_resource::{CommandEncoderDescriptor, StoreOp};
|
||||
use bevy_render::{
|
||||
camera::ExtractedCamera,
|
||||
render_graph::{NodeRunError, RenderGraphContext},
|
||||
render_phase::RenderPhase,
|
||||
render_resource::RenderPassDescriptor,
|
||||
renderer::RenderContext,
|
||||
view::ViewDepthTexture,
|
||||
|
@ -28,8 +27,8 @@ pub struct DeferredGBufferPrepassNode;
|
|||
impl ViewNode for DeferredGBufferPrepassNode {
|
||||
type ViewQuery = (
|
||||
&'static ExtractedCamera,
|
||||
&'static RenderPhase<Opaque3dDeferred>,
|
||||
&'static RenderPhase<AlphaMask3dDeferred>,
|
||||
&'static BinnedRenderPhase<Opaque3dDeferred>,
|
||||
&'static BinnedRenderPhase<AlphaMask3dDeferred>,
|
||||
&'static ViewDepthTexture,
|
||||
&'static ViewPrepassTextures,
|
||||
);
|
||||
|
@ -138,14 +137,16 @@ impl ViewNode for DeferredGBufferPrepassNode {
|
|||
}
|
||||
|
||||
// Opaque draws
|
||||
if !opaque_deferred_phase.items.is_empty() {
|
||||
if !opaque_deferred_phase.batchable_keys.is_empty()
|
||||
|| !opaque_deferred_phase.unbatchable_keys.is_empty()
|
||||
{
|
||||
#[cfg(feature = "trace")]
|
||||
let _opaque_prepass_span = info_span!("opaque_deferred").entered();
|
||||
opaque_deferred_phase.render(&mut render_pass, world, view_entity);
|
||||
}
|
||||
|
||||
// Alpha masked draws
|
||||
if !alpha_mask_deferred_phase.items.is_empty() {
|
||||
if !alpha_mask_deferred_phase.is_empty() {
|
||||
#[cfg(feature = "trace")]
|
||||
let _alpha_mask_deferred_span = info_span!("alpha_mask_deferred").entered();
|
||||
alpha_mask_deferred_phase.render(&mut render_pass, world, view_entity);
|
||||
|
|
|
@ -34,8 +34,8 @@ use bevy_ecs::prelude::*;
|
|||
use bevy_reflect::Reflect;
|
||||
use bevy_render::{
|
||||
mesh::Mesh,
|
||||
render_phase::{CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem},
|
||||
render_resource::{CachedRenderPipelineId, Extent3d, TextureFormat, TextureView},
|
||||
render_phase::{BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, PhaseItem},
|
||||
render_resource::{BindGroupId, CachedRenderPipelineId, Extent3d, TextureFormat, TextureView},
|
||||
texture::ColorAttachment,
|
||||
};
|
||||
use nonmax::NonMaxU32;
|
||||
|
@ -111,36 +111,45 @@ impl ViewPrepassTextures {
|
|||
///
|
||||
/// Used to render all 3D meshes with materials that have no transparency.
|
||||
pub struct Opaque3dPrepass {
|
||||
pub entity: Entity,
|
||||
pub asset_id: AssetId<Mesh>,
|
||||
pub pipeline_id: CachedRenderPipelineId,
|
||||
pub draw_function: DrawFunctionId,
|
||||
/// Information that separates items into bins.
|
||||
pub key: OpaqueNoLightmap3dBinKey,
|
||||
|
||||
/// An entity from which Bevy fetches data common to all instances in this
|
||||
/// batch, such as the mesh.
|
||||
pub representative_entity: Entity,
|
||||
|
||||
pub batch_range: Range<u32>,
|
||||
pub dynamic_offset: Option<NonMaxU32>,
|
||||
}
|
||||
|
||||
impl PhaseItem for Opaque3dPrepass {
|
||||
type SortKey = (usize, AssetId<Mesh>);
|
||||
// TODO: Try interning these.
|
||||
/// The data used to bin each opaque 3D mesh in the prepass and deferred pass.
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct OpaqueNoLightmap3dBinKey {
|
||||
/// The ID of the GPU pipeline.
|
||||
pub pipeline: CachedRenderPipelineId,
|
||||
|
||||
/// The function used to draw the mesh.
|
||||
pub draw_function: DrawFunctionId,
|
||||
|
||||
/// The ID of the mesh.
|
||||
pub asset_id: AssetId<Mesh>,
|
||||
|
||||
/// The ID of a bind group specific to the material.
|
||||
///
|
||||
/// In the case of PBR, this is the `MaterialBindGroupId`.
|
||||
pub material_bind_group_id: Option<BindGroupId>,
|
||||
}
|
||||
|
||||
impl PhaseItem for Opaque3dPrepass {
|
||||
#[inline]
|
||||
fn entity(&self) -> Entity {
|
||||
self.entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
// Sort by pipeline, then by mesh to massively decrease drawcall counts in real scenes.
|
||||
(self.pipeline_id.id(), self.asset_id)
|
||||
self.representative_entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_function(&self) -> DrawFunctionId {
|
||||
self.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
items.sort_unstable_by_key(Self::sort_key);
|
||||
self.key.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -164,10 +173,29 @@ impl PhaseItem for Opaque3dPrepass {
|
|||
}
|
||||
}
|
||||
|
||||
impl BinnedPhaseItem for Opaque3dPrepass {
|
||||
type BinKey = OpaqueNoLightmap3dBinKey;
|
||||
|
||||
#[inline]
|
||||
fn new(
|
||||
key: Self::BinKey,
|
||||
representative_entity: Entity,
|
||||
batch_range: Range<u32>,
|
||||
dynamic_offset: Option<NonMaxU32>,
|
||||
) -> Self {
|
||||
Opaque3dPrepass {
|
||||
key,
|
||||
representative_entity,
|
||||
batch_range,
|
||||
dynamic_offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedRenderPipelinePhaseItem for Opaque3dPrepass {
|
||||
#[inline]
|
||||
fn cached_pipeline(&self) -> CachedRenderPipelineId {
|
||||
self.pipeline_id
|
||||
self.key.pipeline
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -177,36 +205,21 @@ impl CachedRenderPipelinePhaseItem for Opaque3dPrepass {
|
|||
///
|
||||
/// Used to render all meshes with a material with an alpha mask.
|
||||
pub struct AlphaMask3dPrepass {
|
||||
pub asset_id: AssetId<Mesh>,
|
||||
pub entity: Entity,
|
||||
pub pipeline_id: CachedRenderPipelineId,
|
||||
pub draw_function: DrawFunctionId,
|
||||
pub key: OpaqueNoLightmap3dBinKey,
|
||||
pub representative_entity: Entity,
|
||||
pub batch_range: Range<u32>,
|
||||
pub dynamic_offset: Option<NonMaxU32>,
|
||||
}
|
||||
|
||||
impl PhaseItem for AlphaMask3dPrepass {
|
||||
type SortKey = (usize, AssetId<Mesh>);
|
||||
|
||||
#[inline]
|
||||
fn entity(&self) -> Entity {
|
||||
self.entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
// Sort by pipeline, then by mesh to massively decrease drawcall counts in real scenes.
|
||||
(self.pipeline_id.id(), self.asset_id)
|
||||
self.representative_entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_function(&self) -> DrawFunctionId {
|
||||
self.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
items.sort_unstable_by_key(Self::sort_key);
|
||||
self.key.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -230,9 +243,28 @@ impl PhaseItem for AlphaMask3dPrepass {
|
|||
}
|
||||
}
|
||||
|
||||
impl BinnedPhaseItem for AlphaMask3dPrepass {
|
||||
type BinKey = OpaqueNoLightmap3dBinKey;
|
||||
|
||||
#[inline]
|
||||
fn new(
|
||||
key: Self::BinKey,
|
||||
representative_entity: Entity,
|
||||
batch_range: Range<u32>,
|
||||
dynamic_offset: Option<NonMaxU32>,
|
||||
) -> Self {
|
||||
Self {
|
||||
key,
|
||||
representative_entity,
|
||||
batch_range,
|
||||
dynamic_offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedRenderPipelinePhaseItem for AlphaMask3dPrepass {
|
||||
#[inline]
|
||||
fn cached_pipeline(&self) -> CachedRenderPipelineId {
|
||||
self.pipeline_id
|
||||
self.key.pipeline
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,7 +4,7 @@ use bevy_render::{
|
|||
camera::ExtractedCamera,
|
||||
diagnostic::RecordDiagnostics,
|
||||
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
|
||||
render_phase::{RenderPhase, TrackedRenderPass},
|
||||
render_phase::{BinnedRenderPhase, TrackedRenderPass},
|
||||
render_resource::{CommandEncoderDescriptor, RenderPassDescriptor, StoreOp},
|
||||
renderer::RenderContext,
|
||||
view::ViewDepthTexture,
|
||||
|
@ -23,8 +23,8 @@ pub struct PrepassNode;
|
|||
impl ViewNode for PrepassNode {
|
||||
type ViewQuery = (
|
||||
&'static ExtractedCamera,
|
||||
&'static RenderPhase<Opaque3dPrepass>,
|
||||
&'static RenderPhase<AlphaMask3dPrepass>,
|
||||
&'static BinnedRenderPhase<Opaque3dPrepass>,
|
||||
&'static BinnedRenderPhase<AlphaMask3dPrepass>,
|
||||
&'static ViewDepthTexture,
|
||||
&'static ViewPrepassTextures,
|
||||
Option<&'static DeferredPrepass>,
|
||||
|
@ -95,14 +95,16 @@ impl ViewNode for PrepassNode {
|
|||
}
|
||||
|
||||
// Opaque draws
|
||||
if !opaque_prepass_phase.items.is_empty() {
|
||||
if !opaque_prepass_phase.batchable_keys.is_empty()
|
||||
|| !opaque_prepass_phase.unbatchable_keys.is_empty()
|
||||
{
|
||||
#[cfg(feature = "trace")]
|
||||
let _opaque_prepass_span = info_span!("opaque_prepass").entered();
|
||||
opaque_prepass_phase.render(&mut render_pass, world, view_entity);
|
||||
}
|
||||
|
||||
// Alpha masked draws
|
||||
if !alpha_mask_prepass_phase.items.is_empty() {
|
||||
if !alpha_mask_prepass_phase.is_empty() {
|
||||
#[cfg(feature = "trace")]
|
||||
let _alpha_mask_prepass_span = info_span!("alpha_mask_prepass").entered();
|
||||
alpha_mask_prepass_phase.render(&mut render_pass, world, view_entity);
|
||||
|
|
|
@ -17,7 +17,7 @@ use bevy_ecs::{
|
|||
use bevy_math::FloatOrd;
|
||||
use bevy_render::{
|
||||
render_asset::{prepare_assets, RenderAssets},
|
||||
render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
|
||||
render_phase::{AddRenderCommand, DrawFunctions, SetItemPipeline, SortedRenderPhase},
|
||||
render_resource::*,
|
||||
texture::BevyDefault,
|
||||
view::{ExtractedView, Msaa, RenderLayers, ViewTarget},
|
||||
|
@ -256,7 +256,7 @@ fn queue_line_gizmos_2d(
|
|||
line_gizmo_assets: Res<RenderAssets<LineGizmo>>,
|
||||
mut views: Query<(
|
||||
&ExtractedView,
|
||||
&mut RenderPhase<Transparent2d>,
|
||||
&mut SortedRenderPhase<Transparent2d>,
|
||||
Option<&RenderLayers>,
|
||||
)>,
|
||||
) {
|
||||
|
@ -309,7 +309,7 @@ fn queue_line_joint_gizmos_2d(
|
|||
line_gizmo_assets: Res<RenderAssets<LineGizmo>>,
|
||||
mut views: Query<(
|
||||
&ExtractedView,
|
||||
&mut RenderPhase<Transparent2d>,
|
||||
&mut SortedRenderPhase<Transparent2d>,
|
||||
Option<&RenderLayers>,
|
||||
)>,
|
||||
) {
|
||||
|
|
|
@ -21,7 +21,7 @@ use bevy_ecs::{
|
|||
use bevy_pbr::{MeshPipeline, MeshPipelineKey, SetMeshViewBindGroup};
|
||||
use bevy_render::{
|
||||
render_asset::{prepare_assets, RenderAssets},
|
||||
render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
|
||||
render_phase::{AddRenderCommand, DrawFunctions, SetItemPipeline, SortedRenderPhase},
|
||||
render_resource::*,
|
||||
texture::BevyDefault,
|
||||
view::{ExtractedView, Msaa, RenderLayers, ViewTarget},
|
||||
|
@ -281,7 +281,7 @@ fn queue_line_gizmos_3d(
|
|||
line_gizmo_assets: Res<RenderAssets<LineGizmo>>,
|
||||
mut views: Query<(
|
||||
&ExtractedView,
|
||||
&mut RenderPhase<Transparent3d>,
|
||||
&mut SortedRenderPhase<Transparent3d>,
|
||||
Option<&RenderLayers>,
|
||||
(
|
||||
Has<NormalPrepass>,
|
||||
|
@ -364,7 +364,7 @@ fn queue_line_joint_gizmos_3d(
|
|||
line_gizmo_assets: Res<RenderAssets<LineGizmo>>,
|
||||
mut views: Query<(
|
||||
&ExtractedView,
|
||||
&mut RenderPhase<Transparent3d>,
|
||||
&mut SortedRenderPhase<Transparent3d>,
|
||||
Option<&RenderLayers>,
|
||||
(
|
||||
Has<NormalPrepass>,
|
||||
|
|
|
@ -94,7 +94,6 @@ use bevy_render::{
|
|||
extract_resource::ExtractResourcePlugin,
|
||||
render_asset::prepare_assets,
|
||||
render_graph::RenderGraph,
|
||||
render_phase::sort_phase_system,
|
||||
render_resource::Shader,
|
||||
texture::Image,
|
||||
view::VisibilitySystems,
|
||||
|
@ -375,7 +374,6 @@ impl Plugin for PbrPlugin {
|
|||
prepare_lights
|
||||
.in_set(RenderSet::ManageViews)
|
||||
.after(prepare_assets::<Image>),
|
||||
sort_phase_system::<Shadow>.in_set(RenderSet::PhaseSort),
|
||||
prepare_clusters.in_set(RenderSet::PrepareResources),
|
||||
),
|
||||
)
|
||||
|
@ -390,7 +388,7 @@ impl Plugin for PbrPlugin {
|
|||
render_app.ignore_ambiguity(
|
||||
bevy_render::Render,
|
||||
bevy_core_pipeline::core_3d::prepare_core_3d_transmission_textures,
|
||||
bevy_render::batching::batch_and_prepare_render_phase::<
|
||||
bevy_render::batching::batch_and_prepare_sorted_render_phase::<
|
||||
bevy_core_pipeline::core_3d::Transmissive3d,
|
||||
MeshPipeline,
|
||||
>,
|
||||
|
|
|
@ -7,10 +7,12 @@ use crate::*;
|
|||
use bevy_asset::{Asset, AssetEvent, AssetId, AssetServer};
|
||||
use bevy_core_pipeline::{
|
||||
core_3d::{
|
||||
AlphaMask3d, Camera3d, Opaque3d, ScreenSpaceTransmissionQuality, Transmissive3d,
|
||||
Transparent3d,
|
||||
AlphaMask3d, Camera3d, Opaque3d, Opaque3dBinKey, ScreenSpaceTransmissionQuality,
|
||||
Transmissive3d, Transparent3d,
|
||||
},
|
||||
prepass::{
|
||||
DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass, OpaqueNoLightmap3dBinKey,
|
||||
},
|
||||
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass, NormalPrepass},
|
||||
tonemapping::{DebandDither, Tonemapping},
|
||||
};
|
||||
use bevy_derive::{Deref, DerefMut};
|
||||
|
@ -541,10 +543,10 @@ pub fn queue_material_meshes<M: Material>(
|
|||
Option<&Camera3d>,
|
||||
Has<TemporalJitter>,
|
||||
Option<&Projection>,
|
||||
&mut RenderPhase<Opaque3d>,
|
||||
&mut RenderPhase<AlphaMask3d>,
|
||||
&mut RenderPhase<Transmissive3d>,
|
||||
&mut RenderPhase<Transparent3d>,
|
||||
&mut BinnedRenderPhase<Opaque3d>,
|
||||
&mut BinnedRenderPhase<AlphaMask3d>,
|
||||
&mut SortedRenderPhase<Transmissive3d>,
|
||||
&mut SortedRenderPhase<Transparent3d>,
|
||||
(
|
||||
Has<RenderViewLightProbes<EnvironmentMapLight>>,
|
||||
Has<RenderViewLightProbes<IrradianceVolume>>,
|
||||
|
@ -679,10 +681,11 @@ pub fn queue_material_meshes<M: Material>(
|
|||
|
||||
mesh_key |= alpha_mode_pipeline_key(material.properties.alpha_mode);
|
||||
|
||||
if render_lightmaps
|
||||
let lightmap_image = render_lightmaps
|
||||
.render_lightmaps
|
||||
.contains_key(visible_entity)
|
||||
{
|
||||
.get(visible_entity)
|
||||
.map(|lightmap| lightmap.image);
|
||||
if lightmap_image.is_some() {
|
||||
mesh_key |= MeshPipelineKey::LIGHTMAPPED;
|
||||
}
|
||||
|
||||
|
@ -722,14 +725,14 @@ pub fn queue_material_meshes<M: Material>(
|
|||
dynamic_offset: None,
|
||||
});
|
||||
} else if forward {
|
||||
opaque_phase.add(Opaque3d {
|
||||
entity: *visible_entity,
|
||||
let bin_key = Opaque3dBinKey {
|
||||
draw_function: draw_opaque_pbr,
|
||||
pipeline: pipeline_id,
|
||||
asset_id: mesh_instance.mesh_asset_id,
|
||||
batch_range: 0..1,
|
||||
dynamic_offset: None,
|
||||
});
|
||||
material_bind_group_id: material.get_bind_group_id().0,
|
||||
lightmap_image,
|
||||
};
|
||||
opaque_phase.add(bin_key, *visible_entity, mesh_instance.should_batch());
|
||||
}
|
||||
}
|
||||
AlphaMode::Mask(_) => {
|
||||
|
@ -746,14 +749,17 @@ pub fn queue_material_meshes<M: Material>(
|
|||
dynamic_offset: None,
|
||||
});
|
||||
} else if forward {
|
||||
alpha_mask_phase.add(AlphaMask3d {
|
||||
entity: *visible_entity,
|
||||
let bin_key = OpaqueNoLightmap3dBinKey {
|
||||
draw_function: draw_alpha_mask_pbr,
|
||||
pipeline: pipeline_id,
|
||||
asset_id: mesh_instance.mesh_asset_id,
|
||||
batch_range: 0..1,
|
||||
dynamic_offset: None,
|
||||
});
|
||||
material_bind_group_id: material.get_bind_group_id().0,
|
||||
};
|
||||
alpha_mask_phase.add(
|
||||
bin_key,
|
||||
*visible_entity,
|
||||
mesh_instance.should_batch(),
|
||||
);
|
||||
}
|
||||
}
|
||||
AlphaMode::Blend
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
mod prepass_bindings;
|
||||
|
||||
use bevy_render::batching::{batch_and_prepare_binned_render_phase, sort_binned_render_phase};
|
||||
use bevy_render::mesh::MeshVertexBufferLayoutRef;
|
||||
use bevy_render::render_resource::binding_types::uniform_buffer;
|
||||
pub use prepass_bindings::*;
|
||||
|
@ -16,7 +17,6 @@ use bevy_ecs::{
|
|||
};
|
||||
use bevy_math::{Affine3A, Mat4};
|
||||
use bevy_render::{
|
||||
batching::batch_and_prepare_render_phase,
|
||||
globals::{GlobalsBuffer, GlobalsUniform},
|
||||
prelude::{Camera, Mesh},
|
||||
render_asset::RenderAssets,
|
||||
|
@ -155,11 +155,17 @@ where
|
|||
.add_systems(
|
||||
Render,
|
||||
(
|
||||
prepare_previous_view_projection_uniforms,
|
||||
batch_and_prepare_render_phase::<Opaque3dPrepass, MeshPipeline>,
|
||||
batch_and_prepare_render_phase::<AlphaMask3dPrepass, MeshPipeline>,
|
||||
(
|
||||
sort_binned_render_phase::<Opaque3dPrepass>,
|
||||
sort_binned_render_phase::<AlphaMask3dPrepass>
|
||||
).in_set(RenderSet::PhaseSort),
|
||||
(
|
||||
prepare_previous_view_projection_uniforms,
|
||||
batch_and_prepare_binned_render_phase::<Opaque3dPrepass, MeshPipeline>,
|
||||
batch_and_prepare_binned_render_phase::<AlphaMask3dPrepass,
|
||||
MeshPipeline>,
|
||||
).in_set(RenderSet::PrepareResources),
|
||||
)
|
||||
.in_set(RenderSet::PrepareResources),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -710,20 +716,20 @@ pub fn queue_prepass_material_meshes<M: Material>(
|
|||
(
|
||||
&ExtractedView,
|
||||
&VisibleEntities,
|
||||
Option<&mut RenderPhase<Opaque3dPrepass>>,
|
||||
Option<&mut RenderPhase<AlphaMask3dPrepass>>,
|
||||
Option<&mut RenderPhase<Opaque3dDeferred>>,
|
||||
Option<&mut RenderPhase<AlphaMask3dDeferred>>,
|
||||
Option<&mut BinnedRenderPhase<Opaque3dPrepass>>,
|
||||
Option<&mut BinnedRenderPhase<AlphaMask3dPrepass>>,
|
||||
Option<&mut BinnedRenderPhase<Opaque3dDeferred>>,
|
||||
Option<&mut BinnedRenderPhase<AlphaMask3dDeferred>>,
|
||||
Option<&DepthPrepass>,
|
||||
Option<&NormalPrepass>,
|
||||
Option<&MotionVectorPrepass>,
|
||||
Option<&DeferredPrepass>,
|
||||
),
|
||||
Or<(
|
||||
With<RenderPhase<Opaque3dPrepass>>,
|
||||
With<RenderPhase<AlphaMask3dPrepass>>,
|
||||
With<RenderPhase<Opaque3dDeferred>>,
|
||||
With<RenderPhase<AlphaMask3dDeferred>>,
|
||||
With<BinnedRenderPhase<Opaque3dPrepass>>,
|
||||
With<BinnedRenderPhase<AlphaMask3dPrepass>>,
|
||||
With<BinnedRenderPhase<Opaque3dDeferred>>,
|
||||
With<BinnedRenderPhase<AlphaMask3dDeferred>>,
|
||||
)>,
|
||||
>,
|
||||
) where
|
||||
|
@ -848,50 +854,54 @@ pub fn queue_prepass_material_meshes<M: Material>(
|
|||
match alpha_mode {
|
||||
AlphaMode::Opaque => {
|
||||
if deferred {
|
||||
opaque_deferred_phase
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.add(Opaque3dDeferred {
|
||||
entity: *visible_entity,
|
||||
opaque_deferred_phase.as_mut().unwrap().add(
|
||||
OpaqueNoLightmap3dBinKey {
|
||||
draw_function: opaque_draw_deferred,
|
||||
pipeline_id,
|
||||
pipeline: pipeline_id,
|
||||
asset_id: mesh_instance.mesh_asset_id,
|
||||
batch_range: 0..1,
|
||||
dynamic_offset: None,
|
||||
});
|
||||
material_bind_group_id: material.get_bind_group_id().0,
|
||||
},
|
||||
*visible_entity,
|
||||
mesh_instance.should_batch(),
|
||||
);
|
||||
} else if let Some(opaque_phase) = opaque_phase.as_mut() {
|
||||
opaque_phase.add(Opaque3dPrepass {
|
||||
entity: *visible_entity,
|
||||
draw_function: opaque_draw_prepass,
|
||||
pipeline_id,
|
||||
asset_id: mesh_instance.mesh_asset_id,
|
||||
batch_range: 0..1,
|
||||
dynamic_offset: None,
|
||||
});
|
||||
opaque_phase.add(
|
||||
OpaqueNoLightmap3dBinKey {
|
||||
draw_function: opaque_draw_prepass,
|
||||
pipeline: pipeline_id,
|
||||
asset_id: mesh_instance.mesh_asset_id,
|
||||
material_bind_group_id: material.get_bind_group_id().0,
|
||||
},
|
||||
*visible_entity,
|
||||
mesh_instance.should_batch(),
|
||||
);
|
||||
}
|
||||
}
|
||||
AlphaMode::Mask(_) => {
|
||||
if deferred {
|
||||
alpha_mask_deferred_phase
|
||||
.as_mut()
|
||||
.unwrap()
|
||||
.add(AlphaMask3dDeferred {
|
||||
entity: *visible_entity,
|
||||
draw_function: alpha_mask_draw_deferred,
|
||||
pipeline_id,
|
||||
asset_id: mesh_instance.mesh_asset_id,
|
||||
batch_range: 0..1,
|
||||
dynamic_offset: None,
|
||||
});
|
||||
} else if let Some(alpha_mask_phase) = alpha_mask_phase.as_mut() {
|
||||
alpha_mask_phase.add(AlphaMask3dPrepass {
|
||||
entity: *visible_entity,
|
||||
draw_function: alpha_mask_draw_prepass,
|
||||
pipeline_id,
|
||||
let bin_key = OpaqueNoLightmap3dBinKey {
|
||||
pipeline: pipeline_id,
|
||||
draw_function: alpha_mask_draw_deferred,
|
||||
asset_id: mesh_instance.mesh_asset_id,
|
||||
batch_range: 0..1,
|
||||
dynamic_offset: None,
|
||||
});
|
||||
material_bind_group_id: material.get_bind_group_id().0,
|
||||
};
|
||||
alpha_mask_deferred_phase.as_mut().unwrap().add(
|
||||
bin_key,
|
||||
*visible_entity,
|
||||
mesh_instance.should_batch(),
|
||||
);
|
||||
} else if let Some(alpha_mask_phase) = alpha_mask_phase.as_mut() {
|
||||
let bin_key = OpaqueNoLightmap3dBinKey {
|
||||
pipeline: pipeline_id,
|
||||
draw_function: alpha_mask_draw_prepass,
|
||||
asset_id: mesh_instance.mesh_asset_id,
|
||||
material_bind_group_id: material.get_bind_group_id().0,
|
||||
};
|
||||
alpha_mask_phase.add(
|
||||
bin_key,
|
||||
*visible_entity,
|
||||
mesh_instance.should_batch(),
|
||||
);
|
||||
}
|
||||
}
|
||||
AlphaMode::Blend
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use bevy_asset::AssetId;
|
||||
use bevy_core_pipeline::core_3d::{Transparent3d, CORE_3D_DEPTH_FORMAT};
|
||||
use bevy_ecs::entity::EntityHashMap;
|
||||
use bevy_ecs::prelude::*;
|
||||
use bevy_ecs::{entity::EntityHashMap, system::lifetimeless::Read};
|
||||
use bevy_math::{Mat4, UVec3, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles};
|
||||
use bevy_render::{
|
||||
camera::Camera,
|
||||
|
@ -684,7 +685,7 @@ pub fn prepare_lights(
|
|||
mut light_meta: ResMut<LightMeta>,
|
||||
views: Query<
|
||||
(Entity, &ExtractedView, &ExtractedClusterConfig),
|
||||
With<RenderPhase<Transparent3d>>,
|
||||
With<SortedRenderPhase<Transparent3d>>,
|
||||
>,
|
||||
ambient_light: Res<AmbientLight>,
|
||||
point_light_shadow_map: Res<PointLightShadowMap>,
|
||||
|
@ -1057,7 +1058,7 @@ pub fn prepare_lights(
|
|||
color_grading: Default::default(),
|
||||
},
|
||||
*frustum,
|
||||
RenderPhase::<Shadow>::default(),
|
||||
BinnedRenderPhase::<Shadow>::default(),
|
||||
LightEntity::Point {
|
||||
light_entity,
|
||||
face_index,
|
||||
|
@ -1116,7 +1117,7 @@ pub fn prepare_lights(
|
|||
color_grading: Default::default(),
|
||||
},
|
||||
*spot_light_frustum.unwrap(),
|
||||
RenderPhase::<Shadow>::default(),
|
||||
BinnedRenderPhase::<Shadow>::default(),
|
||||
LightEntity::Spot { light_entity },
|
||||
))
|
||||
.id();
|
||||
|
@ -1196,7 +1197,7 @@ pub fn prepare_lights(
|
|||
color_grading: Default::default(),
|
||||
},
|
||||
frustum,
|
||||
RenderPhase::<Shadow>::default(),
|
||||
BinnedRenderPhase::<Shadow>::default(),
|
||||
LightEntity::Directional {
|
||||
light_entity,
|
||||
cascade_index,
|
||||
|
@ -1548,7 +1549,7 @@ pub fn prepare_clusters(
|
|||
render_queue: Res<RenderQueue>,
|
||||
mesh_pipeline: Res<MeshPipeline>,
|
||||
global_light_meta: Res<GlobalLightMeta>,
|
||||
views: Query<(Entity, &ExtractedClustersPointLights), With<RenderPhase<Transparent3d>>>,
|
||||
views: Query<(Entity, &ExtractedClustersPointLights), With<SortedRenderPhase<Transparent3d>>>,
|
||||
) {
|
||||
let render_device = render_device.into_inner();
|
||||
let supports_storage_buffers = matches!(
|
||||
|
@ -1605,7 +1606,7 @@ pub fn queue_shadows<M: Material>(
|
|||
pipeline_cache: Res<PipelineCache>,
|
||||
render_lightmaps: Res<RenderLightmaps>,
|
||||
view_lights: Query<(Entity, &ViewLightEntities)>,
|
||||
mut view_light_shadow_phases: Query<(&LightEntity, &mut RenderPhase<Shadow>)>,
|
||||
mut view_light_shadow_phases: Query<(&LightEntity, &mut BinnedRenderPhase<Shadow>)>,
|
||||
point_light_entities: Query<&CubemapVisibleEntities, With<ExtractedPointLight>>,
|
||||
directional_light_entities: Query<&CascadesVisibleEntities, With<ExtractedDirectionalLight>>,
|
||||
spot_light_entities: Query<&VisibleEntities, With<ExtractedPointLight>>,
|
||||
|
@ -1708,52 +1709,48 @@ pub fn queue_shadows<M: Material>(
|
|||
.material_bind_group_id
|
||||
.set(material.get_bind_group_id());
|
||||
|
||||
shadow_phase.add(Shadow {
|
||||
draw_function: draw_shadow_mesh,
|
||||
pipeline: pipeline_id,
|
||||
shadow_phase.add(
|
||||
ShadowBinKey {
|
||||
draw_function: draw_shadow_mesh,
|
||||
pipeline: pipeline_id,
|
||||
asset_id: mesh_instance.mesh_asset_id,
|
||||
},
|
||||
entity,
|
||||
distance: 0.0, // TODO: sort front-to-back
|
||||
batch_range: 0..1,
|
||||
dynamic_offset: None,
|
||||
});
|
||||
mesh_instance.should_batch(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Shadow {
|
||||
pub distance: f32,
|
||||
pub entity: Entity,
|
||||
pub pipeline: CachedRenderPipelineId,
|
||||
pub draw_function: DrawFunctionId,
|
||||
pub key: ShadowBinKey,
|
||||
pub representative_entity: Entity,
|
||||
pub batch_range: Range<u32>,
|
||||
pub dynamic_offset: Option<NonMaxU32>,
|
||||
}
|
||||
|
||||
impl PhaseItem for Shadow {
|
||||
type SortKey = usize;
|
||||
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
pub struct ShadowBinKey {
|
||||
/// The identifier of the render pipeline.
|
||||
pub pipeline: CachedRenderPipelineId,
|
||||
|
||||
/// The function used to draw.
|
||||
pub draw_function: DrawFunctionId,
|
||||
|
||||
/// The mesh.
|
||||
pub asset_id: AssetId<Mesh>,
|
||||
}
|
||||
|
||||
impl PhaseItem for Shadow {
|
||||
#[inline]
|
||||
fn entity(&self) -> Entity {
|
||||
self.entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
self.pipeline.id()
|
||||
self.representative_entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_function(&self) -> DrawFunctionId {
|
||||
self.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
// The shadow phase is sorted by pipeline id for performance reasons.
|
||||
// Grouping all draw commands using the same pipeline together performs
|
||||
// better than rebinding everything at a high rate.
|
||||
radsort::sort_by_key(items, |item| item.sort_key());
|
||||
self.key.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
@ -1777,16 +1774,35 @@ impl PhaseItem for Shadow {
|
|||
}
|
||||
}
|
||||
|
||||
impl BinnedPhaseItem for Shadow {
|
||||
type BinKey = ShadowBinKey;
|
||||
|
||||
#[inline]
|
||||
fn new(
|
||||
key: Self::BinKey,
|
||||
representative_entity: Entity,
|
||||
batch_range: Range<u32>,
|
||||
dynamic_offset: Option<NonMaxU32>,
|
||||
) -> Self {
|
||||
Shadow {
|
||||
key,
|
||||
representative_entity,
|
||||
batch_range,
|
||||
dynamic_offset,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedRenderPipelinePhaseItem for Shadow {
|
||||
#[inline]
|
||||
fn cached_pipeline(&self) -> CachedRenderPipelineId {
|
||||
self.pipeline
|
||||
self.key.pipeline
|
||||
}
|
||||
}
|
||||
|
||||
pub struct ShadowPassNode {
|
||||
main_view_query: QueryState<&'static ViewLightEntities>,
|
||||
view_light_query: QueryState<(&'static ShadowView, &'static RenderPhase<Shadow>)>,
|
||||
main_view_query: QueryState<Read<ViewLightEntities>>,
|
||||
view_light_query: QueryState<(Read<ShadowView>, Read<BinnedRenderPhase<Shadow>>)>,
|
||||
}
|
||||
|
||||
impl ShadowPassNode {
|
||||
|
|
|
@ -13,7 +13,8 @@ use bevy_ecs::{
|
|||
use bevy_math::{Affine3, Rect, UVec2, Vec4};
|
||||
use bevy_render::{
|
||||
batching::{
|
||||
batch_and_prepare_render_phase, write_batched_instance_buffer, GetBatchData,
|
||||
batch_and_prepare_binned_render_phase, batch_and_prepare_sorted_render_phase,
|
||||
sort_binned_render_phase, write_batched_instance_buffer, GetBatchData, GetBinnedBatchData,
|
||||
NoAutomaticBatching,
|
||||
},
|
||||
mesh::*,
|
||||
|
@ -125,13 +126,24 @@ impl Plugin for MeshRenderPlugin {
|
|||
Render,
|
||||
(
|
||||
(
|
||||
batch_and_prepare_render_phase::<Opaque3d, MeshPipeline>,
|
||||
batch_and_prepare_render_phase::<Transmissive3d, MeshPipeline>,
|
||||
batch_and_prepare_render_phase::<Transparent3d, MeshPipeline>,
|
||||
batch_and_prepare_render_phase::<AlphaMask3d, MeshPipeline>,
|
||||
batch_and_prepare_render_phase::<Shadow, MeshPipeline>,
|
||||
batch_and_prepare_render_phase::<Opaque3dDeferred, MeshPipeline>,
|
||||
batch_and_prepare_render_phase::<AlphaMask3dDeferred, MeshPipeline>,
|
||||
sort_binned_render_phase::<Opaque3d>,
|
||||
sort_binned_render_phase::<AlphaMask3d>,
|
||||
sort_binned_render_phase::<Shadow>,
|
||||
sort_binned_render_phase::<Opaque3dDeferred>,
|
||||
sort_binned_render_phase::<AlphaMask3dDeferred>,
|
||||
)
|
||||
.in_set(RenderSet::PhaseSort),
|
||||
(
|
||||
batch_and_prepare_binned_render_phase::<Opaque3d, MeshPipeline>,
|
||||
batch_and_prepare_sorted_render_phase::<Transmissive3d, MeshPipeline>,
|
||||
batch_and_prepare_sorted_render_phase::<Transparent3d, MeshPipeline>,
|
||||
batch_and_prepare_binned_render_phase::<AlphaMask3d, MeshPipeline>,
|
||||
batch_and_prepare_binned_render_phase::<Shadow, MeshPipeline>,
|
||||
batch_and_prepare_binned_render_phase::<Opaque3dDeferred, MeshPipeline>,
|
||||
batch_and_prepare_binned_render_phase::<
|
||||
AlphaMask3dDeferred,
|
||||
MeshPipeline,
|
||||
>,
|
||||
)
|
||||
.in_set(RenderSet::PrepareResources),
|
||||
write_batched_instance_buffer::<MeshPipeline>
|
||||
|
@ -471,6 +483,25 @@ impl GetBatchData for MeshPipeline {
|
|||
}
|
||||
}
|
||||
|
||||
impl GetBinnedBatchData for MeshPipeline {
|
||||
type Param = (SRes<RenderMeshInstances>, SRes<RenderLightmaps>);
|
||||
|
||||
type BufferData = MeshUniform;
|
||||
|
||||
fn get_batch_data(
|
||||
(mesh_instances, lightmaps): &SystemParamItem<Self::Param>,
|
||||
entity: Entity,
|
||||
) -> Option<Self::BufferData> {
|
||||
let mesh_instance = mesh_instances.get(&entity)?;
|
||||
let maybe_lightmap = lightmaps.render_lightmaps.get(&entity);
|
||||
|
||||
Some(MeshUniform::new(
|
||||
&mesh_instance.transforms,
|
||||
maybe_lightmap.map(|lightmap| lightmap.uv_rect),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
#[repr(transparent)]
|
||||
|
|
|
@ -5,9 +5,13 @@ use bevy_ecs::{
|
|||
system::{Query, ResMut, StaticSystemParam, SystemParam, SystemParamItem},
|
||||
};
|
||||
use nonmax::NonMaxU32;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use crate::{
|
||||
render_phase::{CachedRenderPipelinePhaseItem, DrawFunctionId, RenderPhase},
|
||||
render_phase::{
|
||||
BinnedPhaseItem, BinnedRenderPhase, BinnedRenderPhaseBatch, CachedRenderPipelinePhaseItem,
|
||||
DrawFunctionId, SortedPhaseItem, SortedRenderPhase,
|
||||
},
|
||||
render_resource::{CachedRenderPipelineId, GpuArrayBuffer, GpuArrayBufferable},
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
};
|
||||
|
@ -56,6 +60,8 @@ impl<T: PartialEq> BatchMeta<T> {
|
|||
/// A trait to support getting data used for batching draw commands via phase
|
||||
/// items.
|
||||
pub trait GetBatchData {
|
||||
/// The system parameters [`GetBatchData::get_batch_data`] needs in
|
||||
/// order to compute the batch data.
|
||||
type Param: SystemParam + 'static;
|
||||
/// Data used for comparison between phase items. If the pipeline id, draw
|
||||
/// function id, per-instance data buffer dynamic offset and this data
|
||||
|
@ -74,13 +80,35 @@ pub trait GetBatchData {
|
|||
) -> Option<(Self::BufferData, Option<Self::CompareData>)>;
|
||||
}
|
||||
|
||||
/// Batch the items in a render phase. This means comparing metadata needed to draw each phase item
|
||||
/// and trying to combine the draws into a batch.
|
||||
pub fn batch_and_prepare_render_phase<I: CachedRenderPipelinePhaseItem, F: GetBatchData>(
|
||||
/// When implemented on a pipeline, this trait allows the batching logic to
|
||||
/// compute the per-batch data that will be uploaded to the GPU.
|
||||
///
|
||||
/// This includes things like the mesh transforms.
|
||||
pub trait GetBinnedBatchData {
|
||||
/// The system parameters [`GetBinnedBatchData::get_batch_data`] needs
|
||||
/// in order to compute the batch data.
|
||||
type Param: SystemParam + 'static;
|
||||
/// The per-instance data to be inserted into the [`GpuArrayBuffer`]
|
||||
/// containing these data for all instances.
|
||||
type BufferData: GpuArrayBufferable + Sync + Send + 'static;
|
||||
|
||||
/// Get the per-instance data to be inserted into the [`GpuArrayBuffer`].
|
||||
fn get_batch_data(
|
||||
param: &SystemParamItem<Self::Param>,
|
||||
entity: Entity,
|
||||
) -> Option<Self::BufferData>;
|
||||
}
|
||||
|
||||
/// Batch the items in a sorted render phase. This means comparing metadata
|
||||
/// needed to draw each phase item and trying to combine the draws into a batch.
|
||||
pub fn batch_and_prepare_sorted_render_phase<I, F>(
|
||||
gpu_array_buffer: ResMut<GpuArrayBuffer<F::BufferData>>,
|
||||
mut views: Query<&mut RenderPhase<I>>,
|
||||
mut views: Query<&mut SortedRenderPhase<I>>,
|
||||
param: StaticSystemParam<F::Param>,
|
||||
) {
|
||||
) where
|
||||
I: CachedRenderPipelinePhaseItem + SortedPhaseItem,
|
||||
F: GetBatchData,
|
||||
{
|
||||
let gpu_array_buffer = gpu_array_buffer.into_inner();
|
||||
let system_param_item = param.into_inner();
|
||||
|
||||
|
@ -115,6 +143,80 @@ pub fn batch_and_prepare_render_phase<I: CachedRenderPipelinePhaseItem, F: GetBa
|
|||
}
|
||||
}
|
||||
|
||||
/// Sorts a render phase that uses bins.
|
||||
pub fn sort_binned_render_phase<BPI>(mut views: Query<&mut BinnedRenderPhase<BPI>>)
|
||||
where
|
||||
BPI: BinnedPhaseItem,
|
||||
{
|
||||
for mut phase in &mut views {
|
||||
phase.batchable_keys.sort_unstable();
|
||||
phase.unbatchable_keys.sort_unstable();
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates batches for a render phase that uses bins.
|
||||
pub fn batch_and_prepare_binned_render_phase<BPI, GBBD>(
|
||||
gpu_array_buffer: ResMut<GpuArrayBuffer<GBBD::BufferData>>,
|
||||
mut views: Query<&mut BinnedRenderPhase<BPI>>,
|
||||
param: StaticSystemParam<GBBD::Param>,
|
||||
) where
|
||||
BPI: BinnedPhaseItem,
|
||||
GBBD: GetBinnedBatchData,
|
||||
{
|
||||
let gpu_array_buffer = gpu_array_buffer.into_inner();
|
||||
let system_param_item = param.into_inner();
|
||||
|
||||
for mut phase in &mut views {
|
||||
let phase = &mut *phase; // Borrow checker.
|
||||
|
||||
// Prepare batchables.
|
||||
|
||||
for key in &phase.batchable_keys {
|
||||
let mut batch_set: SmallVec<[BinnedRenderPhaseBatch; 1]> = smallvec![];
|
||||
for &entity in &phase.batchable_values[key] {
|
||||
let Some(buffer_data) = GBBD::get_batch_data(&system_param_item, entity) else {
|
||||
continue;
|
||||
};
|
||||
|
||||
let instance = gpu_array_buffer.push(buffer_data);
|
||||
|
||||
// If the dynamic offset has changed, flush the batch.
|
||||
//
|
||||
// This is the only time we ever have more than one batch per
|
||||
// bin. Note that dynamic offsets are only used on platforms
|
||||
// with no storage buffers.
|
||||
if !batch_set.last().is_some_and(|batch| {
|
||||
batch.instance_range.end == instance.index
|
||||
&& batch.dynamic_offset == instance.dynamic_offset
|
||||
}) {
|
||||
batch_set.push(BinnedRenderPhaseBatch {
|
||||
representative_entity: entity,
|
||||
instance_range: instance.index..instance.index,
|
||||
dynamic_offset: instance.dynamic_offset,
|
||||
});
|
||||
}
|
||||
|
||||
if let Some(batch) = batch_set.last_mut() {
|
||||
batch.instance_range.end = instance.index + 1;
|
||||
}
|
||||
}
|
||||
|
||||
phase.batch_sets.push(batch_set);
|
||||
}
|
||||
|
||||
// Prepare unbatchables.
|
||||
for key in &phase.unbatchable_keys {
|
||||
let unbatchables = phase.unbatchable_values.get_mut(key).unwrap();
|
||||
for &entity in &unbatchables.entities {
|
||||
if let Some(buffer_data) = GBBD::get_batch_data(&system_param_item, entity) {
|
||||
let instance = gpu_array_buffer.push(buffer_data);
|
||||
unbatchables.buffer_indices.add(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn write_batched_instance_buffer<F: GetBatchData>(
|
||||
render_device: Res<RenderDevice>,
|
||||
render_queue: Res<RenderQueue>,
|
||||
|
|
|
@ -110,13 +110,15 @@ pub enum RenderSet {
|
|||
PrepareAssets,
|
||||
/// Create any additional views such as those used for shadow mapping.
|
||||
ManageViews,
|
||||
/// Queue drawable entities as phase items in [`RenderPhase`](crate::render_phase::RenderPhase)s
|
||||
/// ready for sorting
|
||||
/// Queue drawable entities as phase items in render phases ready for
|
||||
/// sorting (if necessary)
|
||||
Queue,
|
||||
/// A sub-set within [`Queue`](RenderSet::Queue) where mesh entity queue systems are executed. Ensures `prepare_assets::<Mesh>` is completed.
|
||||
QueueMeshes,
|
||||
// TODO: This could probably be moved in favor of a system ordering abstraction in `Render` or `Queue`
|
||||
/// Sort the [`RenderPhases`](render_phase::RenderPhase) here.
|
||||
// TODO: This could probably be moved in favor of a system ordering
|
||||
// abstraction in `Render` or `Queue`
|
||||
/// Sort the [`SortedRenderPhase`](render_phase::SortedRenderPhase)s and
|
||||
/// [`BinKey`](render_phase::BinnedPhaseItem::BinKey)s here.
|
||||
PhaseSort,
|
||||
/// Prepare render resources from extracted data for the GPU based on their sorted order.
|
||||
/// Create [`BindGroups`](render_resource::BindGroup) that depend on those data.
|
||||
|
|
|
@ -39,7 +39,7 @@ pub trait Draw<P: PhaseItem>: Send + Sync + 'static {
|
|||
|
||||
// TODO: make this generic?
|
||||
/// An identifier for a [`Draw`] function stored in [`DrawFunctions`].
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash)]
|
||||
#[derive(Copy, Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Hash)]
|
||||
pub struct DrawFunctionId(u32);
|
||||
|
||||
/// Stores all [`Draw`] functions for the [`PhaseItem`] type.
|
||||
|
@ -209,6 +209,7 @@ pub trait RenderCommand<P: PhaseItem> {
|
|||
}
|
||||
|
||||
/// The result of a [`RenderCommand`].
|
||||
#[derive(Debug)]
|
||||
pub enum RenderCommandResult {
|
||||
Success,
|
||||
Failure,
|
||||
|
@ -301,7 +302,7 @@ where
|
|||
/// Registers a [`RenderCommand`] as a [`Draw`] function.
|
||||
/// They are stored inside the [`DrawFunctions`] resource of the app.
|
||||
pub trait AddRenderCommand {
|
||||
/// Adds the [`RenderCommand`] for the specified [`RenderPhase`](super::RenderPhase) to the app.
|
||||
/// Adds the [`RenderCommand`] for the specified render phase to the app.
|
||||
fn add_render_command<P: PhaseItem, C: RenderCommand<P> + Send + Sync + 'static>(
|
||||
&mut self,
|
||||
) -> &mut Self
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
//! The modular rendering abstraction responsible for queuing, preparing, sorting and drawing
|
||||
//! entities as part of separate render phases.
|
||||
//!
|
||||
//! In Bevy each view (camera, or shadow-casting light, etc.) has one or multiple [`RenderPhase`]s
|
||||
//! In Bevy each view (camera, or shadow-casting light, etc.) has one or multiple render phases
|
||||
//! (e.g. opaque, transparent, shadow, etc).
|
||||
//! They are used to queue entities for rendering.
|
||||
//! Multiple phases might be required due to different sorting/batching behaviors
|
||||
|
@ -29,17 +29,20 @@ mod draw;
|
|||
mod draw_state;
|
||||
mod rangefinder;
|
||||
|
||||
use bevy_utils::{default, hashbrown::hash_map::Entry, HashMap};
|
||||
pub use draw::*;
|
||||
pub use draw_state::*;
|
||||
use encase::{internal::WriteInto, ShaderSize};
|
||||
use nonmax::NonMaxU32;
|
||||
pub use rangefinder::*;
|
||||
|
||||
use crate::render_resource::{CachedRenderPipelineId, PipelineCache};
|
||||
use crate::render_resource::{CachedRenderPipelineId, GpuArrayBufferIndex, PipelineCache};
|
||||
use bevy_ecs::{
|
||||
prelude::*,
|
||||
system::{lifetimeless::SRes, SystemParamItem},
|
||||
};
|
||||
use std::{ops::Range, slice::SliceIndex};
|
||||
use smallvec::SmallVec;
|
||||
use std::{hash::Hash, ops::Range, slice::SliceIndex};
|
||||
|
||||
/// A collection of all rendering instructions, that will be executed by the GPU, for a
|
||||
/// single render phase for a single view.
|
||||
|
@ -51,18 +54,351 @@ use std::{ops::Range, slice::SliceIndex};
|
|||
/// the rendered texture of the previous phase (e.g. for screen-space reflections).
|
||||
/// All [`PhaseItem`]s are then rendered using a single [`TrackedRenderPass`].
|
||||
/// The render pass might be reused for multiple phases to reduce GPU overhead.
|
||||
///
|
||||
/// This flavor of render phase is used for phases in which the ordering is less
|
||||
/// critical: for example, `Opaque3d`. It's generally faster than the
|
||||
/// alternative [`SortedRenderPhase`].
|
||||
#[derive(Component)]
|
||||
pub struct RenderPhase<I: PhaseItem> {
|
||||
pub struct BinnedRenderPhase<BPI>
|
||||
where
|
||||
BPI: BinnedPhaseItem,
|
||||
{
|
||||
/// A list of `BinKey`s for batchable items.
|
||||
///
|
||||
/// These are accumulated in `queue_material_meshes` and then sorted in
|
||||
/// `batch_and_prepare_binned_render_phase`.
|
||||
pub batchable_keys: Vec<BPI::BinKey>,
|
||||
|
||||
/// The batchable bins themselves.
|
||||
///
|
||||
/// Each bin corresponds to a single batch set. For unbatchable entities,
|
||||
/// prefer `unbatchable_values` instead.
|
||||
pub(crate) batchable_values: HashMap<BPI::BinKey, Vec<Entity>>,
|
||||
|
||||
/// A list of `BinKey`s for unbatchable items.
|
||||
///
|
||||
/// These are accumulated in `queue_material_meshes` and then sorted in
|
||||
/// `batch_and_prepare_binned_render_phase`.
|
||||
pub unbatchable_keys: Vec<BPI::BinKey>,
|
||||
|
||||
/// The unbatchable bins.
|
||||
///
|
||||
/// Each entity here is rendered in a separate drawcall.
|
||||
pub(crate) unbatchable_values: HashMap<BPI::BinKey, UnbatchableBinnedEntities>,
|
||||
|
||||
/// Information on each batch set.
|
||||
///
|
||||
/// A *batch set* is a set of entities that will be batched together unless
|
||||
/// we're on a platform that doesn't support storage buffers (e.g. WebGL 2)
|
||||
/// and differing dynamic uniform indices force us to break batches. On
|
||||
/// platforms that support storage buffers, a batch set always consists of
|
||||
/// at most one batch.
|
||||
///
|
||||
/// The unbatchable entities immediately follow the batches in the storage
|
||||
/// buffers.
|
||||
pub(crate) batch_sets: Vec<SmallVec<[BinnedRenderPhaseBatch; 1]>>,
|
||||
}
|
||||
|
||||
/// Information about a single batch of entities rendered using binned phase
|
||||
/// items.
|
||||
#[derive(Debug)]
|
||||
pub struct BinnedRenderPhaseBatch {
|
||||
/// An entity that's *representative* of this batch.
|
||||
///
|
||||
/// Bevy uses this to fetch the mesh. It can be any entity in the batch.
|
||||
pub representative_entity: Entity,
|
||||
|
||||
/// The range of instance indices in this batch.
|
||||
pub instance_range: Range<u32>,
|
||||
|
||||
/// The dynamic offset of the batch.
|
||||
///
|
||||
/// Note that dynamic offsets are only used on platforms that don't support
|
||||
/// storage buffers.
|
||||
pub dynamic_offset: Option<NonMaxU32>,
|
||||
}
|
||||
|
||||
/// Information about the unbatchable entities in a bin.
|
||||
pub(crate) struct UnbatchableBinnedEntities {
|
||||
/// The entities.
|
||||
pub(crate) entities: Vec<Entity>,
|
||||
|
||||
/// The GPU array buffer indices of each unbatchable binned entity.
|
||||
pub(crate) buffer_indices: UnbatchableBinnedEntityBufferIndex,
|
||||
}
|
||||
|
||||
/// Stores instance indices and dynamic offsets for unbatchable entities in a
|
||||
/// binned render phase.
|
||||
///
|
||||
/// This is conceptually `Vec<UnbatchableBinnedEntityDynamicOffset>`, but it
|
||||
/// avoids the overhead of storing dynamic offsets on platforms that support
|
||||
/// them. In other words, this allows a fast path that avoids allocation on
|
||||
/// platforms that aren't WebGL 2.
|
||||
#[derive(Default)]
|
||||
|
||||
pub(crate) enum UnbatchableBinnedEntityBufferIndex {
|
||||
/// There are no unbatchable entities in this bin (yet).
|
||||
#[default]
|
||||
NoEntities,
|
||||
|
||||
/// The instances for all unbatchable entities in this bin are contiguous,
|
||||
/// and there are no dynamic uniforms.
|
||||
///
|
||||
/// This is the typical case on platforms other than WebGL 2. We special
|
||||
/// case this to avoid allocation on those platforms.
|
||||
NoDynamicOffsets {
|
||||
/// The range of indices.
|
||||
instance_range: Range<u32>,
|
||||
},
|
||||
|
||||
/// Dynamic uniforms are present for unbatchable entities in this bin.
|
||||
///
|
||||
/// We fall back to this on WebGL 2.
|
||||
DynamicOffsets(Vec<UnbatchableBinnedEntityDynamicOffset>),
|
||||
}
|
||||
|
||||
/// The instance index and dynamic offset (if present) for an unbatchable entity.
|
||||
///
|
||||
/// This is only useful on platforms that don't support storage buffers.
|
||||
#[derive(Clone, Copy)]
|
||||
pub(crate) struct UnbatchableBinnedEntityDynamicOffset {
|
||||
/// The instance index.
|
||||
instance_index: u32,
|
||||
/// The dynamic offset, if present.
|
||||
dynamic_offset: Option<NonMaxU32>,
|
||||
}
|
||||
|
||||
impl<BPI> BinnedRenderPhase<BPI>
|
||||
where
|
||||
BPI: BinnedPhaseItem,
|
||||
{
|
||||
/// Bins a new entity.
|
||||
///
|
||||
/// `batchable` specifies whether the entity can be batched with other
|
||||
/// entities of the same type.
|
||||
pub fn add(&mut self, key: BPI::BinKey, entity: Entity, batchable: bool) {
|
||||
if batchable {
|
||||
match self.batchable_values.entry(key.clone()) {
|
||||
Entry::Occupied(mut entry) => entry.get_mut().push(entity),
|
||||
Entry::Vacant(entry) => {
|
||||
self.batchable_keys.push(key);
|
||||
entry.insert(vec![entity]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
match self.unbatchable_values.entry(key.clone()) {
|
||||
Entry::Occupied(mut entry) => entry.get_mut().entities.push(entity),
|
||||
Entry::Vacant(entry) => {
|
||||
self.unbatchable_keys.push(key);
|
||||
entry.insert(UnbatchableBinnedEntities {
|
||||
entities: vec![entity],
|
||||
buffer_indices: default(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Encodes the GPU commands needed to render all entities in this phase.
|
||||
pub fn render<'w>(
|
||||
&self,
|
||||
render_pass: &mut TrackedRenderPass<'w>,
|
||||
world: &'w World,
|
||||
view: Entity,
|
||||
) {
|
||||
let draw_functions = world.resource::<DrawFunctions<BPI>>();
|
||||
let mut draw_functions = draw_functions.write();
|
||||
draw_functions.prepare(world);
|
||||
|
||||
// Encode draws for batchables.
|
||||
debug_assert_eq!(self.batchable_keys.len(), self.batch_sets.len());
|
||||
for (key, batch_set) in self.batchable_keys.iter().zip(self.batch_sets.iter()) {
|
||||
for batch in batch_set {
|
||||
let binned_phase_item = BPI::new(
|
||||
key.clone(),
|
||||
batch.representative_entity,
|
||||
batch.instance_range.clone(),
|
||||
batch.dynamic_offset,
|
||||
);
|
||||
|
||||
// Fetch the draw function.
|
||||
let Some(draw_function) = draw_functions.get_mut(binned_phase_item.draw_function())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
draw_function.draw(world, render_pass, view, &binned_phase_item);
|
||||
}
|
||||
}
|
||||
|
||||
// Encode draws for unbatchables.
|
||||
|
||||
for key in &self.unbatchable_keys {
|
||||
let unbatchable_entities = &self.unbatchable_values[key];
|
||||
for (entity_index, &entity) in unbatchable_entities.entities.iter().enumerate() {
|
||||
let unbatchable_dynamic_offset = match &unbatchable_entities.buffer_indices {
|
||||
UnbatchableBinnedEntityBufferIndex::NoEntities => {
|
||||
// Shouldn't happen…
|
||||
continue;
|
||||
}
|
||||
UnbatchableBinnedEntityBufferIndex::NoDynamicOffsets { instance_range } => {
|
||||
UnbatchableBinnedEntityDynamicOffset {
|
||||
instance_index: instance_range.start + entity_index as u32,
|
||||
dynamic_offset: None,
|
||||
}
|
||||
}
|
||||
UnbatchableBinnedEntityBufferIndex::DynamicOffsets(ref dynamic_offsets) => {
|
||||
dynamic_offsets[entity_index]
|
||||
}
|
||||
};
|
||||
|
||||
let binned_phase_item = BPI::new(
|
||||
key.clone(),
|
||||
entity,
|
||||
unbatchable_dynamic_offset.instance_index
|
||||
..(unbatchable_dynamic_offset.instance_index + 1),
|
||||
unbatchable_dynamic_offset.dynamic_offset,
|
||||
);
|
||||
|
||||
// Fetch the draw function.
|
||||
let Some(draw_function) = draw_functions.get_mut(binned_phase_item.draw_function())
|
||||
else {
|
||||
continue;
|
||||
};
|
||||
|
||||
draw_function.draw(world, render_pass, view, &binned_phase_item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.batchable_keys.is_empty() && self.unbatchable_keys.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl<BPI> Default for BinnedRenderPhase<BPI>
|
||||
where
|
||||
BPI: BinnedPhaseItem,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
batchable_keys: vec![],
|
||||
batchable_values: HashMap::default(),
|
||||
unbatchable_keys: vec![],
|
||||
unbatchable_values: HashMap::default(),
|
||||
batch_sets: vec![],
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl UnbatchableBinnedEntityBufferIndex {
|
||||
/// Adds a new entity to the list of unbatchable binned entities.
|
||||
pub fn add<T>(&mut self, gpu_array_buffer_index: GpuArrayBufferIndex<T>)
|
||||
where
|
||||
T: ShaderSize + WriteInto + Clone,
|
||||
{
|
||||
match (&mut *self, gpu_array_buffer_index.dynamic_offset) {
|
||||
(UnbatchableBinnedEntityBufferIndex::NoEntities, None) => {
|
||||
// This is the first entity we've seen, and we're not on WebGL
|
||||
// 2. Initialize the fast path.
|
||||
*self = UnbatchableBinnedEntityBufferIndex::NoDynamicOffsets {
|
||||
instance_range: gpu_array_buffer_index.index
|
||||
..(gpu_array_buffer_index.index + 1),
|
||||
}
|
||||
}
|
||||
|
||||
(UnbatchableBinnedEntityBufferIndex::NoEntities, Some(dynamic_offset)) => {
|
||||
// This is the first entity we've seen, and we're on WebGL 2.
|
||||
// Initialize an array.
|
||||
*self = UnbatchableBinnedEntityBufferIndex::DynamicOffsets(vec![
|
||||
UnbatchableBinnedEntityDynamicOffset {
|
||||
instance_index: gpu_array_buffer_index.index,
|
||||
dynamic_offset: Some(dynamic_offset),
|
||||
},
|
||||
]);
|
||||
}
|
||||
|
||||
(
|
||||
UnbatchableBinnedEntityBufferIndex::NoDynamicOffsets {
|
||||
ref mut instance_range,
|
||||
},
|
||||
None,
|
||||
) if instance_range.end == gpu_array_buffer_index.index => {
|
||||
// This is the normal case on non-WebGL 2.
|
||||
instance_range.end += 1;
|
||||
}
|
||||
|
||||
(
|
||||
UnbatchableBinnedEntityBufferIndex::DynamicOffsets(ref mut offsets),
|
||||
dynamic_offset,
|
||||
) => {
|
||||
// This is the normal case on WebGL 2.
|
||||
offsets.push(UnbatchableBinnedEntityDynamicOffset {
|
||||
instance_index: gpu_array_buffer_index.index,
|
||||
dynamic_offset,
|
||||
});
|
||||
}
|
||||
|
||||
(
|
||||
UnbatchableBinnedEntityBufferIndex::NoDynamicOffsets { instance_range },
|
||||
dynamic_offset,
|
||||
) => {
|
||||
// We thought we were in non-WebGL 2 mode, but we got a dynamic
|
||||
// offset or non-contiguous index anyway. This shouldn't happen,
|
||||
// but let's go ahead and do the sensible thing anyhow: demote
|
||||
// the compressed `NoDynamicOffsets` field to the full
|
||||
// `DynamicOffsets` array.
|
||||
let mut new_dynamic_offsets: Vec<_> = instance_range
|
||||
.map(|instance_index| UnbatchableBinnedEntityDynamicOffset {
|
||||
instance_index,
|
||||
dynamic_offset: None,
|
||||
})
|
||||
.collect();
|
||||
new_dynamic_offsets.push(UnbatchableBinnedEntityDynamicOffset {
|
||||
instance_index: gpu_array_buffer_index.index,
|
||||
dynamic_offset,
|
||||
});
|
||||
*self = UnbatchableBinnedEntityBufferIndex::DynamicOffsets(new_dynamic_offsets);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A collection of all items to be rendered that will be encoded to GPU
|
||||
/// commands for a single render phase for a single view.
|
||||
///
|
||||
/// Each view (camera, or shadow-casting light, etc.) can have one or multiple render phases.
|
||||
/// They are used to queue entities for rendering.
|
||||
/// Multiple phases might be required due to different sorting/batching behaviors
|
||||
/// (e.g. opaque: front to back, transparent: back to front) or because one phase depends on
|
||||
/// the rendered texture of the previous phase (e.g. for screen-space reflections).
|
||||
/// All [`PhaseItem`]s are then rendered using a single [`TrackedRenderPass`].
|
||||
/// The render pass might be reused for multiple phases to reduce GPU overhead.
|
||||
///
|
||||
/// This flavor of render phase is used only for meshes that need to be sorted
|
||||
/// back-to-front, such as transparent meshes. For items that don't need strict
|
||||
/// sorting, [`BinnedRenderPhase`] is preferred, for performance.
|
||||
#[derive(Component)]
|
||||
pub struct SortedRenderPhase<I>
|
||||
where
|
||||
I: SortedPhaseItem,
|
||||
{
|
||||
pub items: Vec<I>,
|
||||
}
|
||||
|
||||
impl<I: PhaseItem> Default for RenderPhase<I> {
|
||||
impl<I> Default for SortedRenderPhase<I>
|
||||
where
|
||||
I: SortedPhaseItem,
|
||||
{
|
||||
fn default() -> Self {
|
||||
Self { items: Vec::new() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: PhaseItem> RenderPhase<I> {
|
||||
impl<I> SortedRenderPhase<I>
|
||||
where
|
||||
I: SortedPhaseItem,
|
||||
{
|
||||
/// Adds a [`PhaseItem`] to this render phase.
|
||||
#[inline]
|
||||
pub fn add(&mut self, item: I) {
|
||||
|
@ -123,22 +459,31 @@ impl<I: PhaseItem> RenderPhase<I> {
|
|||
}
|
||||
|
||||
/// An item (entity of the render world) which will be drawn to a texture or the screen,
|
||||
/// as part of a [`RenderPhase`].
|
||||
/// as part of a render phase.
|
||||
///
|
||||
/// The data required for rendering an entity is extracted from the main world in the
|
||||
/// [`ExtractSchedule`](crate::ExtractSchedule).
|
||||
/// Then it has to be queued up for rendering during the
|
||||
/// [`RenderSet::Queue`](crate::RenderSet::Queue), by adding a corresponding phase item to
|
||||
/// a render phase.
|
||||
/// Afterwards it will be sorted and rendered automatically in the
|
||||
/// Afterwards it will be possibly sorted and rendered automatically in the
|
||||
/// [`RenderSet::PhaseSort`](crate::RenderSet::PhaseSort) and
|
||||
/// [`RenderSet::Render`](crate::RenderSet::Render), respectively.
|
||||
///
|
||||
/// `PhaseItem`s come in two flavors: [`BinnedPhaseItem`]s and
|
||||
/// [`SortedPhaseItem`]s.
|
||||
///
|
||||
/// * Binned phase items have a `BinKey` which specifies what bin they're to be
|
||||
/// placed in. All items in the same bin are eligible to be batched together.
|
||||
/// The `BinKey`s are sorted, but the individual bin items aren't. Binned phase
|
||||
/// items are good for opaque meshes, in which the order of rendering isn't
|
||||
/// important. Generally, binned phase items are faster than sorted phase items.
|
||||
///
|
||||
/// * Sorted phase items, on the other hand, are placed into one large buffer
|
||||
/// and then sorted all at once. This is needed for transparent meshes, which
|
||||
/// have to be sorted back-to-front to render with the painter's algorithm.
|
||||
/// These types of phase items are generally slower than binned phase items.
|
||||
pub trait PhaseItem: Sized + Send + Sync + 'static {
|
||||
/// The type used for ordering the items. The smallest values are drawn first.
|
||||
/// This order can be calculated using the [`ViewRangefinder3d`],
|
||||
/// based on the view-space `Z` value of the corresponding view matrix.
|
||||
type SortKey: Ord;
|
||||
|
||||
/// Whether or not this `PhaseItem` should be subjected to automatic batching. (Default: `true`)
|
||||
const AUTOMATIC_BATCHING: bool = true;
|
||||
|
||||
|
@ -148,12 +493,63 @@ pub trait PhaseItem: Sized + Send + Sync + 'static {
|
|||
/// from the render world .
|
||||
fn entity(&self) -> Entity;
|
||||
|
||||
/// Determines the order in which the items are drawn.
|
||||
fn sort_key(&self) -> Self::SortKey;
|
||||
|
||||
/// Specifies the [`Draw`] function used to render the item.
|
||||
fn draw_function(&self) -> DrawFunctionId;
|
||||
|
||||
/// The range of instances that the batch covers. After doing a batched draw, batch range
|
||||
/// length phase items will be skipped. This design is to avoid having to restructure the
|
||||
/// render phase unnecessarily.
|
||||
fn batch_range(&self) -> &Range<u32>;
|
||||
fn batch_range_mut(&mut self) -> &mut Range<u32>;
|
||||
|
||||
fn dynamic_offset(&self) -> Option<NonMaxU32>;
|
||||
fn dynamic_offset_mut(&mut self) -> &mut Option<NonMaxU32>;
|
||||
}
|
||||
|
||||
/// Represents phase items that are placed into bins. The `BinKey` specifies
|
||||
/// which bin they're to be placed in. Bin keys are sorted, and items within the
|
||||
/// same bin are eligible to be batched together. The elements within the bins
|
||||
/// aren't themselves sorted.
|
||||
///
|
||||
/// An example of a binned phase item is `Opaque3d`, for which the rendering
|
||||
/// order isn't critical.
|
||||
pub trait BinnedPhaseItem: PhaseItem {
|
||||
/// The key used for binning [`PhaseItem`]s into bins. Order the members of
|
||||
/// [`BinnedPhaseItem::BinKey`] by the order of binding for best
|
||||
/// performance. For example, pipeline id, draw function id, mesh asset id,
|
||||
/// lowest variable bind group id such as the material bind group id, and
|
||||
/// its dynamic offsets if any, next bind group and offsets, etc. This
|
||||
/// reduces the need for rebinding between bins and improves performance.
|
||||
type BinKey: Clone + Send + Sync + Eq + Ord + Hash;
|
||||
|
||||
/// Creates a new binned phase item from the key and per-entity data.
|
||||
///
|
||||
/// Unlike [`SortedPhaseItem`]s, this is generally called "just in time"
|
||||
/// before rendering. The resulting phase item isn't stored in any data
|
||||
/// structures, resulting in significant memory savings.
|
||||
fn new(
|
||||
key: Self::BinKey,
|
||||
representative_entity: Entity,
|
||||
batch_range: Range<u32>,
|
||||
dynamic_offset: Option<NonMaxU32>,
|
||||
) -> Self;
|
||||
}
|
||||
|
||||
/// Represents phase items that must be sorted. The `SortKey` specifies the
|
||||
/// order that these items are drawn in. These are placed into a single array,
|
||||
/// and the array as a whole is then sorted.
|
||||
///
|
||||
/// An example of a sorted phase item is `Transparent3d`, which must be sorted
|
||||
/// back to front in order to correctly render with the painter's algorithm.
|
||||
pub trait SortedPhaseItem: PhaseItem {
|
||||
/// The type used for ordering the items. The smallest values are drawn first.
|
||||
/// This order can be calculated using the [`ViewRangefinder3d`],
|
||||
/// based on the view-space `Z` value of the corresponding view matrix.
|
||||
type SortKey: Ord;
|
||||
|
||||
/// Determines the order in which the items are drawn.
|
||||
fn sort_key(&self) -> Self::SortKey;
|
||||
|
||||
/// Sorts a slice of phase items into render order. Generally if the same type
|
||||
/// is batched this should use a stable sort like [`slice::sort_by_key`].
|
||||
/// In almost all other cases, this should not be altered from the default,
|
||||
|
@ -170,15 +566,6 @@ pub trait PhaseItem: Sized + Send + Sync + 'static {
|
|||
fn sort(items: &mut [Self]) {
|
||||
items.sort_unstable_by_key(|item| item.sort_key());
|
||||
}
|
||||
|
||||
/// The range of instances that the batch covers. After doing a batched draw, batch range
|
||||
/// length phase items will be skipped. This design is to avoid having to restructure the
|
||||
/// render phase unnecessarily.
|
||||
fn batch_range(&self) -> &Range<u32>;
|
||||
fn batch_range_mut(&mut self) -> &mut Range<u32>;
|
||||
|
||||
fn dynamic_offset(&self) -> Option<NonMaxU32>;
|
||||
fn dynamic_offset_mut(&mut self) -> &mut Option<NonMaxU32>;
|
||||
}
|
||||
|
||||
/// A [`PhaseItem`] item, that automatically sets the appropriate render pipeline,
|
||||
|
@ -218,8 +605,12 @@ impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline {
|
|||
}
|
||||
}
|
||||
|
||||
/// This system sorts the [`PhaseItem`]s of all [`RenderPhase`]s of this type.
|
||||
pub fn sort_phase_system<I: PhaseItem>(mut render_phases: Query<&mut RenderPhase<I>>) {
|
||||
/// This system sorts the [`PhaseItem`]s of all [`SortedRenderPhase`]s of this
|
||||
/// type.
|
||||
pub fn sort_phase_system<I>(mut render_phases: Query<&mut SortedRenderPhase<I>>)
|
||||
where
|
||||
I: SortedPhaseItem,
|
||||
{
|
||||
for mut phase in &mut render_phases {
|
||||
phase.sort();
|
||||
}
|
||||
|
|
|
@ -50,7 +50,7 @@ pub enum Pipeline {
|
|||
type CachedPipelineId = usize;
|
||||
|
||||
/// Index of a cached render pipeline in a [`PipelineCache`].
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
#[derive(Copy, Clone, Debug, Hash, Eq, PartialEq, PartialOrd, Ord)]
|
||||
pub struct CachedRenderPipelineId(CachedPipelineId);
|
||||
|
||||
impl CachedRenderPipelineId {
|
||||
|
|
|
@ -148,7 +148,7 @@ macro_rules! render_resource_wrapper {
|
|||
#[macro_export]
|
||||
macro_rules! define_atomic_id {
|
||||
($atomic_id_type:ident) => {
|
||||
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
|
||||
#[derive(Copy, Clone, Hash, Eq, PartialEq, PartialOrd, Ord, Debug)]
|
||||
pub struct $atomic_id_type(core::num::NonZeroU32);
|
||||
|
||||
// We use new instead of default to indicate that each ID created will be unique.
|
||||
|
|
|
@ -17,7 +17,7 @@ use bevy_render::{
|
|||
render_asset::{prepare_assets, RenderAssets},
|
||||
render_phase::{
|
||||
AddRenderCommand, DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult,
|
||||
RenderPhase, SetItemPipeline, TrackedRenderPass,
|
||||
SetItemPipeline, SortedRenderPhase, TrackedRenderPass,
|
||||
},
|
||||
render_resource::{
|
||||
AsBindGroup, AsBindGroupError, BindGroup, BindGroupId, BindGroupLayout,
|
||||
|
@ -388,7 +388,7 @@ pub fn queue_material2d_meshes<M: Material2d>(
|
|||
&VisibleEntities,
|
||||
Option<&Tonemapping>,
|
||||
Option<&DebandDither>,
|
||||
&mut RenderPhase<Transparent2d>,
|
||||
&mut SortedRenderPhase<Transparent2d>,
|
||||
)>,
|
||||
) where
|
||||
M::Data: PartialEq + Eq + Hash + Clone,
|
||||
|
|
|
@ -14,7 +14,7 @@ use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
|||
use bevy_render::mesh::MeshVertexBufferLayoutRef;
|
||||
use bevy_render::{
|
||||
batching::{
|
||||
batch_and_prepare_render_phase, write_batched_instance_buffer, GetBatchData,
|
||||
batch_and_prepare_sorted_render_phase, write_batched_instance_buffer, GetBatchData,
|
||||
NoAutomaticBatching,
|
||||
},
|
||||
globals::{GlobalsBuffer, GlobalsUniform},
|
||||
|
@ -101,7 +101,7 @@ impl Plugin for Mesh2dRenderPlugin {
|
|||
.add_systems(
|
||||
Render,
|
||||
(
|
||||
batch_and_prepare_render_phase::<Transparent2d, Mesh2dPipeline>
|
||||
batch_and_prepare_sorted_render_phase::<Transparent2d, Mesh2dPipeline>
|
||||
.in_set(RenderSet::PrepareResources),
|
||||
write_batched_instance_buffer::<Mesh2dPipeline>
|
||||
.in_set(RenderSet::PrepareResourcesFlush),
|
||||
|
|
|
@ -19,8 +19,8 @@ use bevy_math::{Affine3A, FloatOrd, Quat, Rect, Vec2, Vec4};
|
|||
use bevy_render::{
|
||||
render_asset::RenderAssets,
|
||||
render_phase::{
|
||||
DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult, RenderPhase, SetItemPipeline,
|
||||
TrackedRenderPass,
|
||||
DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult, SetItemPipeline,
|
||||
SortedRenderPhase, TrackedRenderPass,
|
||||
},
|
||||
render_resource::{
|
||||
binding_types::{sampler, texture_2d, uniform_buffer},
|
||||
|
@ -448,7 +448,7 @@ pub fn queue_sprites(
|
|||
msaa: Res<Msaa>,
|
||||
extracted_sprites: Res<ExtractedSprites>,
|
||||
mut views: Query<(
|
||||
&mut RenderPhase<Transparent2d>,
|
||||
&mut SortedRenderPhase<Transparent2d>,
|
||||
&VisibleEntities,
|
||||
&ExtractedView,
|
||||
Option<&Tonemapping>,
|
||||
|
@ -530,7 +530,7 @@ pub fn prepare_sprites(
|
|||
mut image_bind_groups: ResMut<ImageBindGroups>,
|
||||
gpu_images: Res<RenderAssets<Image>>,
|
||||
extracted_sprites: Res<ExtractedSprites>,
|
||||
mut phases: Query<&mut RenderPhase<Transparent2d>>,
|
||||
mut phases: Query<&mut SortedRenderPhase<Transparent2d>>,
|
||||
events: Res<SpriteAssetEvents>,
|
||||
) {
|
||||
// If an image has changed, the GpuImage has (probably) changed
|
||||
|
|
|
@ -29,7 +29,7 @@ use bevy_render::{
|
|||
camera::Camera,
|
||||
render_asset::RenderAssets,
|
||||
render_graph::{RenderGraph, RunGraphOnViewNode},
|
||||
render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions, RenderPhase},
|
||||
render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions, SortedRenderPhase},
|
||||
render_resource::*,
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::Image,
|
||||
|
@ -704,7 +704,7 @@ pub fn extract_default_ui_camera_view<T: Component>(
|
|||
.id();
|
||||
commands.get_or_spawn(entity).insert((
|
||||
DefaultCameraView(default_camera_view),
|
||||
RenderPhase::<TransparentUi>::default(),
|
||||
SortedRenderPhase::<TransparentUi>::default(),
|
||||
));
|
||||
}
|
||||
}
|
||||
|
@ -874,7 +874,7 @@ pub fn queue_uinodes(
|
|||
extracted_uinodes: Res<ExtractedUiNodes>,
|
||||
ui_pipeline: Res<UiPipeline>,
|
||||
mut pipelines: ResMut<SpecializedRenderPipelines<UiPipeline>>,
|
||||
mut views: Query<(&ExtractedView, &mut RenderPhase<TransparentUi>)>,
|
||||
mut views: Query<(&ExtractedView, &mut SortedRenderPhase<TransparentUi>)>,
|
||||
pipeline_cache: Res<PipelineCache>,
|
||||
draw_functions: Res<DrawFunctions<TransparentUi>>,
|
||||
) {
|
||||
|
@ -921,7 +921,7 @@ pub fn prepare_uinodes(
|
|||
ui_pipeline: Res<UiPipeline>,
|
||||
mut image_bind_groups: ResMut<UiImageBindGroups>,
|
||||
gpu_images: Res<RenderAssets<Image>>,
|
||||
mut phases: Query<&mut RenderPhase<TransparentUi>>,
|
||||
mut phases: Query<&mut SortedRenderPhase<TransparentUi>>,
|
||||
events: Res<SpriteAssetEvents>,
|
||||
mut previous_len: Local<usize>,
|
||||
) {
|
||||
|
|
|
@ -20,7 +20,7 @@ use nonmax::NonMaxU32;
|
|||
pub struct UiPassNode {
|
||||
ui_view_query: QueryState<
|
||||
(
|
||||
&'static RenderPhase<TransparentUi>,
|
||||
&'static SortedRenderPhase<TransparentUi>,
|
||||
&'static ViewTarget,
|
||||
&'static ExtractedCamera,
|
||||
),
|
||||
|
@ -96,28 +96,16 @@ pub struct TransparentUi {
|
|||
}
|
||||
|
||||
impl PhaseItem for TransparentUi {
|
||||
type SortKey = (FloatOrd, u32);
|
||||
|
||||
#[inline]
|
||||
fn entity(&self) -> Entity {
|
||||
self.entity
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
self.sort_key
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn draw_function(&self) -> DrawFunctionId {
|
||||
self.draw_function
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
items.sort_by_key(|item| item.sort_key());
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn batch_range(&self) -> &Range<u32> {
|
||||
&self.batch_range
|
||||
|
@ -139,6 +127,20 @@ impl PhaseItem for TransparentUi {
|
|||
}
|
||||
}
|
||||
|
||||
impl SortedPhaseItem for TransparentUi {
|
||||
type SortKey = (FloatOrd, u32);
|
||||
|
||||
#[inline]
|
||||
fn sort_key(&self) -> Self::SortKey {
|
||||
self.sort_key
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn sort(items: &mut [Self]) {
|
||||
items.sort_by_key(|item| item.sort_key());
|
||||
}
|
||||
}
|
||||
|
||||
impl CachedRenderPipelinePhaseItem for TransparentUi {
|
||||
#[inline]
|
||||
fn cached_pipeline(&self) -> CachedRenderPipelineId {
|
||||
|
|
|
@ -460,7 +460,7 @@ pub fn prepare_uimaterial_nodes<M: UiMaterial>(
|
|||
view_uniforms: Res<ViewUniforms>,
|
||||
globals_buffer: Res<GlobalsBuffer>,
|
||||
ui_material_pipeline: Res<UiMaterialPipeline<M>>,
|
||||
mut phases: Query<&mut RenderPhase<TransparentUi>>,
|
||||
mut phases: Query<&mut SortedRenderPhase<TransparentUi>>,
|
||||
mut previous_len: Local<usize>,
|
||||
) {
|
||||
if let (Some(view_binding), Some(globals_binding)) = (
|
||||
|
@ -757,7 +757,7 @@ pub fn queue_ui_material_nodes<M: UiMaterial>(
|
|||
mut pipelines: ResMut<SpecializedRenderPipelines<UiMaterialPipeline<M>>>,
|
||||
pipeline_cache: Res<PipelineCache>,
|
||||
render_materials: Res<RenderUiMaterials<M>>,
|
||||
mut views: Query<(&ExtractedView, &mut RenderPhase<TransparentUi>)>,
|
||||
mut views: Query<(&ExtractedView, &mut SortedRenderPhase<TransparentUi>)>,
|
||||
) where
|
||||
M::Data: PartialEq + Eq + Hash + Clone,
|
||||
{
|
||||
|
|
|
@ -13,7 +13,7 @@ use bevy::{
|
|||
render::{
|
||||
mesh::{Indices, MeshVertexAttribute},
|
||||
render_asset::{RenderAssetUsages, RenderAssets},
|
||||
render_phase::{AddRenderCommand, DrawFunctions, RenderPhase, SetItemPipeline},
|
||||
render_phase::{AddRenderCommand, DrawFunctions, SetItemPipeline, SortedRenderPhase},
|
||||
render_resource::{
|
||||
BlendState, ColorTargetState, ColorWrites, Face, FragmentState, FrontFace,
|
||||
MultisampleState, PipelineCache, PolygonMode, PrimitiveState, PrimitiveTopology,
|
||||
|
@ -360,7 +360,7 @@ pub fn queue_colored_mesh2d(
|
|||
render_mesh_instances: Res<RenderMesh2dInstances>,
|
||||
mut views: Query<(
|
||||
&VisibleEntities,
|
||||
&mut RenderPhase<Transparent2d>,
|
||||
&mut SortedRenderPhase<Transparent2d>,
|
||||
&ExtractedView,
|
||||
)>,
|
||||
) {
|
||||
|
|
|
@ -16,7 +16,7 @@ use bevy::{
|
|||
render_asset::RenderAssets,
|
||||
render_phase::{
|
||||
AddRenderCommand, DrawFunctions, PhaseItem, RenderCommand, RenderCommandResult,
|
||||
RenderPhase, SetItemPipeline, TrackedRenderPass,
|
||||
SetItemPipeline, SortedRenderPhase, TrackedRenderPass,
|
||||
},
|
||||
render_resource::*,
|
||||
renderer::RenderDevice,
|
||||
|
@ -117,7 +117,7 @@ fn queue_custom(
|
|||
meshes: Res<RenderAssets<Mesh>>,
|
||||
render_mesh_instances: Res<RenderMeshInstances>,
|
||||
material_meshes: Query<Entity, With<InstanceMaterialData>>,
|
||||
mut views: Query<(&ExtractedView, &mut RenderPhase<Transparent3d>)>,
|
||||
mut views: Query<(&ExtractedView, &mut SortedRenderPhase<Transparent3d>)>,
|
||||
) {
|
||||
let draw_custom = transparent_3d_draw_functions.read().id::<DrawCustom>();
|
||||
|
||||
|
|
Loading…
Reference in a new issue