创建一个实例
我们首先创建一个实例来初始化Vulkan库。这个实例指定了一些驱动程序需要使用的应用程序信息。
我们添加了一个createInstance函数调用到initVulkan函数中:
void initVulkan() {
createInstance();
}
添加了一个存储实例句柄的私有成员:
private:
VkInstance instance;
然后,填写应用程序信息,这些信息的填写不是必须的,但填写的信息可能会作为驱动程序的优化依据,让驱动程序进行一些特殊的优化。比如,应用程序使用了某个引擎,驱动程序对这个引擎有一些特殊处理,这时就可能有很大的优化提升:
VkApplicationInfo appInfo = {};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
appInfo.pApplicationName = "Hello Triangle";
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.pEngineName = "No Engine";
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
appInfo.apiVersion = VK_API_VERSION_1_0;
之前提到,Vulkan的很多结构体需要我们显式地在sType成员变量中指定结构体的类型。此外,许多Vulkan的结构体还有一个pNext成员变量,用来指向未来可能扩展的参数信息,现在,我们并没有使用它,将其设置为nullptr。
Vulkan倾向于通过结构体传递信息,我们需要填写一个或多个结构体来提供足够的信息创建Vulkan实例。下面的这个结构体是必须的,它告诉Vulkan的驱动程序需要使用的全局扩展和校验层。全局是指这里的设置对于整个应用程序都有效,而不仅仅对一个设备有效,在之后的章节,我们会对此有更加清晰得认识。
VkInstanceCreateInfo createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
createInfo.pApplicationInfo = &appInfo;
上面代码中填写得两个参数非常直白,不用多解释。接下来,我们需要指定需要的全局扩展。之前提到,Vulkan是平台无关的API,所以需要一个和窗口系统交互的扩展。GLFW库包含了一个可以返回这一扩展的函数,我们可以直接使用它:
uint32_t glfwExtensionCount = 0;
const char** glfwExtensions;
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
createInfo.enabledExtensionCount = glfwExtensionCount;
createInfo.ppEnabledExtensionNames = glfwExtensions;
结构体的最后两个成员变量用来指定全局校验层。我们将在之后的章节更加深入地讨论它,在这里,我们将其设置为0,不使用它:
createInfo.enabledLayerCount = 0;
填写完所有必要的信息,我们就可以调用vkCreateInstance函数来创建Vulkan实例:
VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);
如你所看到的,创建Vulkan对象的函数参数的一般形式就是:
-
一个包含了创建信息的结构体指针
-
一个自定义的分配器回调函数,在本教程,我们没有使用自定义的分配器,总是将它设置为nullptr
-
一个指向新对象句柄存储位置的指针
如果一切顺利,我们创建的实例的句柄就被存储在了类的VkInstance成员变量中。几乎所有Vulkan API函数调用都会返回一个VkResult来反应函数调用的结果,它的值可以是VK_SUCESS表示调用成功,或是一个错误代码,表示调用失败。为了检测实例是否创建成功,我们可以直接将创建函数在条件语句中使用,不需要存储它的返回值:
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
throw std::runtime_error("failed to create instance!");
}
现在可以编译运行程序来确保实例创建成功。