Skip to content
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
25 changes: 14 additions & 11 deletions crates/bevy_solari/src/realtime/bindings.wesl
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,20 @@ enable wgpu_ray_query;
@group(1) @binding(0) var view_output: texture_storage_2d<rgba16float, read_write>;
@group(1) @binding(1) var<storage, read_write> light_tile_samples: array<LightSample>;
@group(1) @binding(2) var<storage, read_write> light_tile_resolved_samples: array<ResolvedLightSamplePacked>;
@group(1) @binding(3) var<storage, read_write> reservoirs_a: array<Reservoir>;
@group(1) @binding(4) var<storage, read_write> reservoirs_b: array<Reservoir>;
@group(1) @binding(5) var gbuffer: texture_2d<u32>;
@group(1) @binding(6) var depth_buffer: texture_depth_2d;
@group(1) @binding(7) var motion_vectors: texture_storage_2d<rg16float, read_write>;
@group(1) @binding(8) var previous_gbuffer: texture_2d<u32>;
@group(1) @binding(9) var previous_depth_buffer: texture_depth_2d;
@group(1) @binding(10) var<uniform> view: View;
@group(1) @binding(11) var<uniform> previous_view: PreviousViewUniforms;
@group(1) @binding(12) var<storage, read_write> world_cache: WorldCache;
@group(1) @binding(13) var<uniform> constants: SolariLightingSettings;
@group(1) @binding(3) var gbuffer: texture_2d<u32>;
@group(1) @binding(4) var depth_buffer: texture_depth_2d;
@group(1) @binding(5) var motion_vectors: texture_storage_2d<rg16float, read_write>;
@group(1) @binding(6) var<uniform> view: View;
@group(1) @binding(7) var<uniform> previous_view: PreviousViewUniforms;
@group(1) @binding(8) var<storage, read_write> world_cache: WorldCache;
@group(1) @binding(9) var<uniform> constants: SolariLightingSettings;

@if(RESTIR) {
@group(1) @binding(10) var previous_gbuffer: texture_2d<u32>;
@group(1) @binding(11) var previous_depth_buffer: texture_depth_2d;
@group(1) @binding(12) var<storage, read_write> reservoirs_a: array<Reservoir>;
@group(1) @binding(13) var<storage, read_write> reservoirs_b: array<Reservoir>;
}

@if(DLSS_RR_GUIDE_BUFFERS) {
@group(2) @binding(0) var diffuse_albedo: texture_storage_2d<rgba8unorm, write>;
Expand Down
307 changes: 173 additions & 134 deletions crates/bevy_solari/src/realtime/initial_path.wesl

Large diffs are not rendered by default.

95 changes: 76 additions & 19 deletions crates/bevy_solari/src/realtime/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ mod node;
mod prepare;

use crate::SolariPlugins;
use bevy_app::{App, Plugin};
use bevy_app::{App, Plugin, PostUpdate};
use bevy_asset::embedded_asset;
use bevy_camera::Hdr;
use bevy_core_pipeline::{
Expand All @@ -14,7 +14,14 @@ use bevy_core_pipeline::{
},
schedule::{Core3d, Core3dSystems},
};
use bevy_ecs::{component::Component, reflect::ReflectComponent, schedule::IntoScheduleConfigs};
use bevy_ecs::{
component::Component,
entity::Entity,
query::Has,
reflect::ReflectComponent,
schedule::IntoScheduleConfigs,
system::{Commands, Query},
};
use bevy_pbr::DefaultOpaqueRendererMethod;
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
use bevy_render::{
Expand All @@ -23,7 +30,9 @@ use bevy_render::{
use bevy_shader::load_shader_library;
use extract::extract_solari_lighting;
use node::{init_solari_lighting_pipelines, solari_lighting};
use prepare::prepare_solari_lighting_resources;
use prepare::{
prepare_solari_lighting_resources, setup_raytracing_scene_needs_previous_frame_data,
};
use tracing::warn;

/// Raytraced direct and indirect lighting.
Expand All @@ -39,6 +48,7 @@ impl Plugin for SolariLightingPlugin {
load_shader_library!(app, "presample_light_tiles.wesl");
load_shader_library!(app, "initial_path.wesl");
embedded_asset!(app, "restir.wesl");
embedded_asset!(app, "no_restir.wesl");
load_shader_library!(app, "world_cache_query.wesl");
embedded_asset!(app, "world_cache_compact.wesl");
embedded_asset!(app, "world_cache_update.wesl");
Expand All @@ -49,10 +59,11 @@ impl Plugin for SolariLightingPlugin {
}

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();
let features = app
.sub_app(RenderApp)
.world()
.resource::<RenderDevice>()
.features();
if !features.contains(SolariPlugins::required_wgpu_features()) {
warn!(
"SolariLightingPlugin not loaded. GPU lacks support for required features: {:?}.",
Expand All @@ -61,12 +72,18 @@ impl Plugin for SolariLightingPlugin {
return;
}

render_app
app.add_systems(PostUpdate, manage_prepass_double_buffers);

app.sub_app_mut(RenderApp)
.add_systems(RenderStartup, init_solari_lighting_pipelines)
.add_systems(ExtractSchedule, extract_solari_lighting)
.add_systems(
Render,
prepare_solari_lighting_resources.in_set(RenderSystems::PrepareResources),
(
prepare_solari_lighting_resources,
setup_raytracing_scene_needs_previous_frame_data,
)
.in_set(RenderSystems::PrepareResources),
)
.add_systems(
Core3d,
Expand All @@ -83,18 +100,27 @@ impl Plugin for SolariLightingPlugin {
/// `Msaa::Off`.
#[derive(Component, Reflect, Clone)]
#[reflect(Component, Default, Clone)]
#[require(
Hdr,
DeferredPrepass,
DepthPrepass,
MotionVectorPrepass,
DeferredPrepassDoubleBuffer,
DepthPrepassDoubleBuffer
)]
#[require(Hdr, DeferredPrepass, DepthPrepass, MotionVectorPrepass)]
pub struct SolariLighting {
/// [ReSTIR](https://en.wikipedia.org/wiki/Spatiotemporal_reservoir_resampling) is a technique to reuse path samples
/// between pixels and frames. This dramatically reduces noise, at the cost of a few extra rays per pixel.
///
/// However, modern denoisers cope well with very noisy input. In many cases, turning this on
/// won't dramatically improve quality after denoising.
///
/// If you want more fine shadow detail, or have scenes with more difficult lighting conditions,
/// turning this on may improve quality and stability, at the cost of a decent chunk of performance.
///
/// Whether to enable this setting or not will be very scene dependent.
///
/// Defaults to `false`.
pub restir: bool,

/// Maximum confidence weight (effective temporal history length) a pixel
/// can accumulate during temporal resampling.
///
/// Has no effect when [`SolariLighting::restir`] is `false`.
///
/// Higher values are more stable but slower to react to lighting changes
/// and will lead to increased artifacts.
pub confidence_weight_cap: f32,
Expand All @@ -115,8 +141,8 @@ pub struct SolariLighting {

/// Maximum number of bounces traced when generating an initial path.
///
/// Higher values capture more indirect light for greater accuracy at the cost
/// of more rays traced per frame. Lower values are faster but lose
/// Higher values capture more detail in nested reflections and more indirect lighting,
/// at the cost of more rays traced per frame. Lower values are faster but lose
/// multi-bounce lighting for specular paths.
pub max_bounces: u32,

Expand Down Expand Up @@ -183,6 +209,7 @@ pub struct SolariLighting {
impl Default for SolariLighting {
fn default() -> Self {
Self {
restir: false,
confidence_weight_cap: 8.0,
primary_di_samples: 8,
secondary_di_samples: 4,
Expand All @@ -197,3 +224,33 @@ impl Default for SolariLighting {
}
}
}

/// Adds or removes the prepass double-buffer components according to [`SolariLighting::restir`].
fn manage_prepass_double_buffers(
views: Query<(
Entity,
&SolariLighting,
Has<DeferredPrepassDoubleBuffer>,
Has<DepthPrepassDoubleBuffer>,
)>,
mut commands: Commands,
) {
for (entity, solari_lighting, deferred_double_buffered, depth_double_buffered) in &views {
let mut entity = commands.entity(entity);
if solari_lighting.restir {
if !deferred_double_buffered {
entity.insert(DeferredPrepassDoubleBuffer);
}
if !depth_double_buffered {
entity.insert(DepthPrepassDoubleBuffer);
}
} else {
if deferred_double_buffered {
Comment on lines +247 to +248

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Anyone actually using these in their app would break if these get removed here wouldn't they?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Yes, but extremely unlikely, and we have no good way of solving this.

entity.remove::<DeferredPrepassDoubleBuffer>();
}
if depth_double_buffered {
entity.remove::<DepthPrepassDoubleBuffer>();
}
}
}
}
30 changes: 30 additions & 0 deletions crates/bevy_solari/src/realtime/no_restir.wesl
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import package::realtime::gbuffer_utils::gpixel_resolve;
import package::realtime::initial_path::generate_initial_path;
import package::realtime::bindings::{constants, depth_buffer, gbuffer, view, view_output};
import package::scene::bindings::RAY_T_MAX;
import package::realtime::world_cache_query::{query_world_cache, WORLD_CACHE_CELL_LIFETIME};

enable wgpu_ray_query;

@compute @workgroup_size(8, 8, 1)
fn initial_and_shade(@builtin(workgroup_id) workgroup_id: vec3<u32>, @builtin(global_invocation_id) global_id: vec3<u32>) {
if any(global_id.xy >= vec2u(view.main_pass_viewport.zw)) { return; }

let pixel_index = global_id.x + global_id.y * u32(view.main_pass_viewport.z);
var rng = pixel_index + constants.frame_rng;

let depth = textureLoad(depth_buffer, global_id.xy, 0);
if depth == 0.0 { return; }

let surface = gpixel_resolve(textureLoad(gbuffer, global_id.xy, 0), depth, global_id.xy, view.main_pass_viewport.zw, view.world_from_clip);

let path = generate_initial_path(surface.world_position, surface.world_normal, surface.material, workgroup_id.xy, global_id.xy, &rng);

var pixel_color = path.radiance;
pixel_color += surface.material.emissive;
pixel_color *= view.exposure;
textureStore(view_output, global_id.xy, vec4(pixel_color, 1.0));

@if(VISUALIZE_WORLD_CACHE)
textureStore(view_output, global_id.xy, vec4(query_world_cache(surface.world_position, surface.world_normal, view.world_position, RAY_T_MAX, WORLD_CACHE_CELL_LIFETIME, &rng) * view.exposure, 1.0));
}
Loading