Skip to content

Use RenderStartup for bevy_solari. #19918

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

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 49 additions & 2 deletions crates/bevy_solari/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,15 @@ pub mod prelude {
use crate::realtime::SolariLightingPlugin;
use crate::scene::RaytracingScenePlugin;
use bevy_app::{App, Plugin};
use bevy_render::settings::WgpuFeatures;
use bevy_ecs::{
resource::Resource,
schedule::{common_conditions::resource_exists, IntoScheduleConfigs, SystemSet},
system::{Commands, Res},
};
use bevy_render::{
renderer::RenderDevice, settings::WgpuFeatures, ExtractSchedule, Render, RenderStartup,
};
use tracing::warn;

/// An experimental plugin for raytraced lighting.
///
Expand All @@ -35,7 +43,23 @@ pub struct SolariPlugin;

impl Plugin for SolariPlugin {
fn build(&self, app: &mut App) {
app.add_plugins((RaytracingScenePlugin, SolariLightingPlugin));
app.add_plugins((RaytracingScenePlugin, SolariLightingPlugin))
// Note: conditions only run once per schedule run. So even though these conditions
// could apply to many systems, they will only be checked once for the entire group.
.configure_sets(
RenderStartup,
SolariSystems
.after(check_solari_has_required_features)
.run_if(resource_exists::<HasSolariRequiredFeatures>),
)
.configure_sets(
ExtractSchedule,
SolariSystems.run_if(resource_exists::<HasSolariRequiredFeatures>),
)
.configure_sets(
Render,
SolariSystems.run_if(resource_exists::<HasSolariRequiredFeatures>),
);
}
}

Expand All @@ -50,3 +74,26 @@ impl SolariPlugin {
| WgpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY
}
}

#[derive(SystemSet, PartialEq, Eq, Debug, Clone, Hash)]
pub struct SolariSystems;

/// A resource to track whether the renderer has the required features for Solari systems.
#[derive(Resource)]
struct HasSolariRequiredFeatures;

/// Check for the Solari required features once in startup, and insert a resource if the features
/// are enabled.
///
/// Now systems can do a cheap check for if the resource exists.
fn check_solari_has_required_features(mut commands: Commands, render_device: Res<RenderDevice>) {
let features = render_device.features();
if !features.contains(SolariPlugin::required_wgpu_features()) {
warn!(
"SolariSystems disabled. GPU lacks support for required features: {:?}.",
SolariPlugin::required_wgpu_features().difference(features)
);
return;
}
commands.insert_resource(HasSolariRequiredFeatures);
}
55 changes: 30 additions & 25 deletions crates/bevy_solari/src/pathtracer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,22 +2,22 @@ mod extract;
mod node;
mod prepare;

use crate::SolariPlugin;
use crate::{scene::init_raytracing_scene_bindings, SolariSystems};
use bevy_app::{App, Plugin};
use bevy_asset::embedded_asset;
use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d};
use bevy_ecs::{component::Component, reflect::ReflectComponent, schedule::IntoScheduleConfigs};
use bevy_ecs::{
component::Component, reflect::ReflectComponent, schedule::IntoScheduleConfigs, world::World,
};
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{
render_graph::{RenderGraphExt, ViewNodeRunner},
renderer::RenderDevice,
view::Hdr,
ExtractSchedule, Render, RenderApp, RenderSystems,
ExtractSchedule, Render, RenderApp, RenderStartup, RenderSystems,
};
use extract::extract_pathtracer;
use node::PathtracerNode;
use prepare::prepare_pathtracer_accumulation_texture;
use tracing::warn;

/// Non-realtime pathtracing.
///
Expand All @@ -30,32 +30,25 @@ impl Plugin for PathtracingPlugin {
embedded_asset!(app, "pathtracer.wgsl");

app.register_type::<Pathtracer>();
}

fn finish(&self, app: &mut App) {
let render_app = app.sub_app_mut(RenderApp);

let render_device = render_app.world().resource::<RenderDevice>();
let features = render_device.features();
if !features.contains(SolariPlugin::required_wgpu_features()) {
warn!(
"PathtracingPlugin not loaded. GPU lacks support for required features: {:?}.",
SolariPlugin::required_wgpu_features().difference(features)
);
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
}
};

render_app
.add_systems(ExtractSchedule, extract_pathtracer)
.add_systems(
Render,
prepare_pathtracer_accumulation_texture.in_set(RenderSystems::PrepareResources),
)
.add_render_graph_node::<ViewNodeRunner<PathtracerNode>>(
Core3d,
node::graph::PathtracerNode,
RenderStartup,
add_solari_pathtracing_render_graph_nodes
.after(init_raytracing_scene_bindings)
.in_set(SolariSystems),
)
.add_render_graph_edges(Core3d, (Node3d::EndMainPass, node::graph::PathtracerNode));
.add_systems(ExtractSchedule, extract_pathtracer.in_set(SolariSystems))
.add_systems(
Render,
prepare_pathtracer_accumulation_texture
.in_set(RenderSystems::PrepareResources)
.in_set(SolariSystems),
);
}
}

Expand All @@ -65,3 +58,15 @@ impl Plugin for PathtracingPlugin {
pub struct Pathtracer {
pub reset: bool,
}

// We only want to add these render graph nodes and edges if Solari required features are present.
// Making this a system that runs at RenderStartup allows a run condition to check for required
// features first.
fn add_solari_pathtracing_render_graph_nodes(world: &mut World) {
world
.add_render_graph_node::<ViewNodeRunner<PathtracerNode>>(
Core3d,
node::graph::PathtracerNode,
)
.add_render_graph_edges(Core3d, (Node3d::EndMainPass, node::graph::PathtracerNode));
}
61 changes: 35 additions & 26 deletions crates/bevy_solari/src/realtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,26 @@ mod extract;
mod node;
mod prepare;

use crate::SolariPlugin;
use crate::{scene::init_raytracing_scene_bindings, SolariSystems};
use bevy_app::{App, Plugin};
use bevy_asset::embedded_asset;
use bevy_core_pipeline::{
core_3d::graph::{Core3d, Node3d},
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass},
};
use bevy_ecs::{component::Component, reflect::ReflectComponent, schedule::IntoScheduleConfigs};
use bevy_ecs::{
component::Component, reflect::ReflectComponent, schedule::IntoScheduleConfigs, world::World,
};
use bevy_pbr::DefaultOpaqueRendererMethod;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{
render_graph::{RenderGraphExt, ViewNodeRunner},
renderer::RenderDevice,
view::Hdr,
ExtractSchedule, Render, RenderApp, RenderSystems,
ExtractSchedule, Render, RenderApp, RenderStartup, RenderSystems,
};
use extract::extract_solari_lighting;
use node::SolariLightingNode;
use prepare::prepare_solari_lighting_resources;
use tracing::warn;

pub struct SolariLightingPlugin;

Expand All @@ -31,33 +31,27 @@ impl Plugin for SolariLightingPlugin {

app.register_type::<SolariLighting>()
.insert_resource(DefaultOpaqueRendererMethod::deferred());
}

fn finish(&self, app: &mut App) {
let render_app = app.sub_app_mut(RenderApp);

let render_device = render_app.world().resource::<RenderDevice>();
let features = render_device.features();
if !features.contains(SolariPlugin::required_wgpu_features()) {
warn!(
"SolariLightingPlugin not loaded. GPU lacks support for required features: {:?}.",
SolariPlugin::required_wgpu_features().difference(features)
);
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
return;
}
};

render_app
.add_systems(ExtractSchedule, extract_solari_lighting)
.add_systems(
Render,
prepare_solari_lighting_resources.in_set(RenderSystems::PrepareResources),
RenderStartup,
add_solari_lighting_render_graph_nodes
.after(init_raytracing_scene_bindings)
.in_set(SolariSystems),
)
.add_render_graph_node::<ViewNodeRunner<SolariLightingNode>>(
Core3d,
node::graph::SolariLightingNode,
.add_systems(
ExtractSchedule,
extract_solari_lighting.in_set(SolariSystems),
)
.add_render_graph_edges(
Core3d,
(Node3d::EndMainPass, node::graph::SolariLightingNode),
.add_systems(
Render,
prepare_solari_lighting_resources
.in_set(RenderSystems::PrepareResources)
.in_set(SolariSystems),
);
}
}
Expand Down Expand Up @@ -87,3 +81,18 @@ impl Default for SolariLighting {
}
}
}

// We only want to add these render graph nodes and edges if Solari required features are present.
// Making this a system that runs at RenderStartup allows a run condition to check for required
// features first.
fn add_solari_lighting_render_graph_nodes(world: &mut World) {
world
.add_render_graph_node::<ViewNodeRunner<SolariLightingNode>>(
Core3d,
node::graph::SolariLightingNode,
)
.add_render_graph_edges(
Core3d,
(Node3d::EndMainPass, node::graph::SolariLightingNode),
);
}
58 changes: 28 additions & 30 deletions crates/bevy_solari/src/scene/binder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ use bevy_color::{ColorToComponents, LinearRgba};
use bevy_ecs::{
entity::{Entity, EntityHashMap},
resource::Resource,
system::{Query, Res, ResMut},
world::{FromWorld, World},
system::{Commands, Query, Res, ResMut},
};
use bevy_math::{ops::cos, Mat4, Vec3};
use bevy_pbr::{ExtractedDirectionalLight, MeshMaterial3d, StandardMaterial};
Expand Down Expand Up @@ -265,36 +264,35 @@ pub fn prepare_raytracing_scene_bindings(
));
}

impl FromWorld for RaytracingSceneBindings {
fn from_world(world: &mut World) -> Self {
let render_device = world.resource::<RenderDevice>();

Self {
bind_group: None,
bind_group_layout: render_device.create_bind_group_layout(
"raytracing_scene_bind_group_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::COMPUTE,
(
storage_buffer_read_only_sized(false, None).count(MAX_MESH_SLAB_COUNT),
storage_buffer_read_only_sized(false, None).count(MAX_MESH_SLAB_COUNT),
texture_2d(TextureSampleType::Float { filterable: true })
.count(MAX_TEXTURE_COUNT),
sampler(SamplerBindingType::Filtering).count(MAX_TEXTURE_COUNT),
storage_buffer_read_only_sized(false, None),
acceleration_structure(),
storage_buffer_read_only_sized(false, None),
storage_buffer_read_only_sized(false, None),
storage_buffer_read_only_sized(false, None),
storage_buffer_read_only_sized(false, None),
storage_buffer_read_only_sized(false, None),
storage_buffer_read_only_sized(false, None),
),
pub(crate) fn init_raytracing_scene_bindings(
mut commands: Commands,
render_device: Res<RenderDevice>,
) {
commands.insert_resource(RaytracingSceneBindings {
bind_group: None,
bind_group_layout: render_device.create_bind_group_layout(
"raytracing_scene_bind_group_layout",
&BindGroupLayoutEntries::sequential(
ShaderStages::COMPUTE,
(
storage_buffer_read_only_sized(false, None).count(MAX_MESH_SLAB_COUNT),
storage_buffer_read_only_sized(false, None).count(MAX_MESH_SLAB_COUNT),
texture_2d(TextureSampleType::Float { filterable: true })
.count(MAX_TEXTURE_COUNT),
sampler(SamplerBindingType::Filtering).count(MAX_TEXTURE_COUNT),
storage_buffer_read_only_sized(false, None),
acceleration_structure(),
storage_buffer_read_only_sized(false, None),
storage_buffer_read_only_sized(false, None),
storage_buffer_read_only_sized(false, None),
storage_buffer_read_only_sized(false, None),
storage_buffer_read_only_sized(false, None),
storage_buffer_read_only_sized(false, None),
),
),
previous_frame_light_entities: Vec::new(),
}
}
),
previous_frame_light_entities: Vec::new(),
});
}

struct CachedBindingArray<T, I: Eq + Hash> {
Expand Down
Loading