Vulkan Tutorial 13 Render passes

操作系统:Windows8.1

显卡:Nivida GTX965M

开发工具:Visual Studio 2017


Setup

在我们完成管线的创建工作,我们接下来需要告诉Vulkan渲染时候使用的framebuffer帧缓冲区附件相关信息。我们需要指定多少个颜色和深度缓冲区将会被使用,指定多少个采样器及如何在整个渲染操作中处理它们。所有的这些信息都被封装在一个叫做render pass的对象中,我们新添加一个createRenderPass函数。在initVulkan函数中确保createGraphicsPipeline调用之前,调用它。

void initVulkan() {
    createInstance();
    setupDebugCallback();
    createSurface();
    pickPhysicalDevice();
    createLogicalDevice();
    createSwapChain();
    createImageViews();
    createRenderPass();
    createGraphicsPipeline();
}

...

void createRenderPass() {

}

Attachment description



在教程中仅有一个颜色缓冲区附件被交换链中的一个图像所代表。

void createRenderPass() {
    VkAttachmentDescription colorAttachment = {};
    colorAttachment.format = swapChainImageFormat;
    colorAttachment.samples = VK_SAMPLE_COUNT_1_BIT;
}

format是颜色附件的格式,它应该与交换链中图像的格式相匹配,同时我们不会做任何多重采样的工作,所以采样器设置为1。

colorAttachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
colorAttachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;

loadOpstoreOp决定了渲染前和渲染后数据在对应附件的操作行为。有两种选择针对loadOp:

  • VK_ATTACHMENT_LOAD_OP_LOAD: 保存已经存在于当前附件的内容
  • VK_ATTACHMENT_LOAD_OP_CLEAR: 起始阶段以一个常量清理附件内容
  • VK_ATTACHMENT_LOAD_OP_DONT_CARE: 存在的内容未定义,忽略它们

我们要做的是使用清理操作来清理帧缓冲区framebuffer为黑色,在绘制任何新的帧之前。同时有两个选择针对storeOp:

  • VK_ATTACHMENT_STORE_OP_TORE: 渲染的内容会存储在内容,并在过后读取
  • VK_ATTACHMENT_STORE_OP_DONT_CARE: 帧缓冲区的内容在渲染操作完毕后设置为undefined

我们要做的是渲染一个三角形在屏幕上,所以我们选择存储操作。

colorAttachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
colorAttachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;

loadOpstoreOp应用在颜色和深度数据,同时stencilLoadOp / stencilStoreOp应用在模版数据。我们的应用程序不会做任何模版缓冲区的操作,所以它的loading和storing无关紧要。

colorAttachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
colorAttachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;

纹理和帧缓冲区在Vulkan中通常用VkImage 对象配以某种像素格式来代表。但是像素在内存中的布局可以基于预要对image图像进行的操作发生变化。

一些常用的布局:

  • VK_IMAGE_LAYOUT_COLOR_ATTACHMET_OPTIMAL: 图像作为颜色附件
  • VK_IMAGE_LAYOUT_PRESENT_SRC_KHR: 图像在交换链中被呈现
  • VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL: 图像作为目标,用于内存COPY操作

我们会深入讨论这些内容在纹理章节,现在最重要的是为需要转变的图像指定合适的layout布局进行操作。

initialLayout指定图像在开始进入渲染通道render pass前将要使用的布局结构。finalLayout指定当渲染通道结束自动转变时使用的布局。使用VK_IMAGE_LAYOUT_UNDEFINED设置initialLayout,意为不关心图像之前的布局。特殊值表明图像的内容不确定会被保留,但是这并不总要,因为无论如何我们都要清理它。我们希望图像渲染完毕后使用交换链进行呈现,这就解释了为什么finalLayout要设置为VK_IMAGE_LAYOUT_PRESENT_SRC_KHR

Subpasses and attachment references



一个单独的渲染通道可以由多个子通道组成。子通道是渲染操作的一个序列。子通道作用与后续的渲染操作,并依赖之前渲染通道输出到帧缓冲区的内容。比如说后处理效果的序列通常每一步都依赖之前的操作。如果将这些渲染操作分组到一个渲染通道中,通过Vulkan将通道中的渲染操作进行重排序,可以节省内存从而获得更好的性能。对于我们要绘制的三角形,我们只需要一个子通道。

每个子通道引用一个或者多个之前使用结构体描述的附件。这些引用本身就是VkAttachmentReference结构体:

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

attachment附件参数通过附件描述符集合中的索引来持有。我们的集合是由一个VkAttachmentDesription组成的,所以它的索引为0layout为附件指定子通道在持有引用时候的layout。当子通道开始的时候Vulkan会自动转变附件到这个lyaout。因为i我们期望附件起到颜色缓冲区的作用,layout设置为VK_IMAGE_LAYOUT_ATTACHMENT_OPTIMAL会给我们最好的性能。

子通道使用VkSubpassDescription结构体描述:

VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;

Vulkan在未来可能会支持关于compute subpasses的功能,所以在这里我们明确指定graphics subpass图形子通道。下一步为它指定颜色附件的引用:

subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &colorAttachmentRef;

附件在数组中的索引直接从片段着色器引用,其 layout(location = 0) out vec4 outColor 指令!

可以被子通道引用的附件类型如下:

  • pInputAttachments: 附件从着色器中读取
  • pResolveAttachments: 附件用于颜色附件的多重采样
  • pDepthStencilAttachment: 附件用于深度和模版数据
  • pPreserveAttachments: 附件不被子通道使用,但是数据被保存

Render pass



现在附件和基本的子通道已经介绍过了,我们可以创建渲染通道了。首先新建一个类成员变量持有VkRenderPass对象,该变量在pipelineLayout上定义:

VkRenderPass renderPass;
VkPipelineLayout pipelineLayout;

渲染通道对象创建通过填充VkRenderPassCreateInfo结构体,并配合相关附件和子通道来完成。VkAttachmentReference对象引用附件数组。

VkRenderPassCreateInfo renderPassInfo = {};
renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
renderPassInfo.attachmentCount = 1;
renderPassInfo.pAttachments = &colorAttachment;
renderPassInfo.subpassCount = 1;
renderPassInfo.pSubpasses = &subpass;

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

就像pipeline layout一样,渲染通道在整个程序生命周期内都被使用,所以需要在退出阶段进行清理:

void cleanup() {
    vkDestroyPipelineLayout(device, pipelineLayout, nullptr);
    vkDestroyRenderPass(device, renderPass, nullptr);
    ...
}

这看起来很多工作量,但是在下一章节我们会把所有的组件整合起来,创建最终的图形管线对象。

项目代码 GitHub地址。

时间: 2024-08-12 13:34:15

Vulkan Tutorial 13 Render passes的相关文章

Vulkan Tutorial 12 Fixed functions

操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 早起的图形API在图形渲染管线的许多阶段提供了默认的状态.在Vulkan中,从viewport的大小到混色函数,需要凡事做到亲历亲为.在本章节中我们会填充有关固有功能操作的所有结构体. Vertex input VkPipelineVertexInputStateCreateInfo结构体描述了顶点数据的格式,该结构体数据传递到vertex shader中.它以两种方式进行描述:

Vulkan Tutorial 02 编写Vulkan应用程序框架原型

操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 General structure 在上一节中,我们创建了一个正确配置.可运行的的Vulkan应用程序,并使用测试代码进行了测试.本节中我们从头开始,使用如下代码构建一个基于GLFW的Vulkan应用程序原型框架的雏形. #include <vulkan/vulkan.h> #include <iostream> #include <stdexcept>

Vulkan Tutorial 05 物理设备与队列簇

操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Selecting a physical device 通过VkInstance初始化Vulkan后,我们需要在系统中查找并选择一个支持我们所需功能的显卡.实际上,我们可以选择任意数量的显卡并同时使用他们,但在本小节中,我们简单的设定选择规则,即将查找到的第一个图形卡作为我们适合的物理设备. 我们添加函数pickPhysicalDevice并在initVulkan函数中调用. vo

Vulkan Tutorial 01 开发环境搭建之Windows

操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 相信很多人在开始学习Vulkan开发的起始阶段都会在开发环境的配置上下一些功夫,那么本问将会引导大家快速的完成Vulkan在Windows下的开发环境,并使用几个常用的开发工具库. Vulkan SDK 开发Vulkan应用程序所需的最重要的组件就是SDK.它包括核心头文件.标准的Validation layers及调试工具集.和驱动Loader,如果现在这些关键词不是很明白的话,

Vulkan Tutorial 03 理解Instance

操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Creating an instance 与Vulkan打交道,通常的步骤是创建一个intance去初始化Vulkan library.这个instance是您的应用程序与Vulkan库之间的连接桥梁,通常创建过程中,需要向驱动程序提供一些应用层的信息. 首先添加一个createInstance函数,并在initVulkan函数中调用. void initVulkan() { cr

Vulkan Tutorial 04 理解Validation layers

操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 What are validation layers? Vulkan API的设计核心是尽量最小化驱动程序的额外开销,所谓额外开销更多的是指向渲染以外的运算.其中一个具体的表现就是默认条件下,Vulkan API的错误检查的支持非常有限.即使遍历不正确的值或者将需要的参数传递为空指针,也不会有明确的处理逻辑,并且直接导致崩溃或者未定义的异常行为.之所以这样,是因为Vulkan要求每

Vulkan Tutorial 06 逻辑设备与队列

操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 在选择要使用的物理设备之后,我们需要设置一个逻辑设备用于交互.逻辑设备创建过程与instance创建过程类似,也需要描述我们需要使用的功能.因为我们已经查询过哪些队列簇可用,在这里需要进一步为逻辑设备创建具体类型的命令队列.如果有不同的需求,也可以基于同一个物理设备创建多个逻辑设备. 首先添加一个新的类成员来存储逻辑设备句柄. VkDevice devic

Vulkan Tutorial 18 重构交换链

操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 Introduction 现在我们已经成功的在屏幕上绘制出三角形,但是在某些情况下,它会出现异常情况.窗体surface会发生改变,使得交换链不在于其兼容.可能导致这种情况发生的原因之一是窗体的大小变化.我们必须在这个时机重新创建交换链. Recreating the swap chain 添加新的函数recreateSwapChain并调用createSwapChain及依赖于交

Vulkan Tutorial 15 Framebuffers

操作系统:Windows8.1 显卡:Nivida GTX965M 开发工具:Visual Studio 2017 我们在前面的章节中已经讨论了很多次framebuffers帧缓冲区,到目前为止我们配置了render pass渲染通道并希望输出一个与交换链图像格式一致的帧缓冲区,但是我们实际上还没有创建. 在render pass创建阶段我们指定了具体的附件,并通过VkFramebuffer对象包装绑定.帧缓冲区对象引用表示为附件的所有的VkImageView对象.在我们的例子中只会使用一个帧缓