perf(viz): graph_renderer edges via TBO + vertex pulling (issue 0049d)

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.
This commit is contained in:
2026-04-29 22:32:38 +02:00
parent 9a2fe5349b
commit 79b5f0b194
8 changed files with 298 additions and 86 deletions
+60
View File
@@ -0,0 +1,60 @@
// Smoke test for graph_renderer's vertex-pulling edge buffer layout (issue
// 0049d). El test no toca GL — solo verifica las garantias estructurales que
// el renderer asume:
// 1. EdgeStatic mide 16 bytes (alineacion natural sin padding sorpresa).
// 2. Los offsets de source/target/color/flags son 0/4/8/12.
// 3. La paleta de colores por defecto del fallback (gris 0x88) tiene la
// forma RGBA8 esperada por el shader (R en byte 0).
//
// Render-a-FBO-y-readback queda fuera porque exigirseria un contexto GL en
// el runner de tests; ya hay un golden image gate via test_visual + capture
// en primitives_gallery.
#define CATCH_CONFIG_MAIN
#include "catch_amalgamated.hpp"
#include "viz/graph_renderer.h"
#include "viz/graph_types.h"
#include <cstddef>
#include <cstdint>
// Re-declaramos la struct local a graph_renderer.cpp para chequear su layout
// "en espejo". Si cambia el layout en el .cpp, este test debe actualizarse —
// es intencional: el shader y el VAO setup dependen de estos offsets.
namespace test_layout {
struct EdgeStatic {
uint32_t source;
uint32_t target;
uint32_t color;
uint32_t flags;
};
} // namespace test_layout
TEST_CASE("EdgeStatic mide 16 bytes y tiene offsets contiguos", "[viz][edge_static]") {
using test_layout::EdgeStatic;
REQUIRE(sizeof(EdgeStatic) == 16);
REQUIRE(offsetof(EdgeStatic, source) == 0);
REQUIRE(offsetof(EdgeStatic, target) == 4);
REQUIRE(offsetof(EdgeStatic, color) == 8);
REQUIRE(offsetof(EdgeStatic, flags) == 12);
}
TEST_CASE("Fallback gris 0x88 tiene R en el byte LSB", "[viz][edge_static]") {
// El renderer construye `pack_rgba8(0x88, 0x88, 0x88, 0xFF)` cuando
// `e.color == 0`. El shader hace unpack manual asumiendo R en LSB.
uint32_t gray = pack_rgba8(0x88, 0x88, 0x88, 0xFF);
REQUIRE((gray & 0xFFu) == 0x88u); // R
REQUIRE(((gray >> 8) & 0xFFu) == 0x88u); // G
REQUIRE(((gray >> 16) & 0xFFu) == 0x88u); // B
REQUIRE(((gray >> 24) & 0xFFu) == 0xFFu); // A
}
TEST_CASE("GraphEdge.color = 0 indica fallback al gris por defecto",
"[viz][edge_static]") {
GraphEdge e = graph_edge(0, 1, 1.0f);
REQUIRE(e.color == 0u);
// El renderer interpreta esto como "usa el gris 0x88,0x88,0x88,0xFF".
// Un agente que precarga colores debe usar `pack_rgba8` para evitar
// colisionar con el sentinel 0.
}