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

4.7 KiB
Raw Blame History

id, title, status, type, domain, scope, priority, depends, blocks, related, created, updated, tags
id title status type domain scope priority depends blocks related created updated tags
0049d Aristas via vertex pulling con TBO completado feature
multi-app alta
2026-05-17 2026-05-17

0049d — Aristas via vertex pulling con TBO

Metadata

Campo Valor
ID 0049d
Estado pendiente
Prioridad alta
Tipo mejora rendimiento — parte de #0049

Dependencias

Bloqueada por: 0049c (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:
    #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