Search code examples
glslvulkan

Vulkan Different Push Constants in Different Shader Stages


(I've seen similar questions but the answers didn't help me, that's why I'm asking)

I use push constant data for my vertex and tessellation evaluation shaders.

struct i declared for storing matrices:

struct push_constant_data {
    // vertex shader
    alignas(16) glm::mat4 model_matrix;

    // tessellation evaluation shader
    alignas(16) glm::mat4 view_matrix;
    alignas(16) glm::mat4 projection_matrix;
};

vertex shader:

layout(push_constant) uniform push_data {
    mat4 model_matrix;
} push;

tessellation evaluation shader:

layout(push_constant) uniform push_data {
    layout(offset = 64)
    mat4 view_matrix;
    mat4 projection_matrix;
} push;

that's how I created the ranges:

vk::PushConstantRange vertex_range{};
vertex_range.offset = 0;
vertex_range.size = sizeof(glm::mat4);
vertex_range.stageFlags = vk::ShaderStageFlagBits::eVertex;

vk::PushConstantRange TES_range{};
TES_range.offset = sizeof(glm::mat4);
TES_range.size = 2 * sizeof(glm::mat4);
TES_range.stageFlags = vk::ShaderStageFlagBits::eTessellationEvaluation;

filling the struct:

    push_constant_data push{
        {glm::mat4(1.0f)}, //model
        {glm::lookAt(glm::vec3(2.0f, 2.0f, 2.0f), glm::vec3(0.0f, 0.0f, 0.0f), glm::vec3(0.0f, 0.0f, 1.0f))}, //view
        {glm::perspective(glm::radians(45.0f), swapchain_extent.width / (float)swapchain_extent.height, 0.1f, 10.0f)} //projection
    };

binding push constants:

    commandBuffer.pushConstants(pipeline_layout_, vk::ShaderStageFlagBits::eVertex, 0, sizeof(glm::mat4), &push);
    commandBuffer.pushConstants(pipeline_layout_, vk::ShaderStageFlagBits::eTessellationEvaluation, sizeof(glm::mat4), 2 * sizeof(glm::mat4), &push);

as a result, I see nothing. When I use

commandBuffer.pushConstants(
    pipeline_layout_, 
    vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eTessellationEvaluation, 
    0, 
    sizeof(push), 
    &push
);

I get exactly what I need, and a validation layer message of course:

vkCmdPushConstants(): VK_SHADER_STAGE_VERTEX_BIT|VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, VkPushConstantRange in VkPipelineLayout 0x9fde6b0000000014[] overlapping offset = 0 and size = 192, do not contain VK_SHADER_STAGE_VERTEX_BIT|VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT. The Vulkan spec states: For each byte in the range specified by offset and size and for each shader stage in stageFlags, there must be a push constant range in layout that includes that byte and that stage (https://vulkan.lunarg.com/doc/view/1.3.275.0/windows/1.3-extensions/vkspec.html#VUID-vkCmdPushConstants-offset-01795)


Solution

  • The vkCmdPushConstants parameters offset and size are GPU-side only.

    commandBuffer.pushConstants(pipeline_layout_, vk::ShaderStageFlagBits::eVertex, 0, sizeof(glm::mat4), &push.model_matrix);
    
    std::array<glm::mat4, 2> matrices{ push.view_matrix, push.projection_matrix };
    commandBuffer.pushConstants(pipeline_layout_, vk::ShaderStageFlagBits::eTessellationEvaluation, sizeof(glm::mat4), 2 * sizeof(glm::mat4), matrices.data());
    

    Note offset that is not applied to the push variable, use Pointer Arithmetic instead: push + sizeof(glm::mat4) (as the pValue for vkCmdPushConstants).