feat: add d4r inference engines and Windows parity tooling

Add native K/M/RR inference engines, HIP kernels, replay and validation
harnesses, and Windows DLSS setup and runtime support. Include GPU
profiling tools, build and packaging scripts, platform integration
patches, and implementation documentation.
This commit is contained in:
ayo committed 2026-10-16 08:54:47 -04:00
1 parent 25493b3198
commit 7c5d3c9157
430 files changed
+76191

No files matched your search

+162
View File
@@ -0,0 +1,162 @@
#version 460
// d4r engine: automatic exposure, as NGX computes it (luma convert, two reduce-sum passes, auto-exposure copy).
// MODE 0 (preset K): exposure = key * exp(-mean of the log luma).
// MODE 1 (preset M, the cuda_dldn_engine_* kernels): exposure = key / max(mean of the luma clamped to
// [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.
// 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).
// The result is kept as a half float, through the reference's truncating formatted surface store.
// PASS 0: one workgroup sums the mapped luma over a 32x32-pixel block of the measured grid.
// PASS 1: one workgroup adds the block sums, advances the exposure state and writes the exposure fields of the
// input and output stages' parameter blocks. It also runs on frames that do not measure.
// With flag 4 (MODE 0) the game's 1x1 exposure texture replaces the measurement; nothing is measured then.
#extension GL_EXT_shader_explicit_arithmetic_types : require
#ifndef MODE
#define MODE 0
#endif
layout(local_size_x = 64) in;
layout(set = 0, binding = 0, std430) readonly buffer Params
{
ivec2 origin, size; // rectangle of the colour texture; the grid maps onto it
float invCount; // 1 / (sampleSize.x * sampleSize.y)
float scale; // applied to the luma before the logarithm
float key; // exposure * geometric mean
uint flags; // 1: measure this frame; 2: reset (previous exposure = current); 4: game exposure (MODE 0)
uint blocks, blocksX;
// The measured grid: the reference's cuda_luma_convert_kernel samples the colour rectangle at half the
// output size with an endpoint-aligned nearest-texel lookup, not at every render texel. size is the
// source rectangle; invCount is 1 / (sampleSize.x * sampleSize.y) and the workgroups cover the grid.
ivec2 sampleSize;
// The game's exposure scale and 1 / pre-exposure (MODE 0). NGX keeps the measured exposure unscaled and
// applies both when it fills the stages' parameters, in this order: (1 / pre) * (scale * exposure).
float gameScale, invPre;
} p;
shared float part[64];
// The half surface the reference's auto_exposure_copy_kernel writes truncates toward zero. An explicit
// float16_t cast rounds to nearest instead and lands on the next code whenever the exact value sits below
// the midpoint of its binary16 interval - on ordinary frames the measured exposure, so emulate the store.
float truncatedHalf(float v)
{
uint bits = packHalf2x16(vec2(v, 0.0)) & 65535u;
if (unpackHalf2x16(bits).x > v) --bits;
return unpackHalf2x16(bits).x;
}
#if PASS == 0
layout(set = 0, binding = 1) uniform sampler2D colorTex;
layout(set = 0, binding = 2, std430) writeonly buffer Sums { float v[]; } sums;
void main()
{
const uint t = gl_LocalInvocationID.x;
const ivec2 base = ivec2(gl_WorkGroupID.xy) * 32 + ivec2(t & 7u, t >> 3) * 4;
const ivec2 grid = p.sampleSize;
float sum = 0.0;
for (int y = 0; y < 4; ++y)
for (int x = 0; x < 4; ++x)
{
const ivec2 at = base + ivec2(x, y);
if (at.x >= grid.x || at.y >= grid.y) continue;
// endpoint-aligned nearest-texel lookup from the grid to the colour rectangle
const ivec2 sampleAt = ivec2(floor(fma(vec2(at) / vec2(grid - 1), vec2(p.size - 1), vec2(0.5))));
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
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))
sum += (log2(luma + 1e-6) * uintBitsToFloat(0x3F317218u)) * p.invCount;
#else
const float luma = p.scale * (0.25 * c.r + 0.5 * c.g + 0.25 * c.b);
sum += p.invCount * min(max(luma, 0.0), 10000.0);
#endif
}
part[t] = sum;
barrier();
if (t == 0u)
{
float total = 0.0;
for (uint i = 0u; i < 64u; ++i) total += part[i];
sums.v[gl_WorkGroupID.y * p.blocksX + gl_WorkGroupID.x] = total;
}
}
#else
layout(set = 0, binding = 1, std430) readonly buffer Sums { float v[]; } sums;
layout(set = 0, binding = 2, std430) buffer State { float current, previous; } state;
#if MODE == 0
layout(set = 0, binding = 3, std430) buffer InputParams { float w[]; } inp; // expo, expoIn, expoRatio at words 28..30
layout(set = 0, binding = 4, std430) buffer OutputParams { float w[]; } outp; // expo, expoRatio at words 37, 38
layout(set = 0, binding = 5) uniform sampler2D gameExposure; // the game's 1x1 exposure (flag 4)
#elif MODE == 1
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
#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)
layout(set = 0, binding = 3, r16f) uniform writeonly image2D exposureOut;
layout(set = 0, binding = 4, r16f) uniform writeonly image2D previousOut;
#endif
void main()
{
const uint t = gl_LocalInvocationID.x;
float sum = 0.0;
if ((p.flags & 1u) != 0u)
for (uint i = t; i < p.blocks; i += 64u) sum += sums.v[i];
part[t] = sum;
barrier();
if (t != 0u) return;
float current = state.current, previous = current;
#if MODE == 0
if ((p.flags & 4u) != 0u)
{
float value = texelFetch(gameExposure, ivec2(0), 0).r;
if (value == 0.0) value = 1.0; // NGX's texture read maps a zero exposure to 1
// NGX copies the exposure into a half surface with the same truncating store as the measured
// one, and the next frame's ratio uses that half value; a reset carries the live f32 value.
previous = (p.flags & 2u) != 0u ? value : truncatedHalf(current);
current = value;
}
#endif
if ((p.flags & 1u) != 0u)
{
float mean = 0.0;
for (uint i = 0u; i < 64u; ++i) mean += part[i];
#if MODE == 0
// 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).
float measured = truncatedHalf(p.key * exp2(uintBitsToFloat(0xBFB8AA3Bu) * mean));
#else
// The reference's formatted half surface store truncates positive exposure; an explicit f16 cast
// rounds to nearest instead and moves FP8 embedding codes across thresholds.
const float full = p.key / max(mean, 0.005);
float measured = truncatedHalf(full);
#endif
if (!(measured > 0.0) || isinf(measured)) measured = 1.0;
#if MODE == 0
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)
#endif
current = measured;
}
state.current = current; state.previous = previous;
#if MODE == 0
precise const float scaled = p.gameScale * current, scaledPrevious = p.gameScale * previous;
precise const float exposure = p.invPre * scaled;
precise const float ratio = scaled / scaledPrevious;
inp.w[28] = exposure; inp.w[29] = 1.0; inp.w[30] = ratio;
outp.w[37] = exposure; outp.w[38] = ratio;
#elif MODE == 1
inp.w[25] = current; inp.w[26] = previous;
post.w[14] = current; post.w[15] = previous;
down.w[12] = current;
#else
imageStore(exposureOut, ivec2(0), vec4(current));
imageStore(previousOut, ivec2(0), vec4(previous));
#endif
}
#endif