Skip to content

Commit 9da0b2a

Browse files
authored
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.
1 parent 53f4c38 commit 9da0b2a

File tree

24 files changed

+609
-295
lines changed

24 files changed

+609
-295
lines changed

crates/bevy_core_pipeline/src/core_2d/main_transparent_pass_2d_node.rs

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use bevy_render::{
44
camera::ExtractedCamera,
55
diagnostic::RecordDiagnostics,
66
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
7-
render_phase::SortedRenderPhase,
7+
render_phase::ViewSortedRenderPhases,
88
render_resource::RenderPassDescriptor,
99
renderer::RenderContext,
1010
view::ViewTarget,
@@ -16,20 +16,25 @@ use bevy_utils::tracing::info_span;
1616
pub struct MainTransparentPass2dNode {}
1717

1818
impl ViewNode for MainTransparentPass2dNode {
19-
type ViewQuery = (
20-
&'static ExtractedCamera,
21-
&'static SortedRenderPhase<Transparent2d>,
22-
&'static ViewTarget,
23-
);
19+
type ViewQuery = (&'static ExtractedCamera, &'static ViewTarget);
2420

2521
fn run<'w>(
2622
&self,
2723
graph: &mut RenderGraphContext,
2824
render_context: &mut RenderContext<'w>,
29-
(camera, transparent_phase, target): bevy_ecs::query::QueryItem<'w, Self::ViewQuery>,
25+
(camera, target): bevy_ecs::query::QueryItem<'w, Self::ViewQuery>,
3026
world: &'w World,
3127
) -> Result<(), NodeRunError> {
28+
let Some(transparent_phases) =
29+
world.get_resource::<ViewSortedRenderPhases<Transparent2d>>()
30+
else {
31+
return Ok(());
32+
};
33+
3234
let view_entity = graph.view_entity();
35+
let Some(transparent_phase) = transparent_phases.get(&view_entity) else {
36+
return Ok(());
37+
};
3338

3439
// This needs to run at least once to clear the background color, even if there are no items to render
3540
{

crates/bevy_core_pipeline/src/core_2d/mod.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,15 @@ pub use camera_2d::*;
3232
pub use main_transparent_pass_2d_node::*;
3333

3434
use bevy_app::{App, Plugin};
35-
use bevy_ecs::prelude::*;
35+
use bevy_ecs::{entity::EntityHashSet, prelude::*};
3636
use bevy_math::FloatOrd;
3737
use bevy_render::{
3838
camera::Camera,
3939
extract_component::ExtractComponentPlugin,
4040
render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner},
4141
render_phase::{
4242
sort_phase_system, CachedRenderPipelinePhaseItem, DrawFunctionId, DrawFunctions, PhaseItem,
43-
PhaseItemExtraIndex, SortedPhaseItem, SortedRenderPhase,
43+
PhaseItemExtraIndex, SortedPhaseItem, ViewSortedRenderPhases,
4444
},
4545
render_resource::CachedRenderPipelineId,
4646
Extract, ExtractSchedule, Render, RenderApp, RenderSet,
@@ -62,6 +62,7 @@ impl Plugin for Core2dPlugin {
6262
};
6363
render_app
6464
.init_resource::<DrawFunctions<Transparent2d>>()
65+
.init_resource::<ViewSortedRenderPhases<Transparent2d>>()
6566
.add_systems(ExtractSchedule, extract_core_2d_camera_phases)
6667
.add_systems(
6768
Render,
@@ -158,13 +159,23 @@ impl CachedRenderPipelinePhaseItem for Transparent2d {
158159

159160
pub fn extract_core_2d_camera_phases(
160161
mut commands: Commands,
162+
mut transparent_2d_phases: ResMut<ViewSortedRenderPhases<Transparent2d>>,
161163
cameras_2d: Extract<Query<(Entity, &Camera), With<Camera2d>>>,
164+
mut live_entities: Local<EntityHashSet>,
162165
) {
166+
live_entities.clear();
167+
163168
for (entity, camera) in &cameras_2d {
164-
if camera.is_active {
165-
commands
166-
.get_or_spawn(entity)
167-
.insert(SortedRenderPhase::<Transparent2d>::default());
169+
if !camera.is_active {
170+
continue;
168171
}
172+
173+
commands.get_or_spawn(entity);
174+
transparent_2d_phases.insert_or_clear(entity);
175+
176+
live_entities.insert(entity);
169177
}
178+
179+
// Clear out all dead views.
180+
transparent_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
170181
}

crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,12 @@ use crate::{
22
core_3d::Opaque3d,
33
skybox::{SkyboxBindGroup, SkyboxPipelineId},
44
};
5-
use bevy_ecs::{prelude::World, query::QueryItem};
5+
use bevy_ecs::{entity::Entity, prelude::World, query::QueryItem};
66
use bevy_render::{
77
camera::ExtractedCamera,
88
diagnostic::RecordDiagnostics,
99
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
10-
render_phase::{BinnedRenderPhase, TrackedRenderPass},
10+
render_phase::{TrackedRenderPass, ViewBinnedRenderPhases},
1111
render_resource::{CommandEncoderDescriptor, PipelineCache, RenderPassDescriptor, StoreOp},
1212
renderer::RenderContext,
1313
view::{ViewDepthTexture, ViewTarget, ViewUniformOffset},
@@ -24,9 +24,8 @@ use super::AlphaMask3d;
2424
pub struct MainOpaquePass3dNode;
2525
impl ViewNode for MainOpaquePass3dNode {
2626
type ViewQuery = (
27+
Entity,
2728
&'static ExtractedCamera,
28-
&'static BinnedRenderPhase<Opaque3d>,
29-
&'static BinnedRenderPhase<AlphaMask3d>,
3029
&'static ViewTarget,
3130
&'static ViewDepthTexture,
3231
Option<&'static SkyboxPipelineId>,
@@ -39,9 +38,8 @@ impl ViewNode for MainOpaquePass3dNode {
3938
graph: &mut RenderGraphContext,
4039
render_context: &mut RenderContext<'w>,
4140
(
41+
view,
4242
camera,
43-
opaque_phase,
44-
alpha_mask_phase,
4543
target,
4644
depth,
4745
skybox_pipeline,
@@ -50,6 +48,19 @@ impl ViewNode for MainOpaquePass3dNode {
5048
): QueryItem<'w, Self::ViewQuery>,
5149
world: &'w World,
5250
) -> Result<(), NodeRunError> {
51+
let (Some(opaque_phases), Some(alpha_mask_phases)) = (
52+
world.get_resource::<ViewBinnedRenderPhases<Opaque3d>>(),
53+
world.get_resource::<ViewBinnedRenderPhases<AlphaMask3d>>(),
54+
) else {
55+
return Ok(());
56+
};
57+
58+
let (Some(opaque_phase), Some(alpha_mask_phase)) =
59+
(opaque_phases.get(&view), alpha_mask_phases.get(&view))
60+
else {
61+
return Ok(());
62+
};
63+
5364
let diagnostics = render_context.diagnostic_recorder();
5465

5566
let color_attachments = [Some(target.get_color_attachment())];

crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use bevy_ecs::{prelude::*, query::QueryItem};
44
use bevy_render::{
55
camera::ExtractedCamera,
66
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
7-
render_phase::SortedRenderPhase,
7+
render_phase::ViewSortedRenderPhases,
88
render_resource::{Extent3d, RenderPassDescriptor, StoreOp},
99
renderer::RenderContext,
1010
view::{ViewDepthTexture, ViewTarget},
@@ -22,7 +22,6 @@ impl ViewNode for MainTransmissivePass3dNode {
2222
type ViewQuery = (
2323
&'static ExtractedCamera,
2424
&'static Camera3d,
25-
&'static SortedRenderPhase<Transmissive3d>,
2625
&'static ViewTarget,
2726
Option<&'static ViewTransmissionTexture>,
2827
&'static ViewDepthTexture,
@@ -32,13 +31,21 @@ impl ViewNode for MainTransmissivePass3dNode {
3231
&self,
3332
graph: &mut RenderGraphContext,
3433
render_context: &mut RenderContext,
35-
(camera, camera_3d, transmissive_phase, target, transmission, depth): QueryItem<
36-
Self::ViewQuery,
37-
>,
34+
(camera, camera_3d, target, transmission, depth): QueryItem<Self::ViewQuery>,
3835
world: &World,
3936
) -> Result<(), NodeRunError> {
4037
let view_entity = graph.view_entity();
4138

39+
let Some(transmissive_phases) =
40+
world.get_resource::<ViewSortedRenderPhases<Transmissive3d>>()
41+
else {
42+
return Ok(());
43+
};
44+
45+
let Some(transmissive_phase) = transmissive_phases.get(&view_entity) else {
46+
return Ok(());
47+
};
48+
4249
let physical_target_size = camera.physical_target_size.unwrap();
4350

4451
let render_pass_descriptor = RenderPassDescriptor {

crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ use bevy_render::{
44
camera::ExtractedCamera,
55
diagnostic::RecordDiagnostics,
66
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
7-
render_phase::SortedRenderPhase,
7+
render_phase::ViewSortedRenderPhases,
88
render_resource::{RenderPassDescriptor, StoreOp},
99
renderer::RenderContext,
1010
view::{ViewDepthTexture, ViewTarget},
@@ -20,19 +20,28 @@ pub struct MainTransparentPass3dNode;
2020
impl ViewNode for MainTransparentPass3dNode {
2121
type ViewQuery = (
2222
&'static ExtractedCamera,
23-
&'static SortedRenderPhase<Transparent3d>,
2423
&'static ViewTarget,
2524
&'static ViewDepthTexture,
2625
);
2726
fn run(
2827
&self,
2928
graph: &mut RenderGraphContext,
3029
render_context: &mut RenderContext,
31-
(camera, transparent_phase, target, depth): QueryItem<Self::ViewQuery>,
30+
(camera, target, depth): QueryItem<Self::ViewQuery>,
3231
world: &World,
3332
) -> Result<(), NodeRunError> {
3433
let view_entity = graph.view_entity();
3534

35+
let Some(transparent_phases) =
36+
world.get_resource::<ViewSortedRenderPhases<Transparent3d>>()
37+
else {
38+
return Ok(());
39+
};
40+
41+
let Some(transparent_phase) = transparent_phases.get(&view_entity) else {
42+
return Ok(());
43+
};
44+
3645
if !transparent_phase.items.is_empty() {
3746
// Run the transparent pass, sorted back-to-front
3847
// NOTE: Scoped to drop the mutable borrow of render_context

0 commit comments

Comments
 (0)