Drawing a triangle/Graphics pipeline basics/Render passes

目录

Setup

Attachment description

Subpasses and attachment references

Render pass


Setup

在完成创建管道之前,我们需要告诉Vulkan渲染时将使用的帧缓冲区附件。我们需要指定将有多少颜色和深度缓冲区,每个缓冲区要使用多少样本,以及在整个渲染操作中如何处理它们的内容。所有这些信息都封装在渲染过程对象中,我们将为其创建一个新的createRenderPass函数。在创建GraphicsPipeline之前,从initVulkan调用此函数。

void initVulkan() {
    createInstance();
    setupDebugMessenger();
    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;
}

颜色附件的格式应该与交换链图像的格式相匹配,我们还没有对多采样进行任何操作,因此我们将只使用一个样本。

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

loadOp和storeOp决定在渲染之前和渲染之后如何处理附件中的数据。我们有以下loadOp选项:

  • VK_ATTACHMENT_LOAD_OP_LOAD:保留附件的现有内容
  • VK_ATTACHMENT_LOAD_OP_CLEAR:在开始时将值清除为常量
  • VK_ATTACHMENT_LOAD_OP_DONT_CARE:现有内容未定义;我们不在乎他们

在本例中,我们将使用clear操作在绘制新帧之前将帧缓冲区清除为黑色。storeOp只有两种可能性:

  • VK_ATTACHMENT_STORE_OP_STORE:渲染的内容将存储在内存中,稍后可以读取
  • VK_ATTACHMENT_STORE_OP_DONT_CARE:渲染操作后,帧缓冲区的内容将未定义

我们对在屏幕上看到渲染的三角形很感兴趣,所以我们在这里进行storeOp。

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

loadOp和storeOp应用于颜色和深度数据,而stencilLoadOp/stencilStoreOp适用于模板数据。我们的应用程序不会对模板缓冲区执行任何操作,因此加载和存储的结果无关紧要。

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

Vulkan中的纹理和帧缓冲区由具有特定像素格式的VkImage对象表示,但内存中像素的布局可能会根据您尝试对图像进行的操作而改变。

一些最常见的布局是:

  • VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:用作颜色附件的图像
  • VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:要在交换链中显示的图像
  • VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:要用作内存复制操作目标的图像

我们将在纹理一章中更深入地讨论这个主题,但现在需要知道的是,图像需要转换为适合于接下来将要涉及的操作的特定布局。

initialLayout指定渲染过程开始之前图像的布局。finalLayout指定渲染过程完成时自动转换到的布局。对initialLayout使用VK_IMAGE_LAYOUT_UNDEFINED意味着我们不关心图像的先前布局。这个特殊值的警告是,不能保证图像的内容被保留,但这并不重要,因为我们无论如何都要清除它。我们希望图像在渲染后可以使用交换链进行显示,这就是我们使用VK_IMAGE_LAYOUT_PRESENT_SRC_KHR作为finalLayout的原因。

Subpasses and attachment references

单个渲染过程可以由多个子过程组成。子过程是后续的渲染操作,取决于先前过程中帧缓冲区的内容,例如,一系列相继应用的后处理效果。如果将这些渲染操作分组到一个渲染过程中,则Vulkan能够重新排序这些操作并节省内存带宽,以获得可能更好的性能。然而,对于我们的第一个三角形,我们将只使用一个子路径。

每个子过程都引用一个或多个附件,这些附件是我们在前面章节中使用结构描述的。这些引用本身是VkAttachmentReference结构,如下所示:

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

附件参数通过附件描述数组中的索引指定要引用的附件。我们的数组由一个VkAttachmentDescription组成,因此其索引为0。布局指定我们希望附件在使用此引用的子过程中具有的布局。启动子进程时,Vulkan将自动将附件转换到此布局。我们打算使用附件作为颜色缓冲区,VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL布局将为我们提供最佳性能,正如其名称所暗示的。

子通路使用VkSubpassDescription结构描述:

VkSubpassDescription subpass{};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;

Vulkan将来也可能支持计算子通道,所以我们必须明确这是一个图形子通道。接下来,我们指定颜色附件的引用:

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

此数组中附件的索引直接从具有布局(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!");
}

与管道布局一样,渲染过程将在整个程序中被引用,因此只应在最后清理:

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

这是一个很大的工作,但在下一章中,所有的工作都集中在一起,最终创建了图形管道对象!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值