1. 程式人生 > >Vulkan Tutorial 13 Render passes

Vulkan Tutorial 13 Render passes

upd tro tac swap com visual htm html 管線

操作系統: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 地址。

Vulkan Tutorial 13 Render passes