结论

      +

      我们现在可以将前面章节中的所有结构和对象组合起来创建图形管线!以下是我们现在拥有的对象类型,作为快速回顾:

      • 着色器阶段:定义图形管线可编程阶段功能的着色器模块

      • 固定功能状态:定义管线固定功能阶段的所有结构,如输入组装、光栅化器、视口和颜色混合

      • 管线布局:着色器引用的统一和推送值,可以在绘制时更新

      • 动态渲染:渲染过程中将使用的附件格式

      所有这些组合在一起完全定义了图形管线的功能,因此我们现在可以开始在 createGraphicsPipeline 函数的末尾填充 VkGraphicsPipelineCreateInfo 结构。但在调用 vkDestroyShaderModule 之前,因为这些在创建过程中仍在使用。

      vk::GraphicsPipelineCreateInfo pipelineInfo({}, 2, shaderStages);

      我们首先引用 VkPipelineShaderStageCreateInfo 结构数组。

      vk::GraphicsPipelineCreateInfo pipelineInfo({}, 2, shaderStages, &vertexInputInfo, &inputAssembly, {}, &viewportState, &rasterizer, &multisampling, {}, &colorBlending,
                  &dynamicState);

      然后我们引用描述固定功能阶段的所有结构。

      vk::GraphicsPipelineCreateInfo pipelineInfo({}, 2, shaderStages, &vertexInputInfo, &inputAssembly, {}, &viewportState, &rasterizer, &multisampling, {}, &colorBlending,
                  &dynamicState, *pipelineLayout);

      接下来是管线布局,它是一个 Vulkan 句柄而不是结构指针。

      vk::PipelineRenderingCreateInfo pipelineRenderingCreateInfo{ .colorAttachmentCount = 1, .pColorAttachmentFormats = &swapChainImageFormat };
      vk::GraphicsPipelineCreateInfo pipelineInfo{ .pNext = &pipelineRenderingCreateInfo,
          .stageCount = 2, .pStages = shaderStages,
          .pVertexInputState = &vertexInputInfo, .pInputAssemblyState = &inputAssembly,
          .pViewportState = &viewportState, .pRasterizationState = &rasterizer,
          .pMultisampleState = &multisampling, .pColorBlendState = &colorBlending,
          .pDynamicState = &dynamicState, .layout = pipelineLayout, .renderPass = nullptr };

      请注意,我们使用的是动态渲染而不是传统的渲染通道,因此我们将 renderPass 参数设置为 nullptr,并在 pNext 链中包含一个 vk::PipelineRenderingCreateInfo 结构。此结构指定渲染过程中将使用的附件格式。

      pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // 可选
      pipelineInfo.basePipelineIndex = -1; // 可选

      实际上还有两个参数:basePipelineHandlebasePipelineIndex。 Vulkan 允许您通过从现有管线派生来创建新的图形管线。 管线派生的思想是,当新管线与现有管线有许多共同功能时,设置管线的成本较低,并且从同一父管线切换管线也可以更快地完成。 您可以使用 basePipelineHandle 指定现有管线的句柄,或者使用 basePipelineIndex 引用即将创建的另一个管线的索引。 现在只有一个管线,因此我们只需指定一个空句柄和一个无效索引。 只有在 VkGraphicsPipelineCreateInfoflags 字段中也指定了 VK_PIPELINE_CREATE_DERIVATIVE_BIT 标志时,才会使用这些值。

      现在通过创建一个类成员来保存 VkPipeline 对象,为最后一步做准备:

      vk::raii::Pipeline graphicsPipeline = nullptr;

      最后,创建图形管线:

      graphicsPipeline = vk::raii::Pipeline(device, nullptr, pipelineInfo);

      vkCreateGraphicsPipelines 函数实际上比 Vulkan 中通常的对象创建函数有更多的参数。 它设计为接受多个 VkGraphicsPipelineCreateInfo 对象,并在一次调用中创建多个 VkPipeline 对象。

      第二个参数(我们传递了 VK_NULL_HANDLE 参数)引用一个可选的 VkPipelineCache 对象。 管线缓存可用于存储和重用与管线创建相关的数据,跨多个 vkCreateGraphicsPipelines 调用,甚至跨程序执行(如果缓存存储到文件中)。 这使得以后可以显著加快管线创建速度。 我们将在管线缓存章节中讨论这一点。

      图形管线是所有常见绘图操作所必需的。

      现在运行您的程序,确认所有这些辛勤工作已成功创建管线! 我们已经非常接近在屏幕上看到一些东西了。 在 接下来的几章,我们将从交换链图像设置实际的帧缓冲区并准备绘制命令。