Files
Faset_Engine/docs/validation/p3-temporal-2026-09-24/README.md
T

7.0 KiB
Raw Blame History

P3 temporal reconstruction: image quality and cost, 2026-09-24

This record covers Faset's first-generation, opt-in TAA and temporal upscaler. The temporal shader source under test is 1a1f69f7e88b3ea69986931dfcf6baf9b43a431c on feat/p3-temporal-integration; the combined integration source revision is 4a3453e. Final CI results are tracked with that integration branch. The physical Vulkan device was an NVIDIA GeForce RTX 2080 Ti with proprietary driver 595.84 on Linux. Both fixtures requested Vulkan validation and every recorded frame reported zero validation errors. These observations do not establish physical Windows GPU behavior or high-end reconstruction quality.

Raw artifacts and method

  • Lossless PNG example and frame-level CSV linking every capture cover the Direct visibility path at 160×120, except the 319×241 resize frame. The sequences are static subpixel wire (16 phases), slow camera pan (16), moving cube (16), unobstructed background (16), opening door (4), camera cut (2), resize (2), and translucent world geometry plus sharp UI (2). Each has Off, jittered current-only TAA, accumulated TAA, current-only Upscale, and accumulated Upscale captures. The current-only controls mark each frame as a camera cut: they retain the same 16-phase jitter and internal extent while rejecting history. Upscale uses render scale 0.67. Off has no jitter.
  • Computed metrics contain every sample, fixed ROI coordinates, steady-state timing distributions and paired image errors. The contact sheet shows representative source frames without artistic retouching. The door edge detail crops phase 3 and enlarges pixels 5× with nearest-neighbor sampling; its second row is the same-jitter unobstructed reference. tools/analyze_temporal_quality.py converts the raw PPM output of faset_render_temporal_acceptance_tests --capture-quality DIR to PNG losslessly and computes the metrics. It needs Python with NumPy and Pillow.
  • 720p raw profile contains 30 measured frames per Off/TAA/Upscale mode after 10 warm-up frames, with rotating mode order. faset_render_temporal_acceptance_tests --profile-720p CSV reproduces this fixed Direct scene: one opaque cube and one thin wire, output 1280×720, Upscale internal 858×483. It was a Linux Debug run with validation requested, not a Release or gameplay frame-rate result. GPU timestamps include submitted work and the synchronous image-to-buffer capture; renderer CPU time includes the wait and host readback.

Reproduction from a configured build:

cmake --build build/linux-debug --target faset_render_temporal_acceptance_tests --parallel 2
ctest --test-dir build/linux-debug --no-tests=error -R '^render_temporal_acceptance$' --output-on-failure
build/linux-debug/faset_render_temporal_acceptance_tests --capture-quality /tmp/faset-p3-quality
python3 tools/analyze_temporal_quality.py --input /tmp/faset-p3-quality \
  --output docs/validation/p3-temporal-2026-09-24 \
  --revision 1a1f69f7e88b3ea69986931dfcf6baf9b43a431c \
  --driver 'NVIDIA proprietary 595.84'
build/linux-debug/faset_render_temporal_acceptance_tests --profile-720p /tmp/faset-p3-720p.csv

Image-quality observations

The static variation measure is the mean absolute RGB difference between consecutive frames over phases 5–15 in the fixed ROI; lower means less frame shimmer, but it says nothing by itself about retained contrast. Wire energy is the mean summed RGB value over phases 4–15 in that ROI, and peak is the mean brightest channel per frame. All color figures use 8-bit output values.

160×120 fixture Current-only Accumulated Change
Static wire RGB frame delta, TAA 0.388 0.360 −7.2%
Static wire RGB frame delta, Upscale 0.439 0.412 −6.1%
Static wire ROI RGB energy, TAA 8,234 8,275 +0.5%
Static wire ROI RGB energy, Upscale 7,429 7,485 +0.8%
Static wire mean peak, TAA 179 167 −6.7%
Static wire mean peak, Upscale 179 161 −9.9%
Slow pan RGB frame delta, TAA 0.399 0.398 −0.2%
Moving cube RGB frame delta, TAA 1.930 1.917 includes real motion

The separate static cube-edge acceptance ROI fell from 0.832 jittered current-only to about 0.739 under TAA. Its exact percentage does not transfer to the thin wire. The original resolver achieved a much larger apparent wire variance reduction by dimming the wire about 15%; the final depth-aware resolver retains total wire energy within 1% in this fixture. It still reduces peak contrast and offers little gain during a slow pan. Upscale is not equivalent to a full-resolution reference for thin detail. Off has zero static frame variation because it has no jitter; that does not make its edges alias-free.

The door's newly exposed center matched current-only within one RGB value on the first open frame. No red pixels from the old door appeared in the tested old-door ROI. Against a phase-aligned, unobstructed temporal background, the 30×35 edge ROI had 121 TAA and 176 Upscale pixels with any channel difference above 8 on the first open frame, then 17 and 28 respectively on the next frame. The enlarged comparison shows a one-pixel dark green/top-bottom edge difference, with no displaced red silhouette. It is a bounded residual, not a claim of zero halo. The explicit cut output matched current-only exactly in its ROI; resize and UI pixels matched their controls. The existing Direct, GPU-frustum and GPU-occlusion tests separately cover moving reveal, cut, reset, resize, shader reload and Vulkan validation.

1280×720 cost on this sparse scene

All values below are milliseconds except allocation. p95 is linearly interpolated at rank (n − 1) × 0.95 among the 30 measured frames.

Mode Full GPU p50 / p95 Resolve p50 / p95 Composite p50 / p95 Renderer CPU p50 / p95 Live Vulkan allocation
Off 0.560 / 0.580 — — 2.256 / 2.527 43.02 MiB
TAA 0.671 / 0.715 0.081 / 0.101 0.026 / 0.028 2.545 / 2.927 76.77 MiB (+33.75)
Upscale 0.67 0.672 / 0.703 0.079 / 0.099 0.025 / 0.027 2.630 / 3.448 68.52 MiB (+25.50)

Main raster p50 was only 0.015–0.018 ms because this fixture is sparse; at this resolution and content, lower scene resolution did not pay for the output resolve/composite. The depth-aware 2×2 history sampling is included in these costs. A dense game scene, Release build, another driver or display path may have different results. The renderer's synchronous full-frame capture makes these CPU and GPU totals unsuitable as unqualified gameplay FPS predictions.

TAA and Upscale therefore remain opt-in. Compare Off and a jittered current-only capture at the game's target resolution, watch thin-object peak contrast and newly revealed edges, and profile the whole frame. These fixtures are a bounded regression check, not parity with Unreal TSR, FSR or DLSS.