操作系統:Windows8.1
顯卡:Nivida GTX965M
開發工具:Visual Studio 2017
Creating an instance
與Vulkan打交道,通常的步驟是創建一個intance去初始化Vulkan library。這個instance是您的應用程序與Vulkan庫之間的連接橋梁,通常創建過程中,需要向驅動程序提供一些應用層的信息。
首先添加一個createInstance函數,並在initVulkan函數中調用。
void initVulkan() { createInstance(); }
另外添加一個類成員來保存instance句柄:
private: VkInstance instance;
現在我們創建一個instance,並且為該數據結構賦予自定義應用程序的信息。這些數據從技術角度是可選擇的,但是它可以為驅動程序提供一些有用的信息來優化程序特殊的使用情景,比如驅動程序使用一些圖形引擎的特殊行為。這個數據結構稱為VkApplicationInfo:
VkApplicationInfo appInfo = {}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pNext = nullptr; appInfo.pApplicationName = "Hello Triangle"; appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0); appInfo.pEngineName = "No Engine"; appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0); appInfo.apiVersion = VK_API_VERSION_1_0;
如前所述,Vulkan中的許多數據結構要求在sType成員中明確的指定類型。pNext成員可用於指向特定的擴展結構。我們在這里使用默認初始化,將其設置為nullptr。
Vulkan中的大量信息通過結構體而不是函數參數傳遞,我們將填充一個結構體以提供足夠的信息創建instance。下一個結構體不是可選的,它需要告知Vulkan驅動程序我們需要使用哪些全局的 extensions 和 validation layers。這里的全局意味着它適用於整個程序,而不是特定的設備,這些內容將在接下來的小節中說明。
VkInstanceCreateInfo createInfo = {}; createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
前幾個參數比較簡單。接下來的兩個指定需要的全局擴展,Vulakn對於平台特性是零API支持的(至少暫時這樣),這意味着需要一個擴展才能與不同平台的窗體系統進行交互。GLFW有一個方便的內置函數,返回它有關的擴展信息,我們可以傳遞給struct:
unsigned int glfwExtensionCount = 0; const char** glfwExtensions; glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount); createInfo.enabledExtensionCount = glfwExtensionCount; createInfo.ppEnabledExtensionNames = glfwExtensions;
結構體的最后兩個成員確定需要開啟的全局的validation layers。我們將會在下一節中深入探討這部分內容,在這一節設置為空。
createInfo.enabledLayerCount = 0;
我們現在已經指定了Vulkan創建一個實例需要的一切信息,調用vkCreateInstance創建屬於我們的第一個instance:
VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);
如你所見,Vulkan中創建、實例化相關的函數參數一般遵循如下原則定義:
- 使用有關creation info 的結構體指針
- 使用自定義分配器回調的指針
- 使用保存新對象句柄的指針
如果一切順利,此刻instance的句柄應該存儲在VkInstance類成員中了。幾乎所有的Vulkan函數都返回一個值為VK_SUCCESS或錯誤代碼的VkResult類型的值。要檢查instance是否已經成功創建,我們不需要保存結果,僅僅使用 VK_SUCCESS 值來檢測即可:
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) { throw std::runtime_error("failed to create instance!"); }
現在運行程序,確認我們的instance創建成功。
Checking for extension support
如果你查看vkCreateInstance的文檔,你會看到一個可能出現的錯誤代碼是VK_ERROR_EXTENSION_NOT_PRESENT。我們可以簡單地指定我們需要的擴展,如果該錯誤代碼返回,則終止它們。這對於窗體系統或者諸如此類的擴展是有意義的,那么如何檢查可選功能呢?
在創建instance之前檢索支持的擴展列表,通過vkEnumerateInstanceExtensionProperties函數。它指向一個變量,該變量存儲擴展數量和一個VkExtensionProperties數組來存儲擴展的詳細信息。它也接受一個可選擇的參數,允許我們通過特定的validation layers過濾擴展,現在我們暫時忽略這些。
要分配一個數組來保存擴展的詳細信息,我們首先需要知道有多少個擴展存在。可以通過將后一個參數置空來獲取擴展數量:
uint32_t extensionCount = 0; vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
現在我們分配一個集合去持有擴展的詳細信息(include <vector>)
std::vector<VkExtensionProperties> extensions(extensionCount);
最后我們可以遍歷擴展的詳細信息:
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
每個VkExtensionProperties結構體包含擴展的名稱和版本。我們可以用簡單的for循環打印他們(\t是縮進)
std::cout << "available extensions:" << std::endl; for (const auto& extension : extensions) { std::cout << "\t" << extension.extensionName << std::endl; }
如果需要獲取有關Vulkan支持的一些詳細信息,可以將此代碼添加到createInstance函數。作為一個嘗試,創建一個函數,檢查glfwGetRequiredInstanceExtensions返回的所有擴展是否都包含在受支持的擴展列表中。
Cleaning up
在程序退出前,請正確銷毀VkInstance。這部分可以定義在cleanup函數中,調用vkDestroyInstance函數完成。
void cleanup() { vkDestroyInstance(instance, nullptr); glfwDestroyWindow(window); glfwTerminate(); }
vkDestroyInstance函數的參數很簡單。像之前小節提到的,Vulkan中的分配和釋放功能有一個可選的分配器回調,我們通過將nullptr設置忽略。后續小節中創建的所有Vulkan相關資源,集中在cleanup函數中進行清理,且確保在銷毀instance之前銷毀。
在進行更復雜的內容之前,是時候了解validation layers了。
獲取工程代碼 GitHub checkout