Vulkan中如何为顶点与片段着色器阶段设置不同Push Constant?
我想为顶点着色器和片段着色器分别使用不同的Push Constant:
- 顶点着色器对应的C++结构体:
struct vertex_push_constant { matrix4x4 model{}; matrix4x4 normal{}; };
- 片段着色器对应的C++结构体:
struct fragment_push_constant { vector4 camera_position{}; vector4 light_position{}; vector4 light_color{}; };
我把它们合并成一个总结构体:
struct push_constant { vertex_push_constant vertex{}; fragment_push_constant fragment{}; };
着色器里也对应定义了结构体,顶点着色器代码:
struct vertex_push_constant_data { mat4 model; mat4 normal; }; layout(push_constant) uniform push_constant { vertex_push_constant_data data; } push;
片段着色器代码类似:
struct fragment_push_constant_data { vec4 camera_position{}; vec4 light_position{}; vec4 light_color{}; }; layout(push_constant) uniform push_constant { fragment_push_constant_data data; } push;
创建管线时配置的Push Constant范围:
const auto push_constant_ranges = std::array<VkPushConstantRange, 2>{ VkPushConstantRange{ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, .offset = offsetof(push_constant, vertex_push_constant), .size = sizeof(vertex_push_constant) }, VkPushConstantRange{ .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .offset = offsetof(push_constant, fragment_push_constant), .size = sizeof(fragment_push_constant) }, };
尝试用以下代码填充Push Constant时触发错误:
vkCmdPushConstants(command_buffer, layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(push_constant), &push_constant_data)
错误信息:
Validation Error: [ VUID-vkCmdPushConstants-offset-01795 ] Object 0: handle = 0x1f66f6c4000, type = VK_OBJECT_TYPE_COMMAND_BUFFER; | MessageID = 0x27bc88c6 | vkCmdPushConstants(): VK_SHADER_STAGE_VERTEX_BIT|VK_SHADER_STAGE_FRAGMENT_BIT, VkPushConstantRange in VkPipelineLayout 0x301e6c0000000022[] overlapping offset = 0 and size = 176, do not contain VK_SHADER_STAGE_VERTEX_BIT|VK_SHADER_STAGE_FRAGMENT_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(command_buffer, layout, VK_SHADER_STAGE_VERTEX_BIT, offsetof(push_constant, vertex_push_constant), sizeof(vertex_push_constant), &push_constant_data) vkCmdPushConstants(command_buffer, layout, VK_SHADER_STAGE_FRAGMENT_BIT, offsetof(push_constant, fragment_push_constant), sizeof(fragment_push_constant), &push_constant_data)
但还是出现运行时错误。我不想让两个阶段共用同一个Push Constant块,因为片段着色器不需要模型矩阵,顶点着色器不需要相机位置。请问该如何正确为不同阶段设置独立的Push Constant数据?
核心问题分析
错误根源有两点:
- 管线布局的Push Constant范围配置,与着色器中Push Constant块的定义不匹配,Vulkan无法正确映射数据到对应着色器的块中。
- 分阶段调用
vkCmdPushConstants时,传递的是整个合并结构体的指针,而非对应子结构体的地址。
正确实现步骤
1. 调整管线布局的Push Constant范围
无需合并结构体,直接为每个阶段定义独立范围,注意偏移要符合Vulkan的对齐要求(mat4等类型需要16字节对齐):
const auto push_constant_ranges = std::array<VkPushConstantRange, 2>{ VkPushConstantRange{ .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, .offset = 0, // 顶点Push Constant从偏移0开始 .size = sizeof(vertex_push_constant) }, VkPushConstantRange{ .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT, .offset = sizeof(vertex_push_constant), // 片段Push Constant接在顶点之后,保证对齐 .size = sizeof(fragment_push_constant) }, };
若
sizeof(vertex_push_constant)不是16字节倍数,需手动调整偏移,或用alignas(16)修饰C++结构体强制对齐。
2. 修正着色器中的Push Constant定义
着色器中为每个阶段定义独立的Push Constant块,保证与C++端的偏移、大小一致:
顶点着色器:
layout(push_constant) uniform VertexPushConstants { mat4 model; mat4 normal; } push;
片段着色器:
layout(push_constant) uniform FragmentPushConstants { vec4 camera_position; vec4 light_position; vec4 light_color; } push;
3. 正确调用vkCmdPushConstants
分阶段传递对应子结构体的地址,匹配管线布局的偏移设置:
// 设置顶点Push Constant vkCmdPushConstants( command_buffer, layout, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof(vertex_push_constant), &push_constant_data.vertex ); // 设置片段Push Constant vkCmdPushConstants( command_buffer, layout, VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(vertex_push_constant), sizeof(fragment_push_constant), &push_constant_data.fragment );
4. 额外注意事项
- 保证C与GLSL结构体内存布局一致:GLSL中
mat4是列主序,C的matrix4x4需同步为列主序,避免数据错乱。 - 检查总Push Constant大小不超过设备上限,可通过
VkPhysicalDeviceLimits::maxPushConstantsSize获取。 - 严格遵循验证层的错误提示,定位范围或阶段的匹配问题。
内容的提问来源于stack exchange,提问作者Symlink

