Files
fn_registry/dev/issues/completed/0049c-graph-renderer-tier1.md
T

121 lines
4.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
---
id: "0049c"
title: "`graph_renderer` Tier 1: RGBA8, orphan buffers, frustum cull, auto-pause"
status: completado
type: feature
domain: []
scope: multi-app
priority: alta
depends: []
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 0049c — `graph_renderer` Tier 1: RGBA8, orphan buffers, frustum cull, auto-pause
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | 0049c |
| **Estado** | pendiente |
| **Prioridad** | alta |
| **Tipo** | mejora rendimiento — parte de [#0049](0049-osint-graph-viewer.md) |
## Dependencias
**Bloqueada por:** [0049b](0049b-cpp-bump-gl-43.md) (recomendado pero no estricto — cambios funcionan en 3.3 tambien).
**Desbloquea:** [0049d](0049d-graph-edges-vertex-pulling.md), demos perf-realistas para issues posteriores.
---
## Objetivo
Optimizaciones baratas y de gran impacto sobre `graph_renderer.cpp` y `graph_force_layout` para subir de ~5k nodos a ~20k nodos a 60fps en GPU integrada **sin cambiar la API publica**.
## Contexto
Hoy el renderer:
- Empaqueta colores como 4 floats × N (16 bytes/nodo) en el instance buffer.
- Llama `glBufferData` cada frame → driver realloca el VBO.
- Sube todas las aristas siempre, aunque esten fuera del viewport.
- Force layout corre cada frame aunque la energia sea minima (estado convergido).
## Arquitectura
```
cpp/functions/viz/
├── graph_renderer.{h,cpp} # MOD
├── graph_renderer.md # MOD: bump version (1.x)
├── graph_force_layout.{h,cpp} # MOD: helper auto_pause
└── graph_force_layout.md # MOD
```
Sin cambios en la API publica — son optimizaciones internas.
## Tareas
### Fase 1 — Color packing RGBA8
- [ ] **1.1** En el instance buffer, cambiar layout de `(x, y, size, r, g, b, a)` floats a `(x, y, size, color_rgba8)` donde `color_rgba8` es uint32 packed.
- [ ] **1.2** Ajustar shader vertex de nodos: `layout(location=3) in uint a_color; vec4 col = unpackUnorm4x8(a_color);`.
- [ ] **1.3** Ajustar el packing en CPU: helper `pack_rgba8(r,g,b,a) = (a<<24)|(b<<16)|(g<<8)|r`.
- [ ] **1.4** Idem para el buffer de aristas (color por vertex → uint32 por vertex).
### Fase 2 — Orphan buffer pattern
- [ ] **2.1** Reemplazar `glBufferData(GL_ARRAY_BUFFER, sz, data, GL_DYNAMIC_DRAW)` por:
```cpp
glBufferData(GL_ARRAY_BUFFER, capacity_bytes, nullptr, GL_STREAM_DRAW); // orphan
glBufferSubData(GL_ARRAY_BUFFER, 0, used_bytes, data);
```
- [ ] **2.2** Mantener `capacity_bytes` interno en el `GraphRenderer` y crecer al doble si `used_bytes > capacity`.
### Fase 3 — Frustum cull aristas
- [ ] **3.1** Calcular AABB visible en world coords:
```cpp
float wx0 = cam_x - (width/2)/zoom; float wx1 = cam_x + (width/2)/zoom;
float wy0 = cam_y - (height/2)/zoom; float wy1 = cam_y + (height/2)/zoom;
```
- [ ] **3.2** En el bucle de aristas, skip si AABB de la arista (segmento source→target con margen) no intersecta el viewport AABB.
- [ ] **3.3** Nodos: similar — skip nodos cuyo AABB (centro ± size) cae fuera. Como son draws instanced, el cull se hace empaquetando solo los visibles en el instance buffer (mantener un counter `visible_count`).
### Fase 4 — Auto-pause force layout
- [ ] **4.1** En `graph_force_layout.h`, anadir helper:
```cpp
// Devuelve true si la energia ha caido bajo el umbral durante N frames consecutivos.
bool graph_force_layout_should_pause(float energy, float threshold, int min_consecutive);
```
- [ ] **4.2** Documentar uso en el `.md`. El consumer guarda un contador interno; el helper es puro.
- [ ] **4.3** Migrar `demos_graph.cpp` para usarlo y para no invocar `_step` cuando `paused == true`. Boton "Resume" ya existe.
### Fase 5 — Tests + benchmark
- [ ] **5.1** Test Catch2 sobre `pack_rgba8`/`unpack_rgba8`: roundtrip exacto.
- [ ] **5.2** Test Catch2 sobre `graph_force_layout_should_pause`: secuencias artificiales.
- [ ] **5.3** Benchmark manual en `demos_graph` con N=20000: anotar fps antes/despues en el .md de la funcion (`notes:`).
### Fase 6 — Cleanup
- [ ] Bump version del .md de `graph_renderer` a 1.1.0 y de `graph_force_layout` a 1.1.0.
- [ ] `fn index` y verificar.
- [ ] Commit `perf(viz): graph_renderer Tier 1 (RGBA8, orphan, cull) + force_layout auto-pause`.
## Criterio de done
- [ ] `demos_graph` con 20k nodos a 60fps en GPU integrada de pruebas.
- [ ] Tests verdes.
- [ ] `nvidia-smi` o `radeontop` muestran que la CPU baja respecto al baseline (perfilar con Tracy si TRACY_ENABLE).
## Riesgos
| Riesgo | Mitigacion |
|---|---|
| `unpackUnorm4x8` no esta en GL 3.3 sin extension | Esta en core 4.0+; con bump 0049b ya disponible. Si 0049b no se mergea antes, fallback a `(color>>0)&0xff)/255.0` manual |
| Frustum cull provoca pop-in en bordes | Anadir margen del 10% del viewport AABB |
| Crecimiento de capacity buffer en streaming | Crecer al doble; documentar capacity inicial razonable (4096 nodos) |