Ch7-4 初识Push Constant
本节的main.cpp对应示例代码中的:Ch7-4.hpp
Push constant是在着色器中使用可由CPU侧变更的常量的最简单方式。
这一节尝试使用push constant来为三角形指定位移,并用实例化来绘制多个在不同位置的三角形。
使用Push Constant绘制多个三角形的流程
在之前Ch7-1.hpp用顶点缓冲区绘制三角形代码的基础上,使用顶点和push constant绘制三角形需要经历以下步骤:
1.书写push constant所需数据
2.创建管线布局
3.书写着色器
4.在命令缓冲区中更新push constant并绘制
通过录制命令来更新push constant,更新的常量会被直接记录在命令缓冲区中。这也意味着,每次重新录制命令缓冲区时,都得包含相应的命令。
管线能使用的push constant常量的大小为128个字节。
书写Push Constant所需数据
跟上一节一样,在主函数里添加每个三角形的位移数据,不过这次不建立顶点缓冲区:
glm::vec2 pushConstants[] = {
{ .0f, .0f },
{ -.5f, .0f },
{ .5f, .0f },
};
参见块成员的内存布局,push constant在着色器中默认为std430布局,那么以上数组中vec2类型的对齐为8,同C++代码中一致,不需要在以上C++代码中填补额外的空数据。
创建新的管线布局
创建管线布局时,定义一个VkPushConstantRange:
void CreateLayout() { VkPushConstantRange pushConstantRange = { VK_SHADER_STAGE_VERTEX_BIT, 0,//offset 24//范围大小,3个vec2是24 }; VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo = { .pushConstantRangeCount = 1, .pPushConstantRanges = &pushConstantRange }; pipelineLayout_triangle.Create(pipelineLayoutCreateInfo); }
PushConstant.vert.shader
新建PushConstant.vert.shader,基于InstancedRendering.vert.shader略作修改即可:
#version 460 #pragma shader_stage(vertex) layout(push_constant) uniform pushConstants { vec2 u_Positions[3]; }; layout(location = 0) in vec2 i_Position; layout(location = 1) in vec4 i_Color; layout(location = 0) out vec4 o_Color; void main() { gl_Position = vec4(i_Position + u_Positions[gl_InstanceIndex], 0, 1); o_Color = i_Color; }
-
涉及到的语法:Push Constant的声明方式。
-
实例索引gl_InstanceIndex是顶点着色器的内置输入。
别忘了更改CreatePipeline(...):
void CreatePipeline() { static shaderModule vert("shader/PushConstant.vert.spv"); //省略后续代码 }
在命令缓冲区中更新Push Constant
用vkCmdPushConstants(...)更新push constant:
void VKAPI_CALL vkCmdPushConstants(...) 的参数说明 |
|
---|---|
VkCommandBuffer commandBuffer |
命令缓冲区的handle |
VkPipelineLayout layout |
管线布局的handle |
VkShaderStageFlags stageFlags |
涉及到的可编程管线阶段,必须注明涉及到的所有阶段 |
uint32_t offset |
要更新的范围距离整个push constant范围的起始位置,单位是字节,必须是4的倍数 |
int32_t size |
要更新的范围的大小,单位是字节,必须是4的倍数 |
const void* pValues |
指向用于更新push constant的数据 |
-
若各个阶段的push constant范围有所重叠,更新重叠的部分时必须在stageFlags注明涉及到的所有阶段。而如果某个范围不会被某个阶段使用,更新该范围时不能注明无关的阶段。
-
更新push constant和绑定管线不分先后,只要绘制时所绑定管线的布局和提供给本函数的布局兼容即可。
比如,对于这样的流程:绑定管线A → 使用管线A的布局更新push_constant → 绘制 → 绑定管线B → 绘制
若其中管线A和B的管线布局不同,只要创建管线布局时VkPipelineLayoutCreateInfo::pPushConstantRanges中指定的各个范围相同,那么在绑定管线B前提供的push constant数据对管线B同样有效(官方文档中关于管线布局兼容性的说明)。 -
是否正确遵循上述两条,跟是否正确渲染没有必然联系!各厂家的驱动会有不同表现,Ch8-1 离屏渲染中有个例子。
在vkCmdDraw(...)前加入以下代码:
vkCmdPushConstants(commandBuffer, pipelineLayout_triangle, VK_SHADER_STAGE_VERTEX_BIT, 0, sizeof pushConstants, &pushConstants);
绘制3个实例:
vkCmdDraw(commandBuffer, 3, 3, 0, 0);
运行程序,你应该会看到以下图像(跟上一节的一样):