在我们完成管线的创建工作之前,我们需要告诉Vulkan渲染时候使用的framebuffer帧缓冲区附件相关信息。我们需要指定多少个颜色和深度缓冲区将会被使用,指定多少个采样器被用到及在整个渲染操作中相关的内容如何处理。所有的这些信息都被封装在一个叫做 render pass 的对象中。
pipeline设计了整个渲染的流程,但是pipeline只是定义了各个操作,renderpass定义了整个渲染的过程。
在复杂的图形渲染过程中,我们很难只通过一个操作就完成渲染,大多数情况下需要多步的处理,因此一个renderpass也是由多个子通道组成的,这些子通道被称为subpass,subpass对应整个渲染流程的各个渲染阶段。
1)创建renderpass
所有的渲染操作必须包含在一个renderpass中,下面我们首先来创建renderpass1
2
3
4
5VKAPI_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
11typedef 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
11typedef 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数据不一致的事情,大多数场景下,这个值设置为01
2
3
4
5typedef 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
6typedef 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
6typedef 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
12typedef 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
7typedef 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
4typedef 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
9typedef 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 | void createRenderPass() { |