Add reservoir higher-work-grid reference metrics

This commit is contained in:
Meta Vens 2026-05-30 00:17:44 +01:00
commit 07fb73099d
10 changed files with 71 additions and 10 deletions

View file

@ -389,6 +389,10 @@ temporal-owner disocclusion debug view derived from previous-UV loss,
previous-closer history, and support-loss rejection codes, and the sequence
measurer uses that mask before falling back to broader rejection output. This
is still an error-localization surface, not a high-budget reference result.
For same-time reference checks, the capture script can also emit a native-domain
reference at a larger reservoir work-grid scale. Reference-error rows therefore
compare a candidate against a higher-budget work grid without allocating a
native-resolution reservoir buffer.
\section{Required Evaluation}

Binary file not shown.

View file

@ -672,7 +672,9 @@ The sequence harness can now prefer a temporal-owner disocclusion mask over the
broad rejection debug view. That mask is derived from the reservoir rejection
codes for previous history outside the frame, closer previous history, and
support loss; it localizes where an eventual high-budget reference score should
look first.
look first. It can also capture a same-time native-domain reference at a larger
reservoir work-grid scale, producing reference-error rows without introducing a
native-resolution intermediate reservoir.
The system has cut the false ``nearest-travel-minus-coverage'' priority owner
and the side-cache temptation.

View file

@ -548,6 +548,15 @@ Work packet:
Smoke run `reservoir-sequence-20260530-000151-*` captured native final plus
disocclusion at 320x180; the disocclusion mask covered 3.1098% of frame
pixels and produced a masked temporal-native delta row.
12. Partial: add same-time higher-work-grid reference scoring. The sequence
capture script accepts `-ReferenceFieldReservoirScale`; reference captures
use native-domain mode at that larger scale and are labeled `reference` in
the manifest. The measurer reports `reference-error-*` rows against matching
ready-frame counts and uses disocclusion masks when present. Smoke run
`reservoir-sequence-20260530-001046-*` compared native scale 0.5 against
reference scale 0.75 at 320x180, ready frame 2, final plus disocclusion.
This is a higher-budget work-grid reference, not a native-resolution escape
hatch.
Required verification:

View file

@ -7,6 +7,7 @@ param(
[ValidateSet("native", "baseline", "both")]
[string]$FieldReservoirMode = "both",
[double]$FieldReservoirScale = 0.5,
[double]$ReferenceFieldReservoirScale = 0.0,
[int[]]$RenderDebugModes = @(0, 13),
[int]$RetainSlots = 4,
[int]$TimeoutSeconds = 120
@ -50,7 +51,10 @@ if ($LASTEXITCODE -ne 0) {
$exePath = Join-Path $slotPath "Aquarium.Engine.exe"
$clientAssembly = Join-Path $slotPath "Aquarium.Fensalir.dll"
$modes = if ($FieldReservoirMode -eq "both") { @("native", "baseline") } else { @($FieldReservoirMode) }
$modes = @(if ($FieldReservoirMode -eq "both") { @("native", "baseline") } else { @($FieldReservoirMode) })
if ($ReferenceFieldReservoirScale -gt 0.0) {
$modes += @("reference")
}
$captures = [System.Collections.Generic.List[object]]::new()
function DebugSuffix {
@ -81,6 +85,7 @@ function DebugSuffix {
foreach ($mode in $modes) {
foreach ($readyFrame in $ReadyFrames) {
foreach ($debugMode in $RenderDebugModes) {
$captureScale = if ($mode -eq "reference") { $ReferenceFieldReservoirScale } else { $FieldReservoirScale }
$suffix = DebugSuffix -Mode $debugMode
$outputPath = Join-Path $OutputDirectory ("reservoir-sequence-$Stamp-$mode-f{0:D4}-$suffix.png" -f $readyFrame)
$arguments = @(
@ -91,7 +96,7 @@ foreach ($mode in $modes) {
"--cache", $cachePath,
"--shader-source", (Join-Path $slotPath "Render\Shaders\D3D12HeightField.hlsl"),
"--capture-frame", $outputPath,
"--field-reservoir-scale", $FieldReservoirScale
"--field-reservoir-scale", $captureScale
)
if ($mode -eq "baseline") {
$arguments += @("--field-reservoir-mode", "baseline")
@ -106,6 +111,7 @@ foreach ($mode in $modes) {
Mode = $mode
ReadyFrames = $readyFrame
RenderDebugMode = $debugMode
ReservoirScale = $captureScale
Suffix = $suffix
Path = $outputPath
})
@ -139,6 +145,7 @@ foreach ($mode in $modes) {
Mode = $mode
ReadyFrames = $readyFrame
RenderDebugMode = $debugMode
ReservoirScale = $captureScale
Suffix = $suffix
Path = $outputPath
})

View file

@ -164,6 +164,9 @@ foreach ($group in $groups) {
$baselineFrames = @($group.Group |
Where-Object { $_.Mode -eq "baseline" } |
Sort-Object { [int]$_.ReadyFrames })
$referenceFrames = @($group.Group |
Where-Object { $_.Mode -eq "reference" } |
Sort-Object { [int]$_.ReadyFrames })
foreach ($native in $nativeFrames) {
$baseline = $baselineFrames | Where-Object { [int]$_.ReadyFrames -eq [int]$native.ReadyFrames } | Select-Object -First 1
@ -179,7 +182,26 @@ foreach ($group in $groups) {
}
}
foreach ($modeFrames in @($nativeFrames, $baselineFrames)) {
foreach ($reference in $referenceFrames) {
foreach ($candidateFrames in @($nativeFrames, $baselineFrames)) {
$candidateFrames = @($candidateFrames)
$candidate = $candidateFrames | Where-Object { [int]$_.ReadyFrames -eq [int]$reference.ReadyFrames } | Select-Object -First 1
if ($null -eq $candidate) {
continue
}
$results.Add((Measure-ImageDelta -APath $candidate.Path -BPath $reference.Path -Label "$suffix reference-error-$($candidate.Mode) f$($candidate.ReadyFrames)"))
if ($suffix -eq "final") {
$candidateMask = Get-PreferredMaskPath -Mode $candidate.Mode -ReadyFrames $candidate.ReadyFrames
$referenceMask = Get-PreferredMaskPath -Mode "reference" -ReadyFrames $reference.ReadyFrames
if (-not [string]::IsNullOrWhiteSpace($candidateMask) -or -not [string]::IsNullOrWhiteSpace($referenceMask)) {
$results.Add((Measure-ImageDelta -APath $candidate.Path -BPath $reference.Path -Label "final masked-reference-error-$($candidate.Mode) f$($candidate.ReadyFrames)" -AMaskPath $candidateMask -BMaskPath $referenceMask))
}
}
}
}
foreach ($modeFrames in @($nativeFrames, $baselineFrames, $referenceFrames)) {
$modeFrames = @($modeFrames)
for ($index = 1; $index -lt $modeFrames.Count; $index++) {
$previous = $modeFrames[$index - 1]
@ -216,7 +238,7 @@ foreach ($result in $results) {
$result.MaxChannelDelta))
}
$lines.Add("")
$lines.Add("These are sequence probes, not a final ghosting score. Mode deltas show native/baseline disagreement at each sampled time. Temporal deltas show frame-to-frame output movement per mode. Masked rows prefer disocclusion-debug captures when present, otherwise rejection-debug captures; a high-budget reference is still needed before claiming quality.")
$lines.Add("These are sequence probes, not a final ghosting score. Mode deltas show native/baseline disagreement at each sampled time. Reference-error rows compare a candidate mode against a higher-work-grid native reference at the same ready-frame count when reference captures are present. Temporal deltas show frame-to-frame output movement per mode. Masked rows prefer disocclusion-debug captures when present, otherwise rejection-debug captures.")
Set-Content -LiteralPath $OutputPath -Value $lines -Encoding UTF8
Write-Host "Reservoir sequence metrics:"

View file

@ -301,3 +301,4 @@
{"timestamp":"2026-05-30T00:25:00+01:00","topic":"reservoir-sequence-probe","lesson":"Reservoir comparison now has a sequence-level probe instead of only single-frame deltas. scripts/capture-reservoir-mode-sequence.ps1 builds one disposable slot and captures native/baseline frames across ready-frame counts, debug modes, and reservoir work-grid scale; scripts/measure-reservoir-sequence.ps1 reports per-frame mode disagreement plus per-mode frame-to-frame temporal deltas. The first 640x360 final-only smoke sequence at reservoir-sequence-20260529-234414-* proved the harness works, but the metric remains a probe until disocclusion/rejection masks or high-budget references turn it into a real ghosting/leakage score.","files":["scripts/capture-reservoir-mode-sequence.ps1","scripts/measure-reservoir-sequence.ps1","docs/perfect-fensalir-machine-roadmap.md","state/scratch.md"],"source":"artifacts/fensalir-captures/reservoir-sequence-20260529-234414-*"}
{"timestamp":"2026-05-30T00:40:00+01:00","topic":"reservoir-rejection-masked-sequence-probe","lesson":"The reservoir sequence harness is now resumable and mask-aware. Existing PNGs are skipped and still recorded into the manifest, which lets interrupted sequence captures continue instead of throwing away completed frames. When rejection-debug captures are present, scripts/measure-reservoir-sequence.ps1 reports masked native/baseline and temporal deltas over the union of rejection-mask pixels. The 320x180 smoke set reservoir-sequence-20260529-235031-* covered about 41% of frame pixels with the rejection mask and produced masked rows, proving the pressure signal works; it is still not a final ghosting score without a high-budget reference or explicit disocclusion mask.","files":["scripts/capture-reservoir-mode-sequence.ps1","scripts/measure-reservoir-sequence.ps1","docs/perfect-fensalir-machine-roadmap.md","state/scratch.md"],"source":"artifacts/fensalir-captures/reservoir-sequence-20260529-235031-*"}
{"timestamp":"2026-05-30T01:05:00+01:00","topic":"reservoir-disocclusion-mask","lesson":"Temporal disocclusion is now exposed by the reservoir owner instead of inferred from broad rejection color. Debug mode 19 maps temporal rejection codes for previous UV loss, previous-closer history, and support loss into a focused disocclusion mask, and the sequence measurer prefers disocclusion masks over rejection masks when both exist. The native-only 320x180 smoke set reservoir-sequence-20260530-000151-* produced a 3.1098% frame mask and a masked temporal-native delta row, proving the mask path is live. The remaining proof gap is a high-budget reference or repeated-sample reference mode so masked deltas can become actual error scores.","files":["src/Aquarium.Engine.Contracts/GraphicsSettings.cs","src/Aquarium.Engine/Render/D3D12Renderer.cs","src/Aquarium.Engine/Render/Shaders/D3D12Post.hlsl","scripts/capture-reservoir-mode-sequence.ps1","scripts/measure-reservoir-sequence.ps1","tests/Aquarium.Engine.Tests/FieldReservoirAbiTests.cs","docs/perfect-fensalir-machine-roadmap.md","state/map.yaml","state/scratch.md"],"source":"artifacts/fensalir-captures/reservoir-sequence-20260530-000151-*"}
{"timestamp":"2026-05-30T01:25:00+01:00","topic":"reservoir-higher-work-grid-reference","lesson":"The sequence harness can now score candidates against a same-time higher-work-grid native-domain reference without violating the no-native-reservoir-buffer rule. capture-reservoir-mode-sequence.ps1 accepts -ReferenceFieldReservoirScale, captures reference rows in native-domain mode at that larger scale, and labels them reference in the manifest. measure-reservoir-sequence.ps1 emits reference-error rows and masked reference-error rows using disocclusion/rejection masks when present. The 320x180 smoke set reservoir-sequence-20260530-001046-* compared native scale 0.5 against reference scale 0.75 at the same ready-frame count; masked reference-error-native covered 3.0468% of frame pixels and changed 42.0368%. The next architectural step is adaptive update/sample budget policy driven by these reference-error pressure signals.","files":["scripts/capture-reservoir-mode-sequence.ps1","scripts/measure-reservoir-sequence.ps1","docs/perfect-fensalir-machine-roadmap.md","state/map.yaml","state/scratch.md"],"source":"artifacts/fensalir-captures/reservoir-sequence-20260530-001046-*"}

View file

@ -158,8 +158,12 @@ next_actions:
temporal-owner disocclusion mask from previous UV loss, previous-closer
history, and support loss; the sequence measurer prefers it over broad
rejection masks, and `reservoir-sequence-20260530-000151-*` proves the
narrower mask path. The next cut is adding a high-budget-reference
leakage/ghosting score plus future exact producer SDF replay.
narrower mask path. The sequence harness can also capture a same-time
native-domain reference at a higher reservoir work-grid scale through
`-ReferenceFieldReservoirScale`; `reservoir-sequence-20260530-001046-*`
proves reference-error rows from scale 0.5 against scale 0.75. The next cut
is using those reference-error rows to drive adaptive sampling/update budget
policy plus future exact producer SDF replay.
- Build the planetary flow organ in staged cuts: provenance manifest, static
relief and reanalysis cube-sphere bake pipeline, stochastic advection
contracts, synthetic runtime slice, ERA5/OSCAR baseline learning, then

View file

@ -45,6 +45,18 @@ than expected travel, and support-overlap loss. The sequence measurer prefers
ready frames 2 and 3, final plus disocclusion debug, produced a mask covering
3.1098% of the frame and a masked temporal-native delta of 46.8867%.
Higher-work-grid reference:
`scripts/capture-reservoir-mode-sequence.ps1` accepts
`-ReferenceFieldReservoirScale`, captures native-domain reference frames at that
larger scale, and labels them `reference` in the manifest.
`scripts/measure-reservoir-sequence.ps1` reports `reference-error-*` rows
against matching ready-frame counts, using disocclusion masks when present.
Smoke `artifacts/fensalir-captures/reservoir-sequence-20260530-001046-*`
compared native scale 0.5 against reference scale 0.75 at 320x180, ready frame
2, final plus disocclusion. Whole-frame reference-error-native changed 53.9240%;
masked reference-error-native covered 3.0468% of frame pixels and changed
42.0368%.
## Hot Lesson
Area ReSTIR confirms that reuse over an area domain is invalid unless the
@ -76,9 +88,9 @@ cap remain intentionally non-authoritative.
Follow `docs/perfect-fensalir-machine-roadmap.md` Phase 1:
1. Add a high-budget reference capture mode or repeated-sample reference path
so the disocclusion-masked sequence metric can score error instead of only
motion/disagreement.
1. Use the same-time higher-work-grid reference rows to drive an adaptive
sampling/update budget policy. Do not add native-resolution reservoir
intermediates.
2. Add exact SDF producer replay only when client SDF distance functions have a
shared replay include or manifest instead of duplicating shader policy in
post.