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");
}

1. Vulkan on macOS supports 4096 bytes of push-constant data.