The Vulkan tutorial describes how to use Uniform Buffers to communicate data to a shader program. One disadvantage of uniform buffers is that when we have multiple objects to render with different uniform values, we must allocate multiple uniform buffers and descriptor sets.
Vulkan provides an alternative, but limited, method for supplying data to shaders called push constants. These values are encoded directly into the recorded stream of commands, which makes it easy to change them on a per-mesh or per-pass basis.
To access push constants in our shader code, we use the following syntax:
layout (push_constant) uniform constants {
mat4 MVP;
vec4 color;
} pcBuffer;
In our C++ code, we define a struct with the same layout
struct PCBuffer {
alignas(16) mat4 MVP;
alignas(16) vec4 color;
};
When recording the command buffer, we can set the values of the push constants
using the pushConstants
method of the
command buffer.
For example,
PCBuffer pConsts { projMat * viewMat * model->toWorld, model->color };
cmdBuf->pushConstants (
pipelineLayout, // graphics-pipeline layout
stages, // shader stages where constants are used
0, // offset of push-constant range being updated
sizeof(PCBuffer), // size of push-constant range being updated
&pConsts); // pointer to push-constant data
In order to use push constants, we have to include information about them
when defining the layout for the graphics pipeline. The
vk::PipelineLayoutCreateInfo
struct has fields for specifying
push constant ranges.
vk::PushConstantRange pcRange = {
stages, // shader stages where constants are used
0, // offset of push-constant range being updated
sizeof(PCBuffer), // size of push-constant range being updated
};
vk::PipelineLayoutCreateInfo layoutInfo(
{}, // flags
dsLayout, // descriptor set layouts
pcRange); // push constant ranges
Note that the stages that you specify in the
vk::PushConstantRange
struct
must match the stages specified in the
pushConstants
method call.
Push constants have the advantages over uniform buffers in that they are fast
and do not require memory-backed resources. The disadvantages are that they
can only hold a small amount of data and they require recording the commands
each time we render a frame (we cannot pre-record
the command buffer, unless the push constants never change).
The Vulkan specification requires that implementations provide at least 128 bytes
of push-constants and most implementations will provide more.[1]
You can use the limits
method from the application class to determine if there is
sufficient space for push constants:
if (app->limits()->maxPushConstantsSize < sizeof(PCBuffer)) {
ERROR("insufficient space for push constants");
}