engine: presets E/F AutoExposure, the game's exposure fields and NGX's jitter order

NGX's CNN takes this frame's exposure scale and pre-exposure in both translated kernels (input words 244-256,
reconstruction 592-604) and the previous frame's exposure as the input kernel's texture 352
(cuda_copy_exposure_kernel's half surface). The runtime left all of it at the model dump's values, so HDR games
with a pre-exposure other than 1 or a changing exposure measured 18-39 dB against NGX; now 54-64 dB.

AutoExposure (flag 0x40), which the shim refused for E/F: NGX runs K's log-mean measurement on M's grid every
second frame. exposure_k.comp MODE 3 writes it to the 1x1 image the kernels sample; sr/exposure_copy_e.comp
keeps the previous exposure. 60-66 dB over eleven frames at 960x540 -> 1920x1080, pre-exposure 19-21.

The reconstruction's jitter words 544-556 are (y, x); the runtime wrote (-x, -y), which matches only the
harness's first three jitters, so every later frame of a game's jitter sequence measured 26-28 dB. Twelve-frame
captures now measure 60-66 dB HDR and LDR; on the Steam Deck through the shim 51-67 dB against ZLUDA.

The D3D12 harness takes per-frame lists in D4R_HARNESS_PRE_EXPOSURE / _EXPOSURE_SCALE; d4r-e takes the
AutoExposure flag, per-frame exposure values and the game's exposure texture. E/F models need rebuilding
(the install's model cache key changes); older models keep the old binding and refuse AutoExposure.
This commit is contained in:
woutermans committed 2026-10-19 02:42:48 +02:00
1 parent 916b0d1467
commit 3f66081add
12 files changed
+249 -47

No files matched your search

+10 -3
View File
@@ -5,6 +5,8 @@
// [0, 10000], 0.005).
// MODE 2 (Ray Reconstruction, the same cuda_dldn_engine_* kernels): MODE 1's measurement, written to two 1x1
// images instead of stage parameter blocks.
// MODE 3 (presets E and F, NGX's cuda_luma_convert / auto_exposure_copy kernels): MODE 0's measurement, written to
// the 1x1 image the input and reconstruction kernels sample as the exposure.
// Both modes measure the same grid: the colour rectangle sampled at half the output size with an
// endpoint-aligned nearest-texel lookup (the reference's cuda_luma_convert_kernel; the grid equals the
// rectangle itself only when the render size is half the output size).
@@ -17,6 +19,7 @@
#ifndef MODE
#define MODE 0
#endif
#define LOG_MEAN (MODE == 0 || MODE == 3) // key * exp(-mean of the log luma)
layout(local_size_x = 64) in;
layout(set = 0, binding = 0, std430) readonly buffer Params
{
@@ -63,7 +66,7 @@ void main()
const vec3 c = texelFetch(colorTex, p.origin + sampleAt, 0).rgb;
// the reference accumulates the logarithm of the exposed luma (cuda_luma_convert_kernel). The
// constant is applied to Y = fma(b, 0.25, fma(r, 0.25, g * 0.5)); MODE 1 keeps its own expression.
#if MODE == 0
#if LOG_MEAN
precise const float luma = p.scale * fma(c.b, 0.25, fma(c.r, 0.25, 0.5 * c.g));
// log(luma + 1e-6) as lg2(x) * ln2, and a non-finite or non-positive luma contributes nothing
if (luma > 0.0 && !isinf(luma))
@@ -93,6 +96,8 @@ layout(set = 0, binding = 5) uniform sampler2D gameExposure;
layout(set = 0, binding = 3, std430) buffer InputParams { float w[]; } inp; // enc0_m: expo, expoPrev at words 25, 26
layout(set = 0, binding = 4, std430) buffer PostParams { float w[]; } post; // post_m: words 14, 15
layout(set = 0, binding = 5, std430) buffer DownParams { float w[]; } down; // down_m: word 12
#elif MODE == 3
layout(set = 0, binding = 3, r16f) uniform writeonly image2D exposureOut;
#else
// MODE 2 (Ray Reconstruction): the denoiser's stages sample the exposure as two 1x1 textures, this frame's and
// the previous frame's (zero on the first frame and after a reset)
@@ -124,7 +129,7 @@ void main()
{
float mean = 0.0;
for (uint i = 0u; i < 64u; ++i) mean += part[i];
#if MODE == 0
#if LOG_MEAN
// auto_exposure_copy_kernel: key * exp2(-log2e * sum), written to a half surface. The formatted
// store truncates toward zero, as in MODE 1 (measured on the captured K exposure: the store lands
// one binary16 code below the correctly rounded value).
@@ -136,7 +141,7 @@ void main()
float measured = truncatedHalf(full);
#endif
if (!(measured > 0.0) || isinf(measured)) measured = 1.0;
#if MODE == 0
#if LOG_MEAN
previous = (p.flags & 2u) != 0u ? measured : current;
#else
previous = (p.flags & 2u) != 0u ? 0.0 : current; // 0: no earlier exposure (the stages read it as 1)
@@ -154,6 +159,8 @@ void main()
inp.w[25] = current; inp.w[26] = previous;
post.w[14] = current; post.w[15] = previous;
down.w[12] = current;
#elif MODE == 3
imageStore(exposureOut, ivec2(0), vec4(current));
#else
imageStore(exposureOut, ivec2(0), vec4(current));
imageStore(previousOut, ivec2(0), vec4(previous));