#version 300 es
precision highp float;

// @secao     2. Alcance — o que cada runtime consegue endereçar
// @leitura Uma malha de dispositivos. Cada célula é uma peça de hardware que existe hoje num datacenter ou numa mesa. O brilho é se o seu kernel consegue rodar nela: verde é NVIDIA, vermelho AMD, azul Intel, branco Apple, amarelo FPGA, violeta CPU, ciano acelerador. Leve o slider da esquerda para a direita e a malha inteira acende. Não é analogia — é a contagem de dispositivos endereçáveis.
// @honesto Preciso ser direto sobre o que este desenho NÃO prova. OpenCL endereçar mais hardware é uma afirmação sobre a API, não sobre o desempenho. Em GPU NVIDIA, CUDA normalmente ganha — mais bibliotecas afinadas, mais controle de memória, mais gente otimizando. A minha tese não é "OpenCL é mais rápido". É "OpenCL não te tranca", e essas duas coisas são diferentes.
// @custo A imagem custa um hash por célula. A afirmação custa uma linha: toda GPU NVIDIA fala OpenCL; nem toda GPU que fala OpenCL fala CUDA.

// @titulo    Alcance: CUDA × OpenCL
// @status EXACT
// @subtitulo o mesmo kernel, contado em dispositivos
// @fonte Khronos OpenCL registry · NVIDIA CUDA compatibility docs
// @nota      Cada célula é um dispositivo. Aceso = o kernel roda nela. O slider atravessa de CUDA (só NVIDIA) para OpenCL (GPU, CPU, FPGA, acelerador).
// @param1 Runtime | 0.0 | 1.0 | 0.18

uniform vec2  uRes;
uniform float uTime;
uniform float uParam1;

out vec4 fragColor;

const float FILEIRAS = 5.0;
const float FRONTEIRA_CUDA = 0.34;   // fração de dispositivos que CUDA endereça

float hash21(vec2 p) {
    p = fract(p * vec2(123.34, 456.21));
    p += dot(p, p + 45.32);
    return fract(p.x * p.y);
}

// 0 NVIDIA · 1 AMD · 2 Intel · 3 Apple · 4 FPGA · 5 CPU · 6 acelerador
int classeDe(vec2 id) {
    float h = hash21(id + 17.0);
    if (h < 0.34) return 0;
    if (h < 0.55) return 1;
    if (h < 0.72) return 2;
    if (h < 0.84) return 3;
    if (h < 0.93) return 4;
    if (h < 0.97) return 5;
    return 6;
}

vec3 corClasse(int c) {
    if (c == 0) return vec3(0.36, 0.88, 0.44);   // NVIDIA
    if (c == 1) return vec3(0.95, 0.38, 0.32);   // AMD
    if (c == 2) return vec3(0.34, 0.58, 0.98);   // Intel
    if (c == 3) return vec3(0.88, 0.88, 0.92);   // Apple
    if (c == 4) return vec3(0.98, 0.78, 0.30);   // FPGA
    if (c == 5) return vec3(0.64, 0.46, 0.95);   // CPU
    return vec3(0.40, 0.86, 0.86);               // acelerador
}

void main() {
    vec2 px = gl_FragCoord.xy;
    vec2 uv = (px - 0.5 * uRes) / uRes.y;

    vec3 col = vec3(0.012, 0.014, 0.020);

    // ── Malha fixa de 10 × 3, em espaço de pixels, centralizada e sempre
    //    contida no quadro (independente da proporção do canvas). ──────
    const float COLS = 10.0;
    const float ROWS = 3.0;

    float largura = uRes.x * 0.88;
    float altura  = min(uRes.y * 0.60, largura / COLS * ROWS);
    largura = altura / ROWS * COLS;

    float lado = largura / COLS;
    vec2 origem = vec2((uRes.x - largura) * 0.5, uRes.y * 0.80 - altura);

    vec2 g = (px - origem) / lado;
    vec2 id = floor(g);
    vec2 f  = fract(g) - 0.5;

    float dentro = step(0.0, g.x) * step(g.x, COLS)
                 * step(0.0, g.y) * step(g.y, ROWS);

    int cls = classeDe(id);

    // alcance: NVIDIA sempre aceso; o resto acende com o slider
    float alcance = (cls == 0) ? 1.0 : smoothstep(0.25, 0.75, uParam1);

    // quadrado arredondado com margem entre células
    vec2 q = abs(f) - vec2(0.40, 0.40);
    float d = length(max(q, 0.0)) + min(max(q.x, q.y), 0.0) - 0.10;

    vec3 base = corClasse(cls);
    float pulso = 0.5 + 0.5 * sin(uTime * 1.6 + hash21(id) * 6.28);
    vec3 cAceso   = base * (0.62 + 0.52 * pulso);
    vec3 cApagado = vec3(0.135, 0.150, 0.185);
    vec3 cFinal = mix(cApagado, cAceso, alcance);

    // contorno sempre visível: o dispositivo existe mesmo apagado,
    // e é a própria cor da classe que o identifica
    float contorno = 1.0 - smoothstep(0.0, 0.028, abs(d + 0.010));
    cFinal += base * contorno * mix(0.45, 0.65, alcance);

    col = mix(col, cFinal, (1.0 - smoothstep(-0.02, 0.02, d)) * dentro);

    // ── Barra de alcance no rodapé ─────────────────────────────
    float y0 = uRes.y * 0.10;
    float y1 = uRes.y * 0.20;
    float x0 = uRes.x * 0.06;
    float x1 = uRes.x * 0.94;
    if (px.y >= y0 && px.y <= y1 && px.x >= x0 && px.x <= x1) {
        float bx = (px.x - x0) / (x1 - x0);
        float alvo = mix(FRONTEIRA_CUDA, 1.0, smoothstep(0.25, 0.75, uParam1));
        vec3 cBarra = (bx < alvo) ? vec3(0.26, 0.74, 0.48) : vec3(0.075, 0.085, 0.115);
        col = cBarra;
    }

    // marca na fração endereçável por CUDA
    float xCUDA = x0 + (x1 - x0) * FRONTEIRA_CUDA;
    float tick = 1.0 - smoothstep(0.7, 2.0, abs(px.x - xCUDA));
    tick *= step(y0 - 6.0, px.y) * step(px.y, y1 + 6.0);
    col += vec3(0.95, 0.96, 1.00) * tick * 0.75;

    col *= 1.0 - 0.30 * smoothstep(0.55, 1.10, length(uv));

    fragColor = vec4(pow(max(col, 0.0), vec3(0.4545)), 1.0);
}
