-
Notifications
You must be signed in to change notification settings - Fork 2
Render Graph
SparkEngine uses a declarative render graph (frame graph) system to define the rendering pipeline each frame. Passes declare their resource dependencies, and the graph compiler performs topological sorting, dead-code elimination, lifetime analysis, and resource aliasing automatically.
Source: SparkEngine/Source/Graphics/RenderGraph.h (umbrella), SparkEngine/Source/Graphics/RenderGraph/
Namespace: Spark::Graphics
Tests: Tests/TestRenderGraph.cpp (25 test cases)
- Overview
- Core Concepts
- RenderGraph API
- StandardPipelineBuilder
- Transient Resource Pool
- GraphViz Export
- Integration
- See Also
The render graph replaces a hardcoded render loop with a data-driven pipeline. Instead of manually managing render targets, barriers, and pass ordering, each pass declares what it reads and writes. The graph compiler resolves the optimal execution order and resource lifetimes.
┌─────────────────────────────────────────────────────────────────┐
│ StandardPipelineBuilder │
│ (Builds the standard deferred pipeline from configuration) │
├─────────────────────────────────────────────────────────────────┤
│ RenderGraph │
│ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │
│ │ Shadow │──│ GBuffer │──│ Lighting │──│ PostProc │──... │
│ │ Pass │ │ Pass │ │ Pass │ │ Pass │ │
│ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │
│ | | | | │
│ v v v v │
│ [shadowAtlas] [albedo,norm] [hdrColor] [ldrColor] │
│ [material,mv] [bloom,ssao] │
│ [depth] │
├─────────────────────────────────────────────────────────────────┤
│ TransientResourcePool │
│ (age-based GPU resource recycling) │
├─────────────────────────────────────────────────────────────────┤
│ RenderGraphBlackboard │
│ (type-erased inter-pass data sharing) │
└─────────────────────────────────────────────────────────────────┘
| File | Responsibility |
|---|---|
RenderGraph.h |
Umbrella header + RenderGraph class (AddPass/Compile/Execute) |
RenderGraphTypes.h |
Resource handles, descriptors, enums, registry, stats |
RenderGraphPass.h |
Pass and builder classes for declaring dependencies |
RenderGraphBlackboard.h |
Type-erased data sharing between passes |
RenderGraph/RenderGraphBuilder.h |
StandardPipelineBuilder and pass data structs |
RenderGraph/TransientResourcePool.h |
Age-based GPU resource pooling |
RenderGraph/RenderGraphExporter.h |
GraphViz .dot file export for debugging |
A render pass is a unit of GPU work (graphics, compute, copy, or async compute). Each pass declares:
- Reads: Resources consumed (e.g., shadow atlas for lighting)
- Writes: Resources produced (e.g., GBuffer textures)
- Creates: New transient resources allocated for this pass
- Side effects: Passes that write to the backbuffer or perform I/O
Passes with no consumers for their outputs are eliminated during compilation (dead-code elimination).
Resources are identified by RenderGraphResource handles — lightweight IDs that reference textures or buffers within the graph. Resources can be:
-
Transient: Allocated and released within a single frame by the
TransientResourcePool - Imported: External resources (e.g., the backbuffer) brought into the graph
Resource descriptors (RenderGraphTextureDesc) specify dimensions, format, and usage flags.
The RenderGraphBlackboard provides type-erased data sharing between passes. Each pass can write structured data (e.g., GBufferPassData) to the blackboard, and downstream passes read it to access resource handles.
RenderGraphPass& AddPass(
const std::string& name,
RenderGraphPassType type,
std::function<void(RenderGraphBuilder&)> setup,
std::function<void(const RenderGraphResourceRegistry&)> execute);The setup lambda receives a RenderGraphBuilder to declare resource dependencies. The execute lambda is called at execution time with a registry to resolve handles to GPU objects.
RenderGraph graph("MainFrame", d3dDevice);
graph.AddPass("ToneMapping", RenderGraphPassType::Compute,
[&](RenderGraphBuilder& builder)
{
hdrInput = builder.Read(hdrInput);
ldrOutput = builder.Write(ldrOutput);
},
[=](const RenderGraphResourceRegistry& registry)
{
auto* hdr = registry.GetTexture(hdrInput);
auto* ldr = registry.GetTexture(ldrOutput);
// dispatch tone mapping compute shader ...
});graph.Compile();Compilation performs:
- Topological sort — Orders passes by dependency
- Dead-code elimination — Removes passes with no consumers
- Lifetime analysis — Determines when each resource is first used and last used
- Resource aliasing — Reuses memory for non-overlapping resources
- Barrier placement — Inserts resource transitions between passes
graph.Execute();Allocates transient resources via the pool, runs passes in compiled order, and releases transient resources. The graph is single-use — call Clear() or destroy it after execution.
The StandardPipelineBuilder constructs SparkEngine's canonical deferred rendering pipeline as a RenderGraph. It is the primary way most rendering code interacts with the graph system.
ShadowPass
|
v
GBufferPass ─────┐
| |
v v
LightingPass (reads GBuffer + Shadow)
|
v
PostProcessPass (reads HDR color, motion, depth)
|
v
UIPass (reads LDR color, writes composited output) [side-effect]
|
v
DebugPass (optional, reads depth) [side-effect]
PipelineConfig controls which passes are enabled and their parameters:
| Setting | Default | Description |
|---|---|---|
renderWidth / renderHeight
|
1920 x 1080 | Output resolution |
renderScale |
1.0 | Internal resolution multiplier |
shadowsEnabled |
true | Enable shadow pass |
shadowMapSize |
2048 | Shadow atlas resolution |
shadowCascades |
3 | Cascade shadow map count |
deferredEnabled |
true | Enable deferred GBuffer pass |
gBufferCount |
4 | GBuffer targets (Albedo, Normal, Material, Motion) |
hdrEnabled |
true | HDR lighting |
hdrFormat |
RGBA16_FLOAT | HDR render target format |
bloomEnabled |
true | Post-process bloom |
ssaoEnabled |
false | Screen-space ambient occlusion |
taaEnabled |
false | Temporal anti-aliasing |
motionBlurEnabled |
false | Motion blur |
uiEnabled |
true | UI compositing pass |
debugPassEnabled |
false | Debug visualization pass |
PipelineFrameData provides per-frame camera and timing data:
struct PipelineFrameData
{
XMMATRIX viewMatrix;
XMMATRIX projMatrix;
XMFLOAT3 cameraPosition;
float nearPlane;
float farPlane;
float deltaTime;
};PipelineCallbacks holds user-supplied lambdas that perform actual GPU work in each pass:
struct PipelineCallbacks
{
using ExecuteFn = std::function<void(
const RenderGraphResourceRegistry&,
const PipelineFrameData&)>;
ExecuteFn shadowExecute;
ExecuteFn gBufferExecute;
ExecuteFn lightingExecute;
ExecuteFn postProcessExecute;
ExecuteFn uiExecute;
ExecuteFn debugExecute;
};StandardPipelineBuilder pipelineBuilder;
pipelineBuilder.Configure(config);
pipelineBuilder.SetFrameData(frameData);
pipelineBuilder.SetCallbacks(callbacks);
// Each frame:
RenderGraph graph("MainFrame", d3dDevice);
pipelineBuilder.Build(graph);
graph.Compile();
graph.Execute();Each pass writes structured output to the blackboard for downstream passes:
| Struct | Pass | Contents |
|---|---|---|
ShadowPassData |
Shadow |
shadowAtlas, cascadeCount
|
GBufferPassData |
GBuffer |
albedo, normals, material, motion, depth
|
LightingPassData |
Lighting | hdrColor |
PostProcessPassData |
PostProcess |
ldrColor, bloom, ssao
|
UIPassData |
UI | composited |
The TransientResourcePool manages GPU resources that are allocated and released each frame by render graph passes. Resources are recycled based on descriptor matching and garbage-collected when idle.
auto& pool = TransientResourcePool::GetInstance();
pool.Initialize(4); // destroy resources idle for 4+ frames
pool.BeginFrame(frameIndex);
uint64_t handle = pool.AcquireResource(desc);
// ... use resource ...
pool.ReleaseResource(handle);
pool.GarbageCollect();| Method | Description |
|---|---|
Initialize(maxIdleFrames) |
Set up pool with idle frame threshold |
BeginFrame(frameIndex) |
Mark start of new frame for age tracking |
AcquireResource(desc) |
Get a matching pooled resource or create new |
ReleaseResource(handle) |
Return resource to pool (not destroyed) |
GarbageCollect() |
Destroy resources idle beyond threshold |
GetPooledResourceCount() |
Total resources in pool |
GetActiveResourceCount() |
Resources currently in use |
GetEstimatedMemoryUsage() |
Approximate VRAM usage in bytes |
Resources are matched by width, height, format, and usage flags. Debug names are ignored during matching.
The RenderGraphExporter dumps the pass dependency graph as a .dot file for visualization:
std::vector<RenderPassInfo> passes = { /* ... */ };
RenderGraphExporter::ExportGraphViz(passes, "debug/render_graph.dot");
// Or get the DOT string directly:
std::string dot = RenderGraphExporter::GenerateDotString(passes);Render the output with dot -Tpng render_graph.dot -o render_graph.png.
-
GraphicsEngine: Owns the
StandardPipelineBuilderand callsBuild()/Compile()/Execute()each frame - RHI backends: The graph uses RHI abstractions for resource creation and barrier management
-
Quality settings:
PipelineConfigcan be changed between frames when the player adjusts quality -
Console:
GetPipelineSummary()andTransientResourcePool::Console_GetStatus()provide debug output
- Rendering and Graphics — Overall graphics architecture
- RHI Abstraction Layer — Hardware abstraction used by the graph
- Shader Pipeline — Shader compilation and management
- Profiler and Debugging — GPU timing and profiling
Published from ad9c3c1953cc. Edit the canonical source in wiki/.
- Documentation
- Docs route
- Wiki index
- Guides
- Tutorials
- Samples
- Examples
- API Reference
- API route
- Reference
- Build Guide
- Dependencies
- FAQ
- Changelog
- Roadmap
- Contributing
- Code of Conduct
- Home
- FAQ
- Getting Started
- Quick-Start Tutorial
- Making Your First Game
- Making Your First Multiplayer Game
- Artist Workflow Guide
- Editor Walkthrough
- Migration Guide
- How SparkEngine Works
- Architecture Overview
- Engine Architecture Flowchart
- Creating a Game Module
- Game Modules (catalog)
- Entity Component System
- Rendering and Graphics
- Physics
- Cloth Simulation
- Audio
- Input System
- Camera System
- Scripting with AngelScript
- Visual Scripting
- AI and Navigation
- Animation
- 2D Systems
- Networking
- Dedicated Server
- Multiplayer Quick Start
- Area Server Architecture
- Scene Management
- Large World Support
- Collaborative Editing
- Coroutine System
- Event System
- Event Response System
- Job System
- UI System
- UI Layout Extensions
- Localization
- Dialogue System
- Destruction System
- Replay System
- Achievement System
- Loading System
- Mod System
- Content Delivery
- Tween System
- Memory Integrity
- Gameplay Systems
- Terrain and Procedural Generation
- Save System
- Persistence System
- Day Night Cycle and Weather
- Cinematic Sequencer
- Runtime Prefabs
- SparkEditor
- Editor Tutorials
- SparkConsole
- SparkDaemon
- Shader Pipeline
- Asset Pipeline
- Asset Validation
- Asset Migration
- Game Packaging
- Online Services
- DataTable System
- Loot and Crafting System
- CSG System
- Font System
- Timer Manager
- Movie Render Pipeline
- HLOD and World Partition
- Remote Debug System
- Selection Manager
- Asset Dependency Graph
- Editor Automation
- File Watcher
- Project Templates
- System Requirements
- VR Support
- Mobile Platform
- Accessibility
- Platform Input
- Cross-Compilation: Wine Testing
- RHI Abstraction Layer
- D3D11 Backend
- D3D12 Backend
- Vulkan Backend
- OpenGL Backend
- Metal Backend
- DXR Raytracing
- Hybrid Ray Tracing
- Upscaling (DLSS/FSR)
- Render Graph
- Shader Graph
- GPU Particles
- GPU-Driven Rendering
- Volumetric Fog
- Volumetric Clouds
- Global Illumination
- Virtual Texturing
- Water Rendering
- Clustered Lighting
- Material System
- Post-Processing
- Shadow System
- Particle System
- Decal System
- Sky and Atmosphere
- Foliage System
- Mesh Shaders
- Neural Rendering
- Configuration Reference
- Performance Tips
- Benchmark Framework
- Threading Model
- Memory Safety
- Memory Management Patterns
- Build System and CMake Modules
- Profiler and Debugging
- Performance Profiling Guide
- Telemetry System
- Golden Image Testing
- Utilities
- Testing
- Codebase Statistics
- Codebase Health
- Error Handling Patterns
- Hot Reload Overview
- Troubleshooting
- Contributing
- Workflow Patterns
- Build Optimizations
- CI Reproducible Builds
- GitHub API and PR Checks
- Git Rebase Conflicts
- Clang-Format
- Code Quality Violations
- AI Bloat Pattern
- MinGW + Wine Cross-Compilation
- Live Editor Testing
- Engine & Renderer Landscape
- DuetOS Portability Catalog
- Five-Engine Analysis
- Eleven-Engine Analysis
- ThorVG / Unity Graphics Analysis
- Advanced Techniques Catalog
- Third-Party Library Evaluation
- Engine Viability Evaluation
- Engine Feature Recommendations
- Project Recommendations
- Mac Compatibility Analysis
- Codebase Observations
- Codebase Bloat Audit
- Test Suite Audit
- Documentation Coverage Audit
- ThirdParty Dependencies Audit
- Load Test Baseline
- Gameplay Systems Status
- SparkGame Module Status
- Stub and Abandoned Features
- Memory Integrity System
- Memory Safety Evaluation
- Hardware Acceleration Systems
- Jolt Physics Integration
- GPU/CPU Separation Plan
- Daemon Services Architecture
- Reflection & Polymorphism Refactoring Plan
- SparkBuild In-Tree
- Wine No-JobSystem Breakthrough
- Wine Role and Fallback Tiers