文章目录
- 创建实例(**Creating an instance**)
- 检查扩展支持(**Checking for extension support**)
- 销毁清除(**Cleaning up**)
- 最终代码
创建实例(Creating an instance)
您需要做的第一件事是通过创建一个实例来初始化Vulkan库。实例是应用程序和Vulkan库之间的连接,创建它需要向驱动程序指定有关应用程序的一些详细信息。
首先添加一个createInstance函数,并在initVulkan函数中调用它。
void initVulkan() {createInstance();
}
另外,添加一个数据成员来保存实例的句柄:
private:
VkInstance instance;
现在,要创建一个实例,我们首先必须用有关应用程序的一些信息填充一个结构体。这些数据在技术上是可选的,但它可能为驱动程序提供一些有用的信息,以便优化我们的特定应用程序(例如,因为它使用了具有某些特殊行为的知名图形引擎)。这个结构体叫做VkApplicationInfo:
void createInstance() {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成员中显式指定类型。这也是具有pNext成员的众多结构之一,该成员可以在将来指向扩展信息。我们在这里使用值初始化将它保留为nullptr。
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;
结构体的最后两个成员确定要启用的全局验证层。我们将在下一章中更深入地讨论这些,所以现在先把这些留空。
createInfo.enabledLayerCount = 0;
现在我们已经指定了Vulkan创建实例所需的所有内容,我们最终可以发出vkCreateInstance调用:
VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);
正如你将看到的,在Vulkan中对象创建函数参数遵循的一般模式是:
- 指向具有创建信息的结构体的指针
- 指向自定义分配器回调函数的指针,在本教程中总是nullptr
- 指向存储新对象句柄的变量的指针
如果一切顺利,则实例的句柄存储在VkInstance类成员中。几乎所有的Vulkan函数都返回VkResult类型的值,要么是VK_SUCCESS,要么是错误代码。要检查实例是否成功创建,我们不需要存储结果,只需要检查成功值即可:
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {throw std::runtime_error("failed to create instance!");
}
现在运行程序以确保成功创建了实例。
检查扩展支持(Checking for extension support)
如果您查看vkCreateInstance文档,那么您将看到其中一个可能的错误代码是VK_ERROR_EXTENSION_NOT_PRESENT。我们可以简单地指定我们需要的扩展,并在错误代码返回时终止。这对于像窗口系统接口这样的基本扩展来说是有意义的,但是如果我们想要检查可选功能呢?
要在创建实例之前检索支持的扩展列表,可以使用vkEnumerateInstanceExtensionProperties函数。它接受一个指向存储扩展数量的变量的指针和一个存储扩展详细信息的VkExtensionProperties数组。它还有一个可选的第一个参数,允许我们通过特定的验证层过滤扩展,现在我们将忽略它。
为了分配一个数组来保存扩展细节,我们首先需要知道有多少个扩展。您可以通过将后一个参数保留为空来请求扩展的数量:
uint32_t extensionCount = 0;
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
现在分配一个数组来保存扩展细节(include ):
std::vector<VkExtensionProperties> extensions(extensionCount);
最后我们可以查询扩展的详细信息:
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
每个VkExtensionProperties结构体都包含扩展的名称和版本。我们可以用一个简单的for循环列出它们(\t是用于缩进的制表符):
std::cout << "available extensions:\n";for (const auto& extension : extensions) {std::cout << '\t' << extension.extensionName << '\n';
}
如果您想提供有关Vulkan支持的一些细节,可以将此代码添加到createInstance函数中。作为一个挑战,尝试创建一个函数来检查glfwGetRequiredInstanceExtensions返回的所有扩展是否都包含在支持的扩展列表中。
销毁清除(Cleaning up)
VkInstance应该只在程序退出之前销毁。它可以在清理时用vkDestroyInstance函数销毁:
void cleanup() {vkDestroyInstance(instance, nullptr);glfwDestroyWindow(window);glfwTerminate();
}
vkDestroyInstance函数的参数很简单。正如前一章所提到的,Vulkan中的分配和释放函数有一个可选的allocator回调,我们将通过传递nullptr来忽略它。我们将在接下来的章节中创建的所有其他Vulkan资源都应该在销毁实例之前清理干净。
在实例创建之后继续执行更复杂的步骤之前,应该通过检查验证层来评估我们的调试选项。接下来介绍验证层的使用。
最终代码
//01_create_instance.cpp
#define GLFW_INCLUDE_VULKAN
#include <GLFW/glfw3.h>#include <vulkan/vulkan.h>
#include <iostream>
#include <stdexcept>
#include <cstdlib>const int WIDTH = 800;
const int HEIGHT = 600;class HelloTriangleApplication {
public:void run() {initWindow();initVulkan();mainLoop();cleanup();}private:void initWindow() {glfwInit();glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);glfwWindowHint(GLFW_RESIZABLE, GLFW_FALSE);window = glfwCreateWindow(WIDTH, HEIGHT, "Vulkan", nullptr, nullptr);}void initVulkan() {createInstance();}void mainLoop() {}void cleanup() {vkDestroyInstance(instance, nullptr);glfwDestroyWindow(window);glfwTerminate();}VkInstance instance;GLFWwindow* window;void createInstance() {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;VkInstanceCreateInfo createInfo{};createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;createInfo.pApplicationInfo = &appInfo;uint32_t glfwExtensionCount = 0;const char** glfwExtensions;glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);createInfo.enabledExtensionCount = glfwExtensionCount;createInfo.ppEnabledExtensionNames = glfwExtensions;createInfo.enabledLayerCount = 0;VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {throw std::runtime_error("failed to create instance!");}}
};int main() {HelloTriangleApplication app;try {app.run();}catch (const std::exception& e) {std::cerr << e.what() << std::endl;return EXIT_FAILURE;}return EXIT_SUCCESS;
}
运行后将产生一个glfw窗口,并创建了一个instance实例