Vulkan【11】渲染過程

創建一個渲染過程

本節的代碼是 10-init_render_pass.cpp

渲染過程通過指定在渲染操作期間使用的附件、子過程和依賴項的集合來描述渲染操作的範圍。一個渲染過程由至少一個子過程組成。將這些信息與驅動程序通信,使驅動程序能夠知道在呈現開始時將會發生什麼,併爲呈現操作設置最優的硬件。

首先使用vkCreateRenderPass()來定義渲染過程,然後使用vkCmdBeginRenderPass()vkCmdEndRenderPass()來將呈現過程實例插入到命令緩衝區中。

在本節中,您將只創建並定義渲染過程,而不是在命令緩衝區中使用它。

在本例的上下文中,渲染過程附件包括顏色附件,顏色附件是交換鏈裏的圖像,以及深度/模板附件,它是在之前的樣例中分配的深度緩衝。

圖像附件必須準備好,當它們在一個命令緩衝區中執行的渲染過程實例中用作附件時使用。這個準備過程包括將圖像佈局從最初的未定義狀態轉換爲在渲染過程中使用的最優狀態。由於這些佈局轉換是相當複雜的,您將在這裏瞭解它們,然後繼續創建渲染傳遞。

圖像佈局轉換

對備用內存訪問模式的需求

圖像的佈局是指圖像紋理如何從網格座標表示映射到圖像內存中的偏移量。通常情況下,圖像數據是以這種方式線性映射的,對於2D圖像來說,這可能意味着一排texel存儲在相鄰的內存中,下一行連續存儲在這一行之後,以此類推。

換句話說:

offset = rowCoord * pitch + colCoord

pitch是指行的大小。 pitch通常與圖像的寬度相同,,但是可能包含一些額外的填充字節,以確保圖像的每一行從滿足GPU的對齊需求的內存地址開始。

通過改變colCoord,線性佈局對於連續的texel讀或寫在一行中是可以的。但是大多數圖形操作都涉及到通過改變rowCoord來訪問多個相鄰行的texel。如果圖像寬度相當寬,那麼這些相鄰的行就會在線性內存地址空間中引入相當大的跳躍。這可能會導致性能問題,比如由於TLB遺漏和多層緩存內存系統中的緩存遺漏而導致的內存地址轉換較慢。

爲了解決這些低效率問題,許多GPU硬件實現支持“最佳”或平鋪內存訪問方案。在一個最優的佈局中,出現在圖像中間的一個矩形的紋理被存儲在內存中,這樣所有的texel都處於一個連續的內存段中。例如,構成一個矩形的texel,在16,32和右下角的31,47,可能會看到這個16 x 16塊的texel,連續地從一個地址開始存儲。行之間沒有長間隙。

如果GPU想要填充這個塊,例如,用一個純色的顏色,它就可以用少量的內存系統開銷來寫這個256個texel block。

這裏有一個簡單的2x2網格方案的例子。注意,在線性方案中,藍色的texel可以彼此相隔很遠,而它們在平鋪的模式中是相鄰的。

這裏寫圖片描述

大多數實現使用更復雜的平鋪模式和大於2x2的網格大小。

Vulkan對佈局的控制

正如上面所解釋的那樣,GPU硬件通常傾向於優化佈局,以實現更高效的渲染。最優佈局通常是“不透明的”,這意味着優化佈局格式的細節不會被髮布,也不會讓其他需要讀取或寫入圖像數據的組件知道。

例如,您可能希望GPU使用最優佈局呈現給圖像。但是,如果您希望將生成的圖像複製到一個您可以使用CPU讀取和理解的緩衝區中,那麼在嘗試閱讀之前,您可以將佈局從最優變爲一般。

從一個佈局到另一個佈局的轉換稱爲Vulkan中的佈局轉換。您可以控制這些轉換,並且可以通過以下三種方式調用它們:

  1. 內存障礙指令 (通過vkCmdPipelineBarrier)
  2. 渲染最終佈局規範
  3. 渲染過程子過程規範

內存屏障命令是一個顯式的佈局轉換命令,您可以在命令緩衝區中放置。例如,您將使用這個命令在更復雜的情況下同步內存訪問。由於您將使用另外兩種方法來執行佈局轉換,所以您不會在本教程中使用這個barrier命令。

更常見的情況是,在渲染開始之前和渲染完成後,需要對渲染的圖像進行佈局轉換。第一個轉換爲GPU的渲染準備了圖像,最後一個轉換爲顯示圖像到顯示器做準備。在這些情況下,您指定佈局轉換作爲渲染過程定義的一部分。稍後您將在本節中看到如何做到這一點。

佈局轉換可能會,也可能不會觸發實際的GPU佈局轉換操作。例如,如果舊的佈局沒有定義,而新的佈局是最優的,那麼GPU將不得不做其他的工作,而不是通過編程GPU硬件來訪問最優模式的內存。這是因爲圖像的內容沒有定義,不需要通過轉換來保存。另一方面,如果舊的佈局是通用的(非最優的)並且有跡象表明圖像中的數據需要被保留,那麼向優化佈局的轉換可能涉及到GPU對圖像的一些工作。

即使您知道或認爲佈局轉換實際上不會做任何工作,但最好還是去做一下,因爲它給了驅動程序更多的信息,並幫助確保您的應用程序在更多的設備上運行。

例子中的圖像佈局轉換

樣例代碼使用子過程定義和渲染過程定義來指定所需的圖像佈局轉換,而不是使用內存障礙命令。

Render Pass Layout

初始的渲染過程的佈局是沒有定義的,意思是“不關心”,因爲當渲染過程開始時,你不會關心圖像中已經存在的內容,因爲無論如何你都要把它畫出來。在這裏,您只是告訴驅動程序在渲染過程開始時對圖像的佈局是什麼。驅動程序在子過程或直到渲染結束時纔會進行轉換。

子過程佈局被設置爲顏色緩衝區的最優選擇,這表明在子過程的渲染操作期間驅動程序應該將佈局轉換爲最優。樣例代碼爲深度緩衝設置了類似的設置。

最後的渲染過程佈局告訴驅動將佈局轉換爲適合顯示器顯示的佈局。

創建渲染過程

現在您已經知道了如何將圖像佈局轉換爲正確的狀態,您可以繼續定義剩餘的渲染過程。

附件(Attachments)

有兩個附件,一個用於顏色,一個用於深度:

VkAttachmentDescription attachments[2];
attachments[0].format = info.format;
attachments[0].samples = NUM_SAMPLES;
attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;
attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE;
attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
attachments[0].flags = 0;

attachments[1].format = info.depth.format;
attachments[1].samples = NUM_SAMPLES;
attachments[1].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR
attachments[1].storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[1].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
attachments[1].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;
attachments[1].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;
attachments[1].finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;
attachments[1].flags = 0;

在兩個附件中設置loadOp成員爲CLEAR,表明您希望緩衝區在渲染過程實例的開始處被清空。

爲顏色附件設置storeOp成員爲STORE意味着您想要將渲染結果留在這個緩衝區中,這樣它就可以顯示給顯示器了。

storeOp成員設置爲 DONT_CARE的深度附件意味着當渲染過程實例完成時,您不需要緩衝區的內容。告訴驅動程序,在使用緩衝區後,您不關心緩衝區的內容,這是很有用的,因爲它允許驅動程序在不保存內容的情況下丟棄或刪除該內存。

對於圖像佈局,您可以指定顏色和深度緩衝,以開始未定義的佈局,如前所述。

子過程發生在初始佈局和最終佈局之間,將顏色附件設置爲VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,深度附件設置爲VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL

對於顏色附件,您可以指定最終的佈局爲VK_IMAGE_LAYOUT_PRESENT_SRC_KHR佈局,這是最適合顯示操作的,該操作是在渲染過程完成之後發生的。您可以將深度佈局與子過程設置的佈局相同,因爲深度緩衝不作爲顯示操作的一部分使用。

子過程

子過程的定義很簡單,如果你在做多個子過程,會更有趣。如果你正在對你的圖形數據進行預處理或後期處理,可能會對環境光遮蔽或其他一些效果進行處理,你可能會對多次子過程感興趣。但是在這裏,子過程定義對於在子過程表示哪些附件是活動的,以及在子過程中渲染時使用的佈局是很有用的。

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

VkAttachmentReference depth_reference = {};
depth_reference.attachment = 1;
depth_reference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;

attachment成員是您剛纔爲渲染過程所定義的附件數組中的附件的索引。

VkSubpassDescription subpass = {};
subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
subpass.flags = 0;
subpass.inputAttachmentCount = 0;
subpass.pInputAttachments = NULL;
subpass.colorAttachmentCount = 1;
subpass.pColorAttachments = &color_reference;
subpass.pResolveAttachments = NULL;
subpass.pDepthStencilAttachment = &depth_reference;
subpass.preserveAttachmentCount = 0;
subpass.pPreserveAttachments = NULL;

pipelineBindPoint成員的意思是表示這是一個圖形還是一個計算子過程。目前,只有圖形子過程是有效的。

渲染過程

現在你已經擁有所有用來定義渲染過程的東西了:

VkRenderPassCreateInfo rp_info = {};
rp_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
rp_info.pNext = NULL;
rp_info.attachmentCount = 2;
rp_info.pAttachments = attachments;
rp_info.subpassCount = 1;
rp_info.pSubpasses = &subpass;
rp_info.dependencyCount = 0;
rp_info.pDependencies = NULL;
res = vkCreateRenderPass(info.device, &rp_info, NULL, &info.render_pass);

您將在幾個即將到來的示例中使用渲染過程。

© Copyright 2016 LunarG, Inc

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章