Files
fn_registry/dev/issues/completed/0049d-graph-edges-vertex-pulling.md
T

120 lines
4.7 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: "0049d"
title: "Aristas via vertex pulling con TBO"
status: completado
type: feature
domain: []
scope: multi-app
priority: alta
depends: []
blocks: []
related: []
created: 2026-05-17
updated: 2026-05-17
tags: []
---
# 0049d — Aristas via vertex pulling con TBO
## Metadata
| Campo | Valor |
|-------|-------|
| **ID** | 0049d |
| **Estado** | pendiente |
| **Prioridad** | alta |
| **Tipo** | mejora rendimiento — parte de [#0049](0049-osint-graph-viewer.md) |
## Dependencias
**Bloqueada por:** [0049c](0049c-graph-renderer-tier1.md) (orphan + RGBA8 ya en sitio).
---
## Objetivo
Eliminar la reconstruccion del buffer de aristas en CPU cada frame. Las posiciones de nodos viven en un Texture Buffer Object (TBO); el vertex shader de aristas hace fetch de source/target con `gl_VertexID`. El buffer de aristas es estatico (`source_idx`, `target_idx`, `type_id`), solo cambian las posiciones — y eso ya estaba para los nodos.
## Contexto
Tras 0049c, el bottleneck principal restante es el upload de 12 floats × E aristas cada frame. Para 100k aristas: 4.8 MB/frame. Vertex pulling lo elimina por completo.
## Arquitectura
```
cpp/functions/viz/
├── graph_renderer.{h,cpp} # MOD: anadir TBO + buffer estatico de aristas
└── graph_renderer.md # MOD: bump 1.1 → 1.2
```
Cambios:
1. `GraphRenderer` gana `unsigned int node_pos_tbo, node_pos_tex` (texture buffer y su sampler view).
2. `GraphRenderer` gana `unsigned int edge_static_vbo` con `(uint source, uint target, uint color, uint flags)` por arista — subido una vez (o cuando cambia el grafo, no cada frame).
3. Buffer de posiciones de nodos (`vec2[]`) se sube como TBO vinculado al `node_pos_tex`.
4. Vertex shader de aristas:
```glsl
#version 430 core
layout(location=0) in uint a_source;
layout(location=1) in uint a_target;
layout(location=2) in uint a_color;
layout(location=3) in uint a_flags;
uniform samplerBuffer u_node_pos; // vec2[] como TBO
out vec4 v_color;
void main() {
int idx = (gl_VertexID & 1) == 0 ? int(a_source) : int(a_target);
vec2 p = texelFetch(u_node_pos, idx).xy;
// mismo MVP que ya estaba
gl_Position = u_mvp * vec4(p, 0.0, 1.0);
v_color = unpackUnorm4x8(a_color);
}
```
Cada arista renderiza con `glDrawArrays(GL_LINES, 0, edge_count*2)`. El fragment shader ya existia.
## Tareas
### Fase 1 — TBO de posiciones
- [ ] **1.1** En `graph_renderer_create`: crear `node_pos_buf` (VBO) + `node_pos_tex` (texture buffer view) con `glTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, node_pos_buf)`.
- [ ] **1.2** En `graph_renderer_draw`: empaquetar posiciones de nodos en un buffer flotante `(x,y) × N` y `glBufferSubData` al `node_pos_buf` (orphan + sub).
- [ ] **1.3** Antes del draw de aristas: `glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_BUFFER, node_pos_tex); glUniform1i(u_node_pos_loc, 0);`.
### Fase 2 — Buffer estatico de aristas
- [ ] **2.1** Anadir API interna `mark_edges_dirty()` que regenera el `edge_static_vbo`.
- [ ] **2.2** Si el caller pasa `graph.edges_revision != cached_revision`, regenerar. Para el primer paso, usar siempre `cached==false → regenerar` y optimizar luego con un campo `revision` en `GraphData`.
- [ ] **2.3** Layout: `struct EdgeStatic { uint source; uint target; uint color_rgba8; uint flags; }` (16 bytes por arista).
### Fase 3 — Shaders de aristas
- [ ] **3.1** Reescribir vertex shader como en la arquitectura (4.3 core).
- [ ] **3.2** Verificar fragment shader no necesita cambios.
- [ ] **3.3** Reverificar `glLineWidth`/edge_alpha siguen funcionando.
### Fase 4 — Bench + tests
- [ ] **4.1** `demos_graph` con 20k nodos + 100k aristas a 60fps en GPU integrada.
- [ ] **4.2** Profile con Tracy: el bucle de aristas en CPU debe desaparecer.
- [ ] **4.3** Test Catch2 minimo: render a FBO + readback, verificar que un grafo conocido produce un frame no-vacio (smoke test, no golden).
### Fase 5 — Cleanup
- [ ] Bump version `graph_renderer` 1.1.0 → 1.2.0.
- [ ] `fn index`.
- [ ] Commit `perf(viz): graph_renderer edges via TBO + vertex pulling`.
## Criterio de done
- [ ] CPU ms del frame para 100k aristas baja a < 0.5 ms (medible con Tracy o reloj manual).
- [ ] Render visualmente identico al pre-cambio.
- [ ] Demos de la galeria afectados (`demos_graph`) sin regresiones.
## Riesgos
| Riesgo | Mitigacion |
|---|---|
| `samplerBuffer` no funciona en alguna driver Linux | GL 4.3 core lo exige; si falla en WSL software, marcar test como SKIP igual que `test_visual` |
| Mantener `edges_revision` complica la API | Empezar con regenerar siempre y optimizar despues — no premature optimization |
| 4 bytes desperdiciados por arista (`flags`) | Justificado por alineacion + futuras flechas/styles |