Skip to content
 
 

Latest commit

 

History

137 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

turboGraph

TurboGraph is a GPU-accelerated Proof-Of-Concept node graph viewer built for AI model architectures at extreme scale, with interactive pan, zoom, and fluid physics that animate subgraphs as they expand and collapse. Graph size is bounded only by available RAM rather than any fixed limit — tested at 500,000+ nodes and 2.5 million edges on a 32 GB RAM, 10 GB VRAM desktop, and it sizes itself automatically to whatever memory the machine has.

  • Built on Dear ImGui, SDL2 and OpenGL.
  • Not tested on Linux
  • Forked from Intel GVK as plans exist to integrate this library with Pipeline Explorer once ported to Vulkan.
  • Originally ImGui node code based off of ChemistAion/nodes.cpp.

Demo: https://www.youtube.com/watch?v=rYEJ_euHGYw

For a version without fluid-sim subgraph animations with less overall vibe coding, see the branch noSubgraphAnimation


How it works

Tile-based rendering

The world is divided into a sparse grid of 8192×8192 tiles, each an offscreen framebuffer. Nodes are baked into a tile once, then the tile is composited to screen every frame with a camera transform. Two consequences matter:

  • Pan and zoom cost nothing to re-render. Camera changes don't dirty tiles, so navigation is just texture blits of already-baked content.
  • There is no maximum world size. Nothing is ever allocated at world resolution, so the usual GL_MAX_TEXTURE_SIZE ceiling (typically 16384) doesn't apply.

Tiles are allocated lazily (only where nodes exist), and Least Recently Used tiles are evicted when over budget with currently visible tiles protected from eviction. Each tile stores RGB565 (2 bytes/pixel), so one tile costs 128 MB.

GPU rendering

Nodes, edges, text (SDF), and port circles each have their own draw pipeline, with compute shaders building edge geometry. Text uses a pre-generated signed-distance-field atlas so labels stay crisp at any zoom. A CPU fallback path exists for machines without the required compute shader support.

Fluid physics

Expand and collapse are animated by an SPH (smoothed-particle hydrodynamics) simulation running in compute shaders: children flow outward from their parent, neighbors are displaced and spring back. The graph settles organically instead of snapping between layouts.

Memory budgeting

Available RAM is normally the binding limiter. So RAM usage is predicted, budgeted, and enforced to avoid getting too close to the RAM limits at any moment. The enforced ceiling is the minimum of Physical RAM and Commit (pagefile) limits.

If a layout needs more tiles than fit, it degrades gracefully: navigation still works and un-baked tiles simply don't display. It does not crash or thrash.


Use Cases and Comparisons

TurboGraph would be more efficient than other model graph visualizers when ran on a high VRAM/RAM machine with:

  • Very large models (ex. training graphs, 100k+ nodes) whose hierarchy data is unrecoverable/nonexistent
  • Graphs with high edge density, or otherwise complex graphs

Google's Model Explorer frontend is a great library that works as an excellent comparison. It redraws all geometry every frame using a geometric instancing optimization.

TurboGraph is limited by memory (capacity, then bandwidth), whereas Model Explorer is limited by GPU compute. If benchmarked, as node/edge count increases then whichever resource is scarcer on a machine slows down first. If you add enough of both (memory + fast compute) they become indistinguishable.

Building and running

Clone with submodules — Dear ImGui is a submodule, and the build patches its imconfig.h, so it must be present before configuring:

git clone --recursive https://github.com/eversolea/turboGraph.git

Already cloned without --recursive? Run:

git submodule update --init --recursive

Configure and build (Windows / Visual Studio 2022):

cmake -G "Visual Studio 17 2022" -A x64 -B build
cmake --build build --config Release

Run:

./build/Release/turboGraph.exe

Command-line options

Option Description
--data <name> Dataset: test, large, wide, longedge, grid (default large)
--nodes N Node count for --data grid. Omit to auto-size to available memory
--gpu on|off GPU rendering (default on)
--curve 0..3 Edge shape: 0 = bezier direct, 1 = polyline, 2 = Catmull-Rom, 3 = bezier chain
--commit-overrun GB Let the tile budget exceed free RAM by GB, backed by the pagefile (default 0)
--debug Verbose memory/profiling output
--help Show usage

Start here to see it working at scale — this auto-sizes the graph to your machine's avaliable RAM.

./build/Release/turboGraph.exe --data grid

By default the tile cache is sized to fit in physical RAM, so no tile ever needs to be paged out. --commit-overrun trades that guarantee for a higher tile ceiling, which keeps more of a large graph rendered instead of leaving distant tiles blank.

Warning: this can hang your whole machine for some time, not just this app. Be careful using it with 500k+ nodes. The extra tiles this enables are pagefile-backed, so Windows has to grow the pagefile mid-render — a synchronous disk operation that saturates the disk and > leaves the entire system unresponsive, mouse included, for as long as it takes. Program recovery is not guaranteed: When commit-overrun is set at 4 on a high-RAM machine with a large graph (600k+ nodes) the usual outcome is a hang followed by the app being killed.

./build/Release/turboGraph.exe --data grid --commit-overrun 4

Controls: right-drag to pan, mouse wheel to zoom, left-drag to move nodes, shift left-click to expand/collapse nodes


Quick start: hooking up your own data

image

Your graph lives in GraphData::DataModel (DataModel.h) — pure structure, no positions and no rendering state. You build a model, hand it to a layout engine, and point the view at both. Positions are computed for you.

1. Build nodes and connections

A node needs an id, a name, and its input/output ports. Ports are what edges attach to, by index.

#include "DataModel.h"
using namespace GraphData;

DataModel* model = new DataModel();

Node conv(0, "Conv2D");                            // id, display name
conv.inputs.push_back(Port("input", PortType::IMAGE));
conv.outputs.push_back(Port("output", PortType::IMAGE));
model->AddNode(conv);

Node relu(1, "ReLU");
relu.inputs.push_back(Port("input", PortType::IMAGE));
relu.outputs.push_back(Port("output", PortType::IMAGE));
model->AddNode(relu);

// Conv2D output[0] -> ReLU input[0]
model->AddConnection(Connection(0, 0, 1, 0));

Pass id = -1 (or use the Node() default) to have AddNode() assign the next free id; it returns the id it used. Port types are INT, FLOAT, BOOL, STRING, VECTOR, IMAGE, TEXT, GENERIC and affect port appearance.

2. Add hierarchy (optional)

This is what the expand/collapse physics animates. Mark a parent expandable, list its children, and set each child's parentId:

Node block(2, "ResidualBlock");
block.isExpandable = true;
block.childNodeIds = { 0, 1 };     // Conv2D and ReLU live inside
model->AddNode(block);

model->GetNodeMutable(0)->parentId = 2;
model->GetNodeMutable(1)->parentId = 2;

To forward a child's port to the parent's boundary, use parentInputPortMap / parentOutputPortMap: entry i gives the parent port index that child port i represents, or -1 for internal-only ports. Collapsed parents then keep their external edges connected.

3. Lay it out and display it

LayoutEngine* layout = new LayoutEngine();
layout->SetGridParameters(150.0f, 50.0f, 50.0f, 50.0f);  // nodeW, nodeH, hSpacing, vSpacing

layout->ComputeLayoutLayered(*model);   // hierarchical; follows edge direction, reduces crossings
layout->SetEdgeRenderMode(2);           // Catmull-Rom

graphView.SetDataModel(model);
graphView.SetLayoutengine(layout);

Use ComputeLayoutLayered() for real model topology. The alternative, ComputeGridLayout() (paired with SetGridNodesPerRow()), exists for the synthetic stress-test grids and ignores edge direction.

For the pattern in full — layout, world bounds, tile budget, camera fit — see the node setup block in main.cpp, which does exactly the above for the built-in datasets.

4. Push updates from a backend

When your data changes, dirty only the affected region and let the event loop pick it up:

ImRect changed = /* world-space bounds of what moved */;
tileCache.MarkDirty(changed);
nodesDirty = true;

Call layout->ToggleExpand(nodeId) (or ExpandNode / CollapseNode) to drive hierarchy changes; the physics simulation animates the transition. MarkAllDirty() re-bakes everything, which is expensive on a large graph — prefer a region.


Known bugs

Biggest one is graphs with ~1,000,000 nodes stalls the program with no observable bottlenecks (on 64GB RAM, RTX 3060). Some kind of limit seems to have been reached. Another bug listed in todo.txt. Undiscovered bugs likely exists as this is a Proof-of-Concept.


Project structure

main.cpp                  Entry point: memory budgeting, tile bake loop, compositing, camera, event loop
TileCache.h/cpp           Tile allocation, LRU eviction, visibility
DataModel.h/cpp           Graph structure: nodes, ports, connections, hierarchy
LayoutEngine.h/cpp        Layered + grid layout, expand/collapse state, world bounds
PhysicsLayoutSimulation   SPH fluid simulation for subgraph animation (compute shaders)
GPURenderer.h/cpp         Draw pipelines: nodes, edges, text, circles, particles
BMFont.h/cpp              SDF font atlas loading
nodes.h/cpp               Node drawing + culling (pure logic, no GL)
shaders/                  GLSL: render + compute
imgui/                    Dear ImGui (submodule; ImDrawIdx patched to 32-bit for >65k vertices)
sdl2/                     SDL2 (Windows x64)

OpenGL use is concentrated in GPURenderer.cpp (draw pipelines and compute passes), PhysicsLayoutSimulation.cpp (SPH compute + SSBOs), TileCache.cpp (FBO/texture allocation) and main.cpp (context, per-tile FBO binds, frame loop). nodes.cpp, LayoutEngine and DataModel contain no GL calls.

About

A GPU-accelerated node graph viewer designed for AI model architectures, with fluid physics animating subgraphs as they expand and collapse — tested at over 600,000 nodes and 3 million edges visible, and limited only by available RAM. PROOF-OF-CONCEPT

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors

Languages