c29428a187
Extiende el modelo agnostico de graph_types.h para soportar shapes/iconos/ filtros/labels/streaming sin acoplar a backend. Migra el unico consumer (demos_graph) en el mismo cambio. - GraphNode v2: type_id + shape_override/color_override/size_override + flags (NF_PINNED/VISIBLE/SELECTED/HOVERED) + label_idx + user_data. - GraphEdge v2: type_id + style_override + flags (EF_DIRECTED/VISIBLE). - EntityType / RelationType: tablas en GraphData (types, rel_types). - Helpers de resolucion (resolve_node_color/shape/size, resolve_edge_*) y constructores ergonomicos (graph_node, graph_edge, entity_type, relation_type) — sentinel-based para herencia automatica del tipo. - graph_renderer v1.4: lee NF_VISIBLE / EF_VISIBLE, resuelve apariencia via override → EntityType → fallback indexado por type_id. Skipea aristas con endpoints invisibles. Shapes siguen pintandose como circulo (0049f cableara el dispatch real). - graph_force_layout v1.2: pinned ahora vive en flags & NF_PINNED. - graph_viewport v1.1: hover/seleccion publican NF_HOVERED/SELECTED en el grafo (clear-then-set). Drag usa NF_PINNED. Tooltip muestra Type/ user_data en lugar de community/value/label. - demos_graph: 8 EntityType (paleta antigua) + 1 RelationType. type_id por cluster. user_data = indice numerico del nodo. Apariencia visual identica al pre-cambio. - test_graph_types.cpp: 12 casos cubriendo helpers, defaults, bitmask manipulation y resoluciones override-vs-EntityType. test_graph_edge_ static actualizado al nuevo modelo (ya no tiene .color directo). - 4 .md de tipos nuevos (graph_node, graph_edge, entity_type, relation_type) + GraphData v2.0 actualizado. Tests: 31/31 ctest verdes (incluye test_visual golden). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
108 lines
6.6 KiB
Markdown
108 lines
6.6 KiB
Markdown
---
|
||
name: graph_renderer
|
||
kind: function
|
||
lang: cpp
|
||
domain: viz
|
||
version: "1.4.0"
|
||
purity: impure
|
||
signature: "GraphRenderer* graph_renderer_create(int width, int height, const GraphRendererConfig& config)"
|
||
description: "Renderer GPU de grafos con instanced rendering a FBO, compatible con ImGui::Image para visualizacion de grafos grandes"
|
||
tags: [graph, renderer, opengl, gpu, instanced, fbo, visualization, frustum-cull, rgba8, vertex-pulling, tbo]
|
||
uses_functions: ["gl_loader_cpp_gfx"]
|
||
uses_types: ["GraphData_cpp_viz"]
|
||
returns: []
|
||
returns_optional: false
|
||
error_type: "error_go_core"
|
||
imports: [imgui]
|
||
tested: false
|
||
tests: []
|
||
test_file_path: ""
|
||
file_path: "cpp/functions/viz/graph_renderer.cpp"
|
||
framework: imgui
|
||
params:
|
||
- name: width
|
||
desc: "Ancho del framebuffer en pixels"
|
||
- name: height
|
||
desc: "Alto del framebuffer en pixels"
|
||
- name: config
|
||
desc: "Configuracion visual: outline width, edge width, edge alpha, color de fondo, fade de aristas por distancia a camara"
|
||
output: "Handle opaco al renderer. Usar graph_renderer_draw() para obtener texture ID de OpenGL, pasable directamente a ImGui::Image()"
|
||
---
|
||
|
||
# graph_renderer
|
||
|
||
Renderer GPU de grafos basado en OpenGL 3.3 core profile con instanced rendering. Renderiza nodos y aristas de un `GraphData` a un FBO interno y retorna el texture ID para integracion directa con `ImGui::Image()`.
|
||
|
||
## Funciones del API
|
||
|
||
```cpp
|
||
// Ciclo de vida
|
||
GraphRenderer* graph_renderer_create(int width, int height, const GraphRendererConfig& config = {});
|
||
void graph_renderer_destroy(GraphRenderer* r);
|
||
void graph_renderer_resize(GraphRenderer* r, int width, int height);
|
||
|
||
// Renderizado
|
||
unsigned int graph_renderer_draw(GraphRenderer* r, const GraphData& graph,
|
||
float cam_x, float cam_y, float cam_zoom);
|
||
```
|
||
|
||
## Ejemplo de uso con ImGui
|
||
|
||
```cpp
|
||
// Inicializacion (una vez)
|
||
GraphRenderer* renderer = graph_renderer_create(800, 600);
|
||
|
||
// En el render loop
|
||
ImVec2 panel_size = ImGui::GetContentRegionAvail();
|
||
graph_renderer_resize(renderer, (int)panel_size.x, (int)panel_size.y);
|
||
|
||
unsigned int tex = graph_renderer_draw(renderer, graph_data,
|
||
cam_x, cam_y, cam_zoom);
|
||
|
||
ImGui::Image((ImTextureID)(uintptr_t)tex,
|
||
panel_size,
|
||
ImVec2(0, 1), ImVec2(1, 0)); // flip Y para OpenGL
|
||
|
||
// Destruccion
|
||
graph_renderer_destroy(renderer);
|
||
```
|
||
|
||
## Notas de implementacion
|
||
|
||
**Renderizado de nodos:** Instanced rendering con un quad unitario [-0.5, 0.5] expandido por el tamano del nodo. El fragment shader aplica un SDF circular con anti-aliasing via `smoothstep` y un anillo de outline.
|
||
|
||
**Renderizado de aristas:** `GL_LINES` con datos de posicion y color empaquetados por arista. El ancho se controla con `GraphRendererConfig::edge_width`.
|
||
|
||
**Transformacion de camara:**
|
||
```
|
||
tx = -cam_x * zoom + width/2
|
||
ty = -cam_y * zoom + height/2
|
||
ndc = (screen / viewport) * 2 - 1
|
||
```
|
||
|
||
**Paleta de comunidades:** 10 colores ABGR usados cuando `node.color == 0`, seleccionados por `node.community % 10`.
|
||
|
||
**Estado GL:** Guarda y restaura `GL_FRAMEBUFFER_BINDING` y `GL_VIEWPORT` para ser compatible con el render loop de ImGui sin efectos secundarios.
|
||
|
||
**Includes GL:** Usa `gfx/gl_loader.h` (v1.1+). En Linux es no-op (incluye headers con `GL_GLEXT_PROTOTYPES`). En Windows expone los simbolos modernos via `wglGetProcAddress` con macros `#define gl* fn_gl*`. Cualquier app que use `graph_renderer` debe linkear `gl_loader.cpp` y llamar `fn::gfx::gl_loader_init()` una vez tras crear el contexto GL.
|
||
|
||
## Notas
|
||
|
||
- **v1.4** (2026-04-29, issue 0049e): adapta el renderer al modelo extendido de `GraphData`. Lee `n.flags & NF_VISIBLE` para skipear nodos invisibles, resuelve color via `n.color_override` → `EntityType` → fallback indexado por `type_id`. Aristas: skip si `!(EF_VISIBLE)` o si los endpoints no son visibles, color via `RelationType`. Shapes/iconos/dashed-style siguen como circulo solido — el dispatch real llega en 0049f.
|
||
- **v1.3** (2026-04-29, issue 0049d): aristas via vertex pulling. API publica intacta.
|
||
- El buffer de aristas pasa a ser estatico (`source_idx, target_idx, color, flags` × E, 16 bytes/arista) y solo se reupload cuando el grafo cambia (detectado por `(edges_ptr, edge_count)` — heuristica suficiente mientras `GraphData` no tenga `revision`). Para 100k aristas: 1.6 MB iniciales vs 4.8 MB/frame del esquema anterior — el upload baja a cero en regimen estable.
|
||
- Las posiciones de los nodos se suben cada frame a un Texture Buffer Object `RG32F` (`vec2[]`, 8 bytes/nodo). El vertex shader de aristas hace `texelFetch(u_node_pos, idx)` con `idx` derivado de `gl_VertexID & 1` (0=source, 1=target).
|
||
- Draw call: `glDrawArraysInstanced(GL_LINES, 0, 2, edge_count)` — 1 instancia por arista, 2 vertices por linea, todos los atributos con `divisor=1`.
|
||
- Frustum cull de aristas eliminado en CPU (la GPU clipea fuera de viewport por su cuenta). Se mantiene para nodos.
|
||
- `edge_alpha` pasa a uniform en el shader; la pre-multiplicacion en CPU desaparece y permite que el buffer estatico no dependa del config.
|
||
- GLSL: 330 core (con `samplerBuffer`/`texelFetch` que estan en 1.40+). `gl_loader` gana `glBufferSubData`, `glVertexAttribIPointer` y `glTexBuffer` (Linux ya los tenia via `GL_GLEXT_PROTOTYPES`).
|
||
|
||
- **v1.2** (2026-04-29, issue 0049c): tres optimizaciones internas, API publica intacta.
|
||
1. **RGBA8 packing**: el buffer de instancia/vertice usa `uint32` por color en lugar de 4 floats. Nodo: 28 → 16 bytes/instance (-43%). Edge: 24 → 12 bytes/vertex (-50%). Los shaders desempaquetan con bit shifts (compatible GL 3.30+, sin necesidad de `unpackUnorm4x8` que es 4.20+). Helpers expuestos en el .h: `pack_rgba8`, `unpack_rgba8`, `modulate_alpha_rgba8` (testeados en `test_graph_pack_rgba8.cpp`).
|
||
2. **Capacity-tracked streaming buffers**: el VBO se mantiene orphaned con `glBufferData(NULL, capacity)` y se actualiza con `glBufferSubData` solo los bytes usados. La capacidad crece x2 cuando hace falta (inicial: 4096 nodos / 8192 vertices de aristas) → reallocaciones en O(log N). Staging CPU reutilizado entre frames.
|
||
3. **Frustum cull**: nodos y aristas fuera del viewport AABB (con margen 10%) se saltan en CPU antes del upload. Para nodos, solo los visibles entran en el instance buffer (`glDrawArraysInstanced` con `visible_count`). Para aristas, AABB del segmento contra viewport. Pop-in al borde mitigado por el margen.
|
||
|
||
Resultado esperado: ~20k nodos a 60fps en GPU integrada cuando `cam_zoom` mantiene la mayoria fuera del viewport.
|
||
|
||
- **v1.1** (2026-04-25): cambia de raw `<GL/glext.h>` a `gfx/gl_loader.h` para que compile en cross-compile MinGW. Sin cambios funcionales — el binario Linux es bit-equivalente.
|