Synchronization
vk-graph provides a high-performance abstraction over Vulkan synchronization which retains the
low driver overhead of correctly synchronized command buffers.
Pipeline Barriers
Vulkan specifies that resources and pipelines will have synchronized access when barriers are inserted into the command stream. Unsynchronized access results in undefined behavior.
Tip
Unsynchronized access may be detected through debug assertions or Vulkan SDK debugging layers.
Access Type Abstraction
vk-graph uses an enumeration of possible states to define all supported pipeline barriers in an
easy-to-use way.
Sample access types:
| Type | Usage |
|---|---|
AccessType::General | Covers any access - useful for debug, generally avoid for performance reasons |
AccessType::ColorAttachmentWrite | Written as a color attachment during rendering |
AccessType::ComputeShaderReadUniformBuffer | Read as a uniform buffer in a compute shader |
AccessType::AccelerationStructureBuildInputRead | Read geometry or instance input buffers during AS build/update |
AccessType::AccelerationStructureBuildScratchReadWrite | Read and write AS build/update scratch storage |
AccessType::AccelerationStructureBuildRead | Read source or referenced acceleration structures |
AccessType::AccelerationStructureBuildWrite | Write destination acceleration structures |
AccessType::MicromapBuildInputRead | Read encoded opacity or triangle metadata during a micromap build |
AccessType::MicromapBuildScratchReadWrite | Read and write micromap build scratch storage |
AccessType::MicromapBuildWrite | Write a micromap during build, copy, or deserialization |
AccessType::MicromapBuildRead | Read a micromap during copy, serialization, or property query |
AccessType::MicromapBuildBufferRead | Read a serialized buffer during micromap deserialization |
AccessType::MicromapBuildBufferWrite | Write a serialized buffer during micromap serialization |
AccessType::AccelerationStructureBuildMicromapRead | Read a completed micromap during a BLAS build |
Resource Access
The required access varies depending on the function being called and what the Vulkan specification requires for a given command.
Generally, access must be specified before each command uses a resource. It appears as an “access” function call:
#![allow(unused)]
fn main() {
graph
.begin_cmd()
.resource_access(some_buffer, AccessType::TransferRead)
.resource_access(some_image, AccessType::TransferWrite)
.record_cmd(|cmd| {
// we are synchronized!
// You may:
// - Read some_buffer
// - Write some_image
});
}
Resource access is specified for and consumed by the following command buffer recording. For multiple accesses, use multiple “access” and “record” function calls:
#![allow(unused)]
fn main() {
graph
.begin_cmd()
.resource_access(buffer, AccessType::TransferRead)
.resource_access(image, AccessType::TransferWrite)
.record_cmd(|cmd| {
// Safe to copy buffer to image
})
.resource_access(image, AccessType::TransferRead)
.resource_access(buffer, AccessType::TransferWrite)
.record_cmd(|cmd| {
// Safe to copy image to buffer
});
}
AS and micromap build structs contain device addresses, which the graph cannot map to resources. Bind
and declare the encoded-data, triangle-array, scratch, optional index, destination micromap, and BLAS
resources. Put build_micromaps and the consuming BLAS build in separate recordings so the graph can
insert the MicromapBuildWrite to AccelerationStructureBuildMicromapRead dependency.
The unsafe build_acceleration_structures and build_acceleration_structures_indirect commands
use the same declarations for BUILD and UPDATE. Declare both AS read and write access for in-place
updates, and AccessType::AccelerationStructureBuildIndirectRead for indirect AS range buffers.
IndirectBuffer maps to
DRAW_INDIRECT, not the required ACCELERATION_STRUCTURE_BUILD_KHR / INDIRECT_COMMAND_READ
combination. General remains a valid conservative fallback using ALL_COMMANDS
with memory read/write access, but may synchronize more work than needed. No dependencies
are inserted between entries of a build batch. Borrowed geometry and micromap usage slices must remain
alive through recording; resources referenced by address must remain alive through GPU execution. Declarations
do not validate address ranges, input contents, build flags, or non-overlap requirements. See the
AS command contracts and
micromap lifetime rules, including discardable.
Shader Resource Access
When a resource (buffer, image, or acceleration structure) is accessed from a shader the
shader_resource_access function is used:
// clear_image.glsl
#version 460 core
#pragma shader_stage(compute)
layout(binding = 42, rgba8) writeonly uniform image2D dstImage;
void main() {
imageStore(
dstImage,
ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y),
vec4(0)
);
}
#![allow(unused)]
fn main() {
let mut graph = Graph::default();
let fmt = vk::Format::R8G8B8A8_UNORM;
let usage = vk::ImageUsageFlags::STORAGE;
let info = ImageInfo::image_2d(32, 32, fmt, usage);
let image = graph.bind_resource(Image::create(
device,
info,
)?);
graph
.begin_cmd()
.bind_pipeline(ComputePipeline::create(
device,
ComputePipelineInfo::default(),
include_bytes!("clear_image.spv").as_slice(),
)?)
.shader_resource_access(42, image, AccessType::ComputeShaderWrite)
.record_cmd(|cmd| {
cmd.dispatch(32, 32, 1);
});
}
Subresource Access
Buffer ranges and image views are referred to as subresource ranges and accessed using “subresource” function variants:
#![allow(unused)]
fn main() {
let mut graph = Graph::default();
let fmt = vk::Format::R8G8B8A8_UNORM;
let usage = vk::ImageUsageFlags::STORAGE;
let info = ImageInfo::image_2d(32, 32, fmt, usage);
let image = graph.bind_resource(Image::create(
device,
info,
)?);
graph
.begin_cmd()
.bind_pipeline(ComputePipeline::create(
device,
ComputePipelineInfo::default(),
include_bytes!("clear_image.spv").as_slice(),
)?)
.shader_subresource_access(42, image, info, AccessType::ComputeShaderWrite)
.record_cmd(|cmd| {
cmd.dispatch(32, 32, 1);
});
}
Built-In Commands
The commands directly attached to a Graph, such as Graph::copy_buffer_to_image, do not require
any access function calls.
The source code for these built-in commands uses public graph functions and provides good examples of typical usage.