Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Merged
merged 8 commits into from
May 21, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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(());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Since this is now a resource and inserted during extract, is this case even possible? It strikes me that even if it can happen, that it would be surprising/erroneous. Might merit a warning and/or .expect?

(It looks like most of the new resource accesses follow this pattern.)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From what I can tell, Bevy style is to use bailouts like this even when the problem should never happen to reduce unwinding overhead. This may or may not be a good thing, but I try to follow it. We could use error! there though.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, this will happen normally for the view entities that are created for cascaded shadow maps. Since shadow maps don't render any transparent meshes, their view entities won't have entries in ViewSortedRenderPhases<Transparent3d>. This is fine and skipping them is the correct thing to do. So this logic is correct as is, I believe.

};

// This needs to run at least once to clear the background color, even if there are no items to render
{
Expand Down
21 changes: 15 additions & 6 deletions crates/bevy_core_pipeline/src/core_2d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,15 +32,15 @@ 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,
extract_component::ExtractComponentPlugin,
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,
Expand All @@ -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,
Expand Down Expand Up @@ -158,13 +159,21 @@ 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>>>,
) {
let mut live_entities = EntityHashSet::default();
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);
pcwalton marked this conversation as resolved.
Show resolved Hide resolved

live_entities.insert(entity);
}

// Clear out all dead views.
transparent_2d_phases.retain(|camera_entity, _| live_entities.contains(camera_entity));
}
23 changes: 17 additions & 6 deletions crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -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>,
Expand All @@ -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,
Expand All @@ -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())];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -22,7 +22,6 @@ impl ViewNode for MainTransmissivePass3dNode {
type ViewQuery = (
&'static ExtractedCamera,
&'static Camera3d,
&'static SortedRenderPhase<Transmissive3d>,
&'static ViewTarget,
Option<&'static ViewTransmissionTexture>,
&'static ViewDepthTexture,
Expand All @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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},
Expand All @@ -20,19 +20,28 @@ pub struct MainTransparentPass3dNode;
impl ViewNode for MainTransparentPass3dNode {
type ViewQuery = (
&'static ExtractedCamera,
&'static SortedRenderPhase<Transparent3d>,
&'static ViewTarget,
&'static ViewDepthTexture,
);
fn run(
&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
Expand Down