返回 登录
0

没有任何秘密的 API: Vulkan* 简介第 4 部分

本文简介:
本教程的编写以“03-第一个三角形”教程为基础。 我们通过在图像管道中使用顶点属性,并在记录命令缓冲区期间绑定顶点缓冲区,以此改进渲染过程。 我们介绍了顶点属性的数量和布局, 针对视口和 scissors 测试引入了动态管道状态, 并学习了如何创建缓冲区和内存对象,以及如何相互绑定。 我们还映射了内存,并将数据从 CPU 上传至 GPU。

我们创建了渲染资源集,以高效记录和发布渲染命令。 这些资源包括命令缓冲器、旗语、栅栏和帧缓冲器。 我们学习了如何使用栅栏,如何设置动态管道状态的值,以及如何在记录命令缓冲区期间绑定顶点缓冲区(顶点属性数据源)。

下节教程将介绍分期资源。 它们是用于在 CPU 和 GPU 之间拷贝数据的中间缓冲区。 这样应用无需映射用于渲染的缓冲区(或图像),它们也不必绑定至设备的本地(快速)内存。

教程 4: 顶点属性 – 缓冲区、图像和栅栏

我们在之前的教程中学些了一些基本知识。 教程篇幅比较长而且(我希望)介绍得比较详细。 这是因为 Vulkan* API 的学习曲线非常陡峭。 而且大家看,即使是准备最简单的应用,我们也需要了解大量的知识。

但现在我们已经打好了基础。 因此本教程篇幅会短一些,并重点介绍与 Vulkan API 相关的一些话题。 本节我们将通过从顶点缓冲区提供顶点属性,介绍绘制任意几何图形的推荐方法。 由于本课程的代码与“03 – 第一个三角形”教程的代码类似,因此我仅介绍与之不同的部分。

并介绍另外一种整理渲染代码的方法。 之前我们在主渲染循环前记录了命令缓冲区。 但在实际情况中,每帧动画都各不相同,因此我们无法预先记录所有渲染命令。 我们应该尽可能地晚一些记录和提交命令缓冲区,以最大限度地降低输入延迟,并获取最新的输入数据。 我们将正好在提交至队列之前记录命令缓冲区。 不过,一个命令缓冲区远远不够。 必须等到显卡处理完提交的命令缓冲区后,才记录相同的命令缓冲区。 此时将通过栅栏发出信号。 但每一帧都等待栅栏实在非常浪费时间,因此我们需要更多的命令缓冲区,以便交换使用。 命令缓冲区越多,所需的栅栏越多,情况也将越复杂。 本教程将展示如何组织代码,尽可能地实现代码的轻松维护性、灵活性和快速性。

指定渲染通道相关性

我们从创建渲染通道开始,方法与之前教程中所介绍的相同。 但这次我们还需要提供其他信息。 渲染通道描述渲染资源(图像/附件)的内部组织形式,使用方法,以及在渲染流程中的变化。 通过创建图像内存壁垒,可明确更改图像的布局。 如果指定了合适的渲染通道描述(初始布局、子通道布局和最终图像布局),这种操作也可以隐式地进行。 我们首选隐式过渡,因为驱动程序可以更好地执行此类过渡。

在本教程的这一部分,与之前相同,我们将初始和最终图像布局指定为“transfer src”,而将渲染通道指定为“color attachment optimal”子通道布局。 但之前的教程缺乏其他重要信息,尤其是如何使用图像(即执行哪些与图像相关的操作),以及何时使用图像(渲染管道的哪些部分使用图像)。 此类信息可在图像内存壁垒和渲染通道描述中指定。 创建图像内存壁垒时,我们指定与特定图像相关的操作类型(壁垒之前和之后的内存访问类型),而且我们还指定何时放置壁垒(壁垒之前和之后使用图像的管道阶段)。

创建渲染通道并为其提供描述时,通过子通道相关性指定相同的信息。 其他数据对驱动程序也至关重要,可更好地准备隐式壁垒。 以下源代码可创建渲染通道并准备子通道相关性。

  01
std::vector<VkSubpassDependency> dependencies = {
02
  {
03
    VK_SUBPASS_EXTERNAL,                            // uint32_t                       srcSubpass
04
    0,                                              // uint32_t                       dstSubpass
05
    VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,           // VkPipelineStageFlags           srcStageMask
06
    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,  // VkPipelineStageFlags           dstStageMask
07
    VK_ACCESS_MEMORY_READ_BIT,                      // VkAccessFlags                  srcAccessMask
08
    VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,           // VkAccessFlags                  dstAccessMask
09
    VK_DEPENDENCY_BY_REGION_BIT                     // VkDependencyFlags              dependencyFlags
10
  },
11
  {
12
    0,                                              // uint32_t                       srcSubpass
13
    VK_SUBPASS_EXTERNAL,                            // uint32_t                       dstSubpass
14
    VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,  // VkPipelineStageFlags           srcStageMask
15
    VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,           // VkPipelineStageFlags           dstStageMask
16
    VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,           // VkAccessFlags                  srcAccessMask
17
    VK_ACCESS_MEMORY_READ_BIT,                      // VkAccessFlags                  dstAccessMask
18
    VK_DEPENDENCY_BY_REGION_BIT                     // VkDependencyFlags              dependencyFlags
19
  }
20
};
21

22
VkRenderPassCreateInfo render_pass_create_info = {
23
  VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,        // VkStructureType                sType
24
  nullptr,                                          // const void                    *pNext
25
  0,                                                // VkRenderPassCreateFlags        flags
26
  1,                                                // uint32_t                       attachmentCount
27
  attachment_descriptions,                          // const VkAttachmentDescription *pAttachments
28
  1,                                                // uint32_t                       subpassCount
29
  subpass_descriptions,                             // const VkSubpassDescription    *pSubpasses
30
  static_cast<uint32_t>(dependencies.size()),       // uint32_t                       dependencyCount
31
  &dependencies[0]                                  // const VkSubpassDependency     *pDependencies
32
};
33

34
if( vkCreateRenderPass( GetDevice(), &render_pass_create_info, nullptr, &Vulkan.RenderPass ) != VK_SUCCESS ) {
35
  std::cout << "Could not create render pass!" << std::endl;
36
  return false;
37
}

1.Tutorial04.cpp,函数 CreateRenderPass()

子通道相关性描述不同子通道之间的相关性。 当附件以特定方式用于特定子通道(例如渲染)时,会以另一种方式用于另一个子通道(取样),因此我们可创建内存壁垒或提供子通道相关性,以描述附件在这两个子通道中的预期用法。 当然,我们推荐使用后一种选项,因为驱动程序能够(通常)以最佳的方式准备壁垒。 而且代码本身也会得到完善 — 了解代码所需的一切都收集在一个位置,一个对象中。

在我们的简单示例中,我们仅有一个子通道,但我们指定了两种相关性。 因为我们能够(而且应该)指定渲染通道(通过提供特定子通道的编号)与其外侧操作(通过提供 VK_SUBPASS_EXTERNAL 值)之间的相关性。 这里我们为渲染通道与其唯一的子通道之前执行的操作之间…查看全文


了解更多相关内容,请关注CSDN英特尔开发专区

评论