第 3 章 與設(shè)備握手

利用我們前兩章中獲得的知識(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)建方法:

  1. 按照指定的文件夾結(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)
  1. 使用 set CMake 關(guān)鍵字指定用來查找 Vulkan SDK 路徑的必要變量。 另外纬纪,在起一個(gè)有意義的名字:
set (Recipe_Name "3_0_DeviceHandshake")
  1. 在這個(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()
  1. 使用 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)
  1. 在 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()
  1. 以下代碼用于在構(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)
  1. 指定示例的頭文件路徑。 使用 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})
  1. 定義項(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 文件:

  1. 打開命令行終端并轉(zhuǎn)到示例的 build 目錄钢坦。 如果該目錄不存在,請(qǐng)創(chuàng)建一個(gè)咕村。 這個(gè)空的 build 文件夾將包含通過命令行構(gòu)建的 Visual Studio 項(xiàng)目场钉。 您也可以使用 CMake GUI 進(jìn)行操作。
  2. 執(zhí)行以下命令來構(gòu)建項(xiàng)目(選擇正確的 IDE 版本)懈涛。 最后一個(gè)參數(shù)指定平臺(tái)架構(gòu)逛万;因此,如果您使用的是 32 位機(jī)器批钠,請(qǐng)使用 Win32:
cmake - G "Visual Studio 14 2015 Win64" ..

這就是命令行界面的外觀:

3-001.png

命令末尾的兩個(gè)點(diǎn)指定 CMakeLists.txt(當(dāng)前所在文件夾的上一級(jí)目錄宇植,其中就有該文件)的路徑,這是 CMake 命令構(gòu)建項(xiàng)目所需的埋心。 成功執(zhí)行后指郁,根據(jù)之前指定的項(xiàng)目名稱,您將找到以下項(xiàng)目文件:

3-002.png

下圖顯示了本書中所有示例的文件夾結(jié)構(gòu)

3-003.png

擴(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ò)展:

  1. 向 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;

. . .
};
  1. 在此應(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ò)展”梳虽。
  2. 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è)試輸出:

  1. 創(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;
}
  1. 從 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);
}
  1. 編譯項(xiàng)目,打開終端憋他,然后轉(zhuǎn)到包含可執(zhí)行文件的文件夾适室。 鍵入『可執(zhí)行文件名』 .exe> 『輸出重定向的文件名』,例如举瑰,3_0_DeviceHandshake.exe> apiDump.txt:
3-004.png
  1. 這會(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):

3-005.png

查詢物理設(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)锁右。

3-006.png

物理設(shè)備可能由一個(gè)或多個(gè)隊(duì)列族組成失受,在每個(gè)隊(duì)列族內(nèi)公開了存在什么類型的隊(duì)列讶泰。 此外,每個(gè)隊(duì)列族可以具有一個(gè)或多個(gè)隊(duì)列計(jì)數(shù)贱纠。 下圖顯示了三個(gè)隊(duì)列族及其各自的若干隊(duì)列:

3-007.png

查詢隊(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ì)包含不同的功能。

3-008.png

存儲(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 的更好方式殿如。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市最爬,隨后出現(xiàn)的幾起案子涉馁,更是在濱河造成了極大的恐慌,老刑警劉巖爱致,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件烤送,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡糠悯,警方通過查閱死者的電腦和手機(jī)帮坚,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門妻往,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人试和,你說我怎么就攤上這事讯泣。” “怎么了阅悍?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵好渠,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我节视,道長(zhǎng)拳锚,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任寻行,我火速辦了婚禮霍掺,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘拌蜘。我一直安慰自己抗楔,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布拦坠。 她就那樣靜靜地躺著连躏,像睡著了一般。 火紅的嫁衣襯著肌膚如雪贞滨。 梳的紋絲不亂的頭發(fā)上入热,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音晓铆,去河邊找鬼勺良。 笑死,一個(gè)胖子當(dāng)著我的面吹牛骄噪,可吹牛的內(nèi)容都是我干的尚困。 我是一名探鬼主播,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼链蕊,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼事甜!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起滔韵,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤逻谦,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后陪蜻,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體邦马,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了滋将。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片邻悬。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖随闽,靈堂內(nèi)的尸體忽然破棺而出父丰,到底是詐尸還是另有隱情,我是刑警寧澤橱脸,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布,位于F島的核電站分苇,受9級(jí)特大地震影響添诉,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜医寿,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一栏赴、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧靖秩,春花似錦须眷、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至惠拭,卻和暖如春扩劝,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背职辅。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國(guó)打工棒呛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人域携。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓簇秒,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親秀鞭。 傳聞我的和親對(duì)象是個(gè)殘疾皇子趋观,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

推薦閱讀更多精彩內(nèi)容

  • Vulkan 是一套革命性的高性能 3D 圖形、計(jì)算 API锋边,適用于現(xiàn)代 GPU 管線系統(tǒng)拆内,用來滿足社區(qū)的苛刻要求...
    雨中亭_聽雨中閱讀 2,663評(píng)論 0 8
  • 在上一章中,我們提供了一個(gè)比較基本的介紹宠默,以便可視化新一代的 Vulkan API麸恍。 我們通過這套 API 的高級(jí)...
    雨中亭_聽雨中閱讀 2,346評(píng)論 2 2
  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,757評(píng)論 25 707
  • 1 中的 JavaScript JavaScript 函數(shù)和事件上面例子中的 JavaScript 語句,會(huì)...
    一枝妖孽閱讀 913評(píng)論 0 0
  • 在寫這篇文之前,我剛剛看完《歡樂戲劇人》第一季的最后一期抹沪。高曉攀的那個(gè)小品刻肄,我是帶著眼淚看完的。喬山的小品我是用一...
    閑人小夜閱讀 491評(píng)論 0 0