Frame Graph¶
The frame graph is Zest's core execution model. Instead of manually managing barriers, semaphores, and resource states, you declare a graph of render passes with their dependencies, and Zest handles the rest.
Why Frame Graphs?¶
Traditional Vulkan requires explicit synchronization:
// Manual barrier insertion
vkCmdPipelineBarrier(cmd,
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT,
0, 0, nullptr, 0, nullptr, 1, &imageBarrier);
// Explicit semaphore management
VkSubmitInfo submit = {...};
submit.waitSemaphoreCount = 1;
submit.pWaitSemaphores = &renderFinished;
This is error-prone and verbose. Frame graphs solve this by:
- Automatic barriers - Inserted based on resource usage
- Automatic synchronization - Semaphores between queues
- Pass culling - Unused passes are removed
- Resource aliasing - Transient resources can share memory
Basic Structure¶
A frame graph is built by declaring resources and passes:
if (zest_BeginFrameGraph(context, "My Graph", &cache_key)) {
// 1. Import external resources
zest_ImportSwapchainResource();
// 2. Create transient resources (optional)
zest_resource_node depth = zest_AddTransientImageResource("Depth", &depth_info);
zest_resource_node shadow_map = zest_AddTransientImageResource("Shadows", &shadow_info);
// 3. Define passes
zest_BeginRenderPass("Shadow Pass"); {
zest_ConnectOutput(shadow_map);
zest_SetPassTask(RenderShadows, app);
zest_EndPass();
}
zest_BeginRenderPass("Main Pass"); {
zest_ConnectInput(shadow_map);
zest_ConnectSwapChainOutput();
zest_ConnectOutput(depth);
zest_SetPassTask(RenderScene, app);
zest_EndPass();
}
// 4. Compile
frame_graph = zest_EndFrameGraph();
}
Pass Types¶
Zest supports three types of passes:
| Pass Type | Function | Use Case |
|---|---|---|
| Render | zest_BeginRenderPass | Drawing with graphics pipelines |
| Compute | zest_BeginComputePass | Compute shader dispatch |
| Transfer | zest_BeginTransferPass | Data uploads and copies |
See Passes for detailed documentation.
Resource Types¶
Resources flow through the graph as inputs and outputs:
| Resource Type | Description |
|---|---|
| Swapchain | The window's presentation surface |
| Imported | Existing images/buffers from outside the graph (only required to import if they require writing to or in an initial state that requires them to be transitioned with a barrier) |
| Transient | Frame-lifetime resources allocated by the graph |
See Resources for detailed documentation.
Topics¶
| Page | Description |
|---|---|
| Passes | Render, compute, and transfer passes |
| Resources | Importing, transient resources, and resource groups |
| Execution | Building, caching, and executing frame graphs |
| Debugging | Debug output and optimization flags |
Example: Deferred Renderer¶
if (zest_BeginFrameGraph(context, "Deferred Renderer", &cache_key)) {
zest_ImportSwapchainResource();
// Transient G-buffer
zest_resource_node albedo = zest_AddTransientImageResource("Albedo", &rgba_info);
zest_resource_node normal = zest_AddTransientImageResource("Normal", &rgba_info);
zest_resource_node depth = zest_AddTransientImageResource("Depth", &depth_info);
// Pass 1: G-Buffer
zest_BeginRenderPass("G-Buffer"); {
zest_ConnectOutput(albedo);
zest_ConnectOutput(normal);
zest_ConnectOutput(depth);
zest_SetPassTask(RenderGBuffer, app);
zest_EndPass();
}
// Pass 2: Lighting
zest_BeginRenderPass("Lighting"); {
zest_ConnectInput(albedo);
zest_ConnectInput(normal);
zest_ConnectInput(depth);
zest_ConnectSwapChainOutput();
zest_SetPassTask(RenderLighting, app);
zest_EndPass();
}
frame_graph = zest_EndFrameGraph();
}
See Also¶
- First Application - Basic frame graph usage
- Multi-Pass Tutorial - Advanced frame graph patterns
- Frame Graph API - Complete function reference