(fix) SSRPlugin: Don't reference default deferred lighting pass if it doesn't exist (#16932)

Fixes a crash when using deferred rendering but disabling the default
deferred lighting plugin.

# The Issue
The `ScreenSpaceReflectionsPlugin` references
`NodePbr::DeferredLightingPass`, which hasn't been added when
`PbrPlugin::add_default_deferred_lighting_plugin` is `false`.

This yields the following crash:
```
thread 'main' panicked at /Users/marius/Documents/dev/bevy/crates/bevy_render/src/render_graph/graph.rs:155:26:
InvalidNode(DeferredLightingPass)
stack backtrace:
   0: rust_begin_unwind
             at /rustc/90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf/library/std/src/panicking.rs:665:5
   1: core::panicking::panic_fmt
             at /rustc/90b35a6239c3d8bdabc530a6a0816f7ff89a0aaf/library/core/src/panicking.rs:74:14
   2: bevy_render::render_graph::graph::RenderGraph::add_node_edges
             at /Users/marius/Documents/dev/bevy/crates/bevy_render/src/render_graph/graph.rs:155:26
   3: <bevy_app::sub_app::SubApp as bevy_render::render_graph::app::RenderGraphApp>::add_render_graph_edges
             at /Users/marius/Documents/dev/bevy/crates/bevy_render/src/render_graph/app.rs:66:13
   4: <bevy_pbr::ssr::ScreenSpaceReflectionsPlugin as bevy_app::plugin::Plugin>::finish
             at /Users/marius/Documents/dev/bevy/crates/bevy_pbr/src/ssr/mod.rs:234:9
   5: bevy_app::app::App::finish
             at /Users/marius/Documents/dev/bevy/crates/bevy_app/src/app.rs:255:13
   6: bevy_winit::state::winit_runner
             at /Users/marius/Documents/dev/bevy/crates/bevy_winit/src/state.rs:859:9
   7: core::ops::function::FnOnce::call_once
             at /Users/marius/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
   8: core::ops::function::FnOnce::call_once{{vtable.shim}}
             at /Users/marius/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
   9: <alloc::boxed::Box<F,A> as core::ops::function::FnOnce<Args>>::call_once
             at /Users/marius/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/alloc/src/boxed.rs:2454:9
  10: bevy_app::app::App::run
             at /Users/marius/Documents/dev/bevy/crates/bevy_app/src/app.rs:184:9
  11: bevy_deferred_test::main
             at ./src/main.rs:9:5
  12: core::ops::function::FnOnce::call_once
             at /Users/marius/.rustup/toolchains/stable-aarch64-apple-darwin/lib/rustlib/src/rust/library/core/src/ops/function.rs:250:5
```


### Minimal reproduction example:
```rust
use bevy::core_pipeline::prepass::{DeferredPrepass, DepthPrepass};
use bevy::pbr::{DefaultOpaqueRendererMethod, PbrPlugin, ScreenSpaceReflections};
use bevy::prelude::*;

fn main() {
    App::new()
        .add_plugins(DefaultPlugins.set(PbrPlugin {
            add_default_deferred_lighting_plugin: false,
            ..default()
        }))
        .add_systems(Startup, setup)
        .insert_resource(DefaultOpaqueRendererMethod::deferred())
        .run();
}

/// set up a camera
fn setup(
    mut commands: Commands
) {
    // camera
    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(-2.5, 4.5, 9.0).looking_at(Vec3::ZERO, Vec3::Y),
        DepthPrepass,
        DeferredPrepass,
        ScreenSpaceReflections::default(),
    ));
}
```

# The Fix
When no node under the default lighting node's label exists, this label
isn't added to the SSR's graph node edges. It's good to keep the
SSRPlugin enabled, this way, users can plug in their own lighting
system, which I have successfully done on top of this PR.

# Workarounds

A current workaround for this issue is to re-use Bevy's
`NodePbr::DeferredLightingPass` as the label for your own custom
lighting pass node.
This commit is contained in:
Marius Metzger 2024-12-24 04:01:22 +01:00 committed by François Mockers
parent a7d1a73fa8
commit fcce3fb344

View File

@ -25,6 +25,7 @@ use bevy_ecs::{
}; };
use bevy_image::BevyDefault as _; use bevy_image::BevyDefault as _;
use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::render_graph::RenderGraph;
use bevy_render::{ use bevy_render::{
extract_component::{ExtractComponent, ExtractComponentPlugin}, extract_component::{ExtractComponent, ExtractComponentPlugin},
render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner}, render_graph::{NodeRunError, RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner},
@ -233,8 +234,19 @@ impl Plugin for ScreenSpaceReflectionsPlugin {
render_app render_app
.init_resource::<ScreenSpaceReflectionsPipeline>() .init_resource::<ScreenSpaceReflectionsPipeline>()
.init_resource::<SpecializedRenderPipelines<ScreenSpaceReflectionsPipeline>>() .init_resource::<SpecializedRenderPipelines<ScreenSpaceReflectionsPipeline>>();
.add_render_graph_edges(
// only reference the default deferred lighting pass
// if it has been added
let has_default_deferred_lighting_pass = render_app
.world_mut()
.resource_mut::<RenderGraph>()
.sub_graph(Core3d)
.get_node_state(NodePbr::DeferredLightingPass)
.is_ok();
if has_default_deferred_lighting_pass {
render_app.add_render_graph_edges(
Core3d, Core3d,
( (
NodePbr::DeferredLightingPass, NodePbr::DeferredLightingPass,
@ -242,6 +254,12 @@ impl Plugin for ScreenSpaceReflectionsPlugin {
Node3d::MainOpaquePass, Node3d::MainOpaquePass,
), ),
); );
} else {
render_app.add_render_graph_edges(
Core3d,
(NodePbr::ScreenSpaceReflections, Node3d::MainOpaquePass),
);
}
} }
} }