This commit is contained in:
andriyDev 2025-07-18 12:37:46 -04:00 committed by GitHub
commit 46116d57d7
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 179 additions and 113 deletions

View File

@ -20,8 +20,16 @@ pub mod prelude {
use crate::realtime::SolariLightingPlugin; use crate::realtime::SolariLightingPlugin;
use crate::scene::RaytracingScenePlugin; use crate::scene::RaytracingScenePlugin;
use bevy_app::{PluginGroup, PluginGroupBuilder}; use bevy_app::{App, Plugin, PluginGroup, PluginGroupBuilder};
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 set of plugins for raytraced lighting. /// An experimental set of plugins for raytraced lighting.
/// ///
@ -38,6 +46,7 @@ pub struct SolariPlugins;
impl PluginGroup for SolariPlugins { impl PluginGroup for SolariPlugins {
fn build(self) -> PluginGroupBuilder { fn build(self) -> PluginGroupBuilder {
PluginGroupBuilder::start::<Self>() PluginGroupBuilder::start::<Self>()
.add(SolariCorePlugin)
.add(RaytracingScenePlugin) .add(RaytracingScenePlugin)
.add(SolariLightingPlugin) .add(SolariLightingPlugin)
} }
@ -54,3 +63,50 @@ impl SolariPlugins {
| WgpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY | WgpuFeatures::PARTIALLY_BOUND_BINDING_ARRAY
} }
} }
struct SolariCorePlugin;
impl Plugin for SolariCorePlugin {
fn build(&self, app: &mut App) {
app.add_systems(RenderStartup, check_solari_has_required_features)
// 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>),
);
}
}
#[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(SolariPlugins::required_wgpu_features()) {
warn!(
"SolariSystems disabled. GPU lacks support for required features: {:?}.",
SolariPlugins::required_wgpu_features().difference(features)
);
return;
}
commands.insert_resource(HasSolariRequiredFeatures);
}

View File

@ -2,22 +2,22 @@ mod extract;
mod node; mod node;
mod prepare; mod prepare;
use crate::SolariPlugins; use crate::{scene::init_raytracing_scene_bindings, SolariSystems};
use bevy_app::{App, Plugin}; use bevy_app::{App, Plugin};
use bevy_asset::embedded_asset; use bevy_asset::embedded_asset;
use bevy_core_pipeline::core_3d::graph::{Core3d, Node3d}; 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_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{ use bevy_render::{
render_graph::{RenderGraphExt, ViewNodeRunner}, render_graph::{RenderGraphExt, ViewNodeRunner},
renderer::RenderDevice,
view::Hdr, view::Hdr,
ExtractSchedule, Render, RenderApp, RenderSystems, ExtractSchedule, Render, RenderApp, RenderStartup, RenderSystems,
}; };
use extract::extract_pathtracer; use extract::extract_pathtracer;
use node::PathtracerNode; use node::PathtracerNode;
use prepare::prepare_pathtracer_accumulation_texture; use prepare::prepare_pathtracer_accumulation_texture;
use tracing::warn;
/// Non-realtime pathtracing. /// Non-realtime pathtracing.
/// ///
@ -30,32 +30,25 @@ impl Plugin for PathtracingPlugin {
embedded_asset!(app, "pathtracer.wgsl"); embedded_asset!(app, "pathtracer.wgsl");
app.register_type::<Pathtracer>(); app.register_type::<Pathtracer>();
}
fn finish(&self, app: &mut App) { let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
let render_app = app.sub_app_mut(RenderApp);
let render_device = render_app.world().resource::<RenderDevice>();
let features = render_device.features();
if !features.contains(SolariPlugins::required_wgpu_features()) {
warn!(
"PathtracingPlugin not loaded. GPU lacks support for required features: {:?}.",
SolariPlugins::required_wgpu_features().difference(features)
);
return; return;
} };
render_app render_app
.add_systems(ExtractSchedule, extract_pathtracer) .add_systems(
RenderStartup,
add_solari_pathtracing_render_graph_nodes
.after(init_raytracing_scene_bindings)
.in_set(SolariSystems),
)
.add_systems(ExtractSchedule, extract_pathtracer.in_set(SolariSystems))
.add_systems( .add_systems(
Render, Render,
prepare_pathtracer_accumulation_texture.in_set(RenderSystems::PrepareResources), prepare_pathtracer_accumulation_texture
) .in_set(RenderSystems::PrepareResources)
.add_render_graph_node::<ViewNodeRunner<PathtracerNode>>( .in_set(SolariSystems),
Core3d, );
node::graph::PathtracerNode,
)
.add_render_graph_edges(Core3d, (Node3d::EndMainPass, node::graph::PathtracerNode));
} }
} }
@ -65,3 +58,15 @@ impl Plugin for PathtracingPlugin {
pub struct Pathtracer { pub struct Pathtracer {
pub reset: bool, 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));
}

View File

@ -2,26 +2,26 @@ mod extract;
mod node; mod node;
mod prepare; mod prepare;
use crate::SolariPlugins; use crate::{scene::init_raytracing_scene_bindings, SolariSystems};
use bevy_app::{App, Plugin}; use bevy_app::{App, Plugin};
use bevy_asset::embedded_asset; use bevy_asset::embedded_asset;
use bevy_core_pipeline::{ use bevy_core_pipeline::{
core_3d::graph::{Core3d, Node3d}, core_3d::graph::{Core3d, Node3d},
prepass::{DeferredPrepass, DepthPrepass, MotionVectorPrepass}, 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_pbr::DefaultOpaqueRendererMethod;
use bevy_reflect::{std_traits::ReflectDefault, Reflect}; use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{ use bevy_render::{
render_graph::{RenderGraphExt, ViewNodeRunner}, render_graph::{RenderGraphExt, ViewNodeRunner},
renderer::RenderDevice,
view::Hdr, view::Hdr,
ExtractSchedule, Render, RenderApp, RenderSystems, ExtractSchedule, Render, RenderApp, RenderStartup, RenderSystems,
}; };
use extract::extract_solari_lighting; use extract::extract_solari_lighting;
use node::SolariLightingNode; use node::SolariLightingNode;
use prepare::prepare_solari_lighting_resources; use prepare::prepare_solari_lighting_resources;
use tracing::warn;
/// Raytraced direct and indirect lighting. /// Raytraced direct and indirect lighting.
/// ///
@ -36,33 +36,27 @@ impl Plugin for SolariLightingPlugin {
app.register_type::<SolariLighting>() app.register_type::<SolariLighting>()
.insert_resource(DefaultOpaqueRendererMethod::deferred()); .insert_resource(DefaultOpaqueRendererMethod::deferred());
}
fn finish(&self, app: &mut App) { let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
let render_app = app.sub_app_mut(RenderApp);
let render_device = render_app.world().resource::<RenderDevice>();
let features = render_device.features();
if !features.contains(SolariPlugins::required_wgpu_features()) {
warn!(
"SolariLightingPlugin not loaded. GPU lacks support for required features: {:?}.",
SolariPlugins::required_wgpu_features().difference(features)
);
return; return;
} };
render_app render_app
.add_systems(ExtractSchedule, extract_solari_lighting) .add_systems(
RenderStartup,
add_solari_lighting_render_graph_nodes
.after(init_raytracing_scene_bindings)
.in_set(SolariSystems),
)
.add_systems(
ExtractSchedule,
extract_solari_lighting.in_set(SolariSystems),
)
.add_systems( .add_systems(
Render, Render,
prepare_solari_lighting_resources.in_set(RenderSystems::PrepareResources), prepare_solari_lighting_resources
) .in_set(RenderSystems::PrepareResources)
.add_render_graph_node::<ViewNodeRunner<SolariLightingNode>>( .in_set(SolariSystems),
Core3d,
node::graph::SolariLightingNode,
)
.add_render_graph_edges(
Core3d,
(Node3d::EndMainPass, node::graph::SolariLightingNode),
); );
} }
} }
@ -92,3 +86,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),
);
}

View File

@ -4,8 +4,7 @@ use bevy_color::{ColorToComponents, LinearRgba};
use bevy_ecs::{ use bevy_ecs::{
entity::{Entity, EntityHashMap}, entity::{Entity, EntityHashMap},
resource::Resource, resource::Resource,
system::{Query, Res, ResMut}, system::{Commands, Query, Res, ResMut},
world::{FromWorld, World},
}; };
use bevy_math::{ops::cos, Mat4, Vec3}; use bevy_math::{ops::cos, Mat4, Vec3};
use bevy_pbr::{ExtractedDirectionalLight, MeshMaterial3d, StandardMaterial}; use bevy_pbr::{ExtractedDirectionalLight, MeshMaterial3d, StandardMaterial};
@ -265,11 +264,11 @@ pub fn prepare_raytracing_scene_bindings(
)); ));
} }
impl FromWorld for RaytracingSceneBindings { pub(crate) fn init_raytracing_scene_bindings(
fn from_world(world: &mut World) -> Self { mut commands: Commands,
let render_device = world.resource::<RenderDevice>(); render_device: Res<RenderDevice>,
) {
Self { commands.insert_resource(RaytracingSceneBindings {
bind_group: None, bind_group: None,
bind_group_layout: render_device.create_bind_group_layout( bind_group_layout: render_device.create_bind_group_layout(
"raytracing_scene_bind_group_layout", "raytracing_scene_bind_group_layout",
@ -293,8 +292,7 @@ impl FromWorld for RaytracingSceneBindings {
), ),
), ),
previous_frame_light_entities: Vec::new(), previous_frame_light_entities: Vec::new(),
} });
}
} }
struct CachedBindingArray<T, I: Eq + Hash> { struct CachedBindingArray<T, I: Eq + Hash> {

View File

@ -6,9 +6,11 @@ mod types;
pub use binder::RaytracingSceneBindings; pub use binder::RaytracingSceneBindings;
pub use types::RaytracingMesh3d; pub use types::RaytracingMesh3d;
use crate::SolariPlugins; pub(crate) use binder::init_raytracing_scene_bindings;
use crate::SolariSystems;
use bevy_app::{App, Plugin}; use bevy_app::{App, Plugin};
use bevy_ecs::schedule::IntoScheduleConfigs; use bevy_ecs::{schedule::IntoScheduleConfigs, system::ResMut};
use bevy_render::{ use bevy_render::{
extract_resource::ExtractResourcePlugin, extract_resource::ExtractResourcePlugin,
load_shader_library, load_shader_library,
@ -18,13 +20,11 @@ use bevy_render::{
}, },
render_asset::prepare_assets, render_asset::prepare_assets,
render_resource::BufferUsages, render_resource::BufferUsages,
renderer::RenderDevice, ExtractSchedule, Render, RenderApp, RenderStartup, RenderSystems,
ExtractSchedule, Render, RenderApp, RenderSystems,
}; };
use binder::prepare_raytracing_scene_bindings; use binder::prepare_raytracing_scene_bindings;
use blas::{prepare_raytracing_blas, BlasManager}; use blas::{prepare_raytracing_blas, BlasManager};
use extract::{extract_raytracing_scene, StandardMaterialAssets}; use extract::{extract_raytracing_scene, StandardMaterialAssets};
use tracing::warn;
/// Creates acceleration structures and binding arrays of resources for raytracing. /// Creates acceleration structures and binding arrays of resources for raytracing.
pub struct RaytracingScenePlugin; pub struct RaytracingScenePlugin;
@ -34,35 +34,28 @@ impl Plugin for RaytracingScenePlugin {
load_shader_library!(app, "raytracing_scene_bindings.wgsl"); load_shader_library!(app, "raytracing_scene_bindings.wgsl");
load_shader_library!(app, "sampling.wgsl"); load_shader_library!(app, "sampling.wgsl");
app.register_type::<RaytracingMesh3d>(); app.register_type::<RaytracingMesh3d>()
} .add_plugins(ExtractResourcePlugin::<StandardMaterialAssets>::default());
fn finish(&self, app: &mut App) { let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
let render_app = app.sub_app_mut(RenderApp);
let render_device = render_app.world().resource::<RenderDevice>();
let features = render_device.features();
if !features.contains(SolariPlugins::required_wgpu_features()) {
warn!(
"RaytracingScenePlugin not loaded. GPU lacks support for required features: {:?}.",
SolariPlugins::required_wgpu_features().difference(features)
);
return; return;
} };
app.add_plugins(ExtractResourcePlugin::<StandardMaterialAssets>::default());
let render_app = app.sub_app_mut(RenderApp);
render_app
.world_mut()
.resource_mut::<MeshAllocator>()
.extra_buffer_usages |= BufferUsages::BLAS_INPUT | BufferUsages::STORAGE;
render_app render_app
.init_resource::<BlasManager>() .init_resource::<BlasManager>()
.init_resource::<StandardMaterialAssets>() .init_resource::<StandardMaterialAssets>()
.init_resource::<RaytracingSceneBindings>() .add_systems(
.add_systems(ExtractSchedule, extract_raytracing_scene) RenderStartup,
(
add_raytracing_extra_mesh_buffer_usages,
init_raytracing_scene_bindings,
)
.in_set(SolariSystems),
)
.add_systems(
ExtractSchedule,
extract_raytracing_scene.in_set(SolariSystems),
)
.add_systems( .add_systems(
Render, Render,
( (
@ -71,7 +64,12 @@ impl Plugin for RaytracingScenePlugin {
.before(prepare_assets::<RenderMesh>) .before(prepare_assets::<RenderMesh>)
.after(allocate_and_free_meshes), .after(allocate_and_free_meshes),
prepare_raytracing_scene_bindings.in_set(RenderSystems::PrepareBindGroups), prepare_raytracing_scene_bindings.in_set(RenderSystems::PrepareBindGroups),
), )
.in_set(SolariSystems),
); );
} }
} }
fn add_raytracing_extra_mesh_buffer_usages(mut mesh_allocator: ResMut<MeshAllocator>) {
mesh_allocator.extra_buffer_usages |= BufferUsages::BLAS_INPUT | BufferUsages::STORAGE;
}