Skip to content

Commit a770f7d

Browse files
committed
Use RenderStartup for SolariPlugins.
1 parent 1b4cf02 commit a770f7d

File tree

5 files changed

+169
-112
lines changed

5 files changed

+169
-112
lines changed

crates/bevy_solari/src/lib.rs

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,15 @@ pub mod prelude {
2121
use crate::realtime::SolariLightingPlugin;
2222
use crate::scene::RaytracingScenePlugin;
2323
use bevy_app::{App, Plugin};
24-
use bevy_render::settings::WgpuFeatures;
24+
use bevy_ecs::{
25+
resource::Resource,
26+
schedule::{common_conditions::resource_exists, IntoScheduleConfigs, SystemSet},
27+
system::{Commands, Res},
28+
};
29+
use bevy_render::{
30+
renderer::RenderDevice, settings::WgpuFeatures, ExtractSchedule, Render, RenderStartup,
31+
};
32+
use tracing::warn;
2533

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

3644
impl Plugin for SolariPlugin {
3745
fn build(&self, app: &mut App) {
38-
app.add_plugins((RaytracingScenePlugin, SolariLightingPlugin));
46+
app.add_plugins((RaytracingScenePlugin, SolariLightingPlugin))
47+
// Note: conditions only run once per schedule run. So even though these conditions
48+
// could apply to many systems, they will only be checked once for the entire group.
49+
.configure_sets(
50+
RenderStartup,
51+
SolariSystems
52+
.after(check_solari_has_required_features)
53+
.run_if(resource_exists::<HasSolariRequiredFeatures>),
54+
)
55+
.configure_sets(
56+
ExtractSchedule,
57+
SolariSystems.run_if(resource_exists::<HasSolariRequiredFeatures>),
58+
)
59+
.configure_sets(
60+
Render,
61+
SolariSystems.run_if(resource_exists::<HasSolariRequiredFeatures>),
62+
);
3963
}
4064
}
4165

@@ -50,3 +74,26 @@ impl SolariPlugin {
5074
| WgpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY
5175
}
5276
}
77+
78+
#[derive(SystemSet, PartialEq, Eq, Debug, Clone, Hash)]
79+
pub struct SolariSystems;
80+
81+
/// A resource to track whether the renderer has the required features for Solari systems.
82+
#[derive(Resource)]
83+
struct HasSolariRequiredFeatures;
84+
85+
/// Check for the Solari required features once in startup, and insert a resource if the features
86+
/// are enabled.
87+
///
88+
/// Now systems can do a cheap check for if the resource exists.
89+
fn check_solari_has_required_features(mut commands: Commands, render_device: Res<RenderDevice>) {
90+
let features = render_device.features();
91+
if !features.contains(SolariPlugin::required_wgpu_features()) {
92+
warn!(
93+
"SolariSystems disabled. GPU lacks support for required features: {:?}.",
94+
SolariPlugin::required_wgpu_features().difference(features)
95+
);
96+
return;
97+
}
98+
commands.insert_resource(HasSolariRequiredFeatures);
99+
}

crates/bevy_solari/src/pathtracer/mod.rs

Lines changed: 30 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,22 @@ mod extract;
22
mod node;
33
mod prepare;
44

5-
use crate::SolariPlugin;
5+
use crate::{scene::init_raytracing_scene_bindings, SolariSystems};
66
use bevy_app::{App, Plugin};
77
use bevy_asset::embedded_asset;
88
use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d};
9-
use bevy_ecs::{component::Component, reflect::ReflectComponent, schedule::IntoScheduleConfigs};
9+
use bevy_ecs::{
10+
component::Component, reflect::ReflectComponent, schedule::IntoScheduleConfigs, world::World,
11+
};
1012
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
1113
use bevy_render::{
1214
render_graph::{RenderGraphExt, ViewNodeRunner},
13-
renderer::RenderDevice,
1415
view::Hdr,
15-
ExtractSchedule, Render, RenderApp, RenderSystems,
16+
ExtractSchedule, Render, RenderApp, RenderStartup, RenderSystems,
1617
};
1718
use extract::extract_pathtracer;
1819
use node::PathtracerNode;
1920
use prepare::prepare_pathtracer_accumulation_texture;
20-
use tracing::warn;
2121

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

3232
app.register_type::<Pathtracer>();
33-
}
34-
35-
fn finish(&self, app: &mut App) {
36-
let render_app = app.sub_app_mut(RenderApp);
3733

38-
let render_device = render_app.world().resource::<RenderDevice>();
39-
let features = render_device.features();
40-
if !features.contains(SolariPlugin::required_wgpu_features()) {
41-
warn!(
42-
"PathtracingPlugin not loaded. GPU lacks support for required features: {:?}.",
43-
SolariPlugin::required_wgpu_features().difference(features)
44-
);
34+
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
4535
return;
46-
}
36+
};
4737

4838
render_app
49-
.add_systems(ExtractSchedule, extract_pathtracer)
5039
.add_systems(
51-
Render,
52-
prepare_pathtracer_accumulation_texture.in_set(RenderSystems::PrepareResources),
53-
)
54-
.add_render_graph_node::<ViewNodeRunner<PathtracerNode>>(
55-
Core3d,
56-
node::graph::PathtracerNode,
40+
RenderStartup,
41+
add_solari_pathtracing_render_graph_nodes
42+
.after(init_raytracing_scene_bindings)
43+
.in_set(SolariSystems),
5744
)
58-
.add_render_graph_edges(Core3d, (Node3d::EndMainPass, node::graph::PathtracerNode));
45+
.add_systems(ExtractSchedule, extract_pathtracer.in_set(SolariSystems))
46+
.add_systems(
47+
Render,
48+
prepare_pathtracer_accumulation_texture
49+
.in_set(RenderSystems::PrepareResources)
50+
.in_set(SolariSystems),
51+
);
5952
}
6053
}
6154

@@ -65,3 +58,15 @@ impl Plugin for PathtracingPlugin {
6558
pub struct Pathtracer {
6659
pub reset: bool,
6760
}
61+
62+
// We only want to add these render graph nodes and edges if Solari required features are present.
63+
// Making this a system that runs at RenderStartup allows a run condition to check for required
64+
// features first.
65+
fn add_solari_pathtracing_render_graph_nodes(world: &mut World) {
66+
world
67+
.add_render_graph_node::<ViewNodeRunner<PathtracerNode>>(
68+
Core3d,
69+
node::graph::PathtracerNode,
70+
)
71+
.add_render_graph_edges(Core3d, (Node3d::EndMainPass, node::graph::PathtracerNode));
72+
}

crates/bevy_solari/src/realtime/mod.rs

Lines changed: 35 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -2,26 +2,26 @@ mod extract;
22
mod node;
33
mod prepare;
44

5-
use crate::SolariPlugin;
5+
use crate::{scene::init_raytracing_scene_bindings, SolariSystems};
66
use bevy_app::{App, Plugin};
77
use bevy_asset::embedded_asset;
88
use bevy_core_pipeline::{
99
core_3d::graph::{Core3d, Node3d},
1010
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass},
1111
};
12-
use bevy_ecs::{component::Component, reflect::ReflectComponent, schedule::IntoScheduleConfigs};
12+
use bevy_ecs::{
13+
component::Component, reflect::ReflectComponent, schedule::IntoScheduleConfigs, world::World,
14+
};
1315
use bevy_pbr::DefaultOpaqueRendererMethod;
1416
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
1517
use bevy_render::{
1618
render_graph::{RenderGraphExt, ViewNodeRunner},
17-
renderer::RenderDevice,
1819
view::Hdr,
19-
ExtractSchedule, Render, RenderApp, RenderSystems,
20+
ExtractSchedule, Render, RenderApp, RenderStartup, RenderSystems,
2021
};
2122
use extract::extract_solari_lighting;
2223
use node::SolariLightingNode;
2324
use prepare::prepare_solari_lighting_resources;
24-
use tracing::warn;
2525

2626
pub struct SolariLightingPlugin;
2727

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

3232
app.register_type::<SolariLighting>()
3333
.insert_resource(DefaultOpaqueRendererMethod::deferred());
34-
}
35-
36-
fn finish(&self, app: &mut App) {
37-
let render_app = app.sub_app_mut(RenderApp);
3834

39-
let render_device = render_app.world().resource::<RenderDevice>();
40-
let features = render_device.features();
41-
if !features.contains(SolariPlugin::required_wgpu_features()) {
42-
warn!(
43-
"SolariLightingPlugin not loaded. GPU lacks support for required features: {:?}.",
44-
SolariPlugin::required_wgpu_features().difference(features)
45-
);
35+
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
4636
return;
47-
}
37+
};
38+
4839
render_app
49-
.add_systems(ExtractSchedule, extract_solari_lighting)
5040
.add_systems(
51-
Render,
52-
prepare_solari_lighting_resources.in_set(RenderSystems::PrepareResources),
41+
RenderStartup,
42+
add_solari_lighting_render_graph_nodes
43+
.after(init_raytracing_scene_bindings)
44+
.in_set(SolariSystems),
5345
)
54-
.add_render_graph_node::<ViewNodeRunner<SolariLightingNode>>(
55-
Core3d,
56-
node::graph::SolariLightingNode,
46+
.add_systems(
47+
ExtractSchedule,
48+
extract_solari_lighting.in_set(SolariSystems),
5749
)
58-
.add_render_graph_edges(
59-
Core3d,
60-
(Node3d::EndMainPass, node::graph::SolariLightingNode),
50+
.add_systems(
51+
Render,
52+
prepare_solari_lighting_resources
53+
.in_set(RenderSystems::PrepareResources)
54+
.in_set(SolariSystems),
6155
);
6256
}
6357
}
@@ -87,3 +81,18 @@ impl Default for SolariLighting {
8781
}
8882
}
8983
}
84+
85+
// We only want to add these render graph nodes and edges if Solari required features are present.
86+
// Making this a system that runs at RenderStartup allows a run condition to check for required
87+
// features first.
88+
fn add_solari_lighting_render_graph_nodes(world: &mut World) {
89+
world
90+
.add_render_graph_node::<ViewNodeRunner<SolariLightingNode>>(
91+
Core3d,
92+
node::graph::SolariLightingNode,
93+
)
94+
.add_render_graph_edges(
95+
Core3d,
96+
(Node3d::EndMainPass, node::graph::SolariLightingNode),
97+
);
98+
}

crates/bevy_solari/src/scene/binder.rs

Lines changed: 28 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ use bevy_color::{ColorToComponents, LinearRgba};
44
use bevy_ecs::{
55
entity::{Entity, EntityHashMap},
66
resource::Resource,
7-
system::{Query, Res, ResMut},
8-
world::{FromWorld, World},
7+
system::{Commands, Query, Res, ResMut},
98
};
109
use bevy_math::{ops::cos, Mat4, Vec3};
1110
use bevy_pbr::{ExtractedDirectionalLight, MeshMaterial3d, StandardMaterial};
@@ -265,36 +264,35 @@ pub fn prepare_raytracing_scene_bindings(
265264
));
266265
}
267266

268-
impl FromWorld for RaytracingSceneBindings {
269-
fn from_world(world: &mut World) -> Self {
270-
let render_device = world.resource::<RenderDevice>();
271-
272-
Self {
273-
bind_group: None,
274-
bind_group_layout: render_device.create_bind_group_layout(
275-
"raytracing_scene_bind_group_layout",
276-
&BindGroupLayoutEntries::sequential(
277-
ShaderStages::COMPUTE,
278-
(
279-
storage_buffer_read_only_sized(false, None).count(MAX_MESH_SLAB_COUNT),
280-
storage_buffer_read_only_sized(false, None).count(MAX_MESH_SLAB_COUNT),
281-
texture_2d(TextureSampleType::Float { filterable: true })
282-
.count(MAX_TEXTURE_COUNT),
283-
sampler(SamplerBindingType::Filtering).count(MAX_TEXTURE_COUNT),
284-
storage_buffer_read_only_sized(false, None),
285-
acceleration_structure(),
286-
storage_buffer_read_only_sized(false, None),
287-
storage_buffer_read_only_sized(false, None),
288-
storage_buffer_read_only_sized(false, None),
289-
storage_buffer_read_only_sized(false, None),
290-
storage_buffer_read_only_sized(false, None),
291-
storage_buffer_read_only_sized(false, None),
292-
),
267+
pub(crate) fn init_raytracing_scene_bindings(
268+
mut commands: Commands,
269+
render_device: Res<RenderDevice>,
270+
) {
271+
commands.insert_resource(RaytracingSceneBindings {
272+
bind_group: None,
273+
bind_group_layout: render_device.create_bind_group_layout(
274+
"raytracing_scene_bind_group_layout",
275+
&BindGroupLayoutEntries::sequential(
276+
ShaderStages::COMPUTE,
277+
(
278+
storage_buffer_read_only_sized(false, None).count(MAX_MESH_SLAB_COUNT),
279+
storage_buffer_read_only_sized(false, None).count(MAX_MESH_SLAB_COUNT),
280+
texture_2d(TextureSampleType::Float { filterable: true })
281+
.count(MAX_TEXTURE_COUNT),
282+
sampler(SamplerBindingType::Filtering).count(MAX_TEXTURE_COUNT),
283+
storage_buffer_read_only_sized(false, None),
284+
acceleration_structure(),
285+
storage_buffer_read_only_sized(false, None),
286+
storage_buffer_read_only_sized(false, None),
287+
storage_buffer_read_only_sized(false, None),
288+
storage_buffer_read_only_sized(false, None),
289+
storage_buffer_read_only_sized(false, None),
290+
storage_buffer_read_only_sized(false, None),
293291
),
294292
),
295-
previous_frame_light_entities: Vec::new(),
296-
}
297-
}
293+
),
294+
previous_frame_light_entities: Vec::new(),
295+
});
298296
}
299297

300298
struct CachedBindingArray<T, I: Eq + Hash> {

0 commit comments

Comments
 (0)