Vulkan大闷锅之Pipeline

吸取了前几代Api设计的教训,Vulkan采用了Monolith结构的PSO(Pipeline State Objects),是不是有当年Linux内核的既視感。这段时间疯狂发展的微服务,不知道在未来会不会也走回头路。

敢说Monolith,当然是包含的巨量的状态信息:

  • Dynamic State
  • Vertex Input State
  • Input Assembly State
  • Rasterization State
  • Color Blend State
  • Viewport State
  • Depth Stencil State
  • Multisample State

创建状态信息

Dynamic State如字面所说,储存Pipeline的动态信息。

VkDynamicState dynamicStateEnables[VK_DYNAMIC_STATE_RANGE_SIZE];
VkPipelineDynamicStateCreateInfo dynamicState = {};
memset(dynamicStateEnables, 0, sizeof(dynamicStateEnables));
dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
dynamicState.pNext = nullptr;
dynamicState.pDynamicStates = dynamicStateEnables;
dynamicState.dynamicStateCount = 0;

Vertex Input State基本类似OpenGL的VAO,依赖信息前文已经配置好:

VkPipelineVertexInputStateCreateInfo vi;
memset(&vi, 0, sizeof(vi));
vi.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
vi.pNext = nullptr;
vi.flags = 0;
vi.vertexBindingDescriptionCount = 1;
vi.pVertexBindingDescriptions = &vi_binding;
vi.vertexAttributeDescriptionCount = 2;
vi.pVertexAttributeDescriptions = vi_attribs;

Input Assembly State通常用来配置三角形的绘制方式:

VkPipelineInputAssemblyStateCreateInfo ia;
ia.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
ia.pNext = nullptr;
ia.flags = 0;
ia.primitiveRestartEnable = VK_FALSE;
ia.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;

最常用的是VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,即三顶点定位一个三角形。可选的例如VK_PRIMITIVE_TOPOLOGY_TRIANGLE_FAN,参考字面意思也就是共享一个中心顶点,绘图大致呈扇形。

Pipeline Rasterization State通常用来配置Raster的渲染方式:

VkPipelineRasterizationStateCreateInfo rs;
rs.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
rs.pNext = nullptr;
rs.flags = 0;
rs.polygonMode = VK_POLYGON_MODE_FILL;
rs.cullMode = VK_CULL_MODE_BACK_BIT;
rs.frontFace = VK_FRONT_FACE_CLOCKWISE;
rs.depthClampEnable = VK_FALSE;
rs.rasterizerDiscardEnable = VK_FALSE;
rs.depthBiasEnable = VK_FALSE;
rs.depthBiasConstantFactor = 0;
rs.depthBiasClamp = 0;
rs.depthBiasSlopeFactor = 0;
rs.lineWidth = 1.0f;

这里有个小坑,OpenGL默认frontFaceVK_FRONT_FACE_CLOCKWISE,也就是说三角形顶点需要按照逆时针顺序传入,这样的三角形称之为正三角形;顺时针的就是反三角形了。这里cullMode设置为VK_CULL_MODE_BACK_BIT,也就是背面剔除,所有反三角形都不会被渲染。有一次我调试代码,发现总少几个三角形…找了很久才发现忘了关culling。

你可以将VK_POLYGON_MODE_FILL替换为VK_POLYGON_MODE_LINE,只绘制线框图。
Pipeline Color Blend State用来混合重叠的色块:

VkPipelineColorBlendAttachmentState att_state[1];
att_state[0].colorWriteMask = 0xf;
att_state[0].blendEnable = VK_FALSE;
att_state[0].alphaBlendOp = VK_BLEND_OP_ADD;
att_state[0].colorBlendOp = VK_BLEND_OP_ADD;
att_state[0].srcColorBlendFactor = VK_BLEND_FACTOR_ZERO;
att_state[0].dstColorBlendFactor = VK_BLEND_FACTOR_ZERO;
att_state[0].srcAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;
att_state[0].dstAlphaBlendFactor = VK_BLEND_FACTOR_ZERO;

VkPipelineColorBlendStateCreateInfo cb;
cb.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
cb.pNext = nullptr;
cb.flags = 0;
cb.attachmentCount = 1;
cb.pAttachments = att_state;
cb.logicOpEnable = VK_FALSE;
cb.logicOp = VK_LOGIC_OP_NO_OP;
cb.blendConstants[0] = 1.0f;
cb.blendConstants[1] = 1.0f;
cb.blendConstants[2] = 1.0f;
cb.blendConstants[3] = 1.0f;

Pipeline Viewport State看字面就是Viewport的配置:

VkPipelineViewportStateCreateInfo vp = {};
vp.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
vp.pNext = nullptr;
vp.flags = 0;
vp.viewportCount = 1;
dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_VIEWPORT;
vp.scissorCount = 1;
dynamicStateEnables[dynamicState.dynamicStateCount++] = VK_DYNAMIC_STATE_SCISSOR;
vp.pScissors = nullptr;
vp.pViewports = nullptr;

值得注意的是,这里使用动态Viewport和Scissor。所以pScissors和pViewports置空,同时引用了Dynamic State。
Pipeline Depth Stencil State看字面也就是深度模板的配置:

VkPipelineDepthStencilStateCreateInfo ds;
ds.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
ds.pNext = nullptr;
ds.flags = 0;
ds.depthTestEnable = true;
ds.depthWriteEnable = true;
ds.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL;
ds.depthBoundsTestEnable = VK_FALSE;
ds.minDepthBounds = 0;
ds.maxDepthBounds = 0;
ds.stencilTestEnable = VK_FALSE;
ds.back.failOp = VK_STENCIL_OP_KEEP;
ds.back.passOp = VK_STENCIL_OP_KEEP;
ds.back.compareOp = VK_COMPARE_OP_ALWAYS;
ds.back.compareMask = 0;
ds.back.reference = 0;
ds.back.depthFailOp = VK_STENCIL_OP_KEEP;
ds.back.writeMask = 0;
ds.front = ds.back;

Pipeline Multisample State同样看字面,也就是多重采样的配置,暂时也不启用:

VkPipelineMultisampleStateCreateInfo ms;
ms.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
ms.pNext = nullptr;
ms.flags = 0;
ms.pSampleMask = nullptr;
ms.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;
ms.sampleShadingEnable = VK_FALSE;
ms.alphaToCoverageEnable = VK_FALSE;
ms.alphaToOneEnable = VK_FALSE;
ms.minSampleShading = 0.0;

万事具备,创建Pipeline:

VkGraphicsPipelineCreateInfo pipeline_info;
pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_info.pNext = nullptr;
pipeline_info.layout = pipeline_layout;
pipeline_info.basePipelineHandle = VK_NULL_HANDLE;
pipeline_info.basePipelineIndex = 0;
pipeline_info.flags = 0;
pipeline_info.pVertexInputState = &vi;
pipeline_info.pInputAssemblyState = &ia;
pipeline_info.pRasterizationState = &rs;
pipeline_info.pColorBlendState = &cb;
pipeline_info.pTessellationState = nullptr;
pipeline_info.pMultisampleState = &ms;
pipeline_info.pDynamicState = &dynamicState;
pipeline_info.pViewportState = &vp;
pipeline_info.pDepthStencilState = &ds;
pipeline_info.pStages = shaderStages;
pipeline_info.stageCount = 2;
pipeline_info.renderPass = render_pass;
pipeline_info.subpass = 0;
VkPipeline pipeline;
res = vkCreateGraphicsPipelines(device, pipelineCache, 
                                1, &pipeline_info,
                                nullptr, &pipeline);
assert(res == VK_SUCCESS);

到这里Pipeline已经创建好,距离一个完整的程序只差一步了,敬请关注后续博文。

Leave a Reply

Your email address will not be published. Required fields are marked *