目录
Subpasses and attachment references
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);
...
}
这是一个很大的工作,但在下一章中,所有的工作都集中在一起,最终创建了图形管道对象!