Make render phases render world resources instead of components. (#13277)

This commit makes us stop using the render world ECS for
`BinnedRenderPhase` and `SortedRenderPhase` and instead use resources
with `EntityHashMap`s inside. There are three reasons to do this:

1. We can use `clear()` to clear out the render phase collections
instead of recreating the components from scratch, allowing us to reuse
allocations.

2. This is a prerequisite for retained bins, because components can't be
retained from frame to frame in the render world, but resources can.

3. We want to move away from storing anything in components in the
render world ECS, and this is a step in that direction.

This patch results in a small performance benefit, due to point (1)
above.

## Changelog

### Changed

* The `BinnedRenderPhase` and `SortedRenderPhase` render world
components have been replaced with `ViewBinnedRenderPhases` and
`ViewSortedRenderPhases` resources.

## Migration Guide

* The `BinnedRenderPhase` and `SortedRenderPhase` render world
components have been replaced with `ViewBinnedRenderPhases` and
`ViewSortedRenderPhases` resources. Instead of querying for the
components, look the camera entity up in the
`ViewBinnedRenderPhases`/`ViewSortedRenderPhases` tables.
This commit is contained in:
Patrick Walton 2024-05-21 11:23:04 -07:00 committed by GitHub
parent 53f4c38e7b
commit 9da0b2a0ec
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
24 changed files with 608 additions and 294 deletions

View File

@ -4,7 +4,7 @@ use bevy_render::{
camera::ExtractedCamera,
diagnostic::RecordDiagnostics,
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_phase::SortedRenderPhase,
render_phase::ViewSortedRenderPhases,
render_resource::RenderPassDescriptor,
renderer::RenderContext,
view::ViewTarget,
@ -16,20 +16,25 @@ use bevy_utils::tracing::info_span;
pub struct MainTransparentPass2dNode {}
impl ViewNode for MainTransparentPass2dNode {
type ViewQuery = (
&'static ExtractedCamera,
&'static SortedRenderPhase<Transparent2d>,
&'static ViewTarget,
);
type ViewQuery = (&'static ExtractedCamera, &'static ViewTarget);
fn run<'w>(
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
(camera, transparent_phase, target): bevy_ecs::query::QueryItem<'w, Self::ViewQuery>,
(camera, target): bevy_ecs::query::QueryItem<'w, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let Some(transparent_phases) =
world.get_resource::<ViewSortedRenderPhases<Transparent2d>>()
else {
return Ok(());
};
let view_entity = graph.view_entity();
let Some(transparent_phase) = transparent_phases.get(&view_entity) else {
return Ok(());
};
// This needs to run at least once to clear the background color, even if there are no items to render
{

View File

@ -32,7 +32,7 @@ pub use camera_2d::*;
pub use main_transparent_pass_2d_node::*;
use bevy_app::{App, Plugin};
use bevy_ecs::prelude::*;
use bevy_ecs::{entity::EntityHashSet, prelude::*};
use bevy_math::FloatOrd;
use bevy_render::{
camera::Camera,
@ -40,7 +40,7 @@ use bevy_render::{
render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner},
render_phase::{
sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem,
PhaseItemExtraIndex, SortedPhaseItem, SortedRenderPhase,
PhaseItemExtraIndex, SortedPhaseItem, ViewSortedRenderPhases,
},
render_resource::CachedRenderPipelineId,
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
@ -62,6 +62,7 @@ impl Plugin for Core2dPlugin {
};
render_app
.init_resource::<DrawFunctions<Transparent2d>>()
.init_resource::<ViewSortedRenderPhases<Transparent2d>>()
.add_systems(ExtractSchedule, extract_core_2d_camera_phases)
.add_systems(
Render,
@ -158,13 +159,23 @@ impl CachedRenderPipelinePhaseItem for Transparent2d {
pub fn extract_core_2d_camera_phases(
mut commands: Commands,
mut transparent_2d_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
cameras_2d: Extract<Query<(Entity, &Camera), With<Camera2d>>>,
mut live_entities: Local<EntityHashSet>,
) {
live_entities.clear();
for (entity, camera) in &cameras_2d {
if camera.is_active {
commands
.get_or_spawn(entity)
.insert(SortedRenderPhase::<Transparent2d>::default());
if !camera.is_active {
continue;
}
commands.get_or_spawn(entity);
transparent_2d_phases.insert_or_clear(entity);
live_entities.insert(entity);
}
// Clear out all dead views.
transparent_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
}

View File

@ -2,12 +2,12 @@ use crate::{
core_3d::Opaque3d,
skybox::{SkyboxBindGroup, SkyboxPipelineId},
};
use bevy_ecs::{prelude::World, query::QueryItem};
use bevy_ecs::{entity::Entity, prelude::World, query::QueryItem};
use bevy_render::{
camera::ExtractedCamera,
diagnostic::RecordDiagnostics,
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_phase::{BinnedRenderPhase, TrackedRenderPass},
render_phase::{TrackedRenderPass, ViewBinnedRenderPhases},
render_resource::{CommandEncoderDescriptor, PipelineCache, RenderPassDescriptor, StoreOp},
renderer::RenderContext,
view::{ViewDepthTexture, ViewTarget, ViewUniformOffset},
@ -24,9 +24,8 @@ use super::AlphaMask3d;
pub struct MainOpaquePass3dNode;
impl ViewNode for MainOpaquePass3dNode {
type ViewQuery = (
Entity,
&'static ExtractedCamera,
&'static BinnedRenderPhase<Opaque3d>,
&'static BinnedRenderPhase<AlphaMask3d>,
&'static ViewTarget,
&'static ViewDepthTexture,
Option<&'static SkyboxPipelineId>,
@ -39,9 +38,8 @@ impl ViewNode for MainOpaquePass3dNode {
graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
(
view,
camera,
opaque_phase,
alpha_mask_phase,
target,
depth,
skybox_pipeline,
@ -50,6 +48,19 @@ impl ViewNode for MainOpaquePass3dNode {
): QueryItem<'w, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let (Some(opaque_phases), Some(alpha_mask_phases)) = (
world.get_resource::<ViewBinnedRenderPhases<Opaque3d>>(),
world.get_resource::<ViewBinnedRenderPhases<AlphaMask3d>>(),
) else {
return Ok(());
};
let (Some(opaque_phase), Some(alpha_mask_phase)) =
(opaque_phases.get(&view), alpha_mask_phases.get(&view))
else {
return Ok(());
};
let diagnostics = render_context.diagnostic_recorder();
let color_attachments = [Some(target.get_color_attachment())];

View File

@ -4,7 +4,7 @@ use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_render::{
camera::ExtractedCamera,
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_phase::SortedRenderPhase,
render_phase::ViewSortedRenderPhases,
render_resource::{Extent3d, RenderPassDescriptor, StoreOp},
renderer::RenderContext,
view::{ViewDepthTexture, ViewTarget},
@ -22,7 +22,6 @@ impl ViewNode for MainTransmissivePass3dNode {
type ViewQuery = (
&'static ExtractedCamera,
&'static Camera3d,
&'static SortedRenderPhase<Transmissive3d>,
&'static ViewTarget,
Option<&'static ViewTransmissionTexture>,
&'static ViewDepthTexture,
@ -32,13 +31,21 @@ impl ViewNode for MainTransmissivePass3dNode {
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
(camera, camera_3d, transmissive_phase, target, transmission, depth): QueryItem<
Self::ViewQuery,
>,
(camera, camera_3d, target, transmission, depth): QueryItem<Self::ViewQuery>,
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.view_entity();
let Some(transmissive_phases) =
world.get_resource::<ViewSortedRenderPhases<Transmissive3d>>()
else {
return Ok(());
};
let Some(transmissive_phase) = transmissive_phases.get(&view_entity) else {
return Ok(());
};
let physical_target_size = camera.physical_target_size.unwrap();
let render_pass_descriptor = RenderPassDescriptor {

View File

@ -4,7 +4,7 @@ use bevy_render::{
camera::ExtractedCamera,
diagnostic::RecordDiagnostics,
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_phase::SortedRenderPhase,
render_phase::ViewSortedRenderPhases,
render_resource::{RenderPassDescriptor, StoreOp},
renderer::RenderContext,
view::{ViewDepthTexture, ViewTarget},
@ -20,7 +20,6 @@ pub struct MainTransparentPass3dNode;
impl ViewNode for MainTransparentPass3dNode {
type ViewQuery = (
&'static ExtractedCamera,
&'static SortedRenderPhase<Transparent3d>,
&'static ViewTarget,
&'static ViewDepthTexture,
);
@ -28,11 +27,21 @@ impl ViewNode for MainTransparentPass3dNode {
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
(camera, transparent_phase, target, depth): QueryItem<Self::ViewQuery>,
(camera, target, depth): QueryItem<Self::ViewQuery>,
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.view_entity();
let Some(transparent_phases) =
world.get_resource::<ViewSortedRenderPhases<Transparent3d>>()
else {
return Ok(());
};
let Some(transparent_phase) = transparent_phases.get(&view_entity) else {
return Ok(());
};
if !transparent_phase.items.is_empty() {
// Run the transparent pass, sorted back-to-front
// NOTE: Scoped to drop the mutable borrow of render_context

View File

@ -50,7 +50,7 @@ pub use main_opaque_pass_3d_node::*;
pub use main_transparent_pass_3d_node::*;
use bevy_app::{App, Plugin, PostUpdate};
use bevy_ecs::prelude::*;
use bevy_ecs::{entity::EntityHashSet, prelude::*};
use bevy_math::FloatOrd;
use bevy_render::{
camera::{Camera, ExtractedCamera},
@ -59,9 +59,9 @@ use bevy_render::{
prelude::Msaa,
render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner},
render_phase::{
sort_phase_system, BinnedPhaseItem, BinnedRenderPhase, CachedRenderPipelinePhaseItem,
DrawFunctionId, DrawFunctions, PhaseItem, PhaseItemExtraIndex, SortedPhaseItem,
SortedRenderPhase,
sort_phase_system, BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId,
DrawFunctions, PhaseItem, PhaseItemExtraIndex, SortedPhaseItem, ViewBinnedRenderPhases,
ViewSortedRenderPhases,
},
render_resource::{
BindGroupId, CachedRenderPipelineId, Extent3d, FilterMode, Sampler, SamplerDescriptor,
@ -473,23 +473,43 @@ impl CachedRenderPipelinePhaseItem for Transparent3d {
pub fn extract_core_3d_camera_phases(
mut commands: Commands,
mut opaque_3d_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
mut alpha_mask_3d_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
mut transmissive_3d_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
mut transparent_3d_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
cameras_3d: Extract<Query<(Entity, &Camera), With<Camera3d>>>,
mut live_entities: Local<EntityHashSet>,
) {
live_entities.clear();
for (entity, camera) in &cameras_3d {
if camera.is_active {
commands.get_or_spawn(entity).insert((
BinnedRenderPhase::<Opaque3d>::default(),
BinnedRenderPhase::<AlphaMask3d>::default(),
SortedRenderPhase::<Transmissive3d>::default(),
SortedRenderPhase::<Transparent3d>::default(),
));
if !camera.is_active {
continue;
}
commands.get_or_spawn(entity);
opaque_3d_phases.insert_or_clear(entity);
alpha_mask_3d_phases.insert_or_clear(entity);
transmissive_3d_phases.insert_or_clear(entity);
transparent_3d_phases.insert_or_clear(entity);
live_entities.insert(entity);
}
opaque_3d_phases.retain(|entity, _| live_entities.contains(entity));
alpha_mask_3d_phases.retain(|entity, _| live_entities.contains(entity));
transmissive_3d_phases.retain(|entity, _| live_entities.contains(entity));
transparent_3d_phases.retain(|entity, _| live_entities.contains(entity));
}
// Extract the render phases for the prepass
pub fn extract_camera_prepass_phase(
mut commands: Commands,
mut opaque_3d_prepass_phases: ResMut<ViewBinnedRenderPhases<Opaque3dPrepass>>,
mut alpha_mask_3d_prepass_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
mut opaque_3d_deferred_phases: ResMut<ViewBinnedRenderPhases<Opaque3dDeferred>>,
mut alpha_mask_3d_deferred_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
cameras_3d: Extract<
Query<
(
@ -503,60 +523,79 @@ pub fn extract_camera_prepass_phase(
With<Camera3d>,
>,
>,
mut live_entities: Local<EntityHashSet>,
) {
live_entities.clear();
for (entity, camera, depth_prepass, normal_prepass, motion_vector_prepass, deferred_prepass) in
cameras_3d.iter()
{
if camera.is_active {
let mut entity = commands.get_or_spawn(entity);
if !camera.is_active {
continue;
}
if depth_prepass || normal_prepass || motion_vector_prepass {
entity.insert((
BinnedRenderPhase::<Opaque3dPrepass>::default(),
BinnedRenderPhase::<AlphaMask3dPrepass>::default(),
));
}
if depth_prepass || normal_prepass || motion_vector_prepass {
opaque_3d_prepass_phases.insert_or_clear(entity);
alpha_mask_3d_prepass_phases.insert_or_clear(entity);
} else {
opaque_3d_prepass_phases.remove(&entity);
alpha_mask_3d_prepass_phases.remove(&entity);
}
if deferred_prepass {
entity.insert((
BinnedRenderPhase::<Opaque3dDeferred>::default(),
BinnedRenderPhase::<AlphaMask3dDeferred>::default(),
));
}
if deferred_prepass {
opaque_3d_deferred_phases.insert_or_clear(entity);
alpha_mask_3d_deferred_phases.insert_or_clear(entity);
} else {
opaque_3d_deferred_phases.remove(&entity);
alpha_mask_3d_deferred_phases.remove(&entity);
}
if depth_prepass {
entity.insert(DepthPrepass);
}
if normal_prepass {
entity.insert(NormalPrepass);
}
if motion_vector_prepass {
entity.insert(MotionVectorPrepass);
}
if deferred_prepass {
entity.insert(DeferredPrepass);
}
live_entities.insert(entity);
let mut entity = commands.get_or_spawn(entity);
if depth_prepass {
entity.insert(DepthPrepass);
}
if normal_prepass {
entity.insert(NormalPrepass);
}
if motion_vector_prepass {
entity.insert(MotionVectorPrepass);
}
if deferred_prepass {
entity.insert(DeferredPrepass);
}
}
opaque_3d_prepass_phases.retain(|entity, _| live_entities.contains(entity));
alpha_mask_3d_prepass_phases.retain(|entity, _| live_entities.contains(entity));
opaque_3d_deferred_phases.retain(|entity, _| live_entities.contains(entity));
alpha_mask_3d_deferred_phases.retain(|entity, _| live_entities.contains(entity));
}
#[allow(clippy::too_many_arguments)]
pub fn prepare_core_3d_depth_textures(
mut commands: Commands,
mut texture_cache: ResMut<TextureCache>,
msaa: Res<Msaa>,
render_device: Res<RenderDevice>,
views_3d: Query<
(Entity, &ExtractedCamera, Option<&DepthPrepass>, &Camera3d),
(
With<BinnedRenderPhase<Opaque3d>>,
With<BinnedRenderPhase<AlphaMask3d>>,
With<SortedRenderPhase<Transmissive3d>>,
With<SortedRenderPhase<Transparent3d>>,
),
>,
opaque_3d_phases: Res<ViewBinnedRenderPhases<Opaque3d>>,
alpha_mask_3d_phases: Res<ViewBinnedRenderPhases<AlphaMask3d>>,
transmissive_3d_phases: Res<ViewSortedRenderPhases<Transmissive3d>>,
transparent_3d_phases: Res<ViewSortedRenderPhases<Transparent3d>>,
views_3d: Query<(Entity, &ExtractedCamera, Option<&DepthPrepass>, &Camera3d)>,
) {
let mut render_target_usage = HashMap::default();
for (_, camera, depth_prepass, camera_3d) in &views_3d {
for (view, camera, depth_prepass, camera_3d) in &views_3d {
if !opaque_3d_phases.contains_key(&view)
|| !alpha_mask_3d_phases.contains_key(&view)
|| !transmissive_3d_phases.contains_key(&view)
|| !transparent_3d_phases.contains_key(&view)
{
continue;
};
// Default usage required to write to the depth texture
let mut usage: TextureUsages = camera_3d.depth_texture_usages.into();
if depth_prepass.is_some() {
@ -621,27 +660,30 @@ pub struct ViewTransmissionTexture {
pub sampler: Sampler,
}
#[allow(clippy::too_many_arguments)]
pub fn prepare_core_3d_transmission_textures(
mut commands: Commands,
mut texture_cache: ResMut<TextureCache>,
render_device: Res<RenderDevice>,
views_3d: Query<
(
Entity,
&ExtractedCamera,
&Camera3d,
&ExtractedView,
&SortedRenderPhase<Transmissive3d>,
),
(
With<BinnedRenderPhase<Opaque3d>>,
With<BinnedRenderPhase<AlphaMask3d>>,
With<SortedRenderPhase<Transparent3d>>,
),
>,
opaque_3d_phases: Res<ViewBinnedRenderPhases<Opaque3d>>,
alpha_mask_3d_phases: Res<ViewBinnedRenderPhases<AlphaMask3d>>,
transmissive_3d_phases: Res<ViewSortedRenderPhases<Transmissive3d>>,
transparent_3d_phases: Res<ViewSortedRenderPhases<Transparent3d>>,
views_3d: Query<(Entity, &ExtractedCamera, &Camera3d, &ExtractedView)>,
) {
let mut textures = HashMap::default();
for (entity, camera, camera_3d, view, transmissive_3d_phase) in &views_3d {
for (entity, camera, camera_3d, view) in &views_3d {
if !opaque_3d_phases.contains_key(&entity)
|| !alpha_mask_3d_phases.contains_key(&entity)
|| !transparent_3d_phases.contains_key(&entity)
{
continue;
};
let Some(transmissive_3d_phase) = transmissive_3d_phases.get(&entity) else {
continue;
};
let Some(physical_target_size) = camera.physical_target_size else {
continue;
};
@ -721,27 +763,24 @@ pub fn check_msaa(
}
// Prepares the textures used by the prepass
#[allow(clippy::too_many_arguments)]
pub fn prepare_prepass_textures(
mut commands: Commands,
mut texture_cache: ResMut<TextureCache>,
msaa: Res<Msaa>,
render_device: Res<RenderDevice>,
views_3d: Query<
(
Entity,
&ExtractedCamera,
Has<DepthPrepass>,
Has<NormalPrepass>,
Has<MotionVectorPrepass>,
Has<DeferredPrepass>,
),
Or<(
With<BinnedRenderPhase<Opaque3dPrepass>>,
With<BinnedRenderPhase<AlphaMask3dPrepass>>,
With<BinnedRenderPhase<Opaque3dDeferred>>,
With<BinnedRenderPhase<AlphaMask3dDeferred>>,
)>,
>,
opaque_3d_prepass_phases: Res<ViewBinnedRenderPhases<Opaque3dPrepass>>,
alpha_mask_3d_prepass_phases: Res<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
opaque_3d_deferred_phases: Res<ViewBinnedRenderPhases<Opaque3dDeferred>>,
alpha_mask_3d_deferred_phases: Res<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
views_3d: Query<(
Entity,
&ExtractedCamera,
Has<DepthPrepass>,
Has<NormalPrepass>,
Has<MotionVectorPrepass>,
Has<DeferredPrepass>,
)>,
) {
let mut depth_textures = HashMap::default();
let mut normal_textures = HashMap::default();
@ -751,6 +790,14 @@ pub fn prepare_prepass_textures(
for (entity, camera, depth_prepass, normal_prepass, motion_vector_prepass, deferred_prepass) in
&views_3d
{
if !opaque_3d_prepass_phases.contains_key(&entity)
&& !alpha_mask_3d_prepass_phases.contains_key(&entity)
&& !opaque_3d_deferred_phases.contains_key(&entity)
&& !alpha_mask_3d_deferred_phases.contains_key(&entity)
{
continue;
};
let Some(physical_target_size) = camera.physical_target_size else {
continue;
};

View File

@ -2,7 +2,7 @@ use bevy_ecs::prelude::*;
use bevy_ecs::query::QueryItem;
use bevy_render::render_graph::ViewNode;
use bevy_render::render_phase::{BinnedRenderPhase, TrackedRenderPass};
use bevy_render::render_phase::{TrackedRenderPass, ViewBinnedRenderPhases};
use bevy_render::render_resource::{CommandEncoderDescriptor, StoreOp};
use bevy_render::{
camera::ExtractedCamera,
@ -26,9 +26,8 @@ pub struct DeferredGBufferPrepassNode;
impl ViewNode for DeferredGBufferPrepassNode {
type ViewQuery = (
Entity,
&'static ExtractedCamera,
&'static BinnedRenderPhase<Opaque3dDeferred>,
&'static BinnedRenderPhase<AlphaMask3dDeferred>,
&'static ViewDepthTexture,
&'static ViewPrepassTextures,
);
@ -37,15 +36,23 @@ impl ViewNode for DeferredGBufferPrepassNode {
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
(
camera,
opaque_deferred_phase,
alpha_mask_deferred_phase,
view_depth_texture,
view_prepass_textures,
): QueryItem<'w, Self::ViewQuery>,
(view, camera, view_depth_texture, view_prepass_textures): QueryItem<'w, Self::ViewQuery>,
world: &'w World,
) -> Result<(), NodeRunError> {
let (Some(opaque_deferred_phases), Some(alpha_mask_deferred_phases)) = (
world.get_resource::<ViewBinnedRenderPhases<Opaque3dDeferred>>(),
world.get_resource::<ViewBinnedRenderPhases<AlphaMask3dDeferred>>(),
) else {
return Ok(());
};
let (Some(opaque_deferred_phase), Some(alpha_mask_deferred_phase)) = (
opaque_deferred_phases.get(&view),
alpha_mask_deferred_phases.get(&view),
) else {
return Ok(());
};
let mut color_attachments = vec![];
color_attachments.push(
view_prepass_textures

View File

@ -4,7 +4,7 @@ use bevy_render::{
camera::ExtractedCamera,
diagnostic::RecordDiagnostics,
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_phase::{BinnedRenderPhase, TrackedRenderPass},
render_phase::{TrackedRenderPass, ViewBinnedRenderPhases},
render_resource::{CommandEncoderDescriptor, RenderPassDescriptor, StoreOp},
renderer::RenderContext,
view::ViewDepthTexture,
@ -22,9 +22,8 @@ pub struct PrepassNode;
impl ViewNode for PrepassNode {
type ViewQuery = (
Entity,
&'static ExtractedCamera,
&'static BinnedRenderPhase<Opaque3dPrepass>,
&'static BinnedRenderPhase<AlphaMask3dPrepass>,
&'static ViewDepthTexture,
&'static ViewPrepassTextures,
Option<&'static DeferredPrepass>,
@ -34,16 +33,26 @@ impl ViewNode for PrepassNode {
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext<'w>,
(
camera,
opaque_prepass_phase,
alpha_mask_prepass_phase,
view_depth_texture,
view_prepass_textures,
deferred_prepass,
): QueryItem<'w, Self::ViewQuery>,
(view, camera, view_depth_texture, view_prepass_textures, deferred_prepass): QueryItem<
'w,
Self::ViewQuery,
>,
world: &'w World,
) -> Result<(), NodeRunError> {
let (Some(opaque_prepass_phases), Some(alpha_mask_prepass_phases)) = (
world.get_resource::<ViewBinnedRenderPhases<Opaque3dPrepass>>(),
world.get_resource::<ViewBinnedRenderPhases<AlphaMask3dPrepass>>(),
) else {
return Ok(());
};
let (Some(opaque_prepass_phase), Some(alpha_mask_prepass_phase)) = (
opaque_prepass_phases.get(&view),
alpha_mask_prepass_phases.get(&view),
) else {
return Ok(());
};
let diagnostics = render_context.diagnostic_recorder();
let mut color_attachments = vec![

View File

@ -19,7 +19,8 @@ use bevy_math::FloatOrd;
use bevy_render::{
render_asset::{prepare_assets, RenderAssets},
render_phase::{
AddRenderCommand, DrawFunctions, PhaseItemExtraIndex, SetItemPipeline, SortedRenderPhase,
AddRenderCommand, DrawFunctions, PhaseItemExtraIndex, SetItemPipeline,
ViewSortedRenderPhases,
},
render_resource::*,
texture::BevyDefault,
@ -257,15 +258,16 @@ fn queue_line_gizmos_2d(
msaa: Res<Msaa>,
line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
mut views: Query<(
&ExtractedView,
&mut SortedRenderPhase<Transparent2d>,
Option<&RenderLayers>,
)>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
mut views: Query<(Entity, &ExtractedView, Option<&RenderLayers>)>,
) {
let draw_function = draw_functions.read().get_id::<DrawLineGizmo2d>().unwrap();
for (view, mut transparent_phase, render_layers) in &mut views {
for (view_entity, view, render_layers) in &mut views {
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
continue;
};
let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples())
| Mesh2dPipelineKey::from_hdr(view.hdr);
@ -310,18 +312,19 @@ fn queue_line_joint_gizmos_2d(
msaa: Res<Msaa>,
line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
mut views: Query<(
&ExtractedView,
&mut SortedRenderPhase<Transparent2d>,
Option<&RenderLayers>,
)>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
mut views: Query<(Entity, &ExtractedView, Option<&RenderLayers>)>,
) {
let draw_function = draw_functions
.read()
.get_id::<DrawLineJointGizmo2d>()
.unwrap();
for (view, mut transparent_phase, render_layers) in &mut views {
for (view_entity, view, render_layers) in &mut views {
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
continue;
};
let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples())
| Mesh2dPipelineKey::from_hdr(view.hdr);

View File

@ -23,7 +23,8 @@ use bevy_pbr::{MeshPipeline, MeshPipelineKey, SetMeshViewBindGroup};
use bevy_render::{
render_asset::{prepare_assets, RenderAssets},
render_phase::{
AddRenderCommand, DrawFunctions, PhaseItemExtraIndex, SetItemPipeline, SortedRenderPhase,
AddRenderCommand, DrawFunctions, PhaseItemExtraIndex, SetItemPipeline,
ViewSortedRenderPhases,
},
render_resource::*,
texture::BevyDefault,
@ -282,9 +283,10 @@ fn queue_line_gizmos_3d(
msaa: Res<Msaa>,
line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
mut views: Query<(
Entity,
&ExtractedView,
&mut SortedRenderPhase<Transparent3d>,
Option<&RenderLayers>,
(
Has<NormalPrepass>,
@ -297,12 +299,16 @@ fn queue_line_gizmos_3d(
let draw_function = draw_functions.read().get_id::<DrawLineGizmo3d>().unwrap();
for (
view_entity,
view,
mut transparent_phase,
render_layers,
(normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
) in &mut views
{
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
continue;
};
let render_layers = render_layers.unwrap_or_default();
let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())
@ -365,9 +371,10 @@ fn queue_line_joint_gizmos_3d(
msaa: Res<Msaa>,
line_gizmos: Query<(Entity, &Handle<LineGizmo>, &GizmoMeshConfig)>,
line_gizmo_assets: Res<RenderAssets<GpuLineGizmo>>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
mut views: Query<(
Entity,
&ExtractedView,
&mut SortedRenderPhase<Transparent3d>,
Option<&RenderLayers>,
(
Has<NormalPrepass>,
@ -383,12 +390,16 @@ fn queue_line_joint_gizmos_3d(
.unwrap();
for (
view_entity,
view,
mut transparent_phase,
render_layers,
(normal_prepass, depth_prepass, motion_vector_prepass, deferred_prepass),
) in &mut views
{
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
continue;
};
let render_layers = render_layers.unwrap_or_default();
let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples())

View File

@ -516,10 +516,17 @@ pub const fn screen_space_specular_transmission_pipeline_key(
/// them to [`BinnedRenderPhase`]s or [`SortedRenderPhase`]s as appropriate.
#[allow(clippy::too_many_arguments)]
pub fn queue_material_meshes<M: Material>(
opaque_draw_functions: Res<DrawFunctions<Opaque3d>>,
alpha_mask_draw_functions: Res<DrawFunctions<AlphaMask3d>>,
transmissive_draw_functions: Res<DrawFunctions<Transmissive3d>>,
transparent_draw_functions: Res<DrawFunctions<Transparent3d>>,
(
opaque_draw_functions,
alpha_mask_draw_functions,
transmissive_draw_functions,
transparent_draw_functions,
): (
Res<DrawFunctions<Opaque3d>>,
Res<DrawFunctions<AlphaMask3d>>,
Res<DrawFunctions<Transmissive3d>>,
Res<DrawFunctions<Transparent3d>>,
),
material_pipeline: Res<MaterialPipeline<M>>,
mut pipelines: ResMut<SpecializedMeshPipelines<MaterialPipeline<M>>>,
pipeline_cache: Res<PipelineCache>,
@ -530,7 +537,12 @@ pub fn queue_material_meshes<M: Material>(
render_material_instances: Res<RenderMaterialInstances<M>>,
render_lightmaps: Res<RenderLightmaps>,
render_visibility_ranges: Res<RenderVisibilityRanges>,
mut opaque_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3d>>,
mut alpha_mask_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3d>>,
mut transmissive_render_phases: ResMut<ViewSortedRenderPhases<Transmissive3d>>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
mut views: Query<(
Entity,
&ExtractedView,
&VisibleEntities,
Option<&Tonemapping>,
@ -546,10 +558,6 @@ pub fn queue_material_meshes<M: Material>(
Option<&Camera3d>,
Has<TemporalJitter>,
Option<&Projection>,
&mut BinnedRenderPhase<Opaque3d>,
&mut BinnedRenderPhase<AlphaMask3d>,
&mut SortedRenderPhase<Transmissive3d>,
&mut SortedRenderPhase<Transparent3d>,
(
Has<RenderViewLightProbes<EnvironmentMapLight>>,
Has<RenderViewLightProbes<IrradianceVolume>>,
@ -559,6 +567,7 @@ pub fn queue_material_meshes<M: Material>(
M::Data: PartialEq + Eq + Hash + Clone,
{
for (
view_entity,
view,
visible_entities,
tonemapping,
@ -569,13 +578,24 @@ pub fn queue_material_meshes<M: Material>(
camera_3d,
temporal_jitter,
projection,
mut opaque_phase,
mut alpha_mask_phase,
mut transmissive_phase,
mut transparent_phase,
(has_environment_maps, has_irradiance_volumes),
) in &mut views
{
let (
Some(opaque_phase),
Some(alpha_mask_phase),
Some(transmissive_phase),
Some(transparent_phase),
) = (
opaque_render_phases.get_mut(&view_entity),
alpha_mask_render_phases.get_mut(&view_entity),
transmissive_render_phases.get_mut(&view_entity),
transparent_render_phases.get_mut(&view_entity),
)
else {
continue;
};
let draw_opaque_pbr = opaque_draw_functions.read().id::<DrawMaterial<M>>();
let draw_alpha_mask_pbr = alpha_mask_draw_functions.read().id::<DrawMaterial<M>>();
let draw_transmissive_pbr = transmissive_draw_functions.read().id::<DrawMaterial<M>>();

View File

@ -700,10 +700,17 @@ pub fn prepare_prepass_view_bind_group<M: Material>(
#[allow(clippy::too_many_arguments)]
pub fn queue_prepass_material_meshes<M: Material>(
opaque_draw_functions: Res<DrawFunctions<Opaque3dPrepass>>,
alpha_mask_draw_functions: Res<DrawFunctions<AlphaMask3dPrepass>>,
opaque_deferred_draw_functions: Res<DrawFunctions<Opaque3dDeferred>>,
alpha_mask_deferred_draw_functions: Res<DrawFunctions<AlphaMask3dDeferred>>,
(
opaque_draw_functions,
alpha_mask_draw_functions,
opaque_deferred_draw_functions,
alpha_mask_deferred_draw_functions,
): (
Res<DrawFunctions<Opaque3dPrepass>>,
Res<DrawFunctions<AlphaMask3dPrepass>>,
Res<DrawFunctions<Opaque3dDeferred>>,
Res<DrawFunctions<AlphaMask3dDeferred>>,
),
prepass_pipeline: Res<PrepassPipeline<M>>,
mut pipelines: ResMut<SpecializedMeshPipelines<PrepassPipeline<M>>>,
pipeline_cache: Res<PipelineCache>,
@ -713,25 +720,20 @@ pub fn queue_prepass_material_meshes<M: Material>(
render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
render_material_instances: Res<RenderMaterialInstances<M>>,
render_lightmaps: Res<RenderLightmaps>,
mut opaque_prepass_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3dPrepass>>,
mut alpha_mask_prepass_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dPrepass>>,
mut opaque_deferred_render_phases: ResMut<ViewBinnedRenderPhases<Opaque3dDeferred>>,
mut alpha_mask_deferred_render_phases: ResMut<ViewBinnedRenderPhases<AlphaMask3dDeferred>>,
mut views: Query<
(
&ExtractedView,
Entity,
&VisibleEntities,
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<BinnedRenderPhase<Opaque3dPrepass>>,
With<BinnedRenderPhase<AlphaMask3dPrepass>>,
With<BinnedRenderPhase<Opaque3dDeferred>>,
With<BinnedRenderPhase<AlphaMask3dDeferred>>,
)>,
With<ExtractedView>,
>,
) where
M::Data: PartialEq + Eq + Hash + Clone,
@ -753,18 +755,35 @@ pub fn queue_prepass_material_meshes<M: Material>(
.get_id::<DrawPrepass<M>>()
.unwrap();
for (
_view,
view,
visible_entities,
mut opaque_phase,
mut alpha_mask_phase,
mut opaque_deferred_phase,
mut alpha_mask_deferred_phase,
depth_prepass,
normal_prepass,
motion_vector_prepass,
deferred_prepass,
) in &mut views
{
let (
mut opaque_phase,
mut alpha_mask_phase,
mut opaque_deferred_phase,
mut alpha_mask_deferred_phase,
) = (
opaque_prepass_render_phases.get_mut(&view),
alpha_mask_prepass_render_phases.get_mut(&view),
opaque_deferred_render_phases.get_mut(&view),
alpha_mask_deferred_render_phases.get_mut(&view),
);
// Skip if there's no place to put the mesh.
if opaque_phase.is_none()
&& alpha_mask_phase.is_none()
&& opaque_deferred_phase.is_none()
&& alpha_mask_deferred_phase.is_none()
{
continue;
}
let mut view_key = MeshPipelineKey::from_msaa_samples(msaa.samples());
if depth_prepass.is_some() {
view_key |= MeshPipelineKey::DEPTH_PREPASS;

View File

@ -1,5 +1,6 @@
use bevy_asset::AssetId;
use bevy_core_pipeline::core_3d::{Transparent3d, CORE_3D_DEPTH_FORMAT};
use bevy_core_pipeline::core_3d::CORE_3D_DEPTH_FORMAT;
use bevy_ecs::entity::EntityHashSet;
use bevy_ecs::prelude::*;
use bevy_ecs::{entity::EntityHashMap, system::lifetimeless::Read};
use bevy_math::{Mat4, UVec3, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles};
@ -688,18 +689,16 @@ pub fn prepare_lights(
render_queue: Res<RenderQueue>,
mut global_light_meta: ResMut<GlobalLightMeta>,
mut light_meta: ResMut<LightMeta>,
views: Query<
(
Entity,
&ExtractedView,
&ExtractedClusterConfig,
Option<&RenderLayers>,
),
With<SortedRenderPhase<Transparent3d>>,
>,
views: Query<(
Entity,
&ExtractedView,
&ExtractedClusterConfig,
Option<&RenderLayers>,
)>,
ambient_light: Res<AmbientLight>,
point_light_shadow_map: Res<PointLightShadowMap>,
directional_light_shadow_map: Res<DirectionalLightShadowMap>,
mut shadow_render_phases: ResMut<ViewBinnedRenderPhases<Shadow>>,
mut max_directional_lights_warning_emitted: Local<bool>,
mut max_cascades_per_light_warning_emitted: Local<bool>,
point_lights: Query<(
@ -708,6 +707,7 @@ pub fn prepare_lights(
AnyOf<(&CubemapFrusta, &Frustum)>,
)>,
directional_lights: Query<(Entity, &ExtractedDirectionalLight)>,
mut live_shadow_mapping_lights: Local<EntityHashSet>,
) {
let views_iter = views.iter();
let views_count = views_iter.len();
@ -958,6 +958,8 @@ pub fn prepare_lights(
.gpu_point_lights
.write_buffer(&render_device, &render_queue);
live_shadow_mapping_lights.clear();
// set up light data for each view
for (entity, extracted_view, clusters, maybe_layers) in &views {
let point_light_depth_texture = texture_cache.get(
@ -1088,7 +1090,6 @@ pub fn prepare_lights(
color_grading: Default::default(),
},
*frustum,
BinnedRenderPhase::<Shadow>::default(),
LightEntity::Point {
light_entity,
face_index,
@ -1096,6 +1097,9 @@ pub fn prepare_lights(
))
.id();
view_lights.push(view_light_entity);
shadow_render_phases.insert_or_clear(view_light_entity);
live_shadow_mapping_lights.insert(view_light_entity);
}
}
@ -1147,12 +1151,14 @@ pub fn prepare_lights(
color_grading: Default::default(),
},
*spot_light_frustum.unwrap(),
BinnedRenderPhase::<Shadow>::default(),
LightEntity::Spot { light_entity },
))
.id();
view_lights.push(view_light_entity);
shadow_render_phases.insert_or_clear(view_light_entity);
live_shadow_mapping_lights.insert(view_light_entity);
}
// directional lights
@ -1241,7 +1247,6 @@ pub fn prepare_lights(
color_grading: Default::default(),
},
frustum,
BinnedRenderPhase::<Shadow>::default(),
LightEntity::Directional {
light_entity,
cascade_index,
@ -1249,6 +1254,9 @@ pub fn prepare_lights(
))
.id();
view_lights.push(view_light_entity);
shadow_render_phases.insert_or_clear(view_light_entity);
live_shadow_mapping_lights.insert(view_light_entity);
}
}
@ -1315,6 +1323,8 @@ pub fn prepare_lights(
},
));
}
shadow_render_phases.retain(|entity, _| live_shadow_mapping_lights.contains(entity));
}
// this must match CLUSTER_COUNT_SIZE in pbr.wgsl
@ -1593,7 +1603,7 @@ pub fn prepare_clusters(
render_queue: Res<RenderQueue>,
mesh_pipeline: Res<MeshPipeline>,
global_light_meta: Res<GlobalLightMeta>,
views: Query<(Entity, &ExtractedClustersPointLights), With<SortedRenderPhase<Transparent3d>>>,
views: Query<(Entity, &ExtractedClustersPointLights)>,
) {
let render_device = render_device.into_inner();
let supports_storage_buffers = matches!(
@ -1649,11 +1659,12 @@ pub fn queue_shadows<M: Material>(
render_mesh_instances: Res<RenderMeshInstances>,
render_materials: Res<RenderAssets<PreparedMaterial<M>>>,
render_material_instances: Res<RenderMaterialInstances<M>>,
mut shadow_render_phases: ResMut<ViewBinnedRenderPhases<Shadow>>,
mut pipelines: ResMut<SpecializedMeshPipelines<PrepassPipeline<M>>>,
pipeline_cache: Res<PipelineCache>,
render_lightmaps: Res<RenderLightmaps>,
view_lights: Query<(Entity, &ViewLightEntities)>,
mut view_light_shadow_phases: Query<(&LightEntity, &mut BinnedRenderPhase<Shadow>)>,
mut view_light_entities: Query<&LightEntity>,
point_light_entities: Query<&CubemapVisibleEntities, With<ExtractedPointLight>>,
directional_light_entities: Query<&CascadesVisibleEntities, With<ExtractedDirectionalLight>>,
spot_light_entities: Query<&VisibleEntities, With<ExtractedPointLight>>,
@ -1663,8 +1674,13 @@ pub fn queue_shadows<M: Material>(
for (entity, view_lights) in &view_lights {
let draw_shadow_mesh = shadow_draw_functions.read().id::<DrawPrepass<M>>();
for view_light_entity in view_lights.lights.iter().copied() {
let (light_entity, mut shadow_phase) =
view_light_shadow_phases.get_mut(view_light_entity).unwrap();
let Ok(light_entity) = view_light_entities.get_mut(view_light_entity) else {
continue;
};
let Some(shadow_phase) = shadow_render_phases.get_mut(&view_light_entity) else {
continue;
};
let is_directional_light = matches!(light_entity, LightEntity::Directional { .. });
let visible_entities = match light_entity {
LightEntity::Directional {
@ -1851,7 +1867,7 @@ impl CachedRenderPipelinePhaseItem for Shadow {
pub struct ShadowPassNode {
main_view_query: QueryState<Read<ViewLightEntities>>,
view_light_query: QueryState<(Read<ShadowView>, Read<BinnedRenderPhase<Shadow>>)>,
view_light_query: QueryState<Read<ShadowView>>,
}
impl ShadowPassNode {
@ -1876,12 +1892,23 @@ impl Node for ShadowPassNode {
world: &'w World,
) -> Result<(), NodeRunError> {
let diagnostics = render_context.diagnostic_recorder();
let time_span = diagnostics.time_span(render_context.command_encoder(), "shadows");
let view_entity = graph.view_entity();
let Some(shadow_render_phases) = world.get_resource::<ViewBinnedRenderPhases<Shadow>>()
else {
return Ok(());
};
let time_span = diagnostics.time_span(render_context.command_encoder(), "shadows");
if let Ok(view_lights) = self.main_view_query.get_manual(world, view_entity) {
for view_light_entity in view_lights.lights.iter().copied() {
let (view_light, shadow_phase) = self
let Some(shadow_phase) = shadow_render_phases.get(&view_light_entity) else {
continue;
};
let view_light = self
.view_light_query
.get_manual(world, view_light_entity)
.unwrap();

View File

@ -18,8 +18,9 @@ use wgpu::{BindingResource, BufferUsages, DownlevelFlags, Features};
use crate::{
render_phase::{
BinnedPhaseItem, BinnedRenderPhase, BinnedRenderPhaseBatch, CachedRenderPipelinePhaseItem,
BinnedPhaseItem, BinnedRenderPhaseBatch, CachedRenderPipelinePhaseItem,
PhaseItemExtraIndex, SortedPhaseItem, SortedRenderPhase, UnbatchableBinnedEntityIndices,
ViewBinnedRenderPhases, ViewSortedRenderPhases,
},
render_resource::{BufferVec, GpuArrayBufferable, RawBufferVec, UninitBufferVec},
renderer::{RenderAdapter, RenderDevice, RenderQueue},
@ -372,7 +373,8 @@ pub fn delete_old_work_item_buffers<GFBD>(
pub fn batch_and_prepare_sorted_render_phase<I, GFBD>(
gpu_array_buffer: ResMut<BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>>,
mut indirect_parameters_buffer: ResMut<IndirectParametersBuffer>,
mut views: Query<(Entity, &mut SortedRenderPhase<I>, Has<GpuCulling>)>,
mut sorted_render_phases: ResMut<ViewSortedRenderPhases<I>>,
mut views: Query<(Entity, Has<GpuCulling>)>,
system_param_item: StaticSystemParam<GFBD::Param>,
) where
I: CachedRenderPipelinePhaseItem + SortedPhaseItem,
@ -385,7 +387,11 @@ pub fn batch_and_prepare_sorted_render_phase<I, GFBD>(
..
} = gpu_array_buffer.into_inner();
for (view, mut phase, gpu_culling) in &mut views {
for (view, gpu_culling) in &mut views {
let Some(phase) = sorted_render_phases.get_mut(&view) else {
continue;
};
// Create the work item buffer if necessary.
let work_item_buffer =
work_item_buffers
@ -433,7 +439,7 @@ pub fn batch_and_prepare_sorted_render_phase<I, GFBD>(
if !can_batch {
// Break a batch if we need to.
if let Some(batch) = batch.take() {
batch.flush(output_index, &mut phase);
batch.flush(output_index, phase);
}
// Start a new batch.
@ -471,7 +477,7 @@ pub fn batch_and_prepare_sorted_render_phase<I, GFBD>(
// Flush the final batch if necessary.
if let Some(batch) = batch.take() {
batch.flush(data_buffer.len() as u32, &mut phase);
batch.flush(data_buffer.len() as u32, phase);
}
}
}
@ -480,7 +486,8 @@ pub fn batch_and_prepare_sorted_render_phase<I, GFBD>(
pub fn batch_and_prepare_binned_render_phase<BPI, GFBD>(
gpu_array_buffer: ResMut<BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>>,
mut indirect_parameters_buffer: ResMut<IndirectParametersBuffer>,
mut views: Query<(Entity, &mut BinnedRenderPhase<BPI>, Has<GpuCulling>)>,
mut binned_render_phases: ResMut<ViewBinnedRenderPhases<BPI>>,
mut views: Query<(Entity, Has<GpuCulling>)>,
param: StaticSystemParam<GFBD::Param>,
) where
BPI: BinnedPhaseItem,
@ -494,8 +501,10 @@ pub fn batch_and_prepare_binned_render_phase<BPI, GFBD>(
..
} = gpu_array_buffer.into_inner();
for (view, mut phase, gpu_culling) in &mut views {
let phase = &mut *phase; // Borrow checker.
for (view, gpu_culling) in &mut views {
let Some(phase) = binned_render_phases.get_mut(&view) else {
continue;
};
// Create the work item buffer if necessary; otherwise, just mark it as
// used this frame.

View File

@ -1,15 +1,15 @@
use bevy_ecs::{
component::Component,
entity::Entity,
system::{Query, SystemParam, SystemParamItem},
system::{ResMut, SystemParam, SystemParamItem},
};
use bytemuck::Pod;
use nonmax::NonMaxU32;
use crate::{
render_phase::{
BinnedPhaseItem, BinnedRenderPhase, CachedRenderPipelinePhaseItem, DrawFunctionId,
SortedPhaseItem, SortedRenderPhase,
BinnedPhaseItem, CachedRenderPipelinePhaseItem, DrawFunctionId, SortedPhaseItem,
SortedRenderPhase, ViewBinnedRenderPhases,
},
render_resource::{CachedRenderPipelineId, GpuArrayBufferable},
};
@ -151,11 +151,11 @@ pub trait GetFullBatchData: GetBatchData {
}
/// Sorts a render phase that uses bins.
pub fn sort_binned_render_phase<BPI>(mut views: Query<&mut BinnedRenderPhase<BPI>>)
pub fn sort_binned_render_phase<BPI>(mut phases: ResMut<ViewBinnedRenderPhases<BPI>>)
where
BPI: BinnedPhaseItem,
{
for mut phase in &mut views {
for phase in phases.values_mut() {
phase.batchable_keys.sort_unstable();
phase.unbatchable_keys.sort_unstable();
}

View File

@ -1,14 +1,14 @@
//! Batching functionality when GPU preprocessing isn't in use.
use bevy_derive::{Deref, DerefMut};
use bevy_ecs::system::{Query, Res, ResMut, Resource, StaticSystemParam};
use bevy_ecs::system::{Res, ResMut, Resource, StaticSystemParam};
use smallvec::{smallvec, SmallVec};
use wgpu::BindingResource;
use crate::{
render_phase::{
BinnedPhaseItem, BinnedRenderPhase, BinnedRenderPhaseBatch, CachedRenderPipelinePhaseItem,
PhaseItemExtraIndex, SortedPhaseItem, SortedRenderPhase,
BinnedPhaseItem, BinnedRenderPhaseBatch, CachedRenderPipelinePhaseItem,
PhaseItemExtraIndex, SortedPhaseItem, ViewBinnedRenderPhases, ViewSortedRenderPhases,
},
render_resource::{GpuArrayBuffer, GpuArrayBufferable},
renderer::{RenderDevice, RenderQueue},
@ -61,7 +61,7 @@ pub fn clear_batched_cpu_instance_buffers<GBD>(
/// and trying to combine the draws into a batch.
pub fn batch_and_prepare_sorted_render_phase<I, GBD>(
batched_instance_buffer: ResMut<BatchedInstanceBuffer<GBD::BufferData>>,
mut views: Query<&mut SortedRenderPhase<I>>,
mut phases: ResMut<ViewSortedRenderPhases<I>>,
param: StaticSystemParam<GBD::Param>,
) where
I: CachedRenderPipelinePhaseItem + SortedPhaseItem,
@ -72,8 +72,8 @@ pub fn batch_and_prepare_sorted_render_phase<I, GBD>(
// We only process CPU-built batch data in this function.
let batched_instance_buffer = batched_instance_buffer.into_inner();
for mut phase in &mut views {
super::batch_and_prepare_sorted_render_phase::<I, GBD>(&mut phase, |item| {
for phase in phases.values_mut() {
super::batch_and_prepare_sorted_render_phase::<I, GBD>(phase, |item| {
let (buffer_data, compare_data) =
GBD::get_batch_data(&system_param_item, item.entity())?;
let buffer_index = batched_instance_buffer.push(buffer_data);
@ -92,7 +92,7 @@ pub fn batch_and_prepare_sorted_render_phase<I, GBD>(
/// building isn't in use.
pub fn batch_and_prepare_binned_render_phase<BPI, GFBD>(
gpu_array_buffer: ResMut<BatchedInstanceBuffer<GFBD::BufferData>>,
mut views: Query<&mut BinnedRenderPhase<BPI>>,
mut phases: ResMut<ViewBinnedRenderPhases<BPI>>,
param: StaticSystemParam<GFBD::Param>,
) where
BPI: BinnedPhaseItem,
@ -101,9 +101,7 @@ pub fn batch_and_prepare_binned_render_phase<BPI, GFBD>(
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.
for phase in phases.values_mut() {
// Prepare batchables.
for key in &phase.batchable_keys {

View File

@ -29,6 +29,7 @@ mod draw_state;
mod rangefinder;
use bevy_app::{App, Plugin};
use bevy_derive::{Deref, DerefMut};
use bevy_utils::{default, hashbrown::hash_map::Entry, HashMap};
pub use draw::*;
pub use draw_state::*;
@ -47,6 +48,7 @@ use crate::{
Render, RenderApp, RenderSet,
};
use bevy_ecs::{
entity::EntityHashMap,
prelude::*,
system::{lifetimeless::SRes, SystemParamItem},
};
@ -60,6 +62,16 @@ use std::{
slice::SliceIndex,
};
/// Stores the rendering instructions for a single phase that uses bins in all
/// views.
///
/// They're cleared out every frame, but storing them in a resource like this
/// allows us to reuse allocations.
#[derive(Resource, Deref, DerefMut)]
pub struct ViewBinnedRenderPhases<BPI>(pub EntityHashMap<BinnedRenderPhase<BPI>>)
where
BPI: BinnedPhaseItem;
/// A collection of all rendering instructions, that will be executed by the GPU, for a
/// single render phase for a single view.
///
@ -74,7 +86,6 @@ use std::{
/// 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 BinnedRenderPhase<BPI>
where
BPI: BinnedPhaseItem,
@ -200,6 +211,29 @@ where
}
}
impl<BPI> Default for ViewBinnedRenderPhases<BPI>
where
BPI: BinnedPhaseItem,
{
fn default() -> Self {
Self(default())
}
}
impl<BPI> ViewBinnedRenderPhases<BPI>
where
BPI: BinnedPhaseItem,
{
pub fn insert_or_clear(&mut self, entity: Entity) {
match self.entry(entity) {
Entry::Occupied(mut entry) => entry.get_mut().clear(),
Entry::Vacant(entry) => {
entry.insert(default());
}
}
}
}
impl<BPI> BinnedRenderPhase<BPI>
where
BPI: BinnedPhaseItem,
@ -315,6 +349,14 @@ where
pub fn is_empty(&self) -> bool {
self.batchable_keys.is_empty() && self.unbatchable_keys.is_empty()
}
pub fn clear(&mut self) {
self.batchable_keys.clear();
self.batchable_values.clear();
self.unbatchable_keys.clear();
self.unbatchable_values.clear();
self.batch_sets.clear();
}
}
impl<BPI> Default for BinnedRenderPhase<BPI>
@ -398,22 +440,58 @@ where
return;
};
render_app.add_systems(
Render,
(
batching::sort_binned_render_phase::<BPI>.in_set(RenderSet::PhaseSort),
render_app
.init_resource::<ViewBinnedRenderPhases<BPI>>()
.add_systems(
Render,
(
no_gpu_preprocessing::batch_and_prepare_binned_render_phase::<BPI, GFBD>
.run_if(resource_exists::<BatchedInstanceBuffer<GFBD::BufferData>>),
gpu_preprocessing::batch_and_prepare_binned_render_phase::<BPI, GFBD>.run_if(
resource_exists::<
BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
>,
),
)
.in_set(RenderSet::PrepareResources),
),
);
batching::sort_binned_render_phase::<BPI>.in_set(RenderSet::PhaseSort),
(
no_gpu_preprocessing::batch_and_prepare_binned_render_phase::<BPI, GFBD>
.run_if(resource_exists::<BatchedInstanceBuffer<GFBD::BufferData>>),
gpu_preprocessing::batch_and_prepare_binned_render_phase::<BPI, GFBD>
.run_if(
resource_exists::<
BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
>,
),
)
.in_set(RenderSet::PrepareResources),
),
);
}
}
/// Stores the rendering instructions for a single phase that sorts items in all
/// views.
///
/// They're cleared out every frame, but storing them in a resource like this
/// allows us to reuse allocations.
#[derive(Resource, Deref, DerefMut)]
pub struct ViewSortedRenderPhases<SPI>(pub EntityHashMap<SortedRenderPhase<SPI>>)
where
SPI: SortedPhaseItem;
impl<SPI> Default for ViewSortedRenderPhases<SPI>
where
SPI: SortedPhaseItem,
{
fn default() -> Self {
Self(default())
}
}
impl<SPI> ViewSortedRenderPhases<SPI>
where
SPI: SortedPhaseItem,
{
pub fn insert_or_clear(&mut self, entity: Entity) {
match self.entry(entity) {
Entry::Occupied(mut entry) => entry.get_mut().clear(),
Entry::Vacant(entry) => {
entry.insert(default());
}
}
}
}
@ -447,19 +525,21 @@ where
return;
};
render_app.add_systems(
Render,
(
no_gpu_preprocessing::batch_and_prepare_sorted_render_phase::<SPI, GFBD>
.run_if(resource_exists::<BatchedInstanceBuffer<GFBD::BufferData>>),
gpu_preprocessing::batch_and_prepare_sorted_render_phase::<SPI, GFBD>.run_if(
resource_exists::<
BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
>,
),
)
.in_set(RenderSet::PrepareResources),
);
render_app
.init_resource::<ViewSortedRenderPhases<SPI>>()
.add_systems(
Render,
(
no_gpu_preprocessing::batch_and_prepare_sorted_render_phase::<SPI, GFBD>
.run_if(resource_exists::<BatchedInstanceBuffer<GFBD::BufferData>>),
gpu_preprocessing::batch_and_prepare_sorted_render_phase::<SPI, GFBD>.run_if(
resource_exists::<
BatchedInstanceBuffers<GFBD::BufferData, GFBD::BufferInputData>,
>,
),
)
.in_set(RenderSet::PrepareResources),
);
}
}
@ -538,7 +618,6 @@ impl UnbatchableBinnedEntityIndexSet {
/// 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,
@ -566,6 +645,12 @@ where
self.items.push(item);
}
/// Removes all [`PhaseItem`]s from this render phase.
#[inline]
pub fn clear(&mut self) {
self.items.clear();
}
/// Sorts all of its [`PhaseItem`]s.
pub fn sort(&mut self) {
I::sort(&mut self.items);
@ -902,11 +987,11 @@ impl<P: CachedRenderPipelinePhaseItem> RenderCommand<P> for SetItemPipeline {
/// 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>>)
pub fn sort_phase_system<I>(mut render_phases: ResMut<ViewSortedRenderPhases<I>>)
where
I: SortedPhaseItem,
{
for mut phase in &mut render_phases {
for phase in render_phases.values_mut() {
phase.sort();
}
}

View File

@ -18,7 +18,7 @@ use bevy_render::{
},
render_phase::{
AddRenderCommand, DrawFunctions, PhaseItem, PhaseItemExtraIndex, RenderCommand,
RenderCommandResult, SetItemPipeline, SortedRenderPhase, TrackedRenderPass,
RenderCommandResult, SetItemPipeline, TrackedRenderPass, ViewSortedRenderPhases,
},
render_resource::{
AsBindGroup, AsBindGroupError, BindGroup, BindGroupId, BindGroupLayout,
@ -374,12 +374,13 @@ pub fn queue_material2d_meshes<M: Material2d>(
render_materials: Res<RenderAssets<PreparedMaterial2d<M>>>,
mut render_mesh_instances: ResMut<RenderMesh2dInstances>,
render_material_instances: Res<RenderMaterial2dInstances<M>>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
mut views: Query<(
Entity,
&ExtractedView,
&VisibleEntities,
Option<&Tonemapping>,
Option<&DebandDither>,
&mut SortedRenderPhase<Transparent2d>,
)>,
) where
M::Data: PartialEq + Eq + Hash + Clone,
@ -388,7 +389,11 @@ pub fn queue_material2d_meshes<M: Material2d>(
return;
}
for (view, visible_entities, tonemapping, dither, mut transparent_phase) in &mut views {
for (view_entity, view, visible_entities, tonemapping, dither) in &mut views {
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
continue;
};
let draw_transparent_2d = transparent_draw_functions.read().id::<DrawMaterial2d<M>>();
let mut view_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples())

View File

@ -20,7 +20,7 @@ use bevy_render::{
render_asset::RenderAssets,
render_phase::{
DrawFunctions, PhaseItem, PhaseItemExtraIndex, RenderCommand, RenderCommandResult,
SetItemPipeline, SortedRenderPhase, TrackedRenderPass,
SetItemPipeline, TrackedRenderPass, ViewSortedRenderPhases,
},
render_resource::{
binding_types::{sampler, texture_2d, uniform_buffer},
@ -447,8 +447,9 @@ pub fn queue_sprites(
pipeline_cache: Res<PipelineCache>,
msaa: Res<Msaa>,
extracted_sprites: Res<ExtractedSprites>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
mut views: Query<(
&mut SortedRenderPhase<Transparent2d>,
Entity,
&VisibleEntities,
&ExtractedView,
Option<&Tonemapping>,
@ -459,7 +460,11 @@ pub fn queue_sprites(
let draw_sprite_function = draw_functions.read().id::<DrawSprite>();
for (mut transparent_phase, visible_entities, view, tonemapping, dither) in &mut views {
for (view_entity, visible_entities, view, tonemapping, dither) in &mut views {
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
continue;
};
let mut view_key = SpritePipelineKey::from_hdr(view.hdr) | msaa_key;
if !view.hdr {
@ -534,7 +539,7 @@ pub fn prepare_sprites(
mut image_bind_groups: ResMut<ImageBindGroups>,
gpu_images: Res<RenderAssets<GpuImage>>,
extracted_sprites: Res<ExtractedSprites>,
mut phases: Query<&mut SortedRenderPhase<Transparent2d>>,
mut phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
events: Res<SpriteAssetEvents>,
) {
// If an image has changed, the GpuImage has (probably) changed
@ -567,7 +572,7 @@ pub fn prepare_sprites(
let image_bind_groups = &mut *image_bind_groups;
for mut transparent_phase in &mut phases {
for transparent_phase in phases.values_mut() {
let mut batch_item_index = 0;
let mut batch_image_size = Vec2::ZERO;
let mut batch_image_handle = AssetId::invalid();

View File

@ -7,6 +7,7 @@ use bevy_core_pipeline::core_2d::graph::{Core2d, Node2d};
use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d};
use bevy_core_pipeline::{core_2d::Camera2d, core_3d::Camera3d};
use bevy_hierarchy::Parent;
use bevy_render::render_phase::ViewSortedRenderPhases;
use bevy_render::{
render_phase::{PhaseItem, PhaseItemExtraIndex},
texture::GpuImage,
@ -27,14 +28,14 @@ use crate::{
use bevy_app::prelude::*;
use bevy_asset::{load_internal_asset, AssetEvent, AssetId, Assets, Handle};
use bevy_ecs::entity::EntityHashMap;
use bevy_ecs::entity::{EntityHashMap, EntityHashSet};
use bevy_ecs::prelude::*;
use bevy_math::{FloatOrd, Mat4, Rect, URect, UVec4, Vec2, Vec3, Vec3Swizzles, Vec4, Vec4Swizzles};
use bevy_render::{
camera::Camera,
render_asset::RenderAssets,
render_graph::{RenderGraph, RunGraphOnViewNode},
render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions, SortedRenderPhase},
render_phase::{sort_phase_system, AddRenderCommand, DrawFunctions},
render_resource::*,
renderer::{RenderDevice, RenderQueue},
texture::Image,
@ -85,6 +86,7 @@ pub fn build_ui_render(app: &mut App) {
.init_resource::<ExtractedUiNodes>()
.allow_ambiguous_resource::<ExtractedUiNodes>()
.init_resource::<DrawFunctions<TransparentUi>>()
.init_resource::<ViewSortedRenderPhases<TransparentUi>>()
.add_render_command::<TransparentUi, DrawUi>()
.configure_sets(
ExtractSchedule,
@ -657,9 +659,13 @@ pub struct DefaultCameraView(pub Entity);
pub fn extract_default_ui_camera_view<T: Component>(
mut commands: Commands,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<TransparentUi>>,
ui_scale: Extract<Res<UiScale>>,
query: Extract<Query<(Entity, &Camera), With<T>>>,
mut live_entities: Local<EntityHashSet>,
) {
live_entities.clear();
let scale = ui_scale.0.recip();
for (entity, camera) in &query {
// ignore inactive cameras
@ -707,12 +713,16 @@ pub fn extract_default_ui_camera_view<T: Component>(
color_grading: Default::default(),
})
.id();
commands.get_or_spawn(entity).insert((
DefaultCameraView(default_camera_view),
SortedRenderPhase::<TransparentUi>::default(),
));
commands
.get_or_spawn(entity)
.insert(DefaultCameraView(default_camera_view));
transparent_render_phases.insert_or_clear(entity);
live_entities.insert(entity);
}
}
transparent_render_phases.retain(|entity, _| live_entities.contains(entity));
}
#[cfg(feature = "bevy_text")]
@ -879,14 +889,18 @@ pub fn queue_uinodes(
extracted_uinodes: Res<ExtractedUiNodes>,
ui_pipeline: Res<UiPipeline>,
mut pipelines: ResMut<SpecializedRenderPipelines<UiPipeline>>,
mut views: Query<(&ExtractedView, &mut SortedRenderPhase<TransparentUi>)>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<TransparentUi>>,
mut views: Query<(Entity, &ExtractedView)>,
pipeline_cache: Res<PipelineCache>,
draw_functions: Res<DrawFunctions<TransparentUi>>,
) {
let draw_function = draw_functions.read().id::<DrawUi>();
for (entity, extracted_uinode) in extracted_uinodes.uinodes.iter() {
let Ok((view, mut transparent_phase)) = views.get_mut(extracted_uinode.camera_entity)
else {
let Ok((view_entity, view)) = views.get_mut(extracted_uinode.camera_entity) else {
continue;
};
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
continue;
};
@ -926,7 +940,7 @@ pub fn prepare_uinodes(
ui_pipeline: Res<UiPipeline>,
mut image_bind_groups: ResMut<UiImageBindGroups>,
gpu_images: Res<RenderAssets<GpuImage>>,
mut phases: Query<&mut SortedRenderPhase<TransparentUi>>,
mut phases: ResMut<ViewSortedRenderPhases<TransparentUi>>,
events: Res<SpriteAssetEvents>,
mut previous_len: Local<usize>,
) {
@ -958,7 +972,7 @@ pub fn prepare_uinodes(
let mut vertices_index = 0;
let mut indices_index = 0;
for mut ui_phase in &mut phases {
for ui_phase in phases.values_mut() {
let mut batch_item_index = 0;
let mut batch_image_handle = AssetId::invalid();

View File

@ -17,14 +17,7 @@ use bevy_render::{
};
pub struct UiPassNode {
ui_view_query: QueryState<
(
&'static SortedRenderPhase<TransparentUi>,
&'static ViewTarget,
&'static ExtractedCamera,
),
With<ExtractedView>,
>,
ui_view_query: QueryState<(&'static ViewTarget, &'static ExtractedCamera), With<ExtractedView>>,
default_camera_view_query: QueryState<&'static DefaultCameraView>,
}
@ -51,11 +44,19 @@ impl Node for UiPassNode {
) -> Result<(), NodeRunError> {
let input_view_entity = graph.view_entity();
let Ok((transparent_phase, target, camera)) =
self.ui_view_query.get_manual(world, input_view_entity)
let Some(transparent_render_phases) =
world.get_resource::<ViewSortedRenderPhases<TransparentUi>>()
else {
return Ok(());
};
let Some(transparent_phase) = transparent_render_phases.get(&input_view_entity) else {
return Ok(());
};
let Ok((target, camera)) = self.ui_view_query.get_manual(world, input_view_entity) else {
return Ok(());
};
if transparent_phase.items.is_empty() {
return Ok(());
}

View File

@ -454,7 +454,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 SortedRenderPhase<TransparentUi>>,
mut phases: ResMut<ViewSortedRenderPhases<TransparentUi>>,
mut previous_len: Local<usize>,
) {
if let (Some(view_binding), Some(globals_binding)) = (
@ -471,7 +471,7 @@ pub fn prepare_uimaterial_nodes<M: UiMaterial>(
));
let mut index = 0;
for mut ui_phase in &mut phases {
for ui_phase in phases.values_mut() {
let mut batch_item_index = 0;
let mut batch_shader_handle = AssetId::invalid();
@ -636,7 +636,8 @@ pub fn queue_ui_material_nodes<M: UiMaterial>(
mut pipelines: ResMut<SpecializedRenderPipelines<UiMaterialPipeline<M>>>,
pipeline_cache: Res<PipelineCache>,
render_materials: Res<RenderAssets<PreparedUiMaterial<M>>>,
mut views: Query<(&ExtractedView, &mut SortedRenderPhase<TransparentUi>)>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<TransparentUi>>,
mut views: Query<&ExtractedView>,
) where
M::Data: PartialEq + Eq + Hash + Clone,
{
@ -646,7 +647,11 @@ pub fn queue_ui_material_nodes<M: UiMaterial>(
let Some(material) = render_materials.get(extracted_uinode.material) else {
continue;
};
let Ok((view, mut transparent_phase)) = views.get_mut(extracted_uinode.camera_entity)
let Ok(view) = views.get_mut(extracted_uinode.camera_entity) else {
continue;
};
let Some(transparent_phase) =
transparent_render_phases.get_mut(&extracted_uinode.camera_entity)
else {
continue;
};

View File

@ -15,7 +15,7 @@ use bevy::{
render_asset::{RenderAssetUsages, RenderAssets},
render_phase::{
AddRenderCommand, DrawFunctions, PhaseItemExtraIndex, SetItemPipeline,
SortedRenderPhase,
ViewSortedRenderPhases,
},
render_resource::{
BlendState, ColorTargetState, ColorWrites, Face, FragmentState, FrontFace,
@ -354,17 +354,18 @@ pub fn queue_colored_mesh2d(
msaa: Res<Msaa>,
render_meshes: Res<RenderAssets<GpuMesh>>,
render_mesh_instances: Res<RenderColoredMesh2dInstances>,
mut views: Query<(
&VisibleEntities,
&mut SortedRenderPhase<Transparent2d>,
&ExtractedView,
)>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
mut views: Query<(Entity, &VisibleEntities, &ExtractedView)>,
) {
if render_mesh_instances.is_empty() {
return;
}
// Iterate each view (a camera is a view)
for (visible_entities, mut transparent_phase, view) in &mut views {
for (view_entity, visible_entities, view) in &mut views {
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
continue;
};
let draw_colored_mesh2d = transparent_draw_functions.read().id::<DrawColoredMesh2d>();
let mesh_key = Mesh2dPipelineKey::from_msaa_samples(msaa.samples())

View File

@ -16,7 +16,7 @@ use bevy::{
render_asset::RenderAssets,
render_phase::{
AddRenderCommand, DrawFunctions, PhaseItem, PhaseItemExtraIndex, RenderCommand,
RenderCommandResult, SetItemPipeline, SortedRenderPhase, TrackedRenderPass,
RenderCommandResult, SetItemPipeline, TrackedRenderPass, ViewSortedRenderPhases,
},
render_resource::*,
renderer::RenderDevice,
@ -117,13 +117,18 @@ fn queue_custom(
meshes: Res<RenderAssets<GpuMesh>>,
render_mesh_instances: Res<RenderMeshInstances>,
material_meshes: Query<Entity, With<InstanceMaterialData>>,
mut views: Query<(&ExtractedView, &mut SortedRenderPhase<Transparent3d>)>,
mut transparent_render_phases: ResMut<ViewSortedRenderPhases<Transparent3d>>,
mut views: Query<(Entity, &ExtractedView)>,
) {
let draw_custom = transparent_3d_draw_functions.read().id::<DrawCustom>();
let msaa_key = MeshPipelineKey::from_msaa_samples(msaa.samples());
for (view, mut transparent_phase) in &mut views {
for (view_entity, view) in &mut views {
let Some(transparent_phase) = transparent_render_phases.get_mut(&view_entity) else {
continue;
};
let view_key = msaa_key | MeshPipelineKey::from_hdr(view.hdr);
let rangefinder = view.rangefinder3d();
for entity in &material_meshes {