Files
d4r/engine/down_m.comp
T

83 lines
4.8 KiB
Plaintext

#version 460
// d4r engine, preset M: the last stage. The reconstruction works at three times the render resolution; this
// resamples its packed result (11-11-10 bits, range-compressed) to the output resolution, 3 -> 2, with a separable
// five-tap filter whose weights depend on the output pixel's parity, limits each pass to the range of its taps
// (no ringing), and undoes the range compression and the exposure.
#extension GL_EXT_shader_explicit_arithmetic_types : require
#extension GL_GOOGLE_include_directive : require
#include "output_store.glsl"
// LDR (NGX's *_ldr_* kernel): the packed image is the display-referred colour itself.
#ifndef LDR
#define LDR 0
#endif
// D4RM0003: the runtime dispatches 8x64 output pixels per workgroup.
#define BLOCK_X 8
#define BLOCK_Y 64
#define TILE_W (3 * BLOCK_X / 2 + 4)
#define TILE_H (3 * BLOCK_Y / 2 + 4)
layout(local_size_x = BLOCK_X, local_size_y = BLOCK_Y) in;
layout(set = 0, binding = 0, std430) readonly buffer Params
{
ivec2 srcSize; // of the packed image
ivec2 outOrigin;
ivec2 colOrigin, colSize;
vec2 out2rnd, rndOffs; // render texel of an output pixel centre: floor(centre * out2rnd + rndOffs)
float expo;
} p;
layout(set = 0, binding = 1) uniform sampler2D colorTex; // render resolution; its alpha is passed through
layout(set = 0, binding = 2, FINAL_OUTPUT_FORMAT) uniform writeonly image2D outImg;
layout(set = 0, binding = 3, std430) readonly buffer Src { uint w[]; } src;
shared f16vec4 tile[TILE_W * TILE_H]; // source texels around the block
shared f16vec4 rows[TILE_H * BLOCK_X]; // after the horizontal pass
// weights of taps -2 .. +2 for an even output coordinate; an odd one uses them mirrored
// as binary16 bit patterns: NGX rounds its f32 constants to the nearest binary16 (cvt.rn), while glslang folds
// float16_t(<literal>) toward zero (W0 became -0.041321 instead of -0.041351: 6% of the captured frame's outputs
// one code or more off). The values are -0.041347627, 0.087289415, 0.6202144, 0.37212864, -0.038284853.
#define W0 uint16BitsToFloat16(uint16_t(0xA94Bu))
#define W1 uint16BitsToFloat16(uint16_t(0x2D96u))
#define W2 uint16BitsToFloat16(uint16_t(0x38F6u))
#define W3 uint16BitsToFloat16(uint16_t(0x35F4u))
#define W4 uint16BitsToFloat16(uint16_t(0xA8E7u))
f16vec3 filter5(f16vec3 a, f16vec3 b, f16vec3 c, f16vec3 d, f16vec3 e, bool odd)
{
const f16vec3 w0 = f16vec3(odd ? W4 : W0), w1 = f16vec3(odd ? W3 : W1), w3 = f16vec3(odd ? W1 : W3), w4 = f16vec3(odd ? W0 : W4);
f16vec3 s = w0 * a;
s = fma(w1, b, s); s = fma(f16vec3(W2), c, s); s = fma(w3, d, s); s = fma(w4, e, s);
return min(max(s, min(min(min(a, b), min(c, d)), e)), max(max(max(a, b), max(c, d)), e));
}
void main()
{
const ivec2 lid = ivec2(gl_LocalInvocationID.xy), pix = ivec2(gl_GlobalInvocationID.xy);
const ivec2 base = ivec2(3 * BLOCK_X / 2, 3 * BLOCK_Y / 2) * ivec2(gl_WorkGroupID.xy) - 2;
for (int i = lid.y * BLOCK_X + lid.x; i < TILE_W * TILE_H; i += BLOCK_X * BLOCK_Y)
{
const ivec2 at = clamp(base + ivec2(i % TILE_W, i / TILE_W), ivec2(0), p.srcSize - 1);
const uint w = src.w[at.y * p.srcSize.x + at.x];
#if LDR
tile[i] = f16vec4(vec4(float(w & 2047u) * (1.0 / 2047.0), float((w >> 11) & 2047u) * (1.0 / 2047.0), float(w >> 22) * (1.0 / 1023.0), 0.0));
#else
tile[i] = f16vec4(vec4(float(w & 2047u) * 0.0011968734906986356, float((w >> 11) & 2047u) * 0.0011968734906986356, float(w >> 22) * 0.002394916955381632, 0.0));
#endif
}
memoryBarrierShared(); barrier();
const ivec2 c = ivec2(floor((vec2(pix) + 0.5) * 1.5)) - base;
for (int y = lid.y; y < TILE_H; y += BLOCK_Y)
{
const int o = y * TILE_W + c.x;
rows[y * BLOCK_X + lid.x].rgb = filter5(tile[o - 2].rgb, tile[o - 1].rgb, tile[o].rgb, tile[o + 1].rgb, tile[o + 2].rgb, (pix.x & 1) != 0);
}
memoryBarrierShared(); barrier();
// After both shared barriers: borrowed images may extend beyond the feature's true output rectangle.
// M accepts even output dimensions and srcSize = outputSize/2*3, so this bounds check is exact.
if (3 * pix.x >= 2 * p.srcSize.x || 3 * pix.y >= 2 * p.srcSize.y) return;
const int o = c.y * BLOCK_X + lid.x;
#if LDR
const vec3 lin = vec3(filter5(rows[o - 2 * BLOCK_X].rgb, rows[o - BLOCK_X].rgb, rows[o].rgb, rows[o + BLOCK_X].rgb, rows[o + 2 * BLOCK_X].rgb, (pix.y & 1) != 0));
#else
const vec3 v = clamp(vec3(filter5(rows[o - 2 * BLOCK_X].rgb, rows[o - BLOCK_X].rgb, rows[o].rgb, rows[o + BLOCK_X].rgb, rows[o + 2 * BLOCK_X].rgb, (pix.y & 1) != 0)), 1e-4, 3.0);
const vec3 lin = (exp2(v * (1.0 / 0.36) * log2(10.0)) - 1.0) * (1.0 / 100.0) * (1.0 / (p.expo == 0.0 ? 1.0 : p.expo));
#endif
const ivec2 r = clamp(ivec2(floor((vec2(pix) + 0.5) * p.out2rnd + p.rndOffs)), ivec2(0), p.colSize - 1) + p.colOrigin;
imageStore(outImg, p.outOrigin + pix, finalOutputValue(vec4(lin, texelFetch(colorTex, r, 0).a)));
}