Push constants are specially designed to update the shader constant data using the command buffer instead of updating the resources with the write or copy descriptors.
In this section, we will quickly implement an example to demonstrate a push constant. We will learn how push constants are used with command buffers to update the resource contents in the shader. This example defines two types of resources in the fragment shader—constColor
and mixerValue
—within the push constant uniform block pushConstantsColorBlock
. The constColor
resource contains an integer value that is used as a flag to render the rotating cube in a solid color (red, green, or blue). The mixerValue
resource is a floating value that mixes with the cube color.
The push constant resource in the shader is defined using the push_constant
keyword in a layout that indicates it is a push constant block. In the following code, we have modified the existing fragment shader and added two push constant variables, namely constColor
and mixerValue
. If the value of constColor
is either 1, 2, or 3, then a solid geometry color (red, green, or blue) is rendered. Otherwise, the original color is mixed with mixerValue
:
// Fragment shader #version 450 layout (location = 0) in vec4 color; layout (location = 0) out vec4 outColor; layout(push_constant) uniform colorBlock { int constColor; float mixerValue; } pushConstantsColorBlock; vec4 red = vec4(1.0, 0.0, 0.0, 1.0); vec4 green = vec4(0.0, 1.0, 0.0, 1.0); vec4 blue = vec4(0.0, 0.0, 1.0, 1.0); void main() { if (pushConstantsColorBlock.constColor == 1) outColor = red; else if (pushConstantsColorBlock.constColor == 2) outColor = green; else if (pushConstantsColorBlock.constColor == 3) outColor = blue; else outColor = color*pushConstantsColorBlock.mixerValue; }
In the next section, we will update this shader push constant resource in the pipeline layout.
Update the pipeline layout indicating the push constant ranges. The push constant range is defined in a single pipeline layout using the VkPushConstantRange
structure. The pipeline layout also needs to be informed how many push constants can be accessed by each stage of the pipeline.
The following is the syntax of this structure:
typedef struct VkPushConstantRange { VkShaderStageFlags stageFlags; uint32_t offset; uint32_t size; } VkPushConstantRange;
The various fields of the VkPushConstantRange
structure are defined here:
Parameters |
Description |
|
This field indicates the shader stage to which this push constant range belongs. If |
|
This is the start offset of the push constant range specified in bytes and is a multiple of four. |
|
This field is also specified in bytes and is a multiple of four, indicating the size of the push constant range. |
Update pushConstantRangeCount
of VkPipelineLayoutCreateInfo
with the push constant range count and pPushConstantRanges
with a pointer to an array of VkPushConstantRange
:
void VulkanDrawable::createPipelineLayout() { // Setup the push constant range const unsigned pushConstantRangeCount = 1; VkPushConstantRange pushConstantRanges[pushConstantRangeCount]={}; pushConstantRanges[0].stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; pushConstantRanges[0].offset = 0; pushConstantRanges[0].size = 8; // Create the pipeline layout with the help of descriptor layout. VkPipelineLayoutCreateInfo pPipelineLayoutCreateInfo = {}; pPipelineLayoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pPipelineLayoutCreateInfo.pNext = NULL; pPipelineLayoutCreateInfo.pushConstantRangeCount = pushConstantRangeCount; pPipelineLayoutCreateInfo.pPushConstantRanges = pushConstantRanges; pPipelineLayoutCreateInfo.setLayoutCount = (uint32_t)descLayout.size(); pPipelineLayoutCreateInfo.pSetLayouts = descLayout.data(); VkResult result; result = vkCreatePipelineLayout(deviceObj->device, &pPipelineLayoutCreateInfo, NULL, &pipelineLayout); assert(result == VK_SUCCESS); }
The resource data can be updated using the vkCmdPushConstants()
API. In order to use this API, allocate a command buffer called cmdPushConstant
, update the resource data with the appropriate values, and execute vkCmdPushConstants()
. The following is the syntax of this API:
void vkCmdPushConstants( VkCommandBuffer commandBuffer, VkPipelineLayout layout, VkShaderStageFlags stageFlags, uint32_t offset, uint32_t size, const void* pValues);
The various fields of the vkCmdPushConstants
structure are defined here:
Parameters |
Description |
|
This is the command buffer object ( |
|
This is |
|
This specifies the shader stage that will utilize the push constants in the updated range. The shader stage is indicated using a bitmask of |
|
This starts the offset in bytes specifying the push constant range for the update. |
|
This refers to the size (in bytes) of the push constant range to be updated. |
|
This is an array containing the new push constant values. |
The size of the push constant must never exceed the size specified in VkPhysicalDeviceProperties::limits::maxPushConstantsSize
.
The following is the implementation of the push constants where the push constant is executed using the allocated command buffer cmdPushConstant
. There are two push constant resource variables: constColorRGBFlag
and mixerValue
. These are set with the desired values and specified in the vkCmdPushConstants()
API:
void VulkanRenderer::createPushConstants() { // Allocate and start recording the push constant buffer. CommandBufferMgr::allocCommandBuffer(&deviceObj->device, cmdPool, &cmdPushConstant); CommandBufferMgr::beginCommandBuffer(cmdPushConstant); enum ColorFlag { RED = 1, GREEN = 2, BLUE = 3, MIXED_COLOR = 4, }; float mixerValue = 0.3f; unsigned constColorRGBFlag = BLUE; // Create push constant data, this contain a constant // color flag and mixer value for non-const color unsigned pushConstants[2] = {}; pushConstants[0] = constColorRGBFlag; memcpy(&pushConstants[1], &mixerValue, sizeof(float)); // Check if number of push constants does // not exceed the allowed size int maxPushContantSize = getDevice()->gpuProps. limits.maxPushConstantsSize; if (sizeof(pushConstants) > maxPushContantSize) { assert(0); printf("Push constant size is greater than expected, max allow size is %d", maxPushContantSize); } for each (VulkanDrawable* drawableObj in drawableList) { vkCmdPushConstants(cmdPushConstant, drawableObj->pipelineLayout, VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(pushConstants), pushConstants); } CommandBufferMgr::endCommandBuffer(cmdPushConstant); CommandBufferMgr::submitCommandBuffer(deviceObj->queue, &cmdPushConstant); }
The following is the output rendering the cube geometry with solid colors. In this example, constColor
must be 1, 2, or 3 in order to produce the solid color:
The following is the output of the original colored cube blended with the mixer value; for this output, constColor
must not be 1, 2, or 3:
18.118.7.102