1. 程式人生 > >Vulkan Programming Guide 第一章(3)

Vulkan Programming Guide 第一章(3)

物件型別和函式約定

事實上,Vulkan中的所有內容都被表示為一個由控制代碼引用的物件。控制代碼分為兩大類:可分派物件和不可分散物件。在大多數情況下,這與應用程式無關,僅影響API的結構以及系統級元件(如Vulkan載入器和層)與這些物件的互操作性。
可分派物件是內部包含排程表的物件。這是各種元件使用的功能表,用於確定當您的應用程式呼叫Vulkan時要執行的程式碼部分。這些型別的物件通常是較大較複雜的結構,目前由例項(VkInstance),物理裝置(VkPhysicalDevice),邏輯裝置(VkDevice),命令緩衝區(VkCommandBuffer)和佇列(VkQueue)組成)構成。所有其他物件被認為是不可分散的。
任何Vulkan函式的第一個引數總是一個可排程的物件。此規則的唯一例外是與例項的建立和初始化相關的功能。

管理記憶體

Vulkan提供兩種型別的記憶體:主機記憶體和裝置記憶體。由Vulkan API建立的物件通常需要一定數量的主機記憶體。這就是Vulkan實現將儲存物件狀態以及實現Vulkan API所需的任何資料。資源物件(如緩衝區和影象)需要一定量的裝置記憶體。這是儲存在資源中的資料的儲存器。
您的應用程式可以管理Vulkan實現的主機記憶體,並且需要您的應用程式管理裝置記憶體。為此,您將需要建立一個裝置記憶體管理子系統。您可以查詢您建立的每個資源,以獲得支援所需的記憶體量和型別。應用程式將分配正確的記憶體量並將其附加到資源物件,然後才能使用它。在諸如OpenGL這樣的更高級別的API中,這個“魔術”是由驅動程式代表你的應用程式執行的。然而,一些應用程式需要非常大量的小資源,而其他應用程式需要較少數量的非常大的資源。一些應用程式在執行過程中建立和銷燬資源,而其他應用程式在啟動時建立所有資源,並且在終止之前不釋放它們。
在這些情況下使用的分配策略可能會有很大的不同。沒有一刀切的策略。 OpenGL驅動程式不知道您的應用程式將如何執行,因此必須調整分配策略以嘗試適應您的使用模式。另一方面,您,應用程式開發人員,您的應用程式的行為準確無誤。您可以將資源分成長期和短暫的組。您可以將將一起使用的資源分成少量池化分配。您可以決定應用程式使用的分配策略
重要的是要注意,每個“實時”記憶體分配會給系統帶來一些成本。因此,將分配物件的數量保持在最小值是很重要的。建議裝置記憶體分配器以大塊分配記憶體。許多小資源可以放置在少得多的裝置記憶體塊內。第2章“記憶體和資源”中討論了裝置記憶體分配器的一個例子,它們更詳細地討論了記憶體分配。

Vulkan的多執行緒

支援多執行緒應用程式是Vulkan設計的一個組成部分。 Vulkan通常假定應用程式將確保沒有兩個執行緒同時在同一物件上進行變異。這被稱為外部同步。在Vulkan的效能關鍵部分(如構建命令緩衝區)中的絕大多數Vulkan命令根本不提供同步。
為了具體定義各種Vulkan命令的執行緒命令,必須保護不受主機併發訪問的每個引數被標記為外部同步。在某些情況下,物件或其他資料的控制代碼將被包含在資料結構中,包含在陣列中,或以其他方式傳遞給命令。這些引數也必須是外部同步的。
這樣做的意圖是,Vulkan實現不需要在內部採用互斥體或使用其他同步原語來保護資料結構。這意味著多執行緒程式很少停止或跨執行緒阻塞。
除了要求主機線上程間使用共享物件的同時訪問之外,Vulkan還提供了許多更高級別的功能,專門用於允許執行緒執行工作而不會相互阻止。這些包括以下內容:
•主機記憶體分配可以通過傳遞給物件建立功能的主機記憶體分配結構來處理。通過使用每個執行緒的分配器,該分配器中的資料結構不需要被保護。主機記憶體分配器在第2章“記憶體和資源”中有介紹。
•命令緩衝區是從池分配的,對池的訪問是外部同步的。如果應用程式每個執行緒使用單獨的命令池,則可以從這些池中分配命令緩衝區,而不會相互阻止。命令緩衝區和池在第3章“佇列和命令”中有介紹。
•描述符按描述符池分配。描述符是在裝置上執行的著色器使用的資源的表示。它們在第6章“著色器和管道”中有詳細描述。如果每個執行緒都使用單獨的池,則可以從這些池中分配描述符集,而不會使執行緒相互阻塞。
•二級命令緩衝區允許並行生成大型renderpass(必須包含在單個命令緩衝區中)的內容,然後在從主命令緩衝區呼叫時分組。第13章“Multipass Rendering”中詳細介紹了二級命令緩衝區。
當您構建一個非常簡單的單執行緒應用程式時,建立從其分配物件的池的需求可能看起來很多不必要的間接。然而,隨著應用程式線上程數量上的擴充套件,這些物件是實現高效能的必不可少的。
在本書的其餘部分中,關於執行緒的任何特殊要求將在引入命令時註明。

數學概念

計算機圖形學和大多數異構計算應用程式都是以數學作為基礎。 大多數Vulkan裝置都是基於非常強大的計算處理器。 在撰寫本文時,即使是適中的移動處理器也能夠提供許多千兆位的處理能力,而高階桌面和工作站處理器則提供了數千倍的數字處理能力。 因此,真正有趣的應用程式將建立在偏重數學的著色器上。 此外,Vulkan處理管道的幾個固定功能部分建立在硬連線到裝置和規範中的數學概念上。

向量和矩陣

任何圖形應用程式的基本構建塊之一是向量。無論它們是位置,方向,顏色還是其他數量的表示,在整個圖形文獻中都使用向量。向量的一種常見形式是均勻向量,它是一個空間中的向量,該空間的一維高於其所代表的數量。這些向量用於儲存投影座標。將均勻向量乘以任何標量產生表示相同投影座標的新向量。要投射一個點向量,通過其最後一個分量進行分割,產生x,y,z,1.0形式的向量(對於四分量向量)。

要將向量從一個座標空間轉換到另一個座標空間,將該向量乘以一個矩陣。正如3D空間中的一點被表示為四分量均勻向量,在3D均勻向量上執行的變換矩陣是4×4矩陣。
3D空間中的點通常表示為常規稱為x,y,z和w的四個分量的均勻向量。對於一個點,w分量通常以1.0開始,並且隨著向量通過投影矩陣變換而改變。在通過w分量分配之後,該點通過以下變數進行投影:如果沒有一個變換是投影變換,則w保持為1.0,除以1.0對向量沒有影響。如果向量進行投影變換,則w不等於1.0,但是通過它將會投射點並將w返回到1.0。
同時,3D空間中的方向也表示為w分量為0.0的均勻向量。用正確構造的4×4投影矩陣乘以方向向量將使w分量保持在0.0,對任何其他分量都不起作用。通過簡單地丟棄附加元件,您可以將3D方向向量通過與4D均勻3D點向量相同的變換,並使其經常進行旋轉,縮放和其他變換。

座標系

Vulkan通過將其端​​點或角點表示為3D空間中的點來處理圖形基元(如線條和三角形)。這些圖元被稱為頂點。 Vulkan系統的輸入是相對於它們所屬物件的原點的3D座標空間中的頂點座標(以w分量為1.0表示為均勻向量)。這個座標空間被稱為物件空間或有時是模型空間。
通常,管道中的第一個著色器將將該頂點轉換為檢視空間,該空間是相對於檢視器的位置。通過將頂點的位置向量乘以變換矩陣來進行該變換。這通常被稱為物件檢視矩陣或模型檢視矩陣。
有時,需要一個頂點的絕對座標,例如當找到一個頂點相對於某個其他物件的座標時。這個全球空間被稱為世界空間,是相對於全球起點的頂點的位置。
從檢視空間,頂點的位置被轉換為剪輯空間。這是Vulkan的幾何處理部分使用的最終空間,並且是將頂點推入典型3D應用程式使用的投影空間時通常會轉換頂點的空間。剪輯空間是所謂的,因為它是大多數實現執行剪輯的座標空間,其中移除位於被呈現的可見區域之外的原始圖元。
從剪下空間中,頂點位置通過劃分其W分量進行歸一化。這產生稱為標準化裝置座標或NDC的座標空間,並且該過程通常被稱為透視分割。在該空間中,座標系的可見部分在x和y方向上為-1.0至1.0,在z方向為0.0至1.0。在透視分割之前,這個地區之外的任何東西都將被剪掉。
最後,頂點的歸一化裝置座標由視口變換,它描述了NDC如何對映到要渲染圖片的視窗或影象中。
( OpenGL檢視變換:[http://www.songho.ca/opengl/index.html]

增強 Vulkan

雖然Vulkan的核心API規範相當廣泛,但絕非全然。 一些功能是可選的,而更多的可用層(其修改或增強現有行為)和擴充套件(向Vulkan新增新功能)的形式。 這兩個增強機制將在以下部分進行描述。
圖層
層是Vulkan的特徵,允許修改其行為。 層通常攔截全部或部分Vulkan,並新增諸如日誌記錄,跟蹤,提供診斷,分析等功能。 可以在例項級別新增一個層,在這種情況下,它會影響整個Vulkan例項以及可能由其建立的每個裝置。 或者,可以在裝置級別新增該層,在這種情況下,該層僅影響其啟用的裝置。
要發現系統上例項可用的層,請呼叫
vkEnumerateInstanceLayerProperties(),其原型是

VkResult vkEnumerateInstanceLayerProperties (
uint32_t*                   pPropertyCount, 
VkLayerProperties*          pProperties);

如果pProperties為nullptr,那麼pPropertyCount應指向一個將被Vulkan可用層數計數覆蓋的變數。 如果pProperties不是nullptr,那麼它應該指向一個VkLayerProperties結構的陣列,它將填充有關係統註冊的層的資訊。 在這種情況下,pPropertyCount指向的變數的初始值是由pProperties指向的陣列的長度,並且該變數將被該命令覆蓋的陣列中的條目數量覆蓋。
屬性陣列的每個元素都是Layer Properties結構的一個例項,它的定義是

typedef struct VkLayerProperties {

char        layerName[VK_MAX_EXTENSION_NAME_SIZE]; 
uint32_t    specVersion;

uint32_t    implementationVersion;

char        description[VK_MAX_DESCRIPTION_SIZE]; 
} VkLayerProperties;

每個層都有一個正式的名稱,它儲存在VkLayerProperties結構的layerName成員中。由於每個層的規範可能會隨著時間的推移而改進,澄清或附加,因此在specVersion中報告了層實現的版本。
隨著規格隨著時間的推移而改進,這些規範的實現也是如此。實現版本儲存在VkLayerProperties結構的implementationVersion欄位中。這允許實現提高效能,修復錯誤,實現更廣泛的可選功能集等。應用程式寫入器可以識別層的特定實現,並且僅當該實現的版本超過特定版本時選擇使用它,其中例如已知固定的關鍵錯誤。
最後,描述層的人類可讀字串儲存在描述中。此欄位的唯一目的是在使用者介面中記錄或顯示,僅供參考。
清單1.4說明了如何查詢Vulkan系統支援的例項層。

uint32_t numInstanceLayers = 0;
std::vector<VkLayerProperties> instanceLayerProperties;

 // Query the instance layers.  vkEnumerateInstanceLayerProperties(
 &numInstanceExtensions, nullptr);

 // If there are any layers, query their properties.
if (numInstanceLayers != 0) 
{   
    instanceLayerProperties.resize(numInstanceLayers);
    vkEnumerateInstanceLayerProperties(nullptr,                                      &numInstanceLayers,
    instanceLayerProperties.data());
} 

如上所述,不僅在例項級別,層可以被注入。 層也可以在裝置級應用。 要確定哪些層可用於裝置,請呼叫
vkEnumerateDeviceLayerProperties(),其原型是

VkResult vkEnumerateDeviceLayerProperties (
    VkPhysicalDevice            physicalDevice,
        uint32_t*               pPropertyCount,
        VkLayerProperties*      pProperties
);

可用於系統中的每個物理裝置的層可以是不同的,因此每個物理裝置可以報告不同的層集合。 要查詢的層的物理裝置在physicalDevice中傳遞。 對vkEnumerateDeviceLayerProperties()的pPropertyCount和pProperties引數與vkEnumerateInstanceLayerProperties()的相同命名的引數類似。 裝置層也由VkLayerProperties結構的例項描述。
要啟用例項級別的層,請將其名稱包含在用於建立例項的VkInstanceCreateInfo結構的ppEnabledLayerNames欄位中。 同樣,為了在建立與系統中的物理裝置相對應的邏輯裝置時啟用層,請在用於建立裝置的VkDeviceCreateInfo的ppEnabledLayerNames成員中包含層名稱。
官方SDK中包含了幾個層次,其中大部分內容與除錯,引數驗證和日誌記錄有關。 這些包括以下內容:

•VK_LAYER_LUNARG_api_dump將Vulkan呼叫及其引數和值列印到控制檯。
•VK_LAYER_LUNARG_core_validation對描述符集,流水線狀態和動態狀態中使用的引數和狀態執行驗證;驗證SPIR-V模組和圖形管道之間的介面;並跟蹤並驗證用於返回物件的GPU記憶體的使用情況。
•VK_LAYER_LUNARG_device_limits確保將值傳遞給Vulkan命令作為引數或資料結構成員屬於裝置支援的功能集限制。
•VK_LAYER_LUNARG_image驗證影象使用情況與支援的格式一致。 •VK_LAYER_LUNARG_object_tracker對Vulkan物件執行跟蹤,嘗試執行
捕獲洩漏,使用後自由錯誤和其他無效物件使用。
•VK_LAYER_LUNARG_parameter_validation確認傳遞給Vulkan函式的所有引數值都有效。
•VK_LAYER_LUNARG_swapchain對第5章“簡報”中描述的WSI(Windows系統整合)擴充套件功能提供的功能進行驗證。
•VK_LAYER_GOOGLE_threading確保了對於執行緒的Vulkan命令的有效使用,確保沒有兩個執行緒在不同時間訪問同一個物件。
•VK_LAYER_GOOGLE_unique_objects確保每個物件都有一個唯一的控制代碼,以便應用程式更容易的跟蹤,避免實現可能會重複使用相同引數表示物件的控制代碼。
除此之外,大量單獨的層被分組成一個更大的單層,稱為VK_LAYER_LUNARG_standard_validation,使其易於開啟。該書的應用程式框架在內建在除錯模式下時可以啟用此層,當內建在釋放模式時,所有層都被禁用。

擴充套件

擴充套件是任何跨平臺,開放API(如Vulkan)的基礎。它們允許實施者進行實驗,創新,並最終推動技術向前發展。最終,作為擴充套件功能最初引入的有用功能在該領域得到證明後可以進入未來版本的API。但是,擴充套件不是沒有成本。有些可能需要實現跟蹤附加狀態,在命令緩衝區構建期間進行額外的檢查,或者即使擴充套件不是直接使用也會帶來一些效能損失。因此,應用程式必須顯式啟用擴充套件,才能使用它們。這意味著不使用擴充套件的應用程式在效能或複雜性方面不支付費用,並且幾乎不可能不小心使用擴充套件功能,從而提高了可移植性。
擴充套件程式分為兩類:例項擴充套件和裝置擴充套件。例項擴充套件是通常在平臺上增強整個Vulkan系統的擴充套件。這種型別的擴充套件是通過與裝置無關的層提供的,或者僅僅是由系統上的每個裝置暴露的擴充套件,並被提升為一個例項。裝置擴充套件是擴充套件系統中的一個或多個裝置的功能,但不一定在每個裝置上可用。
每個擴充套件可以定義新的功能,新的型別,結構,列舉等。一旦啟用,擴充套件程式可以被認為是應用程式可用的API的一部分。必須在建立Vulkan例項時啟用例項擴充套件,並且必須在建立裝置時啟用裝置擴充套件。這些讓我們有一個雞和蛋誰先有的情況:我們如何知道在初始化Vulkan例項之前支援哪些擴充套件?
查詢支援的例項擴充套件是在建立Vulkan例項之前可以使用的幾個Vulkan功能之一。這是使用vkEnumerateInstanceExtensionProperties()函式執行的,其原型是
VkResult vkEnumerateInstanceExtensionProperties (
const char* pLayerName,
uint32_t* pPropertyCount,
VkExtensionProperties* pProperties);
pLayerName是可能提供副檔名的圖層的名稱。 現在,將其設定為nullptr。 pPropertyCount是一個指向包含查詢Vulkan的例項擴充套件數量的變數的變數,pProperties是一個指向將被填充有關受支援擴充套件的資訊的VkExtensionProperties結構陣列的指標。 如果pProperties為nullptr,則pPropertyCount指向的變數的初始值將被忽略,並覆蓋支援的例項擴充套件數。
如果pProperties不為nullptr,則陣列中的條目數假定為pPropertyCount指向的變數,並且直到該陣列的許多條目都填充有關受支援副檔名的資訊。 pPropertyCount指向的變數將被實際寫入pProperties的條目數覆蓋。

要正確查詢所有受支援的例項擴充套件,請呼叫vkEnumerateInstanceExtensionProperties()兩次。 第一次呼叫pProperties設定為nullptr來檢索支援的例項擴充套件的數量。 然後適當地調整陣列以接收擴充套件屬性並再次呼叫vkEnumerateInstanceExtensionProperties(),此時將pProperties中的陣列的地址傳遞給它。 清單1.5演示瞭如何做到這一點。
清單1.5:查詢例項擴充套件

uint32_t numInstanceExtensions = 0;
 std::vector<VkExtensionProperties> instanceExtensionProperties;

 // Query the instance extensions.
 vkEnumerateInstanceExtensionProperties(nullptr,                                          &numInstanceExtensions, nullptr);

// If there are any extensions, query their properties.
if (numInstanceExtensions != 0) { 
instanceExtensionProperties.resize(numInstanceExtensions);
vkEnumerateInstanceExtensionProperties(nullptr,
&numInstanceExtensions,                                instanceExtensionProperties.data());
} 

在清單1.5中的程式碼完成執行後,instanceExtensionProperties將包含例項支援的擴充套件列表。 VkExtensionProperties陣列的每個元素都描述了一個擴充套件。 VkExtensionProperties的定義是

typedef struct VkExtensionProperties {
char extensionName[VK_MAX_EXTENSION_NAME_SIZE]; 
uint32_t specVersion; 
} VkExtensionProperties;

VkExtensionProperties結構只包含副檔名和副檔名的版本。擴充套件可能隨著時間的推移而增加功能,因為擴充套件的新修訂版本將被生成。 specVersion欄位允許更新擴充套件,而不需要建立一個全新的擴充套件,以便新增次要功能。副檔名的名稱儲存在extensionName中。
如前所述,建立Vulkan例項時,VkInstanceCreateInfo結構中有一個名為ppEnabledExtensionNames的成員,它是一個指向要啟用副檔名的字串陣列的指標。如果平臺上的Vulkan系統支援擴充套件,該擴充套件將包含在從vkEnumerateInstanceExtensionProperties()返回的陣列中,並且可以通過VkInstanceCreateInfo結構的ppEnabledExtensionNames欄位將其名稱傳遞給vkCreateInstance()。
查詢對裝置擴充套件的支援是一個類似的過程。要做到這一點,呼叫vkEnumerateDeviceExtensionProperties(),其原型是

VkResult vkEnumerateDeviceExtensionProperties (
VkPhysicalDevice        physicalDevice, 
const char*             pLayerName,

uint32_t*               pPropertyCount, 
VkExtensionProperties*  pProperties);

vkEnumerateDeviceExtensionProperties()的原型與vkEnumerateInstanceExtensionProperties()的原型幾乎相同,只是額外添加了physicalDevice引數。 physicalDevice引數是其查詢副檔名的裝置的控制代碼。與vkEnumerateInstanceExtensionProperties()一樣,如果pProperties為nullptr,則vkEnumerateDeviceExtensionProperties()將覆蓋pPropertyCount與支援的擴充套件數量,如果pProprties不為nullptr,則使用有關所支援擴充套件的資訊填充該陣列。相同的VkExtensionProperties結構用於裝置擴充套件和例項擴充套件。
建立物理裝置時,VkDeviceCreateInfo結構的ppEnabledExtensionNames欄位可能包含指向從vkEnumerateDeviceExtensionProperties()返回的一個字串的指標。
一些擴充套件提供了您可以呼叫的附加入口點形式的新功能。這些顯示為函式指標,其值在啟用擴充套件後必須從例項或裝置查詢。例項函式是對整個例項有效的函式。如果擴充套件擴充套件了例項級功能,則應該使用例項級函式指標來訪問新功能。
要檢索一個例項級的函式指標,呼叫vkGetInstanceProcAddr(),其原型是

PFN_vkVoidFunction vkGetInstanceProcAddr ( 
VkInstance instance, const char* pName);

例項引數是要檢索新函式指標的例項的控制代碼。 如果您的應用程式使用多個Vulkan例項,則從此命令返回的函式指標僅對被引用例項擁有的物件有效。 函式的名稱以pName傳遞,該名稱是一個非終止的UTF-8字串。 如果識別功能名稱並啟用副檔名,則vkGetInstanceProcAddr()的返回值是可從應用程式呼叫的函式指標。
PFN_vkVoidFunction是指向以下宣告的函式的指標的型別定義:
VKAPI_ATTR void VKAPI_CALL vkVoidFunction(void);
Vulkan沒有功能具有這種特殊的簽名,擴充套件不太可能引入這樣的功能。 很可能,您需要將生成的函式指標轉換為相應簽名的指標,然後才能呼叫它。
假設建立物件的裝置(或裝置本身,如果裝置上的功能排程)支援擴充套件,並且該裝置的副檔名已啟用,則例項級別的函式指標對於例項擁有的任何物件都是有效的。 因為每個裝置可能會在不同的Vulkan驅動程式中實現,所以例項函式指標必須通過一個間接層分配到正確的模組中。 管理這種間接可能會產生一些開銷; 為了避免這種情況,您可以獲得直接轉到相應驅動程式的特定於裝置的功能指標。
要獲取裝置級功能指標,請呼叫vkGetDeviceProcAddr(),其原型是
PFN_vkVoidFunction vkGetDeviceProcAddr (
VkDevice device, const char* pName);
將函式指標指向的裝置傳遞到裝置中。 再次,您正在查詢的函式的名稱作為nul終止的UTF-8字串在pName中傳遞。 所產生的函式指標僅在裝置中指定的裝置有效。 裝置必須參考裝置
它支援提供新功能的擴充套件,擴充套件已被啟用。
由vkGetDeviceProcAddr()生成的函式指標特定於裝置。 即使使用相同的物理裝置建立具有完全相同引數的兩個或多個邏輯裝置,您必須使用結果函式指標與其查詢的裝置。
徹底關閉
在您的程式退出之前,您應該自己清理。在許多情況下,作業系統將清理您的應用程式終止時分配的資源。但是,程式碼結束並不總是這樣,應用程式結束。如果您正在編寫較大應用程式的元件,例如,該應用程式可能會終止使用Vulkan的呈現或計算操作,而不會實際退出。
清理時,一般做法如下:
•在與Vulkan相關的所有執行緒中,完成或以其他方式終止您的應用程式在主機和裝置上進行的所有工作。
•按照與建立順序相反的順序銷燬物件。
邏輯裝置可能是您在應用程式初始化期間建立的最後一個物件(除了執行時使用的物件)。在銷燬裝置之前,您應該確保它不會代表您的應用程式執行任何工作。要做到這一點,呼叫vkDeviceWaitIdle(),其原型是
VkResult vkDeviceWaitIdle (VkDevice device);
裝置的手柄通過裝置傳遞。 當vkDeviceWaitIdle()返回時,代表您的應用程式提交給裝置的所有工作都將保證已經完成 - 除非您在此期間向裝置提交更多的工作。 您應該確保可能正在提交到裝置的任何其他執行緒已被終止。
一旦確保裝置空閒,您可以安全地銷燬它。 要做到這一點,呼叫vkDestroyDevice(),其原型是
void vkDestroyDevice (
VkDevice device, const VkAllocationCallbacks* pAllocator);
要銷燬的裝置的控制代碼在裝置引數中傳遞,必須外部訪問其訪問。請注意,對於任何其他命令,對裝置的訪問不需要是外部同步的。然而,應用程式應確保在其他任何訪問它的命令仍在另一個執行緒中執行時,裝置不會被銷燬。
pAllocator應指向與用於建立裝置的分配結構相容的分配結構。一旦裝置物件被破壞,就不能再提交任何命令了。此外,不再可能使用裝置控制代碼作為任何功能的引數,包括

將裝置控制代碼作為其第一個引數的其他物件銷燬功能。這是為什麼你應該從建立順序的相反順序銷燬物件的另一個原因。
一旦與Vulkan例項關聯的所有裝置都已被銷燬,可以安全地銷燬該例項。這是通過呼叫其原型的vkDestroyInstance()函式來實現的
void vkDestroyInstance (
VkInstance instance, const VkAllocationCallbacks* pAllocator);
要破壞的例項的控制代碼被例項傳遞,與vkDestroyDevice()一樣,指向與分配例項的分配結構相容的指標應在pAllocator中傳遞。 如果pAllocator引數為vkCreateInstance()為nullptr,那麼pAllocator引數vkDestroyInstance()也應該是。
請注意,沒有必要銷燬物理裝置。 物理裝置不是通過專用的建立功能建立的,因為邏輯裝置是。 相反,物理裝置從呼叫返回到vkEnumeratePhysicalDevices(),並被認為是由例項擁有的。 因此,當例項被銷燬時,該例項與每個物理裝置相關聯的資源也被釋放。
概要
本章向您介紹了Vulkan。 你已經看到一個例項中包含了全部的Vulkan狀態。 該例項提供對物理裝置的訪問,每個物理裝置暴露了可用於執行工作的多個佇列。 您已經看到如何建立與物理裝置相對應的邏輯裝置。 您已經瞭解瞭如何擴充套件Vulkan,如何確定例項和裝置可用的擴充套件,以及如何啟用這些擴充套件。 您已經看到如何通過等待裝置完成應用程式提交的工作,破壞裝置控制代碼,最終摧毀例項控制代碼來乾淨地關閉Vulkan系統。