Vulkan不同着色器阶段使用独立Push Constants异常问题排查
我曾查阅类似问题但未获得有效解答,特此提问。我在Vertex Shader与Tessellation Evaluation Shader中使用Push Constant数据,定义了如下结构体存储矩阵:
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中声明的Push Constant:
layout(push_constant) uniform push_data { mat4 model_matrix; } push;
Tessellation Evaluation Shader中声明的Push Constant:
layout(push_constant) uniform push_data { layout(offset = 64) mat4 view_matrix; mat4 projection_matrix; } push;
我按如下方式创建PushConstantRange:
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;
填充结构体数据:
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 };
分别调用pushConstants绑定Push Constant后无渲染效果;但使用合并阶段的调用方式:
commandBuffer.pushConstants( pipeline_layout_, vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eTessellationEvaluation, 0, sizeof(push), &push );
可得到预期渲染效果,却触发了验证层警告:
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
请问如何解决该问题,既保证渲染正常又避免验证层警告?
核心原因
验证层警告的本质是:你调用vkCmdPushConstants时指定的阶段(顶点+ Tessellation评估)覆盖了整个Push Constant内存块,但你的Pipeline Layout中没有定义同时包含这两个阶段和该内存块的Push Constant Range。Vulkan要求每个pushConstants调用的阶段+内存范围,必须被至少一个Push Constant Range完全覆盖。
具体修复步骤
1. 调整PushConstantRange定义
在原有分阶段范围的基础上,添加一个覆盖整个Push Constant区域、同时包含两个阶段的范围:
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; // 添加合并阶段的范围,覆盖整个Push Constant块 vk::PushConstantRange combined_range{}; combined_range.offset = 0; combined_range.size = sizeof(push_constant_data); combined_range.stageFlags = vk::ShaderStageFlagBits::eVertex | vk::ShaderStageFlagBits::eTessellationEvaluation; // 将所有范围传入Pipeline Layout创建 std::vector<vk::PushConstantRange> push_ranges = {vertex_range, TES_range, combined_range};
2. 选择合适的调用方式
- 保留合并调用:现在因为有了
combined_range,合并阶段的pushConstants调用会被验证层认可,同时渲染效果正常。 - 改用分阶段调用:如果想拆分调用,确保每个调用的阶段和范围完全匹配对应的PushConstantRange:
// 更新Vertex Shader的Push Constant commandBuffer.pushConstants( pipeline_layout_, vk::ShaderStageFlagBits::eVertex, 0, sizeof(glm::mat4), &push.model_matrix ); // 更新Tessellation Evaluation Shader的Push Constant commandBuffer.pushConstants( pipeline_layout_, vk::ShaderStageFlagBits::eTessellationEvaluation, sizeof(glm::mat4), 2 * sizeof(glm::mat4), &push.view_matrix );
额外注意事项
- 你的
push_constant_data已经用alignas(16)保证了glm::mat4的16字节对齐,这部分符合Vulkan的要求,无需调整。 - Vulkan允许Push Constant Range重叠,新增的
combined_range不会影响原有分阶段范围的有效性,只是为合并调用提供了合法的范围定义。
内容的提问来源于stack exchange,提问作者user20972572

