diff --git a/docs/papers/fensalir-native-domain-reservoirs.pdf b/docs/papers/fensalir-native-domain-reservoirs.pdf index dbf36bb..5bb08d7 100644 Binary files a/docs/papers/fensalir-native-domain-reservoirs.pdf and b/docs/papers/fensalir-native-domain-reservoirs.pdf differ diff --git a/docs/papers/fensalir-native-domain-reservoirs.tex b/docs/papers/fensalir-native-domain-reservoirs.tex index 5db8362..a742c62 100644 --- a/docs/papers/fensalir-native-domain-reservoirs.tex +++ b/docs/papers/fensalir-native-domain-reservoirs.tex @@ -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} diff --git a/docs/papers/fensalir-pipeline.pdf b/docs/papers/fensalir-pipeline.pdf index 982ea94..5213740 100644 Binary files a/docs/papers/fensalir-pipeline.pdf and b/docs/papers/fensalir-pipeline.pdf differ diff --git a/docs/papers/fensalir-pipeline.tex b/docs/papers/fensalir-pipeline.tex index 7138865..e7e03d4 100644 --- a/docs/papers/fensalir-pipeline.tex +++ b/docs/papers/fensalir-pipeline.tex @@ -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. diff --git a/docs/perfect-fensalir-machine-roadmap.md b/docs/perfect-fensalir-machine-roadmap.md index 5cd30e4..3005d7e 100644 --- a/docs/perfect-fensalir-machine-roadmap.md +++ b/docs/perfect-fensalir-machine-roadmap.md @@ -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: diff --git a/scripts/capture-reservoir-mode-sequence.ps1 b/scripts/capture-reservoir-mode-sequence.ps1 index be585a6..a546afe 100644 --- a/scripts/capture-reservoir-mode-sequence.ps1 +++ b/scripts/capture-reservoir-mode-sequence.ps1 @@ -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 }) diff --git a/scripts/measure-reservoir-sequence.ps1 b/scripts/measure-reservoir-sequence.ps1 index bb6c85e..2a15897 100644 --- a/scripts/measure-reservoir-sequence.ps1 +++ b/scripts/measure-reservoir-sequence.ps1 @@ -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:" diff --git a/state/evidence.jsonl b/state/evidence.jsonl index 83287eb..1610611 100644 --- a/state/evidence.jsonl +++ b/state/evidence.jsonl @@ -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-*"} diff --git a/state/map.yaml b/state/map.yaml index b05b0e6..f2d4d0f 100644 --- a/state/map.yaml +++ b/state/map.yaml @@ -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 diff --git a/state/scratch.md b/state/scratch.md index 637ec10..fb25531 100644 --- a/state/scratch.md +++ b/state/scratch.md @@ -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.