Core Docs vulfram-core@0.16

Render Graph (Internal)

This document describes the internal render graph format. The host does not build graphs; the core owns and executes them. If a graph is missing or invalid, the core executes a safe fallback graph. Render graphs are defined per Realm (3D or 2D), not per window.

The core infers any missing resources from node inputs/outputs using default values (texture + frame lifetime).

Goals

  • Host control: The host defines the render sequence and dependencies.
  • Logical IDs only: The host never sees GPU handles or internal IDs.
  • Performance: Minimal per-frame overhead, cacheable plan, and reusable resources.
  • Robustness: Invalid or missing graphs fall back to a default graph.

High-Level Flow

  1. The core constructs a render graph using logical IDs.
  2. The core validates the graph and compiles an execution plan.
  3. On failure, the core uses a fallback graph.

Graph Structure

Graph

Field Type Description
graphId LogicalId Logical graph identifier (cache key)
nodes Node[] Render nodes
edges Edge[] Dependencies between nodes
resources Resource[] Declared resources
fallback bool If true, use fallback when invalid or missing

Node

Field Type Description
nodeId LogicalId Logical node identifier
passId LogicalId Logical pass type (e.g., "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 Logical resource identifier
kind string "texture", "buffer", "attachment" (defaults to "texture")
lifetime string "frame" or "persistent" (defaults to "frame")
aliasGroup LogicalId? Optional alias group for memory reuse

Edge

Field Type Description
fromNodeId LogicalId Dependency source
toNodeId LogicalId Dependency target
reason string? Optional: "read_after_write", "write_after_read"

LogicalId

Logical IDs can be strings or numeric values. The core maps them to internal IDs once per graphId and caches the result to avoid per-frame cost.

Known Pass IDs

  • shadow
  • light-cull
  • skybox
  • forward
  • outline
  • ssao
  • ssao-blur
  • bloom
  • post
  • compose
  • ui

Bloom uses the emissive output from the forward pass when available and falls back to the HDR color buffer otherwise.

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": "outline_pass",
      "passId": "outline",
      "inputs": ["depth"],
      "outputs": ["outline_color"]
    },
    {
      "nodeId": "ssao_pass",
      "passId": "ssao",
      "inputs": ["depth"],
      "outputs": ["ssao_raw"]
    },
    {
      "nodeId": "ssao_blur_pass",
      "passId": "ssao-blur",
      "inputs": ["ssao_raw", "depth"],
      "outputs": ["ssao_blur"]
    },
    {
      "nodeId": "bloom_pass",
      "passId": "bloom",
      "inputs": ["hdr_color"],
      "outputs": ["bloom_color"]
    },
    {
      "nodeId": "post_pass",
      "passId": "post",
      "inputs": ["hdr_color", "outline_color", "ssao_blur", "bloom_color"],
      "outputs": ["post_color"]
    },
    {
      "nodeId": "compose_pass",
      "passId": "compose",
      "inputs": ["post_color"],
      "outputs": ["swapchain"]
    }
  ],
  "edges": [
    { "fromNodeId": "shadow_pass", "toNodeId": "forward_pass" },
    { "fromNodeId": "forward_pass", "toNodeId": "outline_pass" },
    { "fromNodeId": "forward_pass", "toNodeId": "ssao_pass" },
    { "fromNodeId": "ssao_pass", "toNodeId": "ssao_blur_pass" },
    { "fromNodeId": "ssao_blur_pass", "toNodeId": "post_pass" },
    { "fromNodeId": "forward_pass", "toNodeId": "bloom_pass" },
    { "fromNodeId": "bloom_pass", "toNodeId": "post_pass" },
    { "fromNodeId": "outline_pass", "toNodeId": "post_pass" },
    { "fromNodeId": "post_pass", "toNodeId": "compose_pass" }
  ],
  "resources": [
    { "resId": "shadow_atlas" },
    { "resId": "hdr_color" },
    { "resId": "depth" },
    { "resId": "outline_color" },
    { "resId": "ssao_raw" },
    { "resId": "ssao_blur" },
    { "resId": "bloom_color" },
    { "resId": "post_color" },
    { "resId": "swapchain", "kind": "attachment" }
  ],
  "fallback": true
}

Validation Rules (Core)

  • DAG only: No cycles.
  • Resources exist: Every input/output refers to a declared resource.
  • Write ordering: A resource must be produced before it is read.
  • Pass compatibility: Each pass_id must be a known core pass type.
  • Format compatibility: Pass requirements must match declared formats/usages.

If validation fails, the core switches to the fallback graph.

Fallback Graph

The fallback graph represents the default rendering pipeline that always works. It is used when:

  • The host provides no graph.
  • The provided graph fails validation.

Example fallback:

Snippet
shadow -> forward -> outline + ssao -> ssao-blur + bloom -> post -> compose

Performance Notes

  • Cache per graphId: Compile once, reuse execution plan and resource layout.
  • Alias groups: Allow the core to reuse memory for non-overlapping resources.
  • Frame lifetime: lifetime = "frame" resources are recycled automatically.
  • Minimal validation on hot path: Validate only when the graph changes.
Documentation Vulfram Core