// Custom UI compute shader. Demonstrates the Tier 1 dispatch path:
// the user defines their own item struct, writes their own GLSL alongside
// the standard shaders (sharing the same UIDispatchHeader contract via
// ui-shared.glsl), and dispatches it via UIRenderer::Dispatch.
//
// What it does: each item is a circle. For every pixel the workgroup tile
// owns, if the pixel falls inside any item-circle, the pixel's RGB is
// inverted (1 - rgb). Composes naturally with whatever previous dispatches
// drew into the swapchain image — works on top of standard quads, custom
// effects, anything.
#version 460
#extension GL_GOOGLE_include_directive : enable
#include "ui-shared.glsl"

// Application-defined item: just (cx, cy, radius, _pad). Layout matches the
// C++ InverseCircleItem struct in main.cpp byte-for-byte under std430.
struct InverseCircleItem {
    vec4 centerRadius;
};

layout(descriptor_heap, std430) readonly buffer InvCircleBuf {
    InverseCircleItem items[];
} invCircleHeap[];

// NVIDIA workaround — same per-member-load pattern the library shaders use
// for the descriptor-heap'd SSBO composite-load issue.
InverseCircleItem LoadInverseCircleItem(uint heap, uint i) {
    InverseCircleItem it;
    it.centerRadius = invCircleHeap[heap].items[i].centerRadius;
    return it;
}

layout(push_constant) uniform PC {
    UIDispatchHeader hdr;
} pc;

layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;

void main() {
    ivec2 screenPx;
    if (!uiResolveScreenPixel(pc.hdr, screenPx)) return;

    vec2 sp = vec2(screenPx) + 0.5;

    // Find the strongest circle coverage at this pixel — using max instead
    // of literal per-item invert so overlapping circles don't double-invert
    // back to the original.
    float coverage = 0.0;
    for (uint i = 0u; i < pc.hdr.itemCount; ++i) {
        InverseCircleItem it = LoadInverseCircleItem(pc.hdr.itemBuffer, i);
        vec2  c = it.centerRadius.xy;
        float r = it.centerRadius.z;
        float d = length(sp - c) - r;
        coverage = max(coverage, clamp(0.5 - d, 0.0, 1.0));
    }
    if (coverage <= 0.0) return;

    vec4 dst = imageLoad(uiImages[pc.hdr.outImage], screenPx);
    dst.rgb  = mix(dst.rgb, vec3(1.0) - dst.rgb, coverage);
    imageStore(uiImages[pc.hdr.outImage], screenPx, dst);
}
