最初之物 VkInstance¶
更新记录
2024/1/2 增加该文档。
2024/1/25 更新该文档。
2024/1/25 增加
创建 VkInstance章节。2024/1/25 增加
vkCreateInstance章节。2024/1/25 增加
VkInstanceCreateInfo章节。2024/1/25 增加
VkApplicationInfo章节。2024/1/25 增加
vkEnumerateInstanceVersion章节。2024/1/28 增加
Layer章节。2024/1/28 增加
vkEnumerateInstanceLayerProperties章节。2024/1/28 增加
VkLayerProperties章节。2024/1/30 增加
Extension章节。2024/1/30 增加
vkEnumerateInstanceExtensionProperties章节。2024/1/30 修正
vkEnumerateInstanceLayerProperties章节中的打印错误。完善说明。2024/1/30 增加
VkExtensionProperties章节。2024/2/1 增加
示例章节。2024/2/1 更新
vkEnumerateInstanceExtensionProperties章节。修正代码错误。2024/2/1 增加
销毁 VkInstance章节。2024/2/2 更新
示例章节。增加开源鸿蒙平台实例代码。2024/2/2 更新
vkCreateInstance章节。增加分配器说明。2024/2/7 更新
vkEnumerateInstanceExtensionProperties章节。完善说明。2024/2/7 更新
Extension章节。完善说明。2024/2/7 更新
VkExtensionProperties章节。完善说明。
开发 Vulkan 第一步就是创建 VkInstance ,也就是 Vulkan 的 实例 。一个实例代表一整 Vulkan 环境(或上下文)。不同的 Vulkan 环境能够获取到不同的 Vulkan 功能特性。其中最重要的就是配置 Vulkan 要使用的 版本 。
创建 VkInstance¶
通过 vkCreateInstance(...) 函数创建 VkInstance :
vkCreateInstance¶
// 由 VK_VERSION_1_0 提供
VkResult vkCreateInstance(
const VkInstanceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkInstance* pInstance);
pCreateInfo 指向
VkInstanceCreateInfo数据结构对象,用于配置VkInstance的创建信息。pAllocator 内存分配器。为
nullptr表示使用内部默认分配器,否则为自定义分配器。pInstance 返回创建的目标
VkInstance结果。
备注
pAllocator 将会在之后专门章节讲解,这里使用 nullptr 默认分配器即可。
其中 VkInstanceCreateInfo 结构体定义如下:
VkInstanceCreateInfo¶
// 由 VK_VERSION_1_0 提供
typedef struct VkInstanceCreateInfo {
VkStructureType sType;
const void* pNext;
VkInstanceCreateFlags flags;
const VkApplicationInfo* pApplicationInfo;
uint32_t enabledLayerCount;
const char* const* ppEnabledLayerNames;
uint32_t enabledExtensionCount;
const char* const* ppEnabledExtensionNames;
} VkInstanceCreateInfo;
sType 是该结构体的类型枚举值, 必须 是
VkStructureType::VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO。pNext 要么是
NULL要么指向其他结构体来扩展该结构体。flags 是
VkInstanceCreateFlagBits所表示的位域值,用于设置VkInstance的行为。pApplicationInfo 要么是
NULL要么指向应用信息结构体,用于应用细节设置。enabledLayerCount 激活的
layer数量。ppEnabledLayerNames 指向数量为
enabledLayerCount的layer字符串数组,用于设置要激活的layer。enabledExtensionCount 激活
instance扩展的数量。enabledExtensionCount 指向数量为
enabledExtensionCount的扩展字符串数组,用于设置要激活的instance扩展。
其中 VkApplicationInfo 结构体定义如下:
VkApplicationInfo¶
// 由 VK_VERSION_1_0 提供
typedef struct VkApplicationInfo {
VkStructureType sType;
const void* pNext;
const char* pApplicationName;
uint32_t applicationVersion;
const char* pEngineName;
uint32_t engineVersion;
uint32_t apiVersion;
} VkApplicationInfo;
sType 是该结构体的类型枚举值, 必须 是
VkStructureType::VK_STRUCTURE_TYPE_APPLICATION_INFO。pNext 要么是
NULL要么指向其他结构体来扩展该结构体。pApplicationName 要么是
NULL要么指向一个以空字符为结尾的UTF-8字符串,用于表示用户自定义应用名称。applicationVersion 一个无符号整型,用于用户自定义应用版本。
pEngineName 要么是
NULL要么指向一个以空字符为结尾的UTF-8字符串,用于表示用户自定义引擎名称。engineVersion 一个无符号整型,用于用户自定义引擎版本。
apiVersion 应用打算使用的
Vulkan的最高 核心 版本,并且忽略apiVersion的patch版本。
其中 pApplicationName 、 applicationVersion 、 pEngineName 和 engineVersion 这几个值随便设置,甚至可以不设置,赋为 空 都可以,这些参数不影响实例的创建。
而 apiVersion 参数是 最为重要的核心参数 ,当创建实例时,该参数用于指定此实例环境中 Vulkan 的 核心 版本 。目前 Vulkan 有 4 个版本:
Vulkan 1.0主要提供光栅化图形和并行计算的功能。对应VK_API_VERSION_1_0。Vulkan 1.1主要为Vulkan 1.0不完善的地方进行补全。对应VK_API_VERSION_1_1。Vulkan 1.2主要提供硬件光追的功能。对应VK_API_VERSION_1_2。Vulkan 1.3主要提供动态光栅化图形的功能。对应VK_API_VERSION_1_3。
每个 Vulkan 新版本的发布不单单提供基本功能,还会提供一系列扩展功能,并且会将之前版本中的一些扩展功能提升囊括至核心版本中。 VkApplicationInfo::apiVersion 将会在调用 vkCreateInstance 时告诉驱动将使用的 Vulkan 版本,驱动会为您做好必要的初始化。
如果想要使用的功能为高版本中的功能,而创建实例时 VkApplicationInfo::apiVersion 指定的是低版本,此时如果获取高版本的功能函数大概率会返回 空 。所以 VkApplicationInfo::apiVersion 尽可能的设置为自己需要的高版本。比如:
如果
VkApplicationInfo::apiVersion设置为VK_API_VERSION_1_0则表示可以使用该实例在Vulkan Loader中加载Vulkan 1.0版本发布的函数,而不能加载Vulkan 1.1及高版本的接口函数。如下:// 由 VK_VERSION_1_0 提供 vkCmdCopyImageToBuffer(...) // 该函数为 Vulkan 1.0 版本中的函数,可以加载(可有效加载)。 // 由 VK_VERSION_1_3 提供 vkCmdCopyImageToBuffer2(...) // 该函数为 Vulkan 1.3 版本中的函数,不可以加载(加载将返回空)。
在 纵览 中我们知道由于历史原因, Vulkan 在 Vulkan 1.1 版本时推出了 vkEnumerateInstanceVersion(...) 函数,用于获取驱动支持加载 Instance 域函数 的 Vulkan 版本。该函数定义如下:
vkEnumerateInstanceVersion¶
// 由 VK_VERSION_1_0 提供
VkResult vkEnumerateInstanceVersion(
uint32_t* pApiVersion);
pApiVersion 返回支持的
Instance 域函数对应的Vulkan版本。
vkEnumerateInstanceVersion
该函数为全局函数。
该函数返回的版本为可获取的
Instance 域函数所对应的版本。与物理设备(
GPU)支持的Vulkan版本可能会不同,也就是Device 域函数对应的Vulkan版本(VkPhysicalDeviceProperties::apiVersion)。
Layer¶
在创建 VkInstance 时需要通过 VkInstanceCreateInfo::enabledLayerCount 和 VkInstanceCreateInfo::ppEnabledLayerNames 来配置实例要开启的 层 ( Layer )。
Vulkan 中的 层 一般都是用来作正确性验证检查的。如果在开发后执行阶段发生了使用错误, 层 会输出错误信息,帮助开发者修正错误。
其中最常使用的 层 就是 VK_LAYER_KHRONOS_validation ,用于 Vulkan API 验证和错误检查。
目前 Vulkan 支持的 层 如下:
VK_LAYER_KHRONOS_validation
Vulkan API验证和错误检查。VK_LAYER_LUNARG_gfxreconstruct 使用 GFXReconstruct 捕获应用的
Vulkan指令。VK_LAYER_LUNARG_api_dump 输出调用的
API和传入的参数。VK_LAYER_KHRONOS_profiles 帮助测试硬件的性能,而不需要物理接触每个设备。该
层将会覆盖从GPU查询到的数据。VK_LAYER_LUNARG_monitor 在应用界面的标题处显示帧率。
VK_LAYER_LUNARG_screenshot 将显示的画面帧输出到一个图片文件中。
VK_LAYER_KHRONOS_synchronization2 使用系统实现的
VK_KHR_synchronization2扩展,而不是驱动实现的。VK_LAYER_KHRONOS_shader_object 使用系统实现的
VK_EXT_shader_object扩展,而不是驱动实现的。
官方 Layer 文档
Vulkan 支持的所有 Layer 可以在 Vulkan Layers 中找到详细文档。
可以通过 vkEnumerateInstanceLayerProperties(...) 获取系统中 Vulkan 支持的 Layer :
vkEnumerateInstanceLayerProperties¶
// 由 VK_VERSION_1_0 提供
VkResult vkEnumerateInstanceLayerProperties(
uint32_t* pPropertyCount,
VkLayerProperties* pProperties);
pPropertyCount 用于指定
pProperties成员的数组长度。pProperties 如果为
nullptr则将会将系统中支持的层数写入pPropertyCount中。否则会将查询到的元素写入pProperties。
如果 pPropertyCount 数量小于系统中支持的 层 数,该函数将 pPropertyCount 个 层 信息写入 pProperties 中,并返回 VkResult::VK_INCOMPLETE (表示只写入了一部分,并不是所有信息)。
如果 pPropertyCount 数量大于等于系统中支持的 层 数,则会将所有的 层 数据写入 pProperties 中,并返回 VkResult::VK_SUCCESS 。
所以获取 层 信息一般调用两遍 vkEnumerateInstanceLayerProperties(...) 函数:
uint32_t layer_property_count = 0;
vkEnumerateInstanceLayerProperties(&layer_property_count, nullptr);
std::vector<VkLayerProperties> layer_properties(layer_property_count);
vkEnumerateInstanceLayerProperties(&layer_property_count, layer_properties.data());
其中 VkLayerProperties 定义如下:
VkLayerProperties¶
// 由 VK_VERSION_1_0 提供
typedef struct VkLayerProperties {
char layerName[VK_MAX_EXTENSION_NAME_SIZE];
uint32_t specVersion;
uint32_t implementationVersion;
char description[VK_MAX_DESCRIPTION_SIZE];
} VkLayerProperties;
layerName
层名称。specVersion
层实现时的Vulkan版本。implementationVersion
层自身维护的版本。description
层的描述信息。
其中 VK_MAX_EXTENSION_NAME_SIZE 和 VK_MAX_DESCRIPTION_SIZE 定义如下:
#define VK_MAX_EXTENSION_NAME_SIZE 256U
#define VK_MAX_DESCRIPTION_SIZE 256U
Extension¶
在创建 VkInstance 时需要通过 VkInstanceCreateInfo::enabledExtensionCount 和 VkInstanceCreateInfo::ppEnabledExtensionNames 来配置实例要开启的 实例扩展 ( Instance Extension )。
在 Vulkan 中有两类扩展:
Instance 扩展 与使用哪一个
GPU设备无关,与Vulkan环境有关。VkInstanceCreateInfo中的enabledExtensionCount和ppEnabledExtensionNames就是用于配置此类Instance 扩展。Device 扩展 与使用哪一个
GPU设备有关。不同厂家的GPU设备会支持不同的设备扩展。这将会在之后的章节展开。
VkInstance 支持的实例扩展可以通过 vkEnumerateInstanceExtensionProperties(...) 函数获取:
vkEnumerateInstanceExtensionProperties¶
// 由 VK_VERSION_1_0 提供
VkResult vkEnumerateInstanceExtensionProperties(
const char* pLayerName,
uint32_t* pPropertyCount,
VkExtensionProperties* pProperties);
pLayerName 要么为
空要么为层的名称。pPropertyCount 用于指定
pProperties成员的数组长度。pProperties 如果为
nullptr则将会将实例支持的扩展数写入pPropertyCount中。否则会将查询到的元素写入pProperties。
如果 pLayerName 为有效的 层 名,则该函数将会返回该层内部使用的 实例扩展 。如果开启了该 层 ,则其内部使用的 扩展 将自动开启。
如果 pLayerName 为 nullptr ,则该函数将会返回 Vulkan 实现和默认启用的 层 支持的实例扩展信息。
要想获取全部的扩展,该函数的调用与 vkEnumerateInstanceLayerProperties(...) 类似,调用两遍,第一遍 pProperties 为 nullptr ,第二遍为有效值即可:
uint32_t extension_property_count = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extension_property_count, nullptr);
std::vector<VkExtensionProperties> extension_properties(extension_property_count);
vkEnumerateInstanceExtensionProperties(nullptr, &extension_property_count, extension_properties.data());
其中 VkExtensionProperties 定义如下:
VkExtensionProperties¶
// 由 VK_VERSION_1_0 提供
typedef struct VkExtensionProperties {
char extensionName[VK_MAX_EXTENSION_NAME_SIZE];
uint32_t specVersion;
} VkExtensionProperties;
extensionName 为扩展名称。
specVersion 为扩展该扩展的版本。
有一些实例扩展我们需要重点关注一下
VK_KHR_surface 代表窗口通用平面扩展。
- VK_{vender}_{platform}_surface 代表各个平台各自的窗口平面(各自平台适配到通用平面)。其中:
vender 表示该扩展的供应商(或维护方),有的没有提供该供应商字段(取决于扩展开发商)。比如
KHR表示Khronos组织提供维护的该扩展。platform 表示扩展对应的平台。
比如
VK_KHR_win32_surface 为
Windows平台,供应商为Khronos。VK_OHOS_surface 为
OpenHarmony平台,供应商为华为。VK_KHR_android_surface 为
Android平台,供应商为Khronos。VK_KHR_[wayland/xcb/xlib]_surface 为
Linux平台(其中[wayland/xcb/xlib]表示三者其一),供应商为Khronos。
这些扩展在窗口中显示渲染结果非常重要,对于具体如何使用,将会在之后的章节展开。
销毁 VkInstance¶
当创建完 VkInstance 之后可通过 vkDestroyInstance(...) 函数销毁。
vkDestroyInstance¶
// 由 VK_VERSION_1_0 提供
void vkDestroyInstance(
VkInstance instance,
const VkAllocationCallbacks* pAllocator);
instance 要么为
空要么 必须 为有效的VkInstance。pAllocator 分配器。需要与创建
VkInstance时指定的分配器匹配。
当 instance 销毁时,需要确保所有该实例环境下创建的对象(句柄)都已经回收或销毁。
示例¶
uint32_t vulkan_version = VK_MAKE_API_VERSION(0, 1, 0, 0);
if(vkEnumerateInstanceVersion != nullptr && vkEnumerateInstanceVersion(&vulkan_version) != VkResult::VK_SUCCESS)
{
vulkan_version = VK_MAKE_API_VERSION(0, 1, 0, 0);
}
VkInstance instance = VK_NULL_HANDLE;
VkApplicationInfo application_info = {};
application_info.sType = VkStructureType::VK_STRUCTURE_TYPE_APPLICATION_INFO;
application_info.pNext = nullptr;
application_info.pApplicationName = nullptr;
application_info.applicationVersion = 0;
application_info.pEngineName = nullptr;
application_info.engineVersion = 0;
application_info.apiVersion = vulkan_version;
std::vector<const char *> enable_layer_names;
#if defined(_DEBUG) || defined(NDEBUG)
enable_layer_names.push_back("VK_LAYER_KHRONOS_validation");
#endif
std::vector<const char *> enable_extension_names;
enable_extension_names.push_back("VK_KHR_surface");
#if defined(_WIN16) || defined(_WIN32) || defined(_WIN64) // Windows
enable_extension_names.push_back("VK_KHR_win32_surface");
#elif defined(VK_USE_PLATFORM_OHOS) // 开源鸿蒙
enable_extension_names.push_back("VK_OHOS_surface");
#elif 其他平台...
#endif
VkInstanceCreateInfo instance_create_info = {};
instance_create_info.sType = VkStructureType::VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
instance_create_info.pNext = nullptr;
instance_create_info.flags = 0;
instance_create_info.pApplicationInfo = &application_info;
instance_create_info.enabledLayerCount = enable_layer_names.size();
instance_create_info.ppEnabledLayerNames = enable_layer_names.data();
instance_create_info.enabledExtensionCount = enable_extension_names.size();
instance_create_info.ppEnabledExtensionNames = enable_extension_names.data();
VkResult result = vkCreateInstance(&instance_create_info, nullptr, &instance);
if (result != VK_SUCCESS)
{
throw std::runtime_error("VkInstance 创建失败");
}
// 缤纷绚丽的 Vulkan 程序 ...
vkDestroyInstance(instance, nullptr);