daf491cd99
El buffer de aristas pasa a estatico (16B/arista: source, target, color, flags) y solo se reupload cuando cambia el grafo. Las posiciones de los nodos viven en un Texture Buffer Object (RG32F) actualizado por frame; el vertex shader hace texelFetch con gl_VertexID & 1 para elegir endpoint. Draw call: glDrawArraysInstanced(GL_LINES, 0, 2, edge_count) con divisor=1. Para 100k aristas: el upload de 4.8 MB/frame baja a 0 en regimen estable. edge_alpha pasa a uniform; la pre-multiplicacion en CPU desaparece. GLSL sigue en 330 core (samplerBuffer/texelFetch estan en 1.40+). gl_loader gana glBufferSubData, glVertexAttribIPointer y glTexBuffer (en Linux ya estaban via GL_GLEXT_PROTOTYPES; ahora estan disponibles tambien en MinGW/Windows). Tests: nuevo test_graph_edge_static valida el layout de 16B y el packing RGBA8 del fallback. test_visual sigue verde — render visualmente identico. Bump graph_renderer 1.2.0 -> 1.3.0.
4.5 KiB
4.5 KiB
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:
GraphRendererganaunsigned int node_pos_tbo, node_pos_tex(texture buffer y su sampler view).GraphRendererganaunsigned int edge_static_vbocon(uint source, uint target, uint color, uint flags)por arista — subido una vez (o cuando cambia el grafo, no cada frame).- Buffer de posiciones de nodos (
vec2[]) se sube como TBO vinculado alnode_pos_tex. - Vertex shader de aristas:
Cada arista renderiza con
#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); }glDrawArrays(GL_LINES, 0, edge_count*2). El fragment shader ya existia.
Tareas
Fase 1 — TBO de posiciones
- 1.1 En
graph_renderer_create: crearnode_pos_buf(VBO) +node_pos_tex(texture buffer view) conglTexBuffer(GL_TEXTURE_BUFFER, GL_RG32F, node_pos_buf). - 1.2 En
graph_renderer_draw: empaquetar posiciones de nodos en un buffer flotante(x,y) × NyglBufferSubDataalnode_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 eledge_static_vbo. - 2.2 Si el caller pasa
graph.edges_revision != cached_revision, regenerar. Para el primer paso, usar siemprecached==false → regenerary optimizar luego con un camporevisionenGraphData. - 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_graphcon 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_renderer1.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 |