在pipeline layout中定义了着色器推送常量,他是通过vulkan的command buffer进行更新着色器中的常量,而不是将常量写入memory或复制命令进行更新。
在实例化VkPipelineLayoutCreateInfo时,对constant进行描述
typedef struct VkPipelineLayoutCreateInfo {
VkStructureType sType;
const void* pNext;
VkPipelineLayoutCreateFlags flags;
uint32_t setLayoutCount;
const VkDescriptorSetLayout* pSetLayouts;
uint32_t pushConstantRangeCount;
const VkPushConstantRange* pPushConstantRanges; // 描述常量信息
} VkPipelineLayoutCreateInfo;
build command buffer时,调用下面的接口,将常量数据传给着色器。
void vkCmdPushConstants(
VkCommandBuffer commandBuffer,
VkPipelineLayout layout,
VkShaderStageFlags stageFlags,
uint32_t offset,
uint32_t size,
const void* pValues);
注:
push constant是一种快速修改pipeline中常量的方法,比通过memory保存数据进行更新的方式更好。
在command buffer录制开始时,push constant的值是未定义的,需要调用vkCmdPushConstants进行更新。
下面以vulkan example中pushconstants的例子对使用push constant的方法进行说明。
- 着色器中存在常量,如下在着色器中定义了pushConsts的结构体
#version 450
layout (location = 0) in vec3 inPos;
layout (location = 1) in vec3 inNormal;
layout (location = 2) in vec3 inColor;
#define lightCount 6
layout (binding = 0) uniform UBO
{
mat4 projection;
mat4 model;
mat4 view;
} ubo;
// 常量结构体
layout(push_constant) uniform PushConsts {
vec4 color;
vec4 position;
} pushConsts;
layout (location = 0) out vec3 outColor;
void main()
{
outColor = inColor * pushConsts.color.rgb;
vec3 locPos = vec3(ubo.model * vec4(inPos, 1.0));
vec3 worldPos = locPos + pushConsts.position.xyz;
gl_Position = ubo.projection * ubo.view * vec4(worldPos, 1.0);
}
- 代码中定义与着色器中相同的结构体,代表协议一致
// Color and position data for each sphere is uploaded using push constants
struct SpherePushConstantData {
glm::vec4 color;
glm::vec4 position;
};
std::array<SpherePushConstantData, 16> spheres;
- 在创建pipeline layout时,对pipeline中可访问的push constant的范围进行描述定义
void setupDescriptorSetLayout()
{
std::vector<VkDescriptorSetLayoutBinding> setLayoutBindings = {
// Binding 0 : Vertex shader uniform buffer
vks::initializers::descriptorSetLayoutBinding(
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
VK_SHADER_STAGE_VERTEX_BIT,
0),
};
VkDescriptorSetLayoutCreateInfo descriptorLayout = vks::initializers::descriptorSetLayoutCreateInfo(setLayoutBindings);
VK_CHECK_RESULT(vkCreateDescriptorSetLayout(device, &descriptorLayout, nullptr, &descriptorSetLayout));
// 定义pipeline layout中使用的push constant的范围
VkPushConstantRange pushConstantRange{};
pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
pushConstantRange.offset = 0;
pushConstantRange.size = sizeof(SpherePushConstantData);
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = vks::initializers::pipelineLayoutCreateInfo(&descriptorSetLayout, 1);
pipelineLayoutCreateInfo.pushConstantRangeCount = 1;
pipelineLayoutCreateInfo.pPushConstantRanges = &pushConstantRange;
VK_CHECK_RESULT(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
}
注:VkPushConstantRange参数中
offset必须小于VkPhysicalDeviceLimits::maxPushConstantsSize,且是4的倍数size必须大于0,且是4的倍数size必须小于等于 VkPhysicalDeviceLimits::maxPushConstantsSize减去offset
- build command buffer中调用draw call之前更新push constant的值到着色器中
uint32_t spherecount = static_cast<uint32_t>(spheres.size());
for (uint32_t j = 0; j < spherecount; j++) {
// [POI] Pass static sphere data as push constants
vkCmdPushConstants(
drawCmdBuffers[i],
pipelineLayout,
VK_SHADER_STAGE_VERTEX_BIT,
0,
sizeof(SpherePushConstantData),
&spheres[j]);
model.draw(drawCmdBuffers[i]);
}
渲染结果如下:
是不是还是挺简单的,如果还有不清楚的地方,可以阅读vulkan example的源码,也可以在此留言。