68 lines
3.5 KiB
GLSL
68 lines
3.5 KiB
GLSL
#version 450 core
|
||
|
||
// Backdrop downsample fragment shader.
|
||
// Reads source_texture (full-resolution snapshot of pre-bracket framebuffer contents) and
|
||
// writes a downsampled copy at factor 1, 2, or 4. The output is the working texture (sized
|
||
// at full swapchain resolution); larger factors only fill a sub-rect of it via the CPU-set
|
||
// viewport. See backdrop.odin for the factor selection table (Flutter-style).
|
||
//
|
||
// Shader paths by factor:
|
||
//
|
||
// factor=1: identity copy. One bilinear tap aligned to the source pixel center. Useful
|
||
// when sigma is small enough that any downsample round-trip would visibly soften
|
||
// the output (Flutter does this for sigma_phys ≤ 4).
|
||
//
|
||
// factor=2: each output covers a 2×2 source block. Single bilinear tap at the shared
|
||
// corner reads all 4 source pixels with 0.25 weight.
|
||
//
|
||
// factor=4: each output covers a 4×4 source block. We use 4 bilinear taps, each at the
|
||
// shared corner of a 2×2 sub-block. Each tap reads 4 source pixels uniformly;
|
||
// combined, the 4 taps sample 16 source pixels arranged uniformly across the
|
||
// block (full coverage at factor=4). The factor>=4 path is structured so the
|
||
// same shader code would extend to factor=8 (16 pixels of 64) or factor=16 (16
|
||
// of 256) if the CPU-side cap is ever raised, though the current cap is 4.
|
||
//
|
||
// The viewport+scissor are set by the CPU to limit output to the layer's work region in
|
||
// working-texture coords (work_region_phys / factor), clamped to the texture bounds.
|
||
|
||
layout(set = 3, binding = 0) uniform Uniforms {
|
||
vec2 inv_source_size; // 1.0 / source_texture pixel dimensions
|
||
uint downsample_factor; // 1, 2, 4, 8, or 16
|
||
uint _pad0;
|
||
};
|
||
|
||
layout(set = 2, binding = 0) uniform sampler2D source_tex;
|
||
|
||
layout(location = 0) out vec4 out_color;
|
||
|
||
void main() {
|
||
// Output pixel index (i): gl_FragCoord.xy - 0.5. Source-pixel block top-left for this
|
||
// output: i * factor. Center of the block: i*factor + factor/2 = gl_FragCoord.xy * factor.
|
||
vec2 src_block_center = gl_FragCoord.xy * float(downsample_factor);
|
||
|
||
if (downsample_factor == 1u) {
|
||
// Identity copy. UV at src_block_center hits the source pixel center directly.
|
||
vec2 uv = src_block_center * inv_source_size;
|
||
out_color = texture(source_tex, uv);
|
||
} else if (downsample_factor == 2u) {
|
||
// Single tap at the shared corner of the 2×2 source block; one bilinear sample reads
|
||
// all 4 source pixels with equal 0.25 weights — uniform 2×2 box filter for free.
|
||
vec2 uv = src_block_center * inv_source_size;
|
||
out_color = texture(source_tex, uv);
|
||
} else {
|
||
// Four taps at offsets ±(factor/4) from the block center. Each tap lands on a corner
|
||
// shared by 4 source pixels of a (factor/2)×(factor/2) sub-block (equivalent at the
|
||
// bilinear level), giving a 4-tap = 16-source-pixel uniform sample of the block.
|
||
float off = float(downsample_factor) * 0.25;
|
||
vec2 uv_tl = (src_block_center + vec2(-off, -off)) * inv_source_size;
|
||
vec2 uv_tr = (src_block_center + vec2(off, -off)) * inv_source_size;
|
||
vec2 uv_bl = (src_block_center + vec2(-off, off)) * inv_source_size;
|
||
vec2 uv_br = (src_block_center + vec2(off, off)) * inv_source_size;
|
||
vec4 c = texture(source_tex, uv_tl)
|
||
+ texture(source_tex, uv_tr)
|
||
+ texture(source_tex, uv_bl)
|
||
+ texture(source_tex, uv_br);
|
||
out_color = c * 0.25;
|
||
}
|
||
}
|