Vulkan/Renderpasses

发布时间 2023-12-02 14:06:47作者: 经纬视界

能将渲染管线和运算管线区别开了的要素之一是——通用,在你使用一个渲染管线渲染图像之后也可能进行其他处理或展示给yoghurt。在复杂的图形应用中,图像需要经过许多通道才能生成,每个通道都负责不同的部分,比如全屏幕的后处理或合成,或渲染UI元素等。

这些通道可以由vulkan的一个渲染通道对象表达出来。一个渲染通道对象封装了输出图像上的许多子通道或是渲染阶段。一个渲染通道的每个阶段称为子通道(subpass)。渲染通道物体可以包含多个子通道,即便只包含一个通道的最简单的应用程序中,渲染通道对象也包含了关于输出图像的信息。

所有绘制操作都必须包含进一个渲染通道中。此外,渲染管线需要知道它们在哪里进行渲染。因此在创建管线对象之前创建一个渲染通道对象是必要的,这样我们才能告诉管线那些将要渲染的图像。渲染通道将在多通道渲染章节中进行深度地讲述。该章节中,我们将创建最简单的渲染通道对象,能让哦我们输出图像即可。

创建渲染通道对象,调用vkCreateRenderPass

VkRenderPassCreateInfo中,pattachments是一个指向具有attachementCount个的VkAttachmentDescription结构体数组指针,它定义了该渲染通道管关联的诸多附件(attachments)。数组内每个元素都定义了单个被用来给一个或多个子通道当作输入、输出或同时具有二者功能的图像。如果没有附件,你可以将attachmentCount为0,pAttachments为nullptr。然而,在一些高级的使用方法中,几乎所有的渲染都使用至少一个附件。

VkAttachmentDescription结构中,flags字段提供附件的额外信息,唯一值为VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT。如果设置该值,表明该附件可能与同一渲染通道的另一个附件使用相同的内存。这让vulkan不要去做任何能导致该附件中发生数据不一致性的行为。该字段可能在一些需要内存优化的时候设置,大多数时候都是0。

format字段指定了附件的格式。值为VkFormat枚举,并且应该和附件使用的图像格式相匹配。同样,samples表明了用于重采样(multisampling)时的采样数量。如果不适用重采样,设置为VK_SAMPLE_COUNT_1_BIT

接下来四个字段指定了在渲染通道的开头和结尾处对附件的操作。加载操作让vulkan指定了渲染通道在开头时的操作,可以设置为:

  • VK_ATTACHMENT_LOAD_OP_LOAD:表明附件内已经存在数据并且你想继续向其中渲染。这回让vulkan在渲染通道开始时认为附件内容是合法的。
  • VK_ATTACHMENT_LOAD_OP_CLEAR:表明你想在渲染通道开始时清除附件内容。清除所使用的颜色回在渲染通道开始时指定。
  • VK_ATTACHMENT_LOAD_OP_DONT_CARE:指明渲染通道开始时不关心附件内容,vulkan可以随意操作其中的内容。如果你计划显式清除附件或者你知道你将会在通道内替换其中的内容,可以使用该值。

同样的,存储操作让vulkan了解当渲染通道结束时应该对附件进行的操作,可以为:

  • VK_ATTACHMENT_STORE_OP_STORE:表明你希望vulkan保存其中的内容,以便于未来使用。这通常意味着会把其中内容写回内存中。当你想将图像展示给用户,或之后进行读取,或给其他渲染通道当作附件使用(该通道需要指明VK_ATTACHMENT_LOAD_OP_LOAD),使用该值。
  • VK_ATTACHMENT_STORE_OP_DONT_CARE:表明渲染通道结束后便不需要该附件的内容了。一般用于中间存储或深度,模板缓冲。

如果附件是一个深度-模板附件,那么stencilLoadOpstencilStoreOp字段告诉vulkan对附件的模板部分进行的操作(深度部分由常规的loadOpstoreOp指定),允许与深度部分不同。

initialLayoutfinalLayout字段告诉vulkan当渲染通道开始时图像的布局以及当结束时图像的布局。渲染通道对象不会自动将图像转成初始布局。该布局是渲染通道开始时期望的布局。不过相反的是,渲染通道会将图像自动转成结束的布局。

你可以显式的使用屏障来将转移图像的布局,不过可能的话,最好在渲染通道内进行布局转换。这给予vulkan一个机会让渲染通道的各部分都选择合适的布局,甚至能够在渲染的时候并行执行任何转移操作。高阶用法在多通道渲染章节。

在定义完渲染通道所使用的所有附件后,你需要定义所有的子通道。每个子通道都引用附件资源的一部分当作输入或输出。这些信息由VkSubpassDescription数组指定。

VkSubpassDescription中,因为当前版本vulkan只为渲染管线支持了渲染通道,所以pipelineBindPoint设置为VK_PIPELINE_BIND_POINT_GRAPHICS。其余字段描述了改子通道使用的附件。每个子通道能含有数个输入附件,用来读取数据。颜色附件,用来当作输入。深度-模板附件用于深度缓冲和模板缓冲。这些附件在pInputAttachmentspColorAttachmentspDepthStencilAttachment指定。

一个子通道能含有的颜色附件有一个最大值,由maxColorAttachments决定。该值至少为4,所以如果没有超过该值,便不需要具体查询该字段的值。

这些指针的都指向VkAttachmentReference结构。

每个附件引用结构都很简单,只包含一个附件数组的索引值和该附件在被子通道使用时所期望的图像布局。除了输入和输出的附件引用,还有两种额外的功能的附件。

首先,为重解析附件,通过pResolveAttachments指定,它们是用来存储多重采样图像数据重解析后产生的数据的。这些附件与相应的由pColorAttachments指定的颜色附件相关,重解析附件的数量假定于颜色附件相同。

如果颜色附件中某一个为多重采样图像,但是渲染通道只需要该图像对应的解析数据,你可以让vulkan把解析图像作为渲染通道的一部分,然后丢弃原始的多重采样数据。方法是——将多重采样的颜色附件的存储操作设置为VK_ATTACHMENT_STORE_OP_DONT_CARE,将相应的重解析附件的存储操作设置为VK_ATTACHMENT_STORE_OP_STORE,这样vulkan就会保留解析后得到的单个采样数据而丢弃原始多重采样数据。

其次,如果你有一些附件想让其在子通道内存活但却不直接引用,使用pReserveAttachments。该引用会组织vulkan做任何的优化操作,以免扰乱其中的内容。

如果渲染通道内含有一个以上的子通道。vulkan可以根据附件的引用并且根据子通道间的输入和输出弄清楚子通道之间的依赖关系。也有一些不能简单由输入输出依赖决定的关系的情况。比如,一个子通道向资源中直接写入,然后另一个子通道从中读取写入的数据,这种情况vulkan无法自己弄清通道间依赖关系,所以你必须手动提供这些依赖信息。使用pDependencies字段指定依赖关系。

VkSubpassDependency中,每一个依赖关系都从源子通道开始(数据的生产者)到目标子通道结束(数据的消费者),分别由srcSubpassdstSubpass指定。二者都是组成渲染通道的所有子通道中的索引。srcStageMask指定子通道渲染管线的哪一阶段产生数据。与之相似,dstStageMask指定目标管线的哪一阶段消耗数据。

srcAccessMaskdstAccessMask指定两个子通道如何访问数据。比如,源子通道可能在顶点着色器中向图像中写入数据或者在片元着色器中向颜色附件写入输出数据。与此同时,目标子通道可能将其作为输入附件进行读取或作为图像进行加载。

当我们不使用渲染通道对象后,需要调用vkDestroyRenderPass销毁。