利用我們前兩章中獲得的知識(shí),我們現(xiàn)在已經(jīng)達(dá)到了可以從 0 開始進(jìn)行 Vulkan 編程的水平悠反。 這兩章奠定了基礎(chǔ),并幫助我們理解這一革命性 API 的基本原理馍佑。 現(xiàn)在斋否,在更高層次上,我們要了解該技術(shù)背后的動(dòng)機(jī)拭荤,核心塊以及相關(guān)的功能和術(shù)語茵臭。 另外,我們通過 Vulkan 偽代碼舅世,并構(gòu)建了一個(gè)簡(jiǎn)單的程序來理解以及可視化 Vulkan 編程模型旦委。
從本章開始奇徒,我們會(huì)深入 Vulkan 編程的核心,并開始將我們的 Hello World缨硝!偽代碼轉(zhuǎn)換成現(xiàn)實(shí)世界可執(zhí)行的程序摩钙。
注意
本書的所有章節(jié)都是以結(jié)構(gòu)化的方式設(shè)計(jì)和編寫的;每一個(gè)新章節(jié)都依賴于前一章的內(nèi)容查辩。 建議您按照章節(jié)的順序進(jìn)行高效的學(xué)習(xí)體驗(yàn)胖笛。
在本章中,我們將介紹以下主題:
- LunarG SDK 入門
- 用 CMake 設(shè)置第一個(gè)項(xiàng)目
- 層和擴(kuò)展的介紹
- 創(chuàng)建一個(gè) Vulkan 實(shí)例
- 理解物理和邏輯設(shè)備
- 理解隊(duì)列和隊(duì)列族
- 將設(shè)備喝隊(duì)列整合在一起
LunarG SDK 入門
本書中的所有章節(jié)都使用 LunarG SDK 進(jìn)行 Vulkan 編程宜岛。 該 SDK 可以從 https://vulkan.lunarg.com 下載长踊;你需要一個(gè) LunarG 賬戶才能下載。
SDK 的默認(rèn)安裝路徑始終位于 C:\ VulkanSDK \ 『版本』萍倡。 安裝成功后身弊,將 SDK 的 Bin 目錄位置添加到 PATH 環(huán)境變量(C:\ VulkanSDK \ 1.0.26.0 \ Bin)中。 另外列敲,添加 VK_SDK_PATH 環(huán)境變量阱佛,指向 SDK 的路徑(C:\ VulkanSDK \ 1.0.26.0)。
安裝程序還會(huì)將 Vulkan 加載程序(vulkan-1.dll)添加到 C:\ Windows \ System32 目錄中酿炸。 根據(jù)不同的目標(biāo)窗口系統(tǒng)瘫絮,加載器會(huì)是一個(gè) 32 位或 64 位的 DLL。
以下是本章通用的一些常用術(shù)語:
術(shù)語 | h 描述 |
---|---|
ICD | 這是 Installable Client Driver 的縮寫填硕。 這是一個(gè) Vulkan 兼容的顯卡驅(qū)動(dòng)程序麦萤。 多個(gè) ICD - 例如 NVIDIA 和英特爾的驅(qū)動(dòng)程序 - 可以共存,而不會(huì)相互干擾扁眯。 |
Layers | 這些是可插拔的組件壮莹,可以 hook 或攔截 Vulkan 命令。 它們提供諸如調(diào)試姻檀、驗(yàn)證命满、跟蹤等服務(wù)。 |
Loader | 加載程序的工作是定位顯卡驅(qū)動(dòng)程序并以平臺(tái)無關(guān)的方式暴露層的庫(kù)绣版。 在 Windows 上胶台,加載庫(kù)(vulkan-1.dll)使用注冊(cè)表來定位 ICD 和層配置。 |
以下是 LunarG SDK 目錄中文件夾結(jié)構(gòu)以及相應(yīng)的說明:
目錄 | 描述 |
---|---|
Bin and Bin32 | 它們包含可執(zhí)行文件和加載程序的 32 位(Bin32 文件夾)和 64 位(Bin 文件夾)發(fā)行版本杂抽。 它們還包含層的庫(kù)和工具诈唬。 |
Config | 這是為了存儲(chǔ)不同的 Vulkan 配置。 例如缩麸,它包含 vk_layer_settings.txt 文件铸磅,該文件用于在不同驗(yàn)證層設(shè)置配置參數(shù)。 這些配置可以動(dòng)態(tài)地影響層。 |
Demo | 這是 cub阅仔、tri 和 vulkaninfo 程序的 Vulkan 示例源碼吹散。 |
Doc | 其中包含規(guī)范、手冊(cè)八酒,發(fā)行說明以及其他重要的文檔空民。 |
glslang | 其中包含 glslang 的源代碼和頭文件。 它為 GLSL 提供了一個(gè)前端解析器丘跌,并為 shader 驗(yàn)證提供了一個(gè)名為 glslangValidator 的獨(dú)立包裝工具袭景。 |
Include | 其中包含了用于構(gòu)建和編譯 Vulkan 應(yīng)用程序所需的頭文件。 |
Runtime installer | Vulkan 運(yùn)行時(shí)安裝程序提供的 Vulkan 運(yùn)行時(shí)庫(kù)闭树,可由 Vulkan 應(yīng)用程序或驅(qū)動(dòng)程序包含這些運(yùn)行環(huán)境耸棒。 有關(guān)更多信息,請(qǐng)參閱 README.txt 文件报辱。 |
Source | 其中包含加載器(vulkan-1.dll)和層庫(kù)的源代碼實(shí)現(xiàn)与殃。 |
spir-v tools | 其中包括 SPIR-V 工具的源代碼和頭文件。 |
注意
多個(gè) SDK 的安裝不會(huì)影響其他 SDK 的安裝碍现。 PATH 環(huán)境變量總是指向最近安裝的 SDK 版本幅疼。
使用 CMake 設(shè)置我們的第一個(gè)項(xiàng)目
CMake 是一種構(gòu)建過程管理工具,可以以獨(dú)立于編譯器的方式在操作系統(tǒng)中工作昼接。 它利用 CMakeLists.txt 文件構(gòu)建項(xiàng)目的解決方案爽篷。 在本節(jié)中,我們將學(xué)習(xí)為第一個(gè) Vulkan 應(yīng)用程序構(gòu)建 CMake 文件的過程慢睡。 請(qǐng)參閱以下說明了解這個(gè)配置文件(CMakeLists.txt)的創(chuàng)建方法:
- 按照指定的文件夾結(jié)構(gòu)約定創(chuàng)建一個(gè)空的 CMakeLists.txt 文件逐工,即 chapter_3> Sample Name> CMakeLists.txt。 為了確保不同 CMake 版本之間的兼容性漂辐,您需要指定最低支持的 CMake 版本號(hào)泪喊。 如果當(dāng)前版本的 CMake 碰巧低于指定的版本,它就會(huì)停止構(gòu)建解決方案髓涯。 CMake 的最小支持版本是用 cmake_minimum_required 指定的袒啼。 以下是 CMakeList.txt 文件中的代碼:
cmake_minimum_required(VERSION 3.7.1)
- 使用 set CMake 關(guān)鍵字指定用來查找 Vulkan SDK 路徑的必要變量。 另外纬纪,在起一個(gè)有意義的名字:
set (Recipe_Name "3_0_DeviceHandshake")
- 在這個(gè)小節(jié)中蚓再,我們使用的 CMake 版本為 3.7.1,因?yàn)?Vulkan 模塊自帶的就是這個(gè)版本包各。 此模塊有助于自動(dòng)查找 Vulkan SDK 安裝对途,包含目錄以及所需的庫(kù),來構(gòu)建 Vulkan 應(yīng)用程序髓棋。 在下面的 CMake 代碼中,我們首先嘗試使用 CMake Vulkan 模塊來定位 Vulkan SDK,如果不成功按声,我們就使用手動(dòng)的方式指定 Vulkan SDK 路徑膳犹。 請(qǐng)根據(jù)代碼中給定的注釋獲得更加詳細(xì)的描述:
# AUTO_LOCATE_VULKAN - accepted value ON or OFF # ON - Use CMake to auto locate the Vulkan SDK.
# OFF - Vulkan SDK path can be specified manually.
# This is helpful to test the build on various Vulkan version.
option(AUTO_LOCATE_VULKAN "AUTO_LOCATE_VULKAN" ON ) if(AUTO_LOCATE_VULKAN)
message(STATUS "Attempting auto locate Vulkan using CMake ")
# Find Vulkan Path using CMake's Vulkan Module
# This will return Boolean 'Vulkan_FOUND' indicating
# the status of find as success(ON) or fail(OFF).
# Include directory path - 'Vulkan_INCLUDE_DIRS' # and 'Vulkan_LIBRARY' with required libraries.
find_package(Vulkan)
# Try extracting VulkanSDK path from ${Vulkan_INCLUDE_DIRS}
if (NOT ${Vulkan_INCLUDE_DIRS} STREQUAL "")
set(VULKAN_PATH ${Vulkan_INCLUDE_DIRS}) STRING(REGEX REPLACE "/Include" "" VULKAN_PATH
${VULKAN_PATH})
endif()
if(NOT Vulkan_FOUND)
# CMake may fail to locate the libraries but could be able to # provide some path in Vulkan SDK include directory variable # 'Vulkan_INCLUDE_DIRS', try to extract path from this.
message(STATUS "Failed to locate Vulkan SDK, retrying again...")
# Check if Vulkan path is valid, if not switch to manual mode.
if(EXISTS "${VULKAN_PATH}")
message(STATUS "Successfully located the
Vulkan SDK: ${VULKAN_PATH}")
else()
message("Error: Unable to locate Vulkan SDK. Please
turn off auto locate option by
specifying 'AUTO_LOCATE_VULKAN' as 'OFF'") message("and specify manually path using 'VULKAN_SDK'
and 'VULKAN_VERSION' variables in the CMakeLists.txt.")
return()
endif() endif()
else()
message(STATUS "Attempting to locate Vulkan SDK
using manual path ")
set(VULKAN_SDK "C:/VulkanSDK") set(VULKAN_VERSION "1.0.33.0")
set(VULKAN_PATH "${VULKAN_SDK}/${VULKAN_VERSION}")
message(STATUS "Using manual specified path: ${VULKAN_PATH}")
# Check if manual set path exists if(NOT EXISTS "${VULKAN_PATH}")
message("Error: Unable to locate this Vulkan SDK path VULKAN_PATH:
${VULKAN_PATH}, please specify correct path.
For more information on correct installation process, please refer to subsection 'Getting started with
Lunar- G SDK' and 'Setting up first project with CMake' in Chapter 3, 'Shaking hands with the device' in this book 'Learning Vulkan', ISBN - 9781786469809.")
return() endif()
endif()
- 使用 project 關(guān)鍵字,您可以指定任何想要的項(xiàng)目名稱签则。 在 Windows 上须床,窗口系統(tǒng)集成 Window System Integration(WSI)需要 VK_KHR_WIN32_SURFACE_EXTENSION_NAME 擴(kuò)展 API。 為此渐裂,您需要在 CMake 文件中使用 add_definitions()定義 VK_USE_PLATFORM_WIN32_KHR 預(yù)處理器指令(帶 -D 前綴)豺旬。 包含存放 Vulkan 頭文件的路徑。 另外柒凉,添加 Bin 文件夾的路徑用來鏈接必要的 Vulkan 運(yùn)行時(shí)或靜態(tài)庫(kù):
# Specify a suitable project name
project(${Recipe_Name})
# Add preprocessor definitions here
add_definitions(-DVK_USE_PLATFORM_WIN32_KHR)
- 在 VULKAN_LIB_LINK_LIST 變量中指定所有必需的庫(kù)族阅,然后使用 target_link_libraries()將其鏈接到構(gòu)建項(xiàng)目。 另外膝捞,使用 CMake 的 include_directories()API 提供包含 Vulkan 頭文件的正確路徑坦刀。 此外,使用 link_directories()API 指定鏈接庫(kù)所在路徑蔬咬。
# Add ‘vulkan- 1’ library for build Vulkan application.
set(VULKAN_LINK_LIST "vulkan-1") if(${CMAKE_SYSTEM_NAME} MATCHES "Windows")
# Include Vulkan header files from Vulkan SDK
include_directories(AFTER ${VULKAN_PATH}/Include)
# Link directory for vulkan- 1
link_directories(${VULKAN_PATH}/Bin)
endif()
- 以下代碼用于在構(gòu)建源項(xiàng)目中將頭文件和源文件組合在一起鲤遥,以更好地可視化和管理代碼結(jié)構(gòu):
# Bunch the header and source files together
if (WIN32)
source_group ("include" REGULAR_EXPRESSION "include/*") source_group ("source" REGULAR_EXPRESSION "source/*")
endif (WIN32)
- 指定示例的頭文件路徑。 使用 file()API 讀取示例中的所有頭文件和源文件林艘,并將它們存儲(chǔ)在 CPP_Lists 和 HPP_Lists 變量中盖奈。 使用這些列表向構(gòu)建解決方案指定用于編譯所需的所有文件。 為項(xiàng)目構(gòu)建提供一個(gè)名稱并將其鏈接到所有必需的 Vulkan 庫(kù):
# Define include path
include_directories (${CMAKE_CURRENT_SOURCE_DIR}/include)
# Gather list of header and source files for compilation
file (GLOB_RECURSE CPP_FILES
${CMAKE_CURRENT_SOURCE_DIR}/source/*.cpp) file (GLOB_RECURSE HPP_FILES
${CMAKE_CURRENT_SOURCE_DIR}/include/*.*)
# Build project, provide name and include files to be compiled
add_executable (${Recipe_Name} ${CPP_FILES} ${HPP_FILES})
# Link the debug and release libraries to the project
target_link_libraries (${Recipe_Name}${VULKAN_LIB_LIST})
- 定義項(xiàng)目屬性以及要在項(xiàng)目編譯中使用的正確的 C / C ++ 標(biāo)準(zhǔn)版本狐援。 指定二進(jìn)制可執(zhí)行文件的路徑:
# Define project properties
set_property(TARGET ${Recipe_Name} PROPERTY RUNTIME_OUTPUT_- DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/binaries)
set_property(TARGET ${Recipe_Name} PROPERTY RUNTIME_OUTPUT_- DIRECTORY_DEBUG ${CMAKE_CURRENT_SOURCE_DIR}/binaries)
set_property(TARGET ${Recipe_Name} PROPERTY RUNTIME_OUTPUT_- DIRECTORY_RELEASE ${CMAKE_CURRENT_SOURCE_DIR}/binaries)
set_property(TARGET ${Recipe_Name} PROPERTY RUNTIME_OUTPUT_- DIRECTORY_MINSIZEREL ${CMAKE_CURRENT_SOURCE_DIR}/binaries)
set_property(TARGET ${Recipe_Name} PROPERTY RUNTIME_OUTPUT_- DIRECTORY_RELWITHDEBINFO ${CMAKE_CURRENT_SOURCE_DIR}/binaries)
# Define C++ version to be used for building the project set_property(TARGET ${Recipe_Name} PROPERTY CXX_STANDARD 11) set_property(TARGET ${Recipe_Name} PROPERTY
CXX_STANDARD_REQUIRED ON)
# Define C version to be used for building the project set_property(TARGET ${Recipe_Name} PROPERTY C_STANDARD 99) set_property(TARGET ${Recipe_Name} PROPERTY
C_STANDARD_REQUIRED ON)
如何構(gòu)建 CMake 文件
按照以下步驟構(gòu)建 CMake 文件:
- 打開命令行終端并轉(zhuǎn)到示例的 build 目錄钢坦。 如果該目錄不存在,請(qǐng)創(chuàng)建一個(gè)咕村。 這個(gè)空的 build 文件夾將包含通過命令行構(gòu)建的 Visual Studio 項(xiàng)目场钉。 您也可以使用 CMake GUI 進(jìn)行操作。
- 執(zhí)行以下命令來構(gòu)建項(xiàng)目(選擇正確的 IDE 版本)懈涛。 最后一個(gè)參數(shù)指定平臺(tái)架構(gòu)逛万;因此,如果您使用的是 32 位機(jī)器批钠,請(qǐng)使用 Win32:
cmake - G "Visual Studio 14 2015 Win64" ..
這就是命令行界面的外觀:
命令末尾的兩個(gè)點(diǎn)指定 CMakeLists.txt(當(dāng)前所在文件夾的上一級(jí)目錄宇植,其中就有該文件)的路徑,這是 CMake 命令構(gòu)建項(xiàng)目所需的埋心。 成功執(zhí)行后指郁,根據(jù)之前指定的項(xiàng)目名稱,您將找到以下項(xiàng)目文件:
下圖顯示了本書中所有示例的文件夾結(jié)構(gòu)
擴(kuò)展
在實(shí)現(xiàn) Vulkan 應(yīng)用程序時(shí)拷呆,開發(fā)人員可能需要做的第一件事就是關(guān)注 API 的擴(kuò)展特性闲坎、功能以及性能疫粥。 這些設(shè)施能夠讓開發(fā)人員收集一些可用于報(bào)告錯(cuò)誤、調(diào)試以及跟蹤命令的重要信息腰懂;當(dāng)然也可以用于驗(yàn)證目的梗逮。 Vulkan 利用層和擴(kuò)展來暴露這些附加的功能:
- 層:層與現(xiàn)有的 Vulkan API 掛鉤,并將其自身插入到與指定層關(guān)聯(lián)的 Vulkan 命令鏈中绣溜。 它通常用于驗(yàn)證開發(fā)過程慷彤。 例如,驅(qū)動(dòng)程序不需要檢查 Vulkan API 中提供的參數(shù)底哗;驗(yàn)證傳入?yún)?shù)是否正確是層的責(zé)任锚沸。
- 擴(kuò)展:擴(kuò)展提供了擴(kuò)展的功能或特性咒吐,這些擴(kuò)展功能或特性可能是也可能不是標(biāo)準(zhǔn)規(guī)范的一部分恬叹。 擴(kuò)展可以是實(shí)例或設(shè)備的一部分候生。 擴(kuò)展命令不能靜態(tài)鏈接;首先查詢這些擴(kuò)展绽昼,然后將其動(dòng)態(tài)鏈接到函數(shù)指針唯鸭。 這些函數(shù)指針可能已經(jīng)在 vulkan.h 中進(jìn)行了定義,用于注冊(cè)擴(kuò)展以及必要的數(shù)據(jù)結(jié)構(gòu)和枚舉硅确。
擴(kuò)展可以分為兩種類型:
- 基于實(shí)例的擴(kuò)展:這表示獨(dú)立于任何設(shè)備的全局功能目溉,無需任何 VkDevice 即可訪問。
- 基于設(shè)備的擴(kuò)展:擴(kuò)展對(duì)于設(shè)備是說特定的菱农,即特定于設(shè)備缭付,并且需要一個(gè)有效的設(shè)備句柄對(duì)其進(jìn)行操作并且暴露特定的功能。
注意
建議在應(yīng)用程序的開發(fā)階段啟用層和擴(kuò)展循未,并在產(chǎn)品預(yù)期要進(jìn)行發(fā)布時(shí)陷猫,在生產(chǎn)階段關(guān)閉。 在生產(chǎn)階段關(guān)閉擴(kuò)展和層允許應(yīng)用程序節(jié)省不必要的驗(yàn)證開銷的妖,從而提供更高的性能绣檬。
在我們開始編寫應(yīng)用程序之前,讓我們來看一下示例會(huì)使用到哪些用戶自定義的類以及它們的職責(zé)是什么:
- 主程序:這是 Hello World 的入口點(diǎn)嫂粟!!! 它是包含 main()函數(shù)的程序娇未。 程序的控制邏輯保存在 main.cpp 文件中。
- Headers.h:這是包含所有頭文件的地方星虹;我們會(huì)把我們的 Vulkan 頭文件放在這里零抬。
-
VulkanLayerAndExtension:該類在
VulkanLayerAndExtension.h / .cpp 中實(shí)現(xiàn)镊讼,并為實(shí)例和設(shè)備提供基于層和擴(kuò)展的功能。 它還提供調(diào)試的能力平夜。 - VulkanInstance:該類創(chuàng)建 Vulkan 實(shí)例對(duì)象狠毯,在初始化期間很有用,在 VulkanInstance.h / .cpp 中實(shí)現(xiàn)褥芒。
- VulkanDevice:VulkanDevice.h / .cpp 負(fù)責(zé)創(chuàng)建邏輯設(shè)備和物理設(shè)備。 每個(gè)物理設(shè)備都能夠暴露一個(gè)或多個(gè)隊(duì)列嫡良。 該類還管理設(shè)備的隊(duì)列及其相應(yīng)的屬性锰扶。
查詢層和擴(kuò)展
在本節(jié)中,我們將實(shí)現(xiàn) main 函數(shù)寝受、VulkanApplication 以及 VulkanLayerAndExtension 類坷牛。 現(xiàn)在我們就開始 Vulkan 編程。 我們首先查詢公開的 Vulkan 層很澄。 請(qǐng)參閱以下說明來實(shí)現(xiàn)此目的:
- Vulkan 編程首先需要將<vulkan / vulkan.h>添加到頭文件 Header.h 中京闰。 它包含最常用的 Vulkan API 和結(jié)構(gòu)聲明。
- 創(chuàng)建 VulkanLayerAndExtension 類并聲明函數(shù)和變量甩苛,如以下代碼所示蹂楣。 有關(guān)更多詳細(xì)信息,請(qǐng)參閱其中的內(nèi)聯(lián)注釋:
struct LayerProperties {
VkLayerProperties properties; vector<VkExtensionProperties> extensions;
};
class VulkanLayerAndExtension{
// Layers and corresponding extension list std::vector<LayerProperties> // Instance/global layergetInstanceLayerProperties();
// Global extensions
VkResult getExtensionProperties(LayerProperties &layerProps, VkPhysicalDevice* gpu = NULL);
// Device based extensions
VkResult getDeviceExtensionProperties(VkPhysicalDevice*gpu);
};
- 在程序啟動(dòng)時(shí)讯蒲,getInstanceLayerProperties()輔助函數(shù)會(huì)查詢實(shí)例級(jí)或全局級(jí)的層痊土, 獲取層的數(shù)量,并將所有的層信息存儲(chǔ)在名為 layerProperties 變量中墨林,類型為 vector<VkLayerProperties>赁酝。 這兩個(gè)操作(計(jì)數(shù)和存儲(chǔ))是通過調(diào)用 vkEnumerateInstanceLayerProperties()兩次完成的。 第一次旭等,將第二個(gè)參數(shù)設(shè)置為 NULL酌呆,會(huì)在第一個(gè)參數(shù) instanceLayerCount 中返回層的數(shù)量。 在第二次調(diào)用中,不是將第二個(gè)參數(shù)設(shè)置為 NULL弃舒,而是向其傳遞一個(gè) VkLayerProperties 的 vector蚪拦,并將獲得的詳細(xì)屬性信息傳遞到其中進(jìn)行存儲(chǔ)。
注意
根據(jù)提供參數(shù)的不同藤乙,Vulkan 下的大多數(shù)枚舉 API 能夠用于執(zhí)行多個(gè)功能。 就在現(xiàn)在惭墓,我們已經(jīng)看到坛梁,vkEnumerateInstanceLayerProperties API 不僅用于檢索層的數(shù)量(通過設(shè)置 NULL 參數(shù)),還用于獲取包含信息的層數(shù)組(通過提供一個(gè)數(shù)據(jù)結(jié)構(gòu)數(shù)組)腊凶。
上述代碼的語法如下:
VkResult VulkanLayerAndExtension::getInstanceLayerProperties()
{
// Stores number of instance layers
uint32_t instanceLayerCount;
// Vector to store layer properties
std::vector<VkLayerProperties> layerProperties;
// Check Vulkan API result status
VkResult result;
// Query all the layers
do {
result = vkEnumerateInstanceLayerProperties (&instanceLayerCount, NULL);
if (result)
return result;
if (instanceLayerCount == 0)
return VK_INCOMPLETE; // return fail
layerProperties.resize(instanceLayerCount); result = vkEnumerateInstanceLayerProperties
(&instanceLayerCount, layerProperties.data());
} while (result == VK_INCOMPLETE);
// Query all the extensions for each layer and store it.
std::cout << "\nInstanced Layers" << std::endl; std::cout << "===================" << std::endl; for (auto globalLayerProp: layerProperties) {
// Print layer name and its description
std::cout <<"\n"<< globalLayerProp.description << "\n\t|\n\t|---[Layer Name]--> " << globalLayerProp.layerName <<"\n";
LayerProperties layerProps; layerProps.properties = globalLayerProp;
// Get Instance level extensions for
// corresponding layer properties
result = getExtensionProperties(layerProps);
if (result){
continue;
}
layerPropertyList.push_back(layerProps);
// Print extension name for each instance layer
for (auto j : layerProps.extensions){ std::cout << "\t\t|\n\t\t|---
[Layer Extension]--> " << j.extensionName << "\n";
}
}
return result;
}
vkEnumerateInstanceLayerProperties() 的語法如下所示:
VkResult vkEnumerateInstanceLayerProperties (
uint32_t* pPropertyCount, VkLayerProperties* pProperties);
下表介紹了 vkEnumerateInstanceLayerProperties()API 的各個(gè)參數(shù):
h 參數(shù) | 描述 |
---|---|
pPropertyCount | 該變量表示實(shí)例級(jí)的層數(shù)划咐。 該變量可用作輸入或輸出變量拴念,具體取決于傳遞給 pProperties 的值。 |
pProperties | 這個(gè)變量可以取兩個(gè)值褐缠。 當(dāng)指定為 NULL 時(shí)政鼠,API 將返回層數(shù),參數(shù) pPropertyCount 中就是層的總數(shù)队魏。 當(dāng)用作數(shù)組傳遞給該參數(shù)時(shí)公般,API 將檢索同一數(shù)組中層屬性的信息。 |
一旦我們檢索到每個(gè)層的層屬性信息胡桨,我們就會(huì)遍歷所有層官帘,查詢每個(gè)層公開的擴(kuò)展。 我們會(huì)通過調(diào)用用戶自定義的輔助函數(shù) getExtensionProperties()來完成此操作昧谊。
在成功執(zhí)行層及其擴(kuò)展后刽虹,您將在控制臺(tái)上看到以下輸出:
LunarG debug layer
|---[Layer Name]--> VK_LAYER_LUNARG_api_dump
LunarG Validation Layer
|---[Layer Name]--> VK_LAYER_LUNARG_core_validation
|---[Layer Extesion]--> VK_EXT_debug_report
LunarG Standard Validation Layer
|---[Layer Name]--> VK_LAYER_LUNARG_standard_validation
LunarG Validation Layer
|---[Layer Name]--> VK_LAYER_LUNARG_device_limits
|---[Layer Extesion]--> VK_EXT_debug_report
每層可以支持一個(gè)或多個(gè)擴(kuò)展。 getExtensionProperties()函數(shù)首先枚舉層來獲取層所暴露的擴(kuò)展數(shù)量呢诬。 然后,使用 vkEnumerateInstanceExtensionProperties()API 將擴(kuò)展的屬性信息存儲(chǔ)在 LayerProperties 數(shù)據(jù)結(jié)構(gòu)中尚镰。 整個(gè)過程與層的枚舉非常相似阀圾;有關(guān)最后一步的更多信息,請(qǐng)參閱 getInstanceLayerProperties()钓猬。 getExtensionProperties()函數(shù)查詢實(shí)例和設(shè)備的擴(kuò)展:
// This function retrieves extension and its
// properties at instance and device level.
// Pass a valid physical device pointer (gpu) to retrieve
// device level extensions, otherwise use NULL to
// retrieve extension specific to instance level.
VkResult VulkanLayerAndExtension::getExtensionProperties (LayerProperties &layerProps, VkPhysicalDevice* gpu)
{
// Stores number of extension per layer
uint32_t extensionCount; VkResult result;
// Name of the layer
char* layerName = layerProps.properties.layerName;
do {
// Get the total number of extension in this layer
if(gpu){
result = vkEnumerateDeviceExtensionProperties
(*gpu, layerName, &extensionCount, NULL);
}
else{
result = vkEnumerateInstanceExtensionProperties (layerName, &extensionCount, NULL);
}
if (result || extensionCount == 0) continue;
layerProps.extensions.resize(extensionCount);
// Gather all extension properties
if (gpu){
result = vkEnumerateDeviceExtensionProperties (*gpu, layerName, &extensionCount,
layerProps.extensions.data());
}
else{
result = vkEnumerateInstanceExtensionProperties (layerName, &extensionCount, layerProps.extensions.data());
}
} while (result == VK_INCOMPLETE);
創(chuàng)建 Vulkan 實(shí)例
Vulkan 實(shí)例是構(gòu)建應(yīng)用程序需要的主要對(duì)象稍刀,存儲(chǔ)了應(yīng)用程序的所有狀態(tài)。 屬于 VkInstance 類型敞曹,并在 VulkanInstance 類中進(jìn)行管理账月,該類是用戶定義的,實(shí)現(xiàn)于 VulkanInstance.h / cpp 文件中澳迫。 該類負(fù)責(zé)創(chuàng)建和銷毀 Vulkan 實(shí)例對(duì)象局齿。 以下是頭文件的實(shí)現(xiàn):
class VulkanInstance { // Many lines skipped
// Vulkan instance object variable
VkInstance instance;
// Vulkan instance specific layer and extensions
VulkanLayerAndExtension layerExtension;
// Functions for Creation and Deletion of Vulkan instance
VkResult createInstance( vector<const char *>& layers,
vector<const char *>& extensions, const char* applicationName);
// Destroy Vulkan instance
void destroyInstance();
};
創(chuàng)建 Vulkan 實(shí)例需要使用 VkApplicationInfo 結(jié)構(gòu)對(duì)象指定的若干信息,如以下代碼中的 appInfo 所示橄登。 此結(jié)構(gòu)對(duì)象提供有關(guān)應(yīng)用程序的重要信息抓歼,例如其名稱、版本拢锹、引擎等谣妻。 此外,它還會(huì)向驅(qū)動(dòng)程序通知應(yīng)用程序使用的 Vulkan API 版本卒稳。 如果指定的版本與底層驅(qū)動(dòng)程序不兼容蹋半,應(yīng)用程序就會(huì)報(bào)告錯(cuò)誤(如果啟用了驗(yàn)證層)。 有關(guān)更多信息充坑,請(qǐng)參閱本節(jié)后面將介紹的 VkApplicationInfo 結(jié)構(gòu)的 apiVersion 字段:
VkResult VulkanInstance::createInstance( vector<const char *>& layers, vector<const char *>& extensionNames, char const*const appName) {
// Set the instance specific layer and extension information layerExtension.instanceExtensionNames = extensionNames; layerExtension.instanceLayerNames = layers;
// Define the Vulkan application structure
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pNext = NULL; appInfo.pApplicationName = appName; appInfo.applicationVersion = 1;
appInfo.pEngineName = appName;
appInfo.engineVersion = 1;
appInfo.apiVersion = VK_API_VERSION_1_0;
// Define the Vulkan instance create info structure
VkInstanceCreateInfo instInfo = {};
instInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instInfo.pNext = NULL;
instInfo.flags = 0;
instInfo.pApplicationInfo = &appInfo;
VkResult res = vkCreateInstance(&instInfo, NULL, &instance); return res;
}
VkInstance 對(duì)象是使用 vkCreateInstance()API 創(chuàng)建的减江。 該 API 用到了 VkInstanceCreateInfo 控制結(jié)構(gòu)對(duì)象(instInfo)染突。 此結(jié)構(gòu)對(duì)象包含 appInfo(VkApplicationInfo)的引用,以了解應(yīng)用程序特定的屬性辈灼。 另外份企,VkInstanceCreateInfo 對(duì)象也可用于啟用實(shí)例特定的層及其相應(yīng)的擴(kuò)展。
注意
要了解有關(guān)層以及在 Vulkan 應(yīng)用程序中啟用層的更多信息巡莹,請(qǐng)參閱下一節(jié)司志。
以下是 Vulkan 實(shí)例創(chuàng)建 API 的語法:
VkResult vkCreateInstance(
const VkInstanceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkInstance* pInstance);
以下是 vkCreateInstance API 的參數(shù)介紹:
參數(shù) | 描述 |
---|---|
pCreateInfo | 該參數(shù)指的是指向 VkInstanceCreateInfo 結(jié)構(gòu)(稍后介紹)的指針,其中包含應(yīng)用程序(應(yīng)用程序創(chuàng)建信息)降宅、層以及 Vulkan 特定的信息俐芯。 |
pAllocator | 該參數(shù)指定了如何控制主機(jī)內(nèi)存的分配。 有關(guān)更多信息钉鸯,請(qǐng)參閱第 5 章,“Vulkan 中的命令緩沖區(qū)以及內(nèi)存管理”中的“主機(jī)內(nèi)存”部分邮辽。 |
pInstance | 該參數(shù)引用了 VkInstance 類型的 Vulkan 實(shí)例對(duì)象唠雕。 |
以下是 VKInstanceCreateInfo 的語法和結(jié)構(gòu)描述:
typedef struct VKInstanceCreateInfo (
VkStructureType type;
const void* pNextnext;
VkInstanceCreateFlags flags;
const VkApplicationInfo* pApplicationInfo;
uint32_t enabledLayerCount;
const char* const* ppEnabledLayerNames;
uint32_t enabledExtensionCount;
const char* const* ppEnabledExtensionNames;
} VkInstanceCreateInfo;
VKInstanceCreateInfo 結(jié)構(gòu)的字段如下所示:
參數(shù) | h 描述 |
---|---|
type | 這是該控制結(jié)構(gòu)的類型信息。 必須將其指定為 VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO吨述。 |
pNext | 該參數(shù)可以是指向擴(kuò)展特定結(jié)構(gòu)的有效指針或 Null岩睁。 |
flags | 該參數(shù)為將來保留并且目前未被使用。 |
pApplicationInfo | 這表示 VkApplicationInfo 的對(duì)象指針揣云,包含特定于應(yīng)用程序的信息捕儒,例如 Vulkan API 版本、名稱和引擎版本等邓夕。有關(guān)更多信息刘莹,請(qǐng)參閱稍后會(huì)詳細(xì)定義的 VkApplicationInfo。 該參數(shù)也可以是 NULL焚刚。 |
enabledLayerCount | 該參數(shù)指定在實(shí)例級(jí)別啟用的層數(shù)点弯。 |
ppEnabledLayerNames | 這個(gè)參數(shù)包含需要在實(shí)例級(jí)別啟用的、數(shù)組形式的層名稱列表矿咕。 有關(guān)更多信息抢肛,請(qǐng)參閱“層和擴(kuò)展簡(jiǎn)介”部分。 |
enabledExtensionCount | 該參數(shù)指定在實(shí)例級(jí)別啟用的擴(kuò)展的數(shù)量碳柱。 |
ppEnabledExtensionNames | 這個(gè)參數(shù)包含需要在實(shí)例級(jí)別啟用的捡絮、數(shù)組形式的擴(kuò)展名列表。 有關(guān)更多詳細(xì)信息莲镣,請(qǐng)參閱“層和擴(kuò)展簡(jiǎn)介”部分福稳。 |
VKInstanceCreateInfo 將 VkApplicationInfo 作為其成員變量之一使用。 我們來看看這個(gè)結(jié)構(gòu)的說明:
typedef struct VkApplicationInfo {
VkStructureType type; const void* pNext;
const char* pApplicationName;
uint32_t applicationVersion;
const char* pEngineName;
uint32_t pEngineVersion;
uint32_t apiVersion;
} VkApplicationInfo;
以下是結(jié)構(gòu)中描述的字段:
參數(shù) | h 描述 |
---|---|
type | 這是該控制結(jié)構(gòu)的類型信息剥悟。 這種結(jié)構(gòu)的類型必須指定為 VK_STRUCTURE_TYPE_APPLICATION_INFO 灵寺。 |
pNext | 該參數(shù)可以是指向特定于擴(kuò)展結(jié)構(gòu)的有效指針曼库,也可以是 Null。 |
pApplicatonName | 此參數(shù)指示為應(yīng)用程序提供的略板、用戶自定義的應(yīng)用程序名稱毁枯,例如 Hello World !!!。 |
applicationVersion | 使用此參數(shù)指示開發(fā)的應(yīng)用程序版本叮称。 這有利于直接從應(yīng)用程序可執(zhí)行文件本身檢索應(yīng)用程序版本信息种玛。 |
engineName | 這是應(yīng)用程序使用的后端引擎的名稱。 |
engineVersion | 這表示后端應(yīng)用程序的引擎版本(如果使用了的話); 如果沒有瓤檐,應(yīng)用程序版本就足夠了赂韵。 |
apiVersion | 此參數(shù)公開了用于運(yùn)行該應(yīng)用程序的 Vulkan API 的版本號(hào)。 實(shí)現(xiàn)會(huì)讀取此值并驗(yàn)證該值是否可以被忽略(如果指定為 0)或可以被使用(如果指定為非零)挠蛉,如果是不支持的 API 版本祭示,就會(huì)報(bào)錯(cuò)。 如果有不兼容的問題谴古,則實(shí)現(xiàn)會(huì)返回 VK_ERROR_INCOMPATIBLE_DRIVER质涛。 |
注意
創(chuàng)建實(shí)例對(duì)象時(shí),會(huì)忽略 apiVersion 中指定的補(bǔ)丁程序版本號(hào)掰担。 只有實(shí)例的主版本和次版本必須與 apiVersion 中請(qǐng)求的版本匹配汇陆。
當(dāng)應(yīng)用程序不再使用時(shí),可以使用用戶自定義的函數(shù) destroyInstance()來銷毀 Vulkan 實(shí)例:
void VulkanInstance::destroyInstance(){ vkDestroyInstance(instance, NULL); // Destroy the instance
}
在這個(gè)函數(shù)內(nèi)部带饱,調(diào)用了 vkDestroyInstance()API毡代,它接受需要銷毀的 Vulkan 實(shí)例的句柄作為參數(shù)。 以下是此 API 的語法及其說明:
VkResult vkDestroyInstance(
VkInstance instance,
const VkAllocationCallbacks* pAllocator);
以下是與 vkDestroyInstance API 相關(guān)聯(lián)的參數(shù):
參數(shù) | 描述 |
---|---|
instance | 這是需要銷毀的 Vulkan 實(shí)例的句柄勺疼。 |
pAllocator | 這個(gè)參數(shù)指定了主機(jī)內(nèi)存的釋放控制教寂。 |
啟用層和擴(kuò)展
在 Vulkan 中啟用層很簡(jiǎn)單。 應(yīng)用程序必須知道當(dāng)前 Vulkan 實(shí)現(xiàn)中的可用層执庐, 這可以通過查詢以及打印可用的孝宗、基于實(shí)例的層輕松實(shí)現(xiàn);我們已經(jīng)在“查詢層和擴(kuò)展”部分中介紹過這個(gè)內(nèi)容耕肩。
請(qǐng)參考以下步驟啟用層和擴(kuò)展:
- 向 VulkanLayerAndExtension 類添加兩個(gè) vector 列表因妇。 第一個(gè)列表包含需要啟用的層名稱。 第二個(gè)列表包含應(yīng)用程序使用的擴(kuò)展列表:
class VulkanLayerAndExtension{
. . . .
// List of layer names requested by the application.
std::vector<const char *> appRequestedLayerNames;
// List of extension names requested by the application.
std::vector<const char *> appRequestedExtensionNames;
. . .
};
- 在此應(yīng)用程序中猿诸,我們已經(jīng)在 main.cpp 中啟用了一個(gè)層(VK_LAYER_LUNARG_api_dump)和兩個(gè)擴(kuò)展(VK_KHR_SURFACE_EXTENSION_NAME 和 VK_KHR_WIN32-
_SURFACE_EXTENSION_NAME)婚被。 有關(guān)更多信息,請(qǐng)參閱下一小節(jié)“測(cè)試啟用的層和擴(kuò)展”梳虽。 - createInstance()函數(shù)包含一個(gè)層和擴(kuò)展列表址芯。 如果沒有要指定的列表,則可以向 ppEnabledLayerNames 和 ppEnabledExtensionNames 指定一個(gè) NULL 指針:
VkResult VulkanInstance::createInstance(char const*const appName, VulkanLayerAndExtension* layerExtension){
. . . // Many line skipped
VkInstanceCreateInfo instInfo = {};
// Specify the list of layer name to be enabled. instInfo.enabledLayerCount = layers.size(); instInfo.ppEnabledLayerNames = layers.data();
// Specify the list of extensions to be enabled. instInfo.enabledExtensionCount = extensionNames.size(); instInfo.ppEnabledExtensionNames = extensionNames.data();
VkResult res = vkCreateInstance(&instInfo,NULL,&instance);
}
注意
LunarG Vulkan SDK 支持用于調(diào)試和驗(yàn)證目的的不同類型的層。 在這個(gè)例子中谷炸,我們將啟用 VK_LAYER_LUNARG_api_dump; 此層將打印 Vulkan API 調(diào)用及其參數(shù)和值北专。 層可以在運(yùn)行時(shí)注入到基于實(shí)例的層。 有關(guān)其他層所提供功能的更多信息旬陡,請(qǐng)參閱下一章中的“理解層特性”部分拓颓。
測(cè)試啟用的層和擴(kuò)展
按照以下說明測(cè)試輸出:
- 創(chuàng)建 VulkanApplication 類并實(shí)現(xiàn)構(gòu)造函數(shù)和一個(gè)包裝函數(shù)(createVulkanInstance)來創(chuàng)建實(shí)例。 請(qǐng)注意描孟,這是一個(gè)單體類驶睦。 有關(guān)更多信息,請(qǐng)參閱 VulkanApplication.h / .cpp 文件:
#include "VulkanInstance.h" #include "VulkanLED.h"
class VulkanApplication { private:
VulkanApplication();
public:
~VulkanApplication();
// Many lines skipped please refer to source
// code for full implementation.
public:
// Create the Vulkan instance object
VkResult createVulkanInstance
(vector<const char *>& layers, vector<const char*
> & extensions, const char* applicationName);
// Vulkan Instance object
VulkanInstance instanceObj;
};
// Application constructor responsible for layer enumeration.
VulkanApplication::VulkanApplication() {
// At application start up, enumerate instance layers
instanceObj.layerExtension.getInstanceLayerProperties();
}
// Wrapper function to create the Vulkan instance
VkResult VulkanApplication::createVulkanInstance (vector<const char*>& layers, vector<const char *>&
extensions, const char* appName){ instanceObj.createInstance(layers, extensions, appName);
return VK_SUCCESS;
}
- 從 main(main.cpp)程序中設(shè)置實(shí)例級(jí)的層和擴(kuò)展匿醒,啟用實(shí)例層 VK_LAYER_LUNARG_api_dump场航。 另外,在添加擴(kuò)展 VK_KHR_SURFACE_EXTENSION_NAME 和 VK_KHR_WIN32_SURFACE_EXTENSION_NAME廉羔。 該層會(huì)輸出 API 調(diào)用溉痢,并帶有它們相應(yīng)的參數(shù)和值:
#include "Headers.h"
#include "VulkanApplication.h"
std::vector<const char *> instanceExtensionNames = {
VK_KHR_SURFACE_EXTENSION_NAME, VK_KHR_WIN32_SURFACE_EXTENSION_NAME
};
std::vector<const char *> layerNames = {
"VK_LAYER_LUNARG_api_dump"
};
int main(int argc, char **argv){ VkResult res;
// Create singleton object, calls Constructor function
VulkanApplication* appObj =VulkanApplication::GetInstance(); appObj->initialize();
}
// Application constructor responsible for layer enumeration.
void VulkanApplication::initialize()
{
char title[] = "Hello World!!!";
// Create the Vulkan instance with
// specified layer and extension names.
createVulkanInstance(layerNames, instanceExtensionNames,
title);
}
- 編譯項(xiàng)目,打開終端憋他,然后轉(zhuǎn)到包含可執(zhí)行文件的文件夾适室。 鍵入『可執(zhí)行文件名』 .exe> 『輸出重定向的文件名』,例如举瑰,3_0_DeviceHandshake.exe> apiDump.txt:
- 這會(huì)產(chǎn)生以下輸出:
t{0} vkCreateInstance(pCreateInfo = 000000C697D0F570, pAllocator = 0000000000000000, pInstance = 0000025A40AED010) = VK_SUCCESS
pCreateInfo (000000C697D0F570)
sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO pNext = 000000C697D0F548
flags = 0
pApplicationInfo = 000000C697D0F6B8 enabledLayerCount = 1 ppEnabledLayerNames = 0000025A3F0ED490 enabledExtensionCount = 2
ppEnabledExtensionNames = 0000025A3F0ED9E0 ppEnabledExtensionNames[0] = VK_KHR_surface ppEnabledExtensionNames[1] = VK_KHR_win32_surface ppEnabledLayerNames[0] = VK_LAYER_LUNARG_api_dump pApplicationInfo (000000C697D0F588)
sType = VK_STRUCTURE_TYPE_APPLICATION_INFO pNext = 0000000000000000
pApplicationName = Draw Cube applicationVersion = 1 pEngineName = Draw Cube engineVersion = 1
apiVersion = 4194304 pNext (000000C697D0F578)
注意
也可以顯式啟用層,例如將 Windows 環(huán)境變量設(shè)置為 VK_INSTANCE_LAYERS = VK_LAYER_LUNARG_api_dump蔬螟。
理解物理設(shè)備和邏輯設(shè)備
Vulkan 將設(shè)備的表示劃分為兩種形式此迅,即邏輯設(shè)備和物理設(shè)備:
- 物理設(shè)備:一個(gè)物理設(shè)備代表一個(gè)實(shí)體,可能包含一個(gè) GPU 以及其他的硬件部分旧巾,這些部分協(xié)同工作以幫助系統(tǒng)完成提交的作業(yè)任務(wù)耸序。 在一個(gè)非常簡(jiǎn)單的系統(tǒng)中,物理設(shè)備可以被視為代表物理 GPU 的單元鲁猩。
- 邏輯設(shè)備:邏輯設(shè)備表示實(shí)際設(shè)備的應(yīng)用程序視圖坎怪。
物理設(shè)備
OpenGL 沒有暴露物理設(shè)備;廓握,是在幕后連接物理設(shè)備的搅窿。 另一方面,Vulkan 將系統(tǒng)真正的計(jì)算設(shè)備或 GPU 公開給應(yīng)用程序隙券。 它允許應(yīng)用程序枚舉系統(tǒng)上可用的物理設(shè)備男应。
注意
在本節(jié)中,我們將添加一個(gè)新的名為 VulkanDevice 的用戶自定義類娱仔;這個(gè)類在 VulkanDevice.h / .cpp 中實(shí)現(xiàn)沐飘。 它負(fù)責(zé)管理物理設(shè)備(VkPhysicalDevice)和邏輯設(shè)備(VkDevice)。 另外,它還管理物理設(shè)備的隊(duì)列族耐朴。
以下是 VulkanDevice 類的聲明借卧;在我們繼續(xù)閱讀本章的過程中,我們還會(huì)遇到本類中使用到的大部分函數(shù)筛峭。 請(qǐng)參閱隨附的源代碼以獲取此頭文件聲明的完整實(shí)現(xiàn):
class VulkanDevice{ public:
VulkanDevice(VkPhysicalDevice* gpu); ~VulkanDevice();
// Device related member variables
VkDevice device; // Logical device VkPhysicalDevice* gpu; // Physical device VkPhysicalDeviceProperties gpuProps; // Physical device attributes VkPhysicalDeviceMemoryProperties memoryProperties;
// Queue related properties
// Vulkan Queues object
VkQueue queue;
// Store all queue families exposed by the physical device.
vector<VkQueueFamilyProperties>queueFamilyProps;
// Stores graphics queue index
uint32_t graphicsQueueFamilyIndex;
// Number of queue family exposed by device
uint32_t queueFamilyCount;
// Device specific extensions
VulkanLayerAndExtension layerExtension;
// This class exposes the below function to the outer world
createDevice(), memoryTypeFromProperties()
destroyDevice(), getGrahicsQueueHandle(), initializeDeviceQueue(), getPhysicalDeviceQueuesAndProperties();
};
枚舉物理設(shè)備
為了建立與可用物理設(shè)備的連接铐刘,應(yīng)用程序必須列舉這些物理設(shè)備。 物理設(shè)備枚舉就是 Vulkan 將系統(tǒng)中可用的實(shí)際設(shè)備數(shù)量暴露給應(yīng)用程序的過程蜒滩。 可以使用 vkEnumeratePhysicalDevices()檢索物理設(shè)備的列表滨达。
該 API 的語法如下所示:
VkResult (
VkInstance instance,
uint32_t pPhysicalDeviceCount,
VkPhysicalDevice* pPhysicalDevice);
以下是與此 API 關(guān)聯(lián)的參數(shù):
參數(shù) | 描述 |
---|---|
instance | 這是 Vulkan 實(shí)例的句柄。 |
pPhysicalDeviceCount | 這指定了物理設(shè)備的數(shù)量俯艰。 |
pPhysicalDevice | 這是 Vulkan 物理設(shè)備對(duì)象捡遍。 |
該 API 包裝在 Application 類的 enumeratePhysicalDevices 函數(shù)中。 它返回系統(tǒng)上可用物理設(shè)備對(duì)象的數(shù)量:
VkResult VulkanApplication::enumeratePhysicalDevices (std::vector<VkPhysicalDevice>& gpuList){
// Holds the gpu count
uint32_t gpuDeviceCount;
// Get the gpu count vkEnumeratePhysicalDevices (instanceObj.instance, &gpuDeviceCount, NULL);
// Make space for retrieval
gpuList.resize(gpuDeviceCount);
// Get Physical device object
return vkEnumeratePhysicalDevices
(instanceObj.instance, &gpuDeviceCount, gpuList.data());
}
下圖顯示了系統(tǒng)上枚舉的物理設(shè)備竹握,并在查詢時(shí)與 VkInstance 對(duì)象關(guān)聯(lián):
查詢物理設(shè)備的擴(kuò)展
物理設(shè)備暴露了類似于 Vulkan 實(shí)例的擴(kuò)展画株。 對(duì)于檢索到的每個(gè)實(shí)例級(jí)的層屬性(VkLayerProperties),可以使用 vkEnumerateDeviceExtensionProperties()API 查詢每個(gè)物理設(shè)備的擴(kuò)展屬性啦辐。
該 API 的語法如下所示:
VkResult vkEnumerateDeviceExtensionProperties (
VkPhysicalDevice physicalDevice,
const char* pLayerName,
uint32_t* pExtensionCount, VkExtensionProperties* pProperties);
以下是與此 API 相關(guān)聯(lián)的參數(shù):
參數(shù) | 描述 |
---|---|
physicalDevice | 這表示將要查詢擴(kuò)展屬性的物理設(shè)備谓传。 |
pLayerName | 這是需要查詢擴(kuò)展的層的名稱。 |
pExtensionCount | 這指的是當(dāng)前 physicalDevice 公開的擴(kuò)展屬性的數(shù)量芹关,與 pLayerName 相對(duì)應(yīng)续挟。 |
pProperties | 這表示檢索到的數(shù)組;它包含擴(kuò)展的屬性對(duì)象侥衬,對(duì)應(yīng)于 pLayerName诗祸。 |
查詢基于設(shè)備的擴(kuò)展屬性的過程與查詢基于實(shí)例的擴(kuò)展屬性的過程非常類似。 以下是該功能的實(shí)現(xiàn):
VkResult VulkanLayerAndExtension::getDeviceExtensionProperties
(VkPhysicalDevice* gpu)
{
// Variable to check Vulkan API result status
VkResult result;
// Query all the extensions for each layer and store it.
std::cout << "\Device extensions" << std::endl; std::cout << "===================" << std::endl;
VulkanApplication* appObj = VulkanApplication::GetInstance(); std::vector<LayerProperties>* instanceLayerProp =
&appObj->GetInstance()->instanceObj. layerExtension.layerPropertyList;
for (auto globalLayerProp : *instanceLayerProp) { LayerProperties layerProps;
layerProps.properties = globalLayerProp.properties;
if (result = getExtensionProperties(layerProps, gpu)) continue;
layerPropertyList.push_back(layerProps);
// Many lines skipped..
}
return result;
}
獲取物理設(shè)備的屬性
物理設(shè)備的屬性可以使用 vkGetPhysicalDeviceProperties()API 檢索轴总;檢索到的屬性保存在 VkPhysicalDeviceProperties 控制結(jié)構(gòu)中直颅。 以下是這個(gè)函數(shù)的語法:
void vkGetPhysicalDeviceMemoryProperties (
VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties );
以下是 vkGetPhysicalDeviceMemoryProperties 的參數(shù):
h 參數(shù) | 描述 |
---|---|
physicalDevice | 這是 GPU 句柄,需要檢索其內(nèi)存屬性怀樟。 |
pMemoryproperties | 這是要檢索的 GPU 內(nèi)存屬性的結(jié)構(gòu)功偿。 |
從物理設(shè)備查詢內(nèi)存屬性
一個(gè)物理設(shè)備可能具有不同的內(nèi)存類型,這些內(nèi)存類型根據(jù)屬性的不同可以進(jìn)一步細(xì)分往堡。 應(yīng)用程序了解內(nèi)存的特性非常重要械荷;這有助于更好地分配資源,具體取決于應(yīng)用程序的邏輯或資源的類型虑灰。 以下的語法是檢索物理設(shè)備的內(nèi)存屬性:
void vkGetPhysicalDeviceMemoryProperties (
VkPhysicalDevice physicalDevice, VkPhysicalDeviceMemoryProperties* pMemoryProperties );
以下是 vkGetPhysicalDeviceMemoryProperties 參數(shù)的描述:
參數(shù) | 描述 |
---|---|
physicalDevice | 該參數(shù)是需要查詢其內(nèi)存屬性的 GPU 句柄养葵。 |
pMemoryProperties | 該參數(shù)用于檢索內(nèi)存屬性。 |
邏輯設(shè)備
邏輯設(shè)備是物理設(shè)備的邏輯表示瘩缆,但它在應(yīng)用程序空間中使用关拒;它提供了物理設(shè)備的特定視圖。 例如,物理設(shè)備可能由三個(gè)隊(duì)列組成:圖形隊(duì)列着绊、計(jì)算隊(duì)列和傳輸隊(duì)列谐算。 然而,可以使用一個(gè)單獨(dú)的隊(duì)列(如圖形隊(duì)列)創(chuàng)建一個(gè)邏輯設(shè)備归露,即附加單個(gè)隊(duì)列到邏輯設(shè)備上洲脂;這使得提交命令緩沖區(qū)變得異常簡(jiǎn)單。
創(chuàng)建邏輯設(shè)備
邏輯設(shè)備使用 VkDevice 表示剧包,可以使用 vkCreateDevice API 創(chuàng)建恐锦。 下面是其語法:
VkResult vkCreateDevice(
VkPhysicalDevice pPhysicalDevice, Const VkDeviceCreateInfo* pCreateInfo, const VkAllocationCallbacks* pAllocator, VkDevice* pDevice);
有關(guān) API 參數(shù)的更多信息,請(qǐng)參閱下表:
參數(shù) | h 描述 |
---|---|
pPhysicalDevice | 這表示要?jiǎng)?chuàng)建邏輯設(shè)備的物理設(shè)備的句柄疆液。 |
pCreateInfo | 這是一個(gè) VkDeviceCreateInfo 結(jié)構(gòu)一铅,其中包含了 vkCreateDevice()API 要使用的特定信息,用于控制邏輯設(shè)備的創(chuàng)建堕油。 |
pAllocator | 這指定了如何控制主機(jī)內(nèi)存的分配潘飘。 有關(guān)更多信息,請(qǐng)參閱第 5 章“Vulkan 中的命令緩沖區(qū)以及內(nèi)存管理”中的“主機(jī)內(nèi)存” 一節(jié)掉缺。 |
pDevice | 該參數(shù)是指創(chuàng)建的邏輯設(shè)備的指針卜录,其中包含了新建的 VkDevice 對(duì)象。 |
該 API 使用 VkDeviceCreateInfo 控制結(jié)構(gòu)對(duì)象(deviceInfo)眶明,其中包含了創(chuàng)建邏輯設(shè)備對(duì)象所需的必要信息艰毒。 例如,它包含層的名稱(此功能已棄用并保留搜囱,用于向后兼容)以及需要在設(shè)備上啟用的擴(kuò)展丑瞧。 另外,它還指定了應(yīng)該創(chuàng)建并連接到哪個(gè)隊(duì)列犬辰。 在我們的案例中,我們對(duì)繪圖操作感興趣冰单;因此幌缝,我們需要一個(gè)隊(duì)列句柄(graphicsQueueIndex),它用來表示具有繪圖功能的隊(duì)列诫欠。 換句話說涵卵,我們需要圖形隊(duì)列句柄。
注意
隊(duì)列信息包含在 VkDeviceQueueCreateInfo 結(jié)構(gòu)對(duì)象 queueInfo 中荒叼。 當(dāng)創(chuàng)建一個(gè)邏輯設(shè)備時(shí)轿偎,它也使用這個(gè)結(jié)構(gòu)創(chuàng)建了與之關(guān)聯(lián)的隊(duì)列。 有關(guān)隊(duì)列的更多信息被廓,如何查找圖形隊(duì)列索引以及隊(duì)列的創(chuàng)建過程坏晦,請(qǐng)參閱下一節(jié)“理解隊(duì)列和隊(duì)列族”。
VulkanDevice :: createDevice 是一個(gè)用戶定義的包裝方法,用來幫助創(chuàng)建邏輯設(shè)備對(duì)象昆婿。 下面是它的實(shí)現(xiàn):
VkResult VulkanDevice::createDevice(vector<const char *>& layers,
vector<const char *>& extensions){
VkResult result;
float queuePriorities[1] = { 0.0 };
// Create the object information
VkDeviceQueueCreateInfo queueInfo = {}; queueInfo.queueFamilyIndex = graphicsQueueIndex; queueInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; queueInfo.pNext = NULL;
queueInfo.queueCount = 1;
queueInfo.pQueuePriorities = queuePriorities;
VkDeviceCreateInfo deviceInfo = {};
deviceInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; deviceInfo.pNext = NULL; deviceInfo.queueCreateInfoCount = 1; deviceInfo.pQueueCreateInfos = &queueInfo; deviceInfo.enabledLayerCount = 0;
// Device layers are deprecated deviceInfo.ppEnabledLayerNames = NULL; deviceInfo.enabledExtensionCount = extensions.size(); deviceInfo.ppEnabledExtensionNames = extensions.data(); deviceInfo.pEnabledFeatures = NULL;
result = vkCreateDevice(*gpu, &deviceInfo, NULL, &device);
assert(result == VK_SUCCESS); return result;
}
在宿主機(jī)上等待
在隊(duì)列中球碉,只要設(shè)備有任務(wù)需要處理,就說設(shè)備處于活動(dòng)狀態(tài)仓蛆。 一旦隊(duì)列沒有更多的命令緩沖區(qū)需要處理睁冬,設(shè)備就會(huì)變成空閑狀態(tài)。 接下來的 vkDeviceWaitIdle API 會(huì)在宿主主機(jī)上等待看疙,直到邏輯設(shè)備的所有隊(duì)列都變?yōu)榭臻e為止豆拨。 該 API 接受需要檢查空閑狀態(tài)的、邏輯設(shè)備對(duì)象的句柄的作為參數(shù)能庆。 以下是其語法:
VkResult vkDeviceWaitIdle( VkDevice device);
丟失設(shè)備
由于某些原因(例如硬件故障施禾,設(shè)備錯(cuò)誤,執(zhí)行超時(shí)相味,電源管理事件和 / 或特定于平臺(tái)的事件)而使用邏輯(VKDevice)設(shè)備和物理(VKPhysicalDevice)設(shè)備時(shí)拾积,設(shè)備可能會(huì)丟失。 這可能導(dǎo)致無法執(zhí)行掛起的命令緩沖區(qū)丰涉。
提示
當(dāng)物理設(shè)備丟失時(shí)拓巧,試圖創(chuàng)建邏輯設(shè)備對(duì)象(VKDevice)就會(huì)失敗并返回 VK_ERROR_DEVICE_LOST。 如果邏輯設(shè)備對(duì)象丟失一死,某些命令在使用時(shí)就會(huì)返回 VK_ERROR_DEVICE_LOST肛度。 不過,相應(yīng)的物理設(shè)備可能仍然不受影響投慈。 無法重置邏輯設(shè)備的丟失狀態(tài)承耿,并且此狀態(tài)的丟失對(duì)于邏輯設(shè)備對(duì)象(VKDevice)來說是局部的,并且不會(huì)影響任何其他活動(dòng)的邏輯設(shè)備對(duì)象伪煤。
理解隊(duì)列以及隊(duì)列族
隊(duì)列是應(yīng)用程序和物理設(shè)備通信的手段加袋。 應(yīng)用程序以向隊(duì)列提交命令緩沖區(qū)的形式提供作業(yè)任務(wù)。 物理設(shè)備對(duì)它們進(jìn)行讀取并異步處理抱既。
物理設(shè)備可能支持四種類型的隊(duì)列职烧,如下圖所示。 物理設(shè)備上可能有多個(gè)相同類型的隊(duì)列防泵;這就允許應(yīng)用程序選擇它需要的隊(duì)列數(shù)量和隊(duì)列類型蚀之。 例如,一個(gè)簡(jiǎn)單的應(yīng)用程序可能需要兩個(gè)隊(duì)列:計(jì)算隊(duì)列和圖形隊(duì)列捷泞;在這里足删,前者用于卷積計(jì)算,第二個(gè)渲染計(jì)算后的高斯模糊圖像(blur image)锁右。
物理設(shè)備可能由一個(gè)或多個(gè)隊(duì)列族組成失受,在每個(gè)隊(duì)列族內(nèi)公開了存在什么類型的隊(duì)列讶泰。 此外,每個(gè)隊(duì)列族可以具有一個(gè)或多個(gè)隊(duì)列計(jì)數(shù)贱纠。 下圖顯示了三個(gè)隊(duì)列族及其各自的若干隊(duì)列:
查詢隊(duì)列族
物理設(shè)備能夠暴露多個(gè)隊(duì)列族峻厚。
隊(duì)列族屬性(queue family properties)的數(shù)量由
vkGetPhysicalDeviceQueueFamilyProperties()API 獲得,如下所示:
VkResult vkGetPhysicalDeviceQueueFamilyProperties ( VkPhysicalDevice physicalDevice,
uint32_t* pQueueFamilyPropertyCount, VkQueueFamilyProperties* pQueueFamilyProperties);
以下是這個(gè) API 的參數(shù)意義:
參數(shù) | 描述 |
---|---|
physicalDevice | 這是要檢索其隊(duì)列屬性的物理設(shè)備的句柄谆焊。 |
pQueueFamilyPropertyCount | 這是指設(shè)備公開的隊(duì)列族的數(shù)量惠桃。 |
pQueueFamilyProperties | 該參數(shù)使用一個(gè)尺寸等于 queueFamilyPropertyCount 的數(shù)組,檢索隊(duì)列族的屬性辖试。 |
按照性質(zhì)相似的功能將若干隊(duì)列分成多個(gè)族辜王。 以下的 VulkanDevice 類的代碼片段顯示了如何查詢 VkQueueFamilyProperties 控制結(jié)構(gòu)對(duì)象中的隊(duì)列族及其屬性绝页,即 queueFamilyProps诅岩。
在我們的實(shí)現(xiàn)中灵疮,在 VulkanDevice 類中定義的名為 getPhysicalDeviceQueuesAndProperties()的包裝函數(shù)中查詢隊(duì)列族的屬性冗荸。 以下是具體的實(shí)現(xiàn):
void VulkanDevice::getPhysicalDeviceQueuesAndProperties(){
// Query queue families count by passing NULL as second parameter vkGetPhysicalDeviceQueueFamilyProperties(*gpu, &queueFamilyCount, NULL);
// Allocate space to accomodate Queue properties
queueFamilyProps.resize(queueFamilyCount);
// Get queue family properties
vkGetPhysicalDeviceQueueFamilyProperties
(*gpu, &queueFamilyCount, queueFamilyProps.data());
}
此結(jié)構(gòu)的 queueFlag 字段包含了族信息,其標(biāo)志位的具體含義如下所示:
標(biāo)志位 | 隊(duì)列家族的含義 |
---|---|
VK_QUEUE_GRAPHICS_BIT | 這是一個(gè)圖形隊(duì)列冬竟;支持與圖形相關(guān)的操作贼穆。 |
VK_QUEUE_COMPUTE_BIT | 這是一個(gè)計(jì)算隊(duì)列页畦;提供計(jì)算能力改艇。 |
VK_QUEUE_TRANSFER_BIT | 這是一個(gè)轉(zhuǎn)移隊(duì)列收班;支持傳輸操作。 |
VK_QUEUE_SPARSE_BINDING_BIT | 這是一個(gè)稀疏隊(duì)列谒兄;能夠進(jìn)行稀疏的內(nèi)存管理摔桦。 |
每個(gè)隊(duì)列族可以支持一個(gè)或多個(gè)隊(duì)列類型,這些隊(duì)列類型由 VkQueueFamilyProperties 的 queueFlag 字段指示承疲。 queueCount 指定了隊(duì)列族中的隊(duì)列數(shù)量邻耕。 第三個(gè)字段 timestampVaildBits 用于為命令的執(zhí)行進(jìn)行計(jì)時(shí)。 最后一個(gè)參數(shù) minImageTransferGranularity 指定當(dāng)前隊(duì)列族中支持的最小粒度的圖像傳輸操作燕鸽。 下面是具體的語法:
typedef struct VkQueueFamilyProperties { VkQueueFlags queueFlags; uint32_t queueCount;
uint32_t timestampValidBits; VkExtent3D minImageTransferGranularity;
} VkQueueFamilyProperties;
下圖顯示了隊(duì)列和隊(duì)列族在物理設(shè)備中的關(guān)系兄世。 在這個(gè)特定的例子中,一個(gè)物理設(shè)備包括四種類型的隊(duì)列族啊研,根據(jù)它支持的隊(duì)列類型(queueFlags)以及每個(gè)族中的隊(duì)列數(shù)量(queueCount)御滩,其中的每個(gè)隊(duì)列會(huì)包含不同的功能。
存儲(chǔ)圖形隊(duì)列句柄
邏輯設(shè)備對(duì)象的創(chuàng)建也需要一個(gè)有效的隊(duì)列句柄(以索引的形式)悲伶,以便用創(chuàng)建關(guān)聯(lián)的隊(duì)列艾恼。 為此住涉,遍歷查詢到的所有隊(duì)列族屬性麸锉,并檢查 VkQueueFamilyProperties :: queueFlags 標(biāo)志位信息,從而找到適當(dāng)?shù)年?duì)列舆声。 例如花沉,我們對(duì)圖形隊(duì)列句柄感興趣柳爽。 以下代碼將圖形隊(duì)列的句柄存儲(chǔ)在 graphicsQueueIndex 中,該圖形隊(duì)列用于創(chuàng)建邏輯設(shè)備(VkDevice)對(duì)象:
uint32_t VulkanDevice::getGrahicsQueueHandle(){ bool found = false;
// 1. Iterate number of Queues supported by the Physical device
for (unsigned int i = 0; i < queueFamilyCount; i++){
// 2. Get the Graphics Queue type
if (queueFamilyProps[i].queueFlags & VK_QUEUE_GRAPHICS_BIT){
// 3. Get the handle/index ID of graphics queue family.
found = true;
graphicsQueueIndex = i; break;
}
} return 0;
}
創(chuàng)建隊(duì)列
當(dāng)使用 vkCreateDevice()API 創(chuàng)建邏輯設(shè)備對(duì)象時(shí)碱屁,就隱式創(chuàng)建了隊(duì)列磷脯。 該 API 還會(huì)以 VkDeviceQueueCreateInfo 的形式提取隊(duì)列信息。 以下是語法和相關(guān)字段的說明:
typedef struct VkDeviceQueueCreateInfo { VkStructureType type;
const void* pNext;
VkDeviceQueueCreateFlags flags;
uint32_t queueFamilyIndex;
uint32_t queueCount;
const float* pQueuePriorities;
} VkDeviceQueueCreateInfo;
下表描述了此結(jié)構(gòu)體的每個(gè)字段:
字段 | 描述 |
---|---|
type | 這是該控制結(jié)構(gòu)的類型信息娩脾。 必須將其指定為 VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO赵誓。 |
pNext | 該參數(shù)可以是指向特定于擴(kuò)展的結(jié)構(gòu)的有效指針或 NULL。 |
flags | 這是未使用的標(biāo)志柿赊,保留供將來使用俩功。 |
queueFamilyIndex | 這個(gè)隊(duì)列族信息是以 32 位無符號(hào)整數(shù)類型的形式進(jìn)行指定的隊(duì)列族索引。 例如碰声,在我們的例子中诡蜓,我們提供了 graphicsQueueIndex 變量,其中包含了圖形隊(duì)列的索引胰挑。 |
queueCount | 該參數(shù)是指要?jiǎng)?chuàng)建的隊(duì)列族的數(shù)量蔓罚。 |
pQueuePriorities | 該參數(shù)表示一組規(guī)范化的浮點(diǎn)值(數(shù)組),用于指定提交給每個(gè)創(chuàng)建隊(duì)列的作業(yè)的優(yōu)先級(jí)瞻颂。 |
我們已經(jīng)知道豺谈,創(chuàng)建一個(gè)邏輯設(shè)備對(duì)象時(shí)會(huì)自動(dòng)創(chuàng)建若干隊(duì)列。 然后蘸朋,應(yīng)用程序可以使用 vkGetDeviceQueue()API 檢索創(chuàng)建的隊(duì)列核无。 如下所示,VulkanDevice 類中的函數(shù)(getDeviceQueue())提供了一個(gè)高級(jí)的包裝函數(shù)藕坯,用來獲取設(shè)備的關(guān)聯(lián)隊(duì)列:
void VulkanDevice::getDeviceQueue(){
vkGetDeviceQueue(device, graphicsQueueWithPresentIndex, 0, &queue);
}
其語法為:
void vkGetDeviceQueue (
VkDevice logicalDevice,
uint32_t queueFamilyIndex,
uint32_t queueIndex,
VkQueue* pQueue);
有關(guān) API 參數(shù)的更多信息团南,請(qǐng)參閱下表:
參數(shù) | 描述 |
---|---|
logicalDevice | 該參數(shù)引用了擁有隊(duì)列的邏輯設(shè)備(VkDevice)對(duì)象。 |
queueFamilyIndex | 該參數(shù)指示隊(duì)列(pQueue)所屬的族的索引號(hào)炼彪。 |
queueIndex | 一個(gè)隊(duì)列族中可能有多個(gè)隊(duì)列吐根,每個(gè)隊(duì)列由唯一的索引標(biāo)識(shí)。 該參數(shù)指示隊(duì)列在隊(duì)列族中的索引(由 queueFamilyIndex 指示)辐马。 |
pQueue | 該參數(shù)指的是由此 API 返回的拷橘、檢索到的隊(duì)列對(duì)象。 |
注意
打算從邏輯設(shè)備對(duì)象查詢所需的隊(duì)列對(duì)象的想法在此刻要暫時(shí)推遲一下了喜爷, 這是因?yàn)楝F(xiàn)在我們對(duì)能夠提供展示功能的隊(duì)列感興趣冗疮,為此,我們需要等到第 6 章檩帐,分配圖像資源和使用 WSI 構(gòu)建 Swapchain术幔。 在本章中,我們將學(xué)習(xí)如何實(shí)現(xiàn)交換鏈湃密,以便用于展示目的诅挑。
整合設(shè)備和隊(duì)列
在本節(jié)中四敞,我們將重新審視,在本章中我們迄今為止獲得的所有知識(shí)拔妥,并實(shí)現(xiàn)一個(gè)程序忿危,以此來從應(yīng)用程序的視角創(chuàng)建設(shè)備和隊(duì)列。 我們來看看描述信息流的分步操作過程没龙。
首先铺厨,使用 enumeratePhysicalDevices()枚舉系統(tǒng)上的物理設(shè)備;檢索到的物理設(shè)備存儲(chǔ)在 gpuList 向量中硬纤。 為了簡(jiǎn)單起見努释,我們假設(shè)系統(tǒng)只有一個(gè) GPU(使用 gpuList 的第一個(gè)元素)。 接下來咬摇,我們使用 handShakeWithDevice()函數(shù)與設(shè)備握手:
/********** VulkanApplication.cpp **********/
// Get the list of physical devices on the system
vector<VkPhysicalDevice> gpuList; enumeratePhysicalDevices(gpuList);
if (gpuList.size() > 0) { appObj->handShakeWithDevice
(&gpuList[0], layerNames, deviceExtensionNames);
}
void VulkanApplication::initialize()
{
// Many lines skipped please refer to the source code.
// Get the list of physical devices on the system std::vector<VkPhysicalDevice> gpuList; enumeratePhysicalDevices(gpuList);
// This example use only one device which is available first.
if (gpuList.size() > 0) { handShakeWithDevice(&gpuList[0], layerNames,
deviceExtensionNames);
}
}
VulkanApplication :: enumeratePhysicalDevices()函數(shù)利用了變量 vkEnumeratePhysicalDevices 并順便獲得了物理設(shè)備(VKPhysicalDevice)的數(shù)量伐蒂。 應(yīng)用程序判斷是否有 GPU 可用。 接下來肛鹏,它分配必要的空間將這個(gè)信息存儲(chǔ)在一個(gè) vector 列表中逸邦,并再次將其提供給同一個(gè) API(vkEnumeratePhysicalDevices)以及 GPU 計(jì)數(shù),用來獲取物理設(shè)備對(duì)象:
/***************** Application.cpp *****************/
VkResult VulkanApplication::enumeratePhysicalDevices
(vector<VkPhysicalDevice>& gpuList){ uint32_t gpuDeviceCount;
VkResult result = vkEnumeratePhysicalDevices (instanceObj.instance, &gpuDeviceCount, NULL);
assert(result == VK_SUCCESS);
gpuList.resize(gpuDeviceCount); assert(gpuDeviceCount);
result = vkEnumeratePhysicalDevices (instanceObj.instance, &gpuDeviceCount, gpuList.data());
assert(result == VK_SUCCESS);
return result;
}
VulkanApplication :: handShakeWithDevice()負(fù)責(zé)創(chuàng)建邏輯設(shè)備對(duì)象以及與它們關(guān)聯(lián)的若干隊(duì)列在扰。 它還會(huì)執(zhí)行一些初始化工作缕减,這些工作在應(yīng)用程序開發(fā)的后期階段是必需的,例如獲取物理設(shè)備的屬性和內(nèi)存屬性芒珠。 以下是此 API 的語法:
void VulkanApplication::handShakeWithDevice ( VkPhysicalDevice* gpu, std::vector<const char*>& extensions, int queueIndex);
以下是對(duì)這些參數(shù)的描述:
參數(shù) | 描述 |
---|---|
gpu | 這是應(yīng)用程序執(zhí)行握手的物理設(shè)備桥狡。 |
layers | 這是需要在 GPU 上啟用的層的名稱。 |
extensions | 這是需要在 GPU 上啟用的擴(kuò)展的名稱皱卓。 |
VulkanApplication :: handShakeWithDevice()函數(shù)的內(nèi)部過程描述如下:
- 使用了 VulkanDevice 對(duì)象并查詢關(guān)聯(lián)的物理設(shè)備公開的擴(kuò)展裹芝。 檢索到的擴(kuò)展可以與應(yīng)用程序請(qǐng)求的擴(kuò)展進(jìn)行交叉查詢,以檢查它們是否受物理設(shè)備所支持娜汁。
- 使用了 VulkanDevice 對(duì)象并查詢關(guān)聯(lián)的物理設(shè)備公開的擴(kuò)展嫂易。 檢索到的擴(kuò)展可以與應(yīng)用程序請(qǐng)求的擴(kuò)展進(jìn)行交叉查詢,以檢查它們是否受物理設(shè)備所支持掐禁。
- 使用 vkGetPhysicalDeviceMemoryProperties()API 獲取由物理設(shè)備提供的內(nèi)存信息及其屬性怜械。
- 使用 VulkanDevice 類的 getPhysicalDeviceQueuesAndProperties()輔助函數(shù)查詢物理設(shè)備支持的所有隊(duì)列族,并存儲(chǔ)它們的屬性供以后使用傅事。
- 遍歷所有的隊(duì)列并檢查哪個(gè)隊(duì)列支持圖形操作缕允。 這是使用 getGraphicsQueueHandle()函數(shù)完成的;此函數(shù)返回(可用于執(zhí)行圖形操作的)隊(duì)列的索引或句柄蹭越。
最后障本,調(diào)用 VulkanDevice :: createDevice()來創(chuàng)建與物理設(shè)備關(guān)聯(lián)的邏輯設(shè)備對(duì)象。 該函數(shù)使用了圖形隊(duì)列句柄般又,并創(chuàng)建與邏輯設(shè)備對(duì)象關(guān)聯(lián)的隊(duì)列彼绷。 此外,該功能還接受需要在物理設(shè)備上啟用的擴(kuò)展名的列表作為參數(shù):
/***************** Application.cpp *****************/
// High level function for creating device and queues
VkResult VulkanApplication::handShakeWithDevice( VkPhysicalDevice* gpu, std::vector<const char *>& layers, std::vector<const char *>& extensions )
{
// The user define Vulkan Device object.
// This will manage the Physical and logical
// device and their queue and properties
deviceObj = new VulkanDevice(gpu); if (!deviceObj){
return VK_ERROR_OUT_OF_HOST_MEMORY;
}
// Print the devices available layer and their extension
deviceObj->layerExtension.
getDeviceExtensionProperties(gpu);
// Get the physical device or GPU properties
vkGetPhysicalDeviceProperties(*gpu, &deviceObj->gpuProps);
// Get memory properties from the physical device or GPU.
vkGetPhysicalDeviceMemoryProperties(*gpu,
&deviceObj->memoryProperties);
// Query the availabe queues on the physical
// device and their properties.
deviceObj->getPhysicalDeviceQueuesAndProperties();
// Retrive the queue which support graphics pipeline.
deviceObj->getGrahicsQueueHandle();
// Create Logical Device, ensure that this
// device is connect to graphics queue
deviceObj->createDevice(layers, extensions);
return VK_SUCCESS;
}
總結(jié)
在本章中茴迁,我們開始了 Vulkan 編程寄悯。 我們知道了使用 CMake 和 LunarG SDK 設(shè)置和構(gòu)建 Vulkan 項(xiàng)目的過程。 我們從 Vulkan 的基礎(chǔ)知識(shí)開始堕义,包括層和擴(kuò)展猜旬,并學(xué)習(xí)了按照分步操作的過程來對(duì)它們進(jìn)行查詢。 我們創(chuàng)建了一個(gè) Vulkan 實(shí)例倦卖,并演示了在實(shí)例級(jí)別啟用以及測(cè)試層和擴(kuò)展的方法洒擦。
此外,我們討論了設(shè)備和隊(duì)列怕膛。 我們研究了物理設(shè)備對(duì)象和邏輯設(shè)備對(duì)象之間的差異熟嫩。 我們對(duì)系統(tǒng)上物理設(shè)備的枚舉進(jìn)行了編程示范,并學(xué)會(huì)了啟用設(shè)備特定的擴(kuò)展褐捻。 我們列舉了每個(gè)物理設(shè)備關(guān)聯(lián)的隊(duì)列族掸茅。 使用隊(duì)列屬性,我們選擇了圖形隊(duì)列并創(chuàng)建邏輯設(shè)備對(duì)象柠逞。
最后昧狮,我們總結(jié)了我們理解的所有內(nèi)容,并實(shí)現(xiàn)了與設(shè)備握手的過程板壮,其中包括創(chuàng)建物理設(shè)備對(duì)象和邏輯設(shè)備對(duì)象以及它們對(duì)應(yīng)的若干隊(duì)列逗鸣。
調(diào)試,提供了一個(gè)學(xué)習(xí) Vulkan 更好的機(jī)會(huì)绰精,通過攜帶有效原因的錯(cuò)誤信息也會(huì)獲得更深的理解撒璧。 在下一章中,我們將學(xué)習(xí) Vulkan 中的調(diào)試過程笨使,從開發(fā)人員的角度來看這非常重要沪悲。 Vulkan 是一種新的圖形 API,與傳統(tǒng)的 API 有著完全不同的編程模式阱表。 調(diào)試功能提供了一種理解這些 API 的更好方式殿如。