#version 460 // d4r engine, preset M: the stage after the network. Expands the last decoder's tokens (64 channels, FP8) to 17 // values per render pixel (one matrix product per 4x4-token block) and turns them into the reconstruction's // per-pixel inputs: a recurrent four-channel feature blended with its reprojected history, three gate values, // three softmax weights and a 2x2 covariance. See M_NOTES.md. #extension GL_KHR_cooperative_matrix : require #extension GL_KHR_memory_scope_semantics : require #extension GL_KHR_shader_subgroup_basic : require #extension GL_EXT_shader_explicit_arithmetic_types : require #extension GL_EXT_float_e4m3 : require #extension GL_GOOGLE_include_directive : require #include "round_half.glsl" #include "normalized_store.glsl" #include "activation_m.glsl" layout(local_size_x = 32) in; // one wave: 16 tokens = 8x8 render pixels, two pixels per lane layout(set = 0, binding = 0, std430) readonly buffer Params { ivec2 size; // render size int tokW; uint reset; vec2 rndOffs; // jitter vec2 rnd2hi; // render pixels -> pixels of the 3x image (the motion vectors' unit) vec2 hiSize; vec2 histOffs; // added to the history uv, in render pixels float sigma, rho; vec2 renderInv; // host-rounded texture coordinates, distinct from GPU rcp.approx offsets } p; layout(set = 0, binding = 1) uniform sampler2D motionTex; // texelFetch layout(set = 0, binding = 2) uniform sampler2D featTex; // the previous feature, bilinear layout(set = 0, binding = 3, rgba8_snorm) uniform writeonly image2D featOut; layout(set = 0, binding = 4, rgba8) uniform writeonly image2D gateOut; layout(set = 0, binding = 5, rgba8) uniform writeonly image2D mixOut; layout(set = 0, binding = 6, rgba16f) uniform writeonly image2D covOut; layout(set = 0, binding = 7, std430) readonly buffer Tokens { floate4m3_t a[]; } tok; // [tokH][tokW][64] layout(set = 0, binding = 8, std430) readonly buffer W8 { floate4m3_t w[]; } w8; // [64][80]: 20 columns per sub-position layout(set = 0, binding = 9, std430) readonly buffer W16 { f16vec4 w[]; } w16; // bias, 16 equal rows of 80 #define ACC coopmat #define HACC coopmat #define A8 coopmat #define B8 coopmat #define RM gl_CooperativeMatrixLayoutRowMajor shared floate4m3_t TK[16 * 72]; shared f16vec4 EO[16 * 88 / 4]; float16_t q8(float16_t v) { return float16_t(floate4m3_t(clamp(v, float16_t(-448.0), float16_t(448.0)))); } f16vec4 q8(f16vec4 v) { return f16vec4(q8(v.x), q8(v.y), q8(v.z), q8(v.w)); } // NVIDIA's expansion GEMM is chained m16n8k32 mma with an f16 accumulator: the bias is the initial C and every // 32 products are summed with one f16 rounding (swin_model.py mma_chain; the same k32 step the native kernels' // SWIN_EXACT build implements). K32_ROUND reproduces that: after each pair of k16 cooperative products the // accumulator is rounded to f16 through the shared EO region (ROUND_HALF_STORE). #ifndef K32_ROUND #define K32_ROUND 1 #endif #if K32_ROUND #define R32(m, column) ROUND_HALF_STORE(m, EO, (column) * 4u, 22u) #else #define R32(m, column) #endif #ifndef PLANES #define PLANES 0 #endif void main() { const uint lane = gl_LocalInvocationID.x, row = lane & 15u, hf = lane >> 4; const ivec2 t0 = 4 * ivec2(gl_WorkGroupID.xy); #if PLANES // NGX's layout, two planes of 32 channels (the HIP network's output); the dispatch covers the token grid const uint src = (hf * 16u * gl_NumWorkGroups.x * gl_NumWorkGroups.y + uint((t0.y + int(row >> 2)) * p.tokW + t0.x + int(row & 3u))) * 32u; #else const uint src = uint((t0.y + int(row >> 2)) * p.tokW + t0.x + int(row & 3u)) * 64u + hf * 32u; #endif for (uint c = 0u; c < 32u; ++c) TK[row * 72u + hf * 32u + c] = tok.a[src + c]; memoryBarrierShared(); barrier(); // Share each activation tile across three output tiles, then the remaining two. // Five live accumulators cost more registers than the saved loads on gfx1201. { HACC b0; coopMatLoad(b0, w16.w, 0u, 20u, RM); ACC a0 = ACC(b0); HACC b1; coopMatLoad(b1, w16.w, 4u, 20u, RM); ACC a1 = ACC(b1); HACC b2; coopMatLoad(b2, w16.w, 8u, 20u, RM); ACC a2 = ACC(b2); for (uint kt = 0u; kt < 4u; ++kt) { A8 a; coopMatLoad(a, TK, kt * 16u, 72u, RM); B8 w0; coopMatLoad(w0, w8.w, kt * 16u * 80u, 80u, RM); a0 = coopMatMulAdd(a, w0, a0); B8 w1; coopMatLoad(w1, w8.w, kt * 16u * 80u + 16u, 80u, RM); a1 = coopMatMulAdd(a, w1, a1); B8 w2; coopMatLoad(w2, w8.w, kt * 16u * 80u + 32u, 80u, RM); a2 = coopMatMulAdd(a, w2, a2); if ((kt & 1u) != 0u) { R32(a0, 0u); R32(a1, 1u); R32(a2, 2u); } } coopMatStore(HACC(a0), EO, 0u, 22u, RM); coopMatStore(HACC(a1), EO, 4u, 22u, RM); coopMatStore(HACC(a2), EO, 8u, 22u, RM); } { HACC b0; coopMatLoad(b0, w16.w, 12u, 20u, RM); ACC a0 = ACC(b0); HACC b1; coopMatLoad(b1, w16.w, 16u, 20u, RM); ACC a1 = ACC(b1); for (uint kt = 0u; kt < 4u; ++kt) { A8 a; coopMatLoad(a, TK, kt * 16u, 72u, RM); B8 w0; coopMatLoad(w0, w8.w, kt * 16u * 80u + 48u, 80u, RM); a0 = coopMatMulAdd(a, w0, a0); B8 w1; coopMatLoad(w1, w8.w, kt * 16u * 80u + 64u, 80u, RM); a1 = coopMatMulAdd(a, w1, a1); if ((kt & 1u) != 0u) { R32(a0, 3u); R32(a1, 4u); } } coopMatStore(HACC(a0), EO, 12u, 22u, RM); coopMatStore(HACC(a1), EO, 16u, 22u, RM); } memoryBarrierShared(); barrier(); for (uint pi = lane; pi < 64u; pi += 32u) { const uint px = pi & 7u, py = pi >> 3; const ivec2 at = 2 * t0 + ivec2(px, py); if (at.x >= p.size.x || at.y >= p.size.y) continue; const uint e = (((py >> 1) * 4u + (px >> 1)) * 88u + ((py & 1u) * 2u + (px & 1u)) * 20u) / 4u; const f16vec4 v0 = q8(EO[e]), v1 = q8(EO[e + 1u]), v2 = q8(EO[e + 2u]), v3 = q8(EO[e + 3u]), v4 = q8(EO[e + 4u]); // values: 0, 1, 10 gates; 2..5 feature; 6..9 its blend; 11..13 mix; 14, 15, 16 covariance imageStore(gateOut, at, unorm8Value(vec4(sig16(v0.x), sig16(v0.y), sig16(v2.z), 0.0))); const vec3 m = vec3(v2.w, v3.x, v3.y), ex = exp2((m - max(max(m.x, m.y), m.z)) * 1.442695); imageStore(mixOut, at, unorm8Value(vec4(ex * (1.0 / (ex.z + (ex.x + ex.y))), 0.0))); const float a = p.sigma * float(sig16(v3.z)); const float b = p.sigma * float(sig16(v3.w)); const float t = float(v4.x) * p.rho, s = t * t, m_ = abs(t); const float big = m_ >= 9.0109138 ? 1.0 : fma(1.0 / (exp2(m_ * 2.88539) + 1.0), -2.0, 1.0); const float rho = m_ >= 0.6 ? (t < 0.0 ? -big : big) : fma(fma(fma(fma(fma(0.01573968306183815, s, -0.05230396240949631), s, 0.13315297663211823), s, -0.33332768082618713), s, 0.0), t, t); imageStore(covOut, at, vec4(a * a, b * b, a * b * rho, 0.0)); precise vec2 f = fma(vec2(at) + 0.5 - p.rndOffs, p.rnd2hi, texelFetch(motionTex, at, 0).xy); f16vec4 hist = f16vec4(0.0); if (p.reset == 0u && all(greaterThanEqual(f, vec2(0.0))) && all(lessThanEqual(f, p.hiSize))) { precise vec2 normalized = fma(f, 1.0 / p.hiSize, p.histOffs * (1.0 / vec2(p.size))); precise vec2 texels = normalized * vec2(p.size); if (any(notEqual(textureSize(featTex, 0), p.size))) texels = clamp(texels, vec2(0.5), vec2(p.size) - 0.5); precise vec2 uv = texels * p.renderInv; hist = f16vec4(roundHalfValue(textureLod(featTex, uv, 0.0))); } // the recurrent feature: gated mix of its reprojected history and the new value. The reference's PTX // computes it with four separate f16 operations (sub, mul, mul, add: // build/gfx1201-perf/ptx/dlss-0162-00.ptx:1416-1447), so `precise` keeps the compiler from fusing a // product into an fma and dropping a rounding. const f16vec4 g = gate16(f16vec4(v1.z, v1.w, v2.x, v2.y)); precise f16vec4 blend = (f16vec4(1.0) - g) * hist + g * tan16(f16vec4(v0.z, v0.w, v1.x, v1.y)); imageStore(featOut, at, snorm8Value(vec4(blend))); } }