Skip to content

Commit 751be72

Browse files
committed
Decouple path tracing preparation from SSGI
1 parent 1041a06 commit 751be72

10 files changed

Lines changed: 107 additions & 24 deletions

File tree

docs/compiled-render-graph.md

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@ counts, allowed usage, load policy, and alias class.
2525
- generates a stable plan ID from the complete execution contract.
2626

2727
The live topology is declared in `renderer/graph/frame_plan.rs`. Optional
28-
SSAO, SSR, SSGI (including acceleration/card/SDF/radiance-cache preparation),
29-
bloom, scene-snapshot, and capture work is selected before compilation by
30-
`FramePlanKey`. Uniform-only changes do not affect the key.
28+
SSAO, SSR, SSGI, PT, bloom, scene-snapshot, and capture work is selected before
29+
compilation by `FramePlanKey`. SSGI and PT independently select their shared
30+
acceleration/card-capture prefix; SDF, clipmap, radiance-cache, and card-light
31+
preparation remain SSGI-only. Uniform-only changes do not affect the key.
3132
`ExecutableGraph` binds only frame-local recording closures to cached pass
3233
positions; it does not rebuild or schedule a declaration graph per frame.
3334

docs/pt/pt-roadmap.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,8 +34,11 @@ fixed exposure) so images are comparable number-to-number.
3434

3535
Mode is a runtime setting (`bloom_set_path_tracing`), toggleable per frame.
3636
When PT is active, SSGI/SSR/GTAO are skipped (their output would be
37-
overwritten; skipping banks their cost). Shadow cascades still render — mode 2
38-
reuses them nowhere, but the card-light pass (Tier 1 hit shading) does.
37+
overwritten; skipping banks their cost). PT independently keeps only the
38+
shared TLAS/geometry rebuild and raw-albedo card capture it consumes. SSGI-only
39+
SDF, clipmap, radiance-cache, and card-light passes remain absent when SSGI is
40+
off. Shadow cascades still render because realtime PT can use them for its
41+
noise-free hybrid sun path.
3942

4043
## Tiers
4144

docs/temporal-history.md

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -331,7 +331,4 @@ silently passing.
331331

332332
Complete the PT-specific moving-object, lighting-change, and reset sequence
333333
coverage on required hardware runners, including the goldens owned by #127 and
334-
the timing/memory qualification owned by #128. PT's shared TLAS/card
335-
preparation is currently scheduled through the SSGI infrastructure feature;
336-
decouple that ownership before claiming an independent PT-on/SSGI-off mode
337-
matrix.
334+
the timing/memory qualification owned by #128.

native/shared/src/renderer/frame_graph_runtime.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -123,7 +123,11 @@ impl Renderer {
123123
quality_tier,
124124
feature_mask,
125125
capability,
126-
path_tracing: graph::PathTracingMode::from_u32(self.pt_mode),
126+
path_tracing: graph::PathTracingMode::from_u32(if self.pt_active() {
127+
self.pt_mode
128+
} else {
129+
0
130+
}),
127131
post_pass_count: self.post_passes.len().min(u16::MAX as usize) as u16,
128132
render_target_output,
129133
}

native/shared/src/renderer/graph/frame_plan.rs

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -406,16 +406,18 @@ pub fn build_renderer_frame_plan(
406406
},
407407
);
408408

409+
let ssgi_preparation = key.feature_mask & super::FRAME_FEATURE_SSGI != 0;
410+
let pt_preparation = key.path_tracing != super::PathTracingMode::Off;
409411
let mut previous_gi = None;
410-
if key.feature_mask & super::FRAME_FEATURE_SSGI != 0 {
411-
for name in [
412-
"accel_rebuild",
413-
"card_capture",
414-
"sdf_bake",
415-
"scene_sdf_clipmap",
416-
"wsrc_bake",
417-
"card_light",
418-
] {
412+
for (name, enabled) in [
413+
("accel_rebuild", ssgi_preparation || pt_preparation),
414+
("card_capture", ssgi_preparation || pt_preparation),
415+
("sdf_bake", ssgi_preparation),
416+
("scene_sdf_clipmap", ssgi_preparation),
417+
("wsrc_bake", ssgi_preparation),
418+
("card_light", ssgi_preparation),
419+
] {
420+
if enabled {
419421
let pass = graph.add_pass(name);
420422
if let Some(previous) = previous_gi {
421423
graph.after(pass, previous);
@@ -746,6 +748,65 @@ mod tests {
746748
assert!(plan.pass("capture_readback").is_none());
747749
}
748750

751+
#[test]
752+
fn path_tracing_owns_shared_ray_scene_preparation_without_ssgi_bakes() {
753+
let mut pt_key = key(0);
754+
pt_key.path_tracing = PathTracingMode::Realtime;
755+
pt_key.capability = CapabilityTier::HardwareRayQuery;
756+
let plan = build_renderer_frame_plan(pt_key, wgpu::TextureFormat::Bgra8UnormSrgb)
757+
.compile(CompileOptions::CONSERVATIVE_ALIASING)
758+
.unwrap();
759+
for present in ["accel_rebuild", "card_capture", "pt"] {
760+
assert!(plan.pass(present).is_some(), "{present} must serve PT");
761+
}
762+
for absent in [
763+
"sdf_bake",
764+
"scene_sdf_clipmap",
765+
"wsrc_bake",
766+
"card_light",
767+
"ssgi",
768+
] {
769+
assert!(
770+
plan.pass(absent).is_none(),
771+
"{absent} must remain SSGI-only"
772+
);
773+
}
774+
let names = plan
775+
.passes
776+
.iter()
777+
.map(|pass| pass.name.as_str())
778+
.collect::<Vec<_>>();
779+
assert!(
780+
names
781+
.iter()
782+
.position(|name| *name == "card_capture")
783+
.unwrap()
784+
< names.iter().position(|name| *name == "pt").unwrap()
785+
);
786+
787+
let mut combined_key = key(FRAME_FEATURE_SSGI);
788+
combined_key.path_tracing = PathTracingMode::Realtime;
789+
combined_key.capability = CapabilityTier::HardwareRayQuery;
790+
let combined = build_renderer_frame_plan(combined_key, wgpu::TextureFormat::Bgra8UnormSrgb)
791+
.compile(CompileOptions::CONSERVATIVE_ALIASING)
792+
.unwrap();
793+
for present in [
794+
"accel_rebuild",
795+
"card_capture",
796+
"sdf_bake",
797+
"scene_sdf_clipmap",
798+
"wsrc_bake",
799+
"card_light",
800+
"pt",
801+
"ssgi",
802+
] {
803+
assert!(
804+
combined.pass(present).is_some(),
805+
"{present} must serve the combined SSGI+PT topology"
806+
);
807+
}
808+
}
809+
749810
#[test]
750811
fn capture_is_a_terminal_copy_pass_over_named_logical_resources() {
751812
let plan = build_renderer_frame_plan(

native/shared/src/renderer/quality_capture.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -727,6 +727,16 @@ impl Renderer {
727727
} else {
728728
"false"
729729
});
730+
out.push_str(",\"ray_scene_preparation\":");
731+
json_string(
732+
&mut out,
733+
match (self.ssgi_enabled, self.pt_active()) {
734+
(true, true) => "ssgi+pt",
735+
(true, false) => "ssgi",
736+
(false, true) => "pt",
737+
(false, false) => "disabled",
738+
},
739+
);
730740
out.push_str(",\"temporal_history\":{");
731741
out.push_str("\"ssr_valid\":");
732742
out.push_str(if self.ssr_history_valid {

native/shared/src/renderer/shaders/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,9 @@ pub(super) use ssgi::{
3030
SSR_TEMPORAL_SHADER_WGSL,
3131
};
3232
mod pt;
33-
pub(super) use pt::{pt_fault_constants, pt_kernel_variant, PT_ATROUS_WGSL, PT_SKIN_WGSL};
3433
#[cfg(test)]
3534
pub(super) use pt::PT_KERNEL_WGSL;
35+
pub(super) use pt::{pt_fault_constants, pt_kernel_variant, PT_ATROUS_WGSL, PT_SKIN_WGSL};
3636

3737
/// Naga's Metal lowering completes a query in `rayQueryInitialize`; its
3838
/// non-modern `rayQueryProceed` only reads a `ready` flag that never clears.

native/shared/tests/golden_render/ssgi_quality.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ fn ssgi_capture_exposes_probe_history_without_normal_frame_resources() {
102102
);
103103

104104
let paths = eng.renderer.quality_runtime_paths_json();
105+
assert!(paths.contains("\"ray_scene_preparation\":\"ssgi\""));
105106
assert!(paths.contains("\"ssgi_diagnostic_persistent_bytes\":0"));
106107
assert!(paths.contains("\"ssgi_diagnostic_capture_passes\":1"));
107108
assert!(paths.contains("\"ssgi_diagnostic_resources_live\":false"));

native/shared/tests/golden_render/temporal_history.rs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -356,6 +356,10 @@ fn path_tracing_mode_transitions_reset_incompatible_history() {
356356
eng.renderer.set_path_tracing(2);
357357
let _ = render(&mut eng, 1, draw_pt_static_frame);
358358
assert!(eng.renderer.path_tracing_sample_count() > 0);
359+
assert!(eng
360+
.renderer
361+
.quality_runtime_paths_json()
362+
.contains("\"ray_scene_preparation\":\"ssgi+pt\""));
359363

360364
eng.renderer.set_path_tracing(1);
361365
assert_eq!(eng.renderer.path_tracing_sample_count(), 0);
@@ -384,9 +388,7 @@ fn realtime_path_tracing_capture_exposes_svgf_history_without_normal_frame_resou
384388
r.set_taa_enabled(false);
385389
r.set_ssao_enabled(false);
386390
r.set_ssr_enabled(false);
387-
// The current frame graph builds PT's shared TLAS/card inputs under the
388-
// SSGI infrastructure node; PT still owns the rendered GI result.
389-
r.set_ssgi_enabled(true);
391+
r.set_ssgi_enabled(false);
390392
r.set_bloom_enabled(false);
391393
r.set_auto_exposure(false);
392394
r.set_path_tracing(2);
@@ -405,6 +407,7 @@ fn realtime_path_tracing_capture_exposes_svgf_history_without_normal_frame_resou
405407
"realtime PT reached only {samples_before_capture} history frames before capture"
406408
);
407409
let normal_paths = eng.renderer.quality_runtime_paths_json();
410+
assert!(normal_paths.contains("\"ray_scene_preparation\":\"pt\""));
408411
assert!(normal_paths.contains("\"pt_diagnostic_persistent_bytes\":0"));
409412
assert!(normal_paths.contains("\"pt_diagnostic_resources_live\":false"));
410413

tools/quality/README.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,10 @@ The native renderer reports the adapter name, vendor/device IDs, device type,
153153
driver fields when exposed by wgpu, backend, capability tier, semantic feature
154154
set, actual SSGI trace backend, and path-tracing availability. Empty driver
155155
strings are valid on Metal because the backend does not expose them; they are
156-
recorded rather than invented.
156+
recorded rather than invented. Runtime evidence also reports
157+
`ray_scene_preparation` as `disabled`, `ssgi`, `pt`, or `ssgi+pt`; this makes
158+
the shared acceleration/card prefix observable without conflating it with
159+
SSGI-only baking.
157160

158161
The one debug-capture API snapshots existing render-graph products:
159162

0 commit comments

Comments
 (0)