一、多管线渲染
设置 vulkan 图形管线要点
vulkan API 绘制不同的拓扑类型,比如三角形、线段、点都要重新设置图形管线。可以在初始化过程中设置多套不同的管线缓存起来,然后在绘制帧的时候绑定需要的管线进行绘制,这比每次绘制的时候重新创建管线性能要好得多。如果缓存了很多管线,每次绘制一个模型实例就绑定一次某个管线,那么性能也会不好。绘制的时候应该根据不同类型的管线对模型实例进行分组绘制,每绑定一种图形管线,就把那种图形管线对应的模型实例都放到一个集合中进行批量绘制,这样总体下来性能影响可以忽略不计。
在vkinit 中创建两套管线分别用于绘制线段和三角形
- 多个渲染管线(VkPipeline)完全可以复用同一个管线布局(VkPipelineLayout),只要它们的 descriptor set layout 和 push constant 定义一致即可,这样可以减少资源和管理复杂度,也方便切换管线。常见做法就是:
- 用同一套 descriptor set layout 和 push constant 定义,创建一个 VkPipelineLayout。
- 所有相关的管线(如三角形管线、线管线、不同着色器管线)都用这个 pipeline layout。
- 不同的管线绑定不同的着色器。画线的绑定线段专用着色器,片段着色器中移除纹理采样可以提供性能;画三角形的继续使用之前带纹理采样的片段着色器。
// 创建图形管线
{
VkPushConstantRange pushConstantRange{
};
pushConstantRange.stageFlags = VK_SHADER_STAGE_VERTEX_BIT;
pushConstantRange.offset = 0;
pushConstantRange.size = sizeof(PushConstants);
VkPipelineLayoutCreateInfo pipelineLayoutInfo{
};
pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipelineLayoutInfo.setLayoutCount = 1;
pipelineLayoutInfo.pSetLayouts = &ctx->descriptorSetLayout;
pipelineLayoutInfo.pushConstantRangeCount = 1;
pipelineLayoutInfo.pPushConstantRanges = &pushConstantRange;
if (vkCreatePipelineLayout(ctx->device, &pipelineLayoutInfo, nullptr, &ctx->pipelineLayout) != VK_SUCCESS) {
throw std::runtime_error("创建管线布局失败!");
}
// 绘制线段的管线
ctx->piplelineLine = createPipeline(ctx,
VK_PRIMITIVE_TOPOLOGY_LINE_LIST,
"assets/shaders/line.vert.spv",
"assets/shaders/line.frag.spv");
// 绘制三角形的管线
ctx->piplelineTriangle = createPipeline(ctx,
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
"assets/shaders/triangle.vert.spv",
"assets/shaders/triangle.frag.spv");
}
模型按类型实例分组
在 VkContext
结构中添加 std::vector<MeshInstance*> lineInstances
属性用于存放线段类型的网格对象,原来的std::vector<scene::MeshInstance*> meshInstances
存储三角形类型的网格对象。
- 更新 main.cpp
...
axis = new Axis();
vkcontext.lineInstances.push_back(axis);
MeshInstance* objModel = ObjModelLoader::loadModel("assets/models/viking_room.obj", "assets/models/viking_room.png");
vkcontext.meshInstances.push_back(objModel);
...
分别为lineInstances
和meshInstances
创建顶点/索引缓冲
// 创建顶点缓冲
for (MeshInstance* mesh : ctx->lineInstances) {
if (mesh->vertices.empty()) continue;
createVertexBuffer(mesh, ctx);
}
for (MeshInstance* mesh : ctx->meshInstances) {
if (mesh->vertices.empty()) continue;
createVertexBuffer(mesh, ctx);
}
// 创建索引缓冲
for (MeshInstance* mesh : ctx->lineInstances) {
if (mesh->indices.empty()) continue;
createIndexBuffer(mesh, ctx);
}
for (MeshInstance* mesh : ctx-&