stack-RFC-025 — Seleção de dispositivo GPU e política de energia (contrato compartilhado, backends por-engine)

draft

Define COMO componentes da Koder Stack decidem em qual GPU física rodar (iGPU integrada vs dGPU discreta, em máquinas híbridas) e como gerenciam a energia da dGPU. A decisão central é **separar política de mecanismo**: (1) **Contrato/política compartilhado (camada 1)** — um único modelo que todos os componentes herdam: preferência por-superfície (`auto`/`igpu`/`dgpu`), heurística de "este workload merece a dGPU", lifecycle de power-management (dGPU dorme em runtime-D3 quando ociosa), modelo de override do usuário e naming. Vive no backbone **Kore** como spec normativa + um helper fino no SDK (`koder_kit`). (2) **Backends por-engine (camada 2)** — cada engine de render implementa o mecanismo conformando ao contrato: **Kroma** (apps nativos, via seleção de adapter wgpu) → todos os apps Kroma herdam de graça; **Kruze** (Chromium/CEF, via GPU-process pinning — KRUZE-277); **caminho de vídeo** (decode HW VA-API/NVDEC). (3) **Apps (camada 3)** — herdam via seu engine, sem código próprio de GPU. Decisão explícita: **NÃO** centralizar a implementação inteira no Kore/Kroma (impossível para o Kruze, que embarca um engine de terceiro com subsistema de GPU próprio), e **NÃO** reimplementar por módulo (drift de thresholds/power-lifecycle/UX). O comum sobe como contrato; o específico fica no engine.

1. Problema

Máquinas híbridas (laptop com iGPU Intel + dGPU NVIDIA — ex.: o Avell do owner) têm duas GPUs físicas. Componentes Koder que renderizam (apps nativos, o navegador Kruze, players de vídeo) precisam decidir qual GPU usar e quando ligar/desligar a dGPU (que consome muito mais energia/calor e, no laptop do owner, é historicamente frágil — manter em runtime-D3 quando ociosa é mandatório). Hoje:

  • Não existe nenhuma spec/política de GPU na Stack.
  • O KRUZE-277 está prestes a implementar isso só para o Kruze.
  • Sem um contrato comum, cada componente inventaria seu próprio modelo

    (thresholds, nomes de setting, lifecycle de energia) → drift garantido.

2. Decisão — separar política de mecanismo (3 camadas)

Camada 1 — Contrato/política compartilhado (Kore + SDK)

Um único modelo normativo que todo componente segue. Conteúdo:

  • GpuPreference por-superfície: auto (default) · igpu · dgpu.

    Superfície = abajanela (Kruze), surfaceview (Kroma), pipeline de mídia.

  • Heurística "merece dGPU" (em auto): sinais canônicos — contexto

    WebGLWebGPU high-performance, canvas grandealto-DPI, vídeo ≥4K, tempo de GPU sustentado acima de limiar. Os thresholds são definidos aqui; a coleta dos sinais é da camada 2.

  • Power-management lifecycle: dGPU permanece em runtime-D3 até existir ≥1

    superfície roteada pra ela; volta a D3 N segundos após a última fechar. Contrato define o lifecycle + o budget; o engine executa o spawn/release.

  • Modelo de override do usuário: setting tri-state por-superfície +

    memória por-site/por-app + indicador visual ("rodando na GPU").

  • Naming + telemetria: nomes de setting, labels, métricas (observability-

    first) padronizados — um vocabulário só em toda a Stack.

Vive como spec normativa no Kore (engines/ backbone) + um helper fino no koder_kit (tipos GpuPreference/GpuRouter + a heurística pura, sem I/O de engine). Reuse-first: ≥3 consumidores → SDK justificado.

Camada 2 — Backends por-engine (conformam ao contrato)

Engine Componentes Mecanismo
Kroma (wgpu/Vello) apps Koder nativos seleção de adapter wgpu (PowerPreference + enumeração) — todos os apps Kroma herdam
Chromium/CEF Kruze (KRUZE-277) GPU-process pinning (env var __NV_PRIME_RENDER_OFFLOAD/DRI_PRIME no Linux; LUID no Windows; powerPreference nativo no macOS)
Decode de vídeo players (mpv etc.) VA-API/NVDEC — escolha de device de decode

Cada backend: (a) coleta os sinais da heurística no vocabulário do seu engine, (b) executa o roteamento/spawn no device escolhido, (c) reporta o estado pro indicador/telemetria do contrato.

Camada 3 — Apps

Herdam via seu engine. Um app Kroma não escreve código de GPU; ganha o roteamento + power-management do backend Kroma. Zero duplicação.

3. Por que não os extremos

  • "Tudo no Kore/Kroma" ❌ — o Kroma só governa apps nativos dele. O Kruze

    embarca o Chromium, que tem subsistema de GPU process próprio que o Kroma não controla; forçar quebraria o caso mais visível. A detecção também é engine-específica (camadas do compositor do Chromium ≠ primitivas de cena do Kroma).

  • "Cada módulo por si" ❌ — duplicar thresholds, lifecycle de energia e

    modelo de setting em cada módulo deriva e contradiz reuse-first.

4. Escopo — o que NÃO é deste RFC

  • Per-elemento numa GPU: já é nativo do Chromium (compositing layers) e do

    Kroma (cena na GPU). Sem trabalho.

  • Per-elemento entre DUAS GPUs físicas: inviável (cópia cross-PCIe por

    frame anula o ganho). Fora de escopo permanentemente.

5. Plano de adoção (slices)

  • S1 — contrato (camada 1): ratificar o modelo + escrever a spec no Kore +

    tipos/heurística pura no koder_kit. Bloqueia os backends (define a interface que eles implementam).

    • S1a — spec normativa: ✅ LANDED 2026-06-27

      meta/docs/stack/specs/gpu/device-selection.kmd (R1–R12, T1–T6).

    • S1b — tipos/heurística + persistência no koder_kit: ✅ DONE 2026-06-27

      koder_kit#101 (tipos+GpuRouter, 0.71.0) + #102 (GpuPreferenceStore, 0.72.0). 1919 testes off-laptop. S1 completo → S2S3 destravados.

  • S2 — backend Kruze: KRUZE-277 implementa contra o contrato (vira o

    backend Chromium; deixa de inventar modelo próprio).

    • S1 spike ✅ 2026-06-27: per-TAB routing inviável (CEF usa 1 GPU process

      compartilhado) → reframe pra per-window/per-instance (2º processo host CEF pinado à dGPU). Linux ainda gated por GPU-OSR (KRUZE-199/200, software-only hoje).

  • S3 — backend Kroma: quando o Kroma existir (roadmap), implementa o

    backend nativo → apps herdam.

  • S4 — backend vídeo: formaliza o caminho VA-API/NVDEC sob o mesmo modelo

    de preferência/telemetria.

  • S5 — UX comum: indicador + setting tri-state + memória por-site, via SDK.
    • SDK widgets ✅ DONE 2026-06-27koder_kit#103 (KoderGpuPreferenceControl + KoderGpuIndicator, 0.73.0). Per-engine wiring remains in the backends.

6. Estado dos engines (2026-06-27, honesto)

  • Kroma ainda não existe como código (roadmap). Por isso o contrato (S1)

    vem primeiro: define o alvo pra quando o Kroma nascer.

  • Kruze existe e tem o KRUZE-277 aberto — é o primeiro backend real e o

    driver desta padronização.

  • Vídeo já usa VA-API hoje (caminho ad-hoc); S4 só o traz pro contrato.