#version 450 core

// Unified backdrop blur vertex shader.
// Handles both the 1D separable blur passes (fullscreen triangle, mode 0; used for
// BOTH the H-pass and V-pass) and the composite pass (instanced unit-quad over
// Gaussian_Blur_Primitive storage buffer, mode 1) for the second PSO of the backdrop bracket.
// The first PSO (downsample) uses backdrop_fullscreen.vert.
//
// No vertex buffer for either mode. Mode 0 uses gl_VertexIndex 0..2 for a single
// fullscreen triangle; mode 1 uses gl_VertexIndex 0..5 for a unit-quad (two
// triangles, TRIANGLELIST topology) and gl_InstanceIndex to select the primitive.
//
// Mode 0 viewport+scissor are CPU-set per sigma group to the work region (union AABB
// of that group's backdrop primitives + halo, clamped to swapchain bounds). Mode 1
// renders into source_texture with the screen-space orthographic projection; the
// per-primitive bounds drive the quad in screen space.
//
// Backdrop primitives have NO rotation — backdrop sampling is in screen space, so
// a rotated mask over a stationary blur sample would look wrong.

// --- Outputs to fragment shader ---
// p_local: shape-local position in physical pixels (origin at shape center).
//          Only meaningful in mode 1 (V-composite). Zero-init for mode 0.
layout(location = 0) out vec2 p_local;
// f_color: tint, unpacked from primitive.color. Only meaningful in mode 1.
layout(location = 1) out mediump vec4 f_color;
// f_half_size_ppx: RRect half extents in physical pixels (mode 1 only).
layout(location = 2) flat out vec2 f_half_size_ppx;
// f_radii_ppx: per-corner radii in physical pixels (mode 1 only).
layout(location = 3) flat out vec4 f_radii_ppx;
// f_half_feather_ppx: SDF anti-aliasing feather in physical pixels (mode 1 only).
layout(location = 4) flat out float f_half_feather_ppx;

// --- Uniforms (set 1) ---
// Backdrop pipeline's own uniform block — distinct from the main pipeline's
// Vertex_Uniforms_2D. `mode` selects between H-blur (0) and V-composite (1).
layout(set = 1, binding = 0) uniform Uniforms {
    mat4 projection;
    float dpi_scale;
    uint mode; // 0 = H-blur, 1 = V-composite
    vec2 _pad0;
};

// --- Gaussian blur primitive storage buffer (set 0) ---
// 48 bytes, std430-natural layout (no implicit padding). vec4 members are
// front-loaded so their 16-byte alignment is satisfied without holes; the
// vec2 and scalar tail packs tight to land the struct at a clean 48-byte
// stride (a multiple of 16, so the array stride needs no rounding either).
// Field semantics match the CPU-side Gaussian_Blur_Primitive declared in
// levlib/draw/backdrop.odin; keep both in sync.
//
// Gaussian blur primitives are tint-only: outline is intentionally absent. Specialized
// edge effects (e.g. liquid-glass-style refraction outlines) would be a dedicated
// primitive type with its own pipeline rather than a flag bit here.
struct Gaussian_Blur_Primitive {
    vec4 bounds; //  0-15: min_xy, max_xy (world-space, logical px)
    vec4 radii_ppx; // 16-31: per-corner radii
    vec2 half_size_ppx; // 32-39: RRect half extents
    float half_feather_ppx; // 40-43: SDF anti-aliasing feather
    uint color; // 44-47: tint, packed RGBA u8x4
};

layout(std430, set = 0, binding = 0) readonly buffer Gaussian_Blur_Primitives {
    Gaussian_Blur_Primitive primitives[];
};

void main() {
    if (mode == 0u) {
        // ---- Mode 0: H-blur fullscreen triangle ----
        // gl_VertexIndex 0 -> ( -1, -1)
        // gl_VertexIndex 1 -> (  3, -1)
        // gl_VertexIndex 2 -> ( -1,  3)
        vec2 ndc = vec2(
                (gl_VertexIndex == 1) ? 3.0 : -1.0,
                (gl_VertexIndex == 2) ? 3.0 : -1.0);
        gl_Position = vec4(ndc, 0.0, 1.0);

        // Mode 0 doesn't read the per-primitive varyings; zero-init for safety.
        p_local = vec2(0.0);
        f_color = vec4(0.0);
        f_half_size_ppx = vec2(0.0);
        f_radii_ppx = vec4(0.0);
        f_half_feather_ppx = 0.0;
    } else {
        // ---- Mode 1: V-composite instanced unit-quad over Gaussian_Blur_Primitive ----
        Gaussian_Blur_Primitive p = primitives[gl_InstanceIndex];

        // Unit-quad corners for TRIANGLELIST (2 triangles, 6 vertices):
        //   index 0 -> (0,0)   index 3 -> (0,1)
        //   index 1 -> (1,0)   index 4 -> (1,0)
        //   index 2 -> (0,1)   index 5 -> (1,1)
        vec2 quad_corners[6] = vec2[6](
                vec2(0.0, 0.0), vec2(1.0, 0.0), vec2(0.0, 1.0),
                vec2(0.0, 1.0), vec2(1.0, 0.0), vec2(1.0, 1.0));
        vec2 corner = quad_corners[gl_VertexIndex];

        vec2 world_pos = mix(p.bounds.xy, p.bounds.zw, corner);
        vec2 center = 0.5 * (p.bounds.xy + p.bounds.zw);

        // Shape-local position in physical pixels (no rotation for backdrops).
        p_local = (world_pos - center) * dpi_scale;

        f_color = unpackUnorm4x8(p.color);
        f_half_size_ppx = p.half_size_ppx;
        f_radii_ppx = p.radii_ppx;
        f_half_feather_ppx = p.half_feather_ppx;

        gl_Position = projection * vec4(world_pos * dpi_scale, 0.0, 1.0);
    }
}
