🧠 Render Graph (Host-Defined)

Vulfram lets the host define a render graph using logical IDs. The core validates the graph, compiles an execution plan, and runs it each frame.

If the graph is missing or invalid, the core falls back to a safe default pipeline so rendering always continues.


1. Why a Host-Defined Graph

  • Host control: you decide the pass order and dependencies.
  • Logical IDs only: no GPU handles leak to the host.
  • Reusable plans: the core compiles a plan from your graph and uses it each frame.
  • Robust: invalid graphs use a fallback automatically.

2. Core Flow

  1. Host builds a graph using IDs and descriptors.
  2. Core validates and compiles a plan.
  3. Core runs the plan each frame.
  4. If validation fails and fallback=true, the fallback plan is used.

3. Graph Structure (Host Payload)

Graph

Field Type Description
graphId LogicalId Cache key for compiled plans
nodes Node[] Render nodes (passes)
edges Edge[] Dependencies between nodes
resources Resource[] Declared logical resources
fallback bool Use fallback if graph is invalid

Node

Field Type Description
nodeId LogicalId Node identifier
passId string Pass type (ex: "forward")
inputs LogicalId[] Resource IDs read by this node
outputs LogicalId[] Resource IDs written by this node
params Map Optional parameters (clear flags, etc.)

Resource

Field Type Description
resId LogicalId Resource identifier
kind string "texture", "buffer", "attachment"
desc Map Logical descriptor (format, size, usage)
lifetime string "frame" or "persistent"
aliasGroup LogicalId? Optional memory aliasing group

Edge

Field Type Description
fromNodeId LogicalId Dependency source
toNodeId LogicalId Dependency target
reason string? Optional hint (read/write ordering)

LogicalId

Logical IDs can be strings or numbers. The core resolves them to internal handles for the active plan.


4. Minimal Example

json
{
  "graphId": "main_render",
  "nodes": [
    { "nodeId": "shadow_pass", "passId": "shadow", "inputs": [], "outputs": ["shadow_atlas"] },
    { "nodeId": "forward_pass", "passId": "forward", "inputs": ["shadow_atlas"], "outputs": ["hdr_color", "depth"] },
    { "nodeId": "compose_pass", "passId": "compose", "inputs": ["hdr_color"], "outputs": ["swapchain"] }
  ],
  "edges": [
    { "fromNodeId": "shadow_pass", "toNodeId": "forward_pass" },
    { "fromNodeId": "forward_pass", "toNodeId": "compose_pass" }
  ],
  "resources": [
    { "resId": "shadow_atlas", "kind": "texture", "desc": { "format": "depth24", "size": "shadow_res" }, "lifetime": "frame" },
    { "resId": "hdr_color", "kind": "texture", "desc": { "format": "rgba16f", "size": "screen" }, "lifetime": "frame" },
    { "resId": "depth", "kind": "texture", "desc": { "format": "depth24", "size": "screen" }, "lifetime": "frame" },
    { "resId": "swapchain", "kind": "attachment", "desc": { "format": "swapchain" }, "lifetime": "frame" }
  ],
  "fallback": true
}

5. Validation Rules (Core)

  • DAG only: no cycles allowed.
  • Resources exist: inputs/outputs must be declared in resources.
  • Pass compatibility: passId must be a known core pass type.

If validation fails, the core uses the fallback graph if enabled.


6. Fallback Graph

The fallback graph is the default pipeline the engine can always execute. It typically looks like:

text
shadow -> forward -> compose

7. Performance Notes

  • Alias groups: reserved for future memory reuse (currently not applied by the core).
  • Frame lifetime: lifetime = "frame" is recycled automatically.
  • Validate on change: the hot path stays minimal.
Documentation Vulfram Core