Vulkan之Renderpass

在我们完成管线的创建工作之前,我们需要告诉Vulkan渲染时候使用的framebuffer帧缓冲区附件相关信息。我们需要指定多少个颜色和深度缓冲区将会被使用,指定多少个采样器被用到及在整个渲染操作中相关的内容如何处理。所有的这些信息都被封装在一个叫做 render pass 的对象中。
pipeline设计了整个渲染的流程,但是pipeline只是定义了各个操作,renderpass定义了整个渲染的过程。

在复杂的图形渲染过程中,我们很难只通过一个操作就完成渲染,大多数情况下需要多步的处理,因此一个renderpass也是由多个子通道组成的,这些子通道被称为subpass,subpass对应整个渲染流程的各个渲染阶段。

1)创建renderpass

所有的渲染操作必须包含在一个renderpass中,下面我们首先来创建renderpass

1
2
3
4
5
VKAPI_ATTR VkResult VKAPI_CALL vkCreateRenderPass(
VkDevice device,
const VkRenderPassCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkRenderPass* pRenderPass);

这里面实际上重要的是VkRenderPassCreateInfo结构

1
2
3
4
5
6
7
8
9
10
11
typedef struct VkRenderPassCreateInfo {
VkStructureType sType; // VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO
const void* pNext; // nullptr
VkRenderPassCreateFlags flags; // 留存后续扩展
uint32_t attachmentCount; //
const VkAttachmentDescription* pAttachments; // renderpass里面使用的附件信息
uint32_t subpassCount; //
const VkSubpassDescription* pSubpasses; // subpass的定义
uint32_t dependencyCount; //
const VkSubpassDependency* pDependencies; // subpass的依赖
} VkRenderPassCreateInfo;

这里面重要是就是三部分:pAttachments,pSubpasses,pDependencies。下面来挨个分析

2)VkAttachmentDescription

pAttachments是一个指向VkAttachmentDescription结构的数组的指针。数组里面存放的是图像attachment,每个图像将在renderpass的多个subpass中用作输入,输出或者同时作为输入输出。

1
2
3
4
5
6
7
8
9
10
11
typedef struct VkAttachmentDescription {
VkAttachmentDescriptionFlags flags;
VkFormat format;
VkSampleCountFlagBits samples;
VkAttachmentLoadOp loadOp;
VkAttachmentStoreOp storeOp;
VkAttachmentLoadOp stencilLoadOp;
VkAttachmentStoreOp stencilStoreOp;
VkImageLayout initialLayout;
VkImageLayout finalLayout;
} VkAttachmentDescription;

flag — 是给vulkan提供attachment的附加信息,这里面实际只有一个VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT,表示attachment可能和同一个renderpass引用的其他attachment使用相同的内存,这告诉vulkan不要做任何导致attachment数据不一致的事情,大多数场景下,这个值设置为0

1
2
3
4
5
typedef enum VkAttachmentDescriptionFlagBits {
VK_ATTACHMENT_DESCRIPTION_MAY_ALIAS_BIT = 0x00000001,
VK_ATTACHMENT_DESCRIPTION_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF
} VkAttachmentDescriptionFlagBits;
typedef VkFlags VkAttachmentDescriptionFlags;

format — 格式信息,对于colorAttachment,这个就是swapChainImageFormat

samples — 采样方式,如果不是多重采样,则设置为VK_SAMPLE_COUNT_1_BIT

下面的这一组VkAttachmentLoadOp和VkAttachmentStoreOp定义了渲染通道开始和结束的时候如何处理attachment中的内容

VkAttachmentLoadOp加载时的处理:

1
2
3
4
5
6
typedef enum VkAttachmentLoadOp {
VK_ATTACHMENT_LOAD_OP_LOAD = 0, // attachment中已经有数据了,继续对其进行渲染
VK_ATTACHMENT_LOAD_OP_CLEAR = 1, // 在渲染开始时,清除attachment中的内容
VK_ATTACHMENT_LOAD_OP_DONT_CARE = 2, // 不关心
VK_ATTACHMENT_LOAD_OP_MAX_ENUM = 0x7FFFFFFF
} VkAttachmentLoadOp;

VkAttachmentStoreOp结束时的处理:

1
2
3
4
5
6
typedef enum VkAttachmentStoreOp {
VK_ATTACHMENT_STORE_OP_STORE = 0, // 保存附件的内容,展示给用户或者给其他的renderpass作为attachemnt,将其写入内存
VK_ATTACHMENT_STORE_OP_DONT_CARE = 1, // 不关心
VK_ATTACHMENT_STORE_OP_NONE_QCOM = 1000301000,
VK_ATTACHMENT_STORE_OP_MAX_ENUM = 0x7FFFFFFF
} VkAttachmentStoreOp;

initialLayout和finalLayout告诉vulkan在渲染通道开始于结束时,期望图像是什么布局

3)subpass

subpass是很有用的,他实际上定义了具体的渲染逻辑

1
2
3
4
5
6
7
8
9
10
11
12
typedef struct VkSubpassDescription {
VkSubpassDescriptionFlags flags; // 留待后续用,一般为0
VkPipelineBindPoint pipelineBindPoint;
uint32_t inputAttachmentCount;
const VkAttachmentReference* pInputAttachments;
uint32_t colorAttachmentCount;
const VkAttachmentReference* pColorAttachments;
const VkAttachmentReference* pResolveAttachments;
const VkAttachmentReference* pDepthStencilAttachment;
uint32_t preserveAttachmentCount;
const uint32_t* pPreserveAttachments;
} VkSubpassDescription;

pipelineBindPoint — 定义了是什么pipeline的subpass,主要有以下的取值

1
2
3
4
5
6
7
typedef enum VkPipelineBindPoint {
VK_PIPELINE_BIND_POINT_GRAPHICS = 0,
VK_PIPELINE_BIND_POINT_COMPUTE = 1,
VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR = 1000165000,
VK_PIPELINE_BIND_POINT_RAY_TRACING_NV = VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR,
VK_PIPELINE_BIND_POINT_MAX_ENUM = 0x7FFFFFFF
} VkPipelineBindPoint;

pInputAttachments — 输入到subpass的attachment的引用,也就是这个subpass用到的attachemnt

pColorAttachments — 实际上是一个数组,对应subpass输出的attachemnt的引用,数组元素的第一个值实际上对应于着色器中的layout(location = 0) out vec4 outColor ,

pResolveAttachments — 用于颜色附件的多重采样的引用

pDepthStencilAttachment — 深度和模板的附件的引用

pPreserveAttachments — 如果我们希望附件的生命周期跨越一个subpass但并不被subpass直接引用,我们就在pPreserveAttachments数组中引用他们。

每个VkAttachmentReference实际上都是下面的结构:

1
2
3
4
typedef struct VkAttachmentReference {
uint32_t attachment; // 在attachment数组中的索引
VkImageLayout layout; // 布局
} VkAttachmentReference;

4)Dependency依赖关系

我们知道一个renderpass可以有多个subpass,而每个subpass又可以引用多个attachment,那么麻烦来了,如何解决这些引用的先后关系呢?甚至如何解决同一attachment的读写问题呢?这就是dependency存在的意义

1
2
3
4
5
6
7
8
9
typedef struct VkSubpassDependency {
uint32_t srcSubpass;
uint32_t dstSubpass;
VkPipelineStageFlags srcStageMask;
VkPipelineStageFlags dstStageMask;
VkAccessFlags srcAccessMask;
VkAccessFlags dstAccessMask;
VkDependencyFlags dependencyFlags;
} VkSubpassDependency;

srcSubpass — 源subpass的索引
dstSubpass — 目的subpass的索引
srcStageMask — 位Mask,指定源subpass的哪些管线阶段产生数据
dstStageMask — 位Mask,指定目的subpass哪些管线阶段使用数据
srcAccessMask — 位Mask,指定源subpass如何访问数据
dstAccessMask — 位Mask,指定目的subpass如何访问数据
dependencyFlags

5)renderpass的销毁

因为renderpass是我们创建的,因此也是需要销毁的,如下所示:

1
vkDestroyRenderPass(device, renderPass, nullptr);

6)实际使用例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
void createRenderPass() {
VkAttachmentDescription colorAttachment{};
colorAttachment.format = swapChainImageFormat;
colorAttachment.samples = msaaSamples;
colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

VkAttachmentDescription depthAttachment{};
depthAttachment.format = findDepthFormat();
depthAttachment.samples = msaaSamples;
depthAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
depthAttachment.storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
depthAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
depthAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
depthAttachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

VkAttachmentDescription colorAttachmentResolve{};
colorAttachmentResolve.format = swapChainImageFormat;
colorAttachmentResolve.samples = VK_SAMPLE_COUNT_1_BIT;
colorAttachmentResolve.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachmentResolve.storeOp = VK_ATTACHMENT_STORE_OP_STORE;
colorAttachmentResolve.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachmentResolve.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
colorAttachmentResolve.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachmentResolve.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

VkAttachmentReference colorAttachmentRef{};
colorAttachmentRef.attachment = 0;
colorAttachmentRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

VkAttachmentReference depthAttachmentRef{};
depthAttachmentRef.attachment = 1;
depthAttachmentRef.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

VkAttachmentReference colorAttachmentResolveRef{};
colorAttachmentResolveRef.attachment = 2;
colorAttachmentResolveRef.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;

VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;
subpass.pDepthStencilAttachment = &depthAttachmentRef;
subpass.pResolveAttachments = &colorAttachmentResolveRef;

VkSubpassDependency dependency{};
dependency.srcSubpass = VK_SUBPASS_EXTERNAL;
dependency.dstSubpass = 0;
dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.srcAccessMask = 0;
dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;

std::array<VkAttachmentDescription, 3> attachments = { colorAttachment, depthAttachment, colorAttachmentResolve };
VkRenderPassCreateInfo renderPassInfo{};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = static_cast<uint32_t>(attachments.size());
renderPassInfo.pAttachments = attachments.data();
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;
renderPassInfo.dependencyCount = 1;
renderPassInfo.pDependencies = &dependency;

if (vkCreateRenderPass(device, &renderPassInfo, nullptr, &renderPass) != VK_SUCCESS) {
throw std::runtime_error("failed to create render pass!");
}
}
显示 Gitment 评论