本节的源代码是 01-init_instance.cpp
Vulkan程序的第一步是创建一个Vulkan实例。
当你阅读到本节的时候,可以在LunarG Vulkan 示例代码仓库中 API-Samples
文件夹下找到01-init_instance.cpp
Vulkan API使用 vkInstance
对象 来存储 所有每个应用的状态。应用程序必须在执行任何其他Vulkan操作之前创建一个Vulkan实例。
基本的Vulkan架构看起来是这样的:
上面的图显示了一个Vulkan应用程序链接到一个通常被称为加载器(Loader)的Vulkan库。创建实例会初始化装载器。加载器还加载并初始化低级图形驱动程序,通常由GPU硬件的供应商提供。
请注意,图中有一些层(Layer),这些层也被加载器加载。层通常用于验证,通常是由驱动执行的错误检查。在Vulkan中,驱动程序比OpenGL等其他API要轻量得多,部分原因是它将功能验证委托给验证层。
层是可选的,每次应用程序创建一个实例时都可以选择性地装载。
Vulkan层超出了本教程的范围和示例的进展。因此,本教程不进一步介绍这些层。关于图层的进一步信息可以在下面的网址找到
LunarG LunarXchange website.
vkCreateInstance
看下 01-init_instance.cpp
代码然后找到vkCreateInstance
函数调用,函数原型如下:
VkResult vkCreateInstance(
const VkInstanceCreateInfo* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkInstance* pInstance);
一点一点来看:
VkResult
-
这是函数的返回状态。你可能想要打开“vulkan.h”的头文件。当我们遇到这些定义时,请检查其中的一些定义。你可以在“include”目录中找到这个文件
VkInstanceCreateInfo
-
这个结构包含了用来创建实例的额外的信息。由于这是一个很重要的一项,您稍后将更详细得了解它。
VkAllocationCallbacks
-
分配主机内存的函数都有一个参数,允许应用程序执行自己的主机内存管理。否则,Vulkan使用默认系统内存管理设施。应用程序可能想要管理自己的主机内存,例如,为了记录内存分配。
本示例不使用这个特性,因此您将始终看到NULL被传递到这个和其他函数的参数中。
VkInstance
-
这是函数返回的句柄如果实例创建成功。
这是一个不可见的句柄,所以不要试图去引用它。
许多Vulkan函数创建对象返回句柄,它们以这种方式创建的对象。
创建对象的Vulkan函数通常有一个Vk_Object_CreateInfo
参数。
找到初始化这个结构的代码:
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;
前两个成员通常出现在许多Vulkan的CreateInfo结构中。
sType
-
“sType”字段表示结构的类型。在本例中,您将其设置为VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO
,因为它是一个VkInstanceCreateInfo 结构。这可能看起来是多余的,因为只有这种类型的结构可以是作为vkCreateInstance()
的第一个参数传递。但它有一些价值,原因如下:
* 一个驱动程序、验证层或结构的其他使用者可以执行一个简单的有效性检查,如果“sType”不像预期的那样,可能会请求失败。
* 通过没有完全定义到接口参数类型化的接口,可以通过无类型(void)指针传递给消费者。例如,如果一个驱动程序支持实例创建的扩展,它可以查看通过无类型pNext
指针(接下来讨论)传递的这样一个结构。在这种情况下,sType
将被设置为扩展识别的值。
由于这个成员总是结构中的第一个成员,所以使用者可以很容易地确定结构的类型并决定如何处理它。
pNext
-
你会经常将pNext
设置为NULL。这个空指针有时用于在类型化结构中传递特定于扩展的信息,其中sType
被设置为一个扩展定义的值。正如上面所讨论的,扩展可以分析在这个pNext
指针链中传递的任何结构,以找到它们识别的结构。
flags
-
目前还没有定义,所以将其设置为零。
pApplicationInfo
-
这是一个指向另一个结构的指针,你需要设置它。你一会儿就会了解到。
enabledLayerCount
和 ppEnabledLayerNames
-
本教程中的示例不使用层,因此这些成员被清空。
enabledExtensionCount
和ppEnabledExtensionNames
-
本教程中的示例不使用扩展。稍后,另一个示例将显示扩展的用法。
该结构为Vulkan的实现提供了关于应用程序的一些基本信息:
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
and pNext
-
含义和vkInstanceCreateInfo
结构中的一样.
pApplicationName
, applicationVersion
, pEngineName
, engineVersion
-
如果需要的话,这些是应用程序可以提供的自由字段。
工具、加载器、层或驱动程序的一些实现在调试或收集时使用这些字段来提供信息,或者信息报告等。
驱动甚至有可能改变它们的行为,取决于正在运行的应用程序。
apiVersion
-
这个字段表示用于编译应用程序的Vulkan API的主版本、子版本和补丁版本。如果你使用的是Vulkan 1.0,那么主版本是1,而子版本应该是0。使用来自vulkan.h
的VK_API_VERSION_1_0
宏来完成,补丁级别为0。补丁版本的差异不会影响只在补丁版本上不同的版本之间的完整兼容性。通常,您应该将这个字段设置为VK_API_VERSION_1_0
,除非您有充分的理由不这样做。
一旦结构被填充,样例应用程序就会创建实例:
VkInstance inst;
VkResult res;
res = vkCreateInstance(&inst_info, NULL, &inst);
if (res == VK_ERROR_INCOMPATIBLE_DRIVER) {
std::cout << "cannot find a compatible Vulkan ICD\n";
exit(-1);
} else if (res) {
std::cout << "unknown error\n";
exit(-1);
}
vkDestroyInstance(inst, NULL);
在上面的代码中,应用程序根据结果快速检查可能出现的错误并报告它,或者其他一些错误。请注意,成功(VK_SUCCESS
)的值为0,许多应用程序将非零结果解释为一个错误。
最后,应用在退出前销毁这个对象。
现在您已经创建了一个Vulkan实例,现在是时候发现您的Vulkan实例可用的图形设备了。