#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;
    }
}
