You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Vulkan中如何为顶点与片段着色器阶段设置不同Push Constant?

为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数据?


解决方案

核心问题分析

错误根源有两点:

  1. 管线布局的Push Constant范围配置,与着色器中Push Constant块的定义不匹配,Vulkan无法正确映射数据到对应着色器的块中。
  2. 分阶段调用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.20 19:06:28