How subpassLoad Reads Input Attachments in Vulkan GLSL
This article explains how the subpassLoad GLSL function
accesses pixel data from input attachments inside Vulkan render passes.
It covers the mechanics of pixel-local data fetching, the distinction
between standard texture sampling and input attachments, memory
interactions on tile-based architectures, and the required shader syntax
and pipeline dependencies.
The Purpose of Input Attachments
In Vulkan, an input attachment is a specialized image view bound to a render pass subpass that allows a fragment shader to read values previously written by an earlier subpass. Unlike standard textures accessed via samplers, input attachments are restricted to reading only the data associated with the current fragment's pixel coordinate.
This restriction enables significant performance optimizations, particularly on Tile-Based Deferred Rendering (TBDR) architectures commonly found in mobile devices. By constraining reads to the current pixel, the GPU can keep framebuffer data within high-speed, on-chip tile memory (transient storage) between subpasses, entirely bypassing costly roundtrips to main system memory (VRAM).
How subpassLoad Operates
The subpassLoad function acts as an intrinsic fetch
operation. When invoked within a fragment shader, it retrieves the
color, depth, or stencil data directly associated with the executing
fragment's coordinates (gl_FragCoord.xy).
Key characteristics of subpassLoad include:
- Implicit Coordinate Mapping: Unlike
texture()ortexelFetch(),subpassLoaddoes not take UV coordinates or integer texel coordinates as an argument. The hardware automatically maps the read operation to the exact pixel location of the current fragment. - No Filtering: The fetch does not perform bilinear, trilinear, or anisotropic filtering. It retrieves raw, un-normalized texel data directly from the attachment.
- Multisample Support: For multisampled attachments,
subpassLoadaccepts an integer sample index to fetch a specific sample from the pixel location.
GLSL Declaration and Syntax
To use subpassLoad, the attachment must be declared in
GLSL as a subpassInput uniform type with an explicit
input_attachment_index.
#version 450
// Declare the input attachment bound to index 0
layout(input_attachment_index = 0, set = 0, binding = 0) uniform subpassInput uDepthAttachment;
layout(input_attachment_index = 1, set = 0, binding = 1) uniform subpassInput uColorAttachment;
// Multisampled variants use subpassInputMS
layout(input_attachment_index = 2, set = 0, binding = 2) uniform subpassInputMS uMSAttachment;
layout(location = 0) out vec4 outFinalColor;
void main()
{
// Reading single-sampled attachments:
vec4 previousColor = subpassLoad(uColorAttachment);
float depthValue = subpassLoad(uDepthAttachment).r;
// Reading multisampled attachments requires an explicit sample index:
int sampleIndex = 2;
vec4 msColor = subpassLoad(uMSAttachment, sampleIndex);
outFinalColor = previousColor * depthValue;
}The input_attachment_index maps directly to the array
index specified in the pInputAttachments field of the
VkSubpassDescription structure on the host side.
Synchronization and Subpass Dependencies
Because subpassLoad reads data produced within the same
render pass, proper execution order and memory visibility must be
enforced via VkSubpassDependency. Without explicit
dependencies, the GPU may attempt to read an attachment before the
writing subpass has completed its output.
A typical dependency configuration includes:
- Source Stage:
VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT(orEARLY_FRAGMENT_TESTS_BIT/LATE_FRAGMENT_TESTS_BITfor depth). - Destination Stage:
VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT. - Source Access Mask:
VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT(orDEPTH_STENCIL_ATTACHMENT_WRITE_BIT). - Destination Access Mask:
VK_ACCESS_INPUT_ATTACHMENT_READ_BIT. - Dependency Flags: Typically marked with
VK_DEPENDENCY_BY_REGION_BIT, which specifies that the dependency is local to the frame's sub-regions or tiles, enabling hardware tile caches to remain un-flushed.
Architectural Constraints
Because subpassLoad strictly binds to the current
fragment coordinate, it cannot be used for operations requiring
neighborhood pixel awareness, such as Gaussian blurs, bloom, or
post-process edge detection. When neighbor-pixel sampling is necessary,
the render pass must be ended, and the image must be bound as a standard
sampled texture (sampler2D) using standard texture
coordinates.