Skip to content

Commit b8f2469

Browse files
committed
Add zh/03_Drawing_a_triangle/00_Setup/01_Instance.md
1 parent b79cba1 commit b8f2469

File tree

1 file changed

+188
-0
lines changed

1 file changed

+188
-0
lines changed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
---
2+
title: 实例
3+
---
4+
5+
## 创建一个实例
6+
7+
一切开始于创建一个*实例*(instance)来初始化 Vulkan 库。实例是连接 Vulkan 库和你的程序之间的桥梁,创建实例还涉及到向驱动指定你的应用程序的
8+
一些细节。
9+
10+
添加一个 `createInstance` 函数,然后在 `initVulkan` 函数中调用它。
11+
12+
```c++
13+
void initVulkan() {
14+
createInstance();
15+
}
16+
```
17+
18+
再在类中添加一个数据成员,用来保存实例的句柄:
19+
20+
```c++
21+
private:
22+
VkInstance instance;
23+
```
24+
25+
现在,为了创建实例,我们首先需要用我们程序的一些信息去填充一个结构体。从技术上来说,这些信息是可有可无的,但是它们或许能够提供一些信息给驱动,以
26+
使驱动针对我们的特定程序进行优化(例如,它使用了一个具有某些特殊行为的知名图形引擎)。这个结构体叫做 `VkApplicationInfo`
27+
28+
```c++
29+
void createInstance() {
30+
VkApplicationInfo appInfo{};
31+
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
32+
appInfo.pApplicationName = "Hello Triangle";
33+
appInfo.applicationVersion = VK_MAKE_VERSION(1, 0, 0);
34+
appInfo.pEngineName = "No Engine";
35+
appInfo.engineVersion = VK_MAKE_VERSION(1, 0, 0);
36+
appInfo.apiVersion = VK_API_VERSION_1_0;
37+
}
38+
```
39+
40+
就像之前提到过的那样,Vulkan 中的许多结构体需要你在 `sType` 成员中显式指定类型。这个结构体也是众多拥有 `pNext` 成员的结构体之一,这个成员在
41+
将来可以指向扩展信息。我们现在执行默认初始化,所以此处置为 `nullptr`(空指针)。
42+
43+
Vulkan 中的许多信息都通过结构体来传递,而不是函数参数。我们还需要填充另一个结构体来为创建实例提供足够多的信息。接下来的这个结构体是必需的,它告
44+
知 Vulkan 驱动我们要使用哪些全局的扩展以及验证层。“全局”意味着它们将在整个程序中生效,而不是某个特定的设备,接下来的几章里我们会说明这个问题。
45+
46+
```c++
47+
VkInstanceCreateInfo createInfo{};
48+
createInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
49+
createInfo.pApplicationInfo = &appInfo;
50+
```
51+
52+
前两个参数的意思非常明显。接下来的两个成员会指定我们想用的全局扩展。就像我们在概述那章里提到过的,Vulkan 是一套平台无关的 API,这意味着你需要
53+
一个扩展与窗口系统(window system)来交互。GLFW 已经集成了一个好用的内置函数,它返回 GLFW 需要的 Vulkan 扩展,我们可以直接把它传给
54+
Vulkan API:
55+
56+
```c++
57+
uint32_t glfwExtensionCount = 0;
58+
const char** glfwExtensions;
59+
60+
glfwExtensions = glfwGetRequiredInstanceExtensions(&glfwExtensionCount);
61+
62+
createInfo.enabledExtensionCount = glfwExtensionCount;
63+
createInfo.ppEnabledExtensionNames = glfwExtensions;
64+
```
65+
66+
最后两个成员指定哪些全局验证层将会被启用。我们会在下一章深入讨论验证层,这里先暂时留空。
67+
68+
```c++
69+
createInfo.enabledLayerCount = 0;
70+
```
71+
72+
我们已经指定了初始化 Vulkan 实例需要的所有信息,现在终于可以调用 `vkCreateInstance` 函数了:
73+
74+
```c++
75+
VkResult result = vkCreateInstance(&createInfo, nullptr, &instance);
76+
```
77+
78+
如你所见,Vulkan 中创建对象的函数,其参数通常是这样的:
79+
80+
* 指向创建信息(creation info)的指针
81+
* 指向自定义分配器回调函数的指针,此教程中永远被置为 `nullptr`
82+
* 指向保存了要被创建的对象的句柄变量的指针
83+
84+
如果一切运行良好,那么创建好的实例的句柄就被保存在 `VkInstance` 类型的成员变量中了。几乎每一个 Vulkan 函数的返回值都是 `VkResult` 类型的,
85+
它要么是 `VK_SUCCESS`,要么是一个错误代码。如果要检查实例是否被成功创建,我们不需要保存这个返回结果,只需要检查一下返回值就行了:
86+
87+
```c++
88+
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
89+
throw std::runtime_error("failed to create instance!");
90+
}
91+
```
92+
93+
现在运行这个程序以确定实例创建成功。
94+
95+
## 遭遇 VK_ERROR_INCOMPATIBLE_DRIVER
96+
97+
如果在 MacOS 上使用最新的 MoltenVK SDK,你可能从 `vkCreateInstance` 得到 `VK_ERROR_INCOMPATIBLE_DRIVER` 返回值。根据
98+
[Vulkan SDK 的入门指南](https://vulkan.lunarg.com/doc/sdk/1.3.216.0/mac/getting_started.html),从 1.3.216 版本开始,
99+
`VK_KHR_PORTABILITY_subset` 扩展必须被启用。
100+
101+
为了解决这个错误,首先添加 `VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR` 标志位到 `VkInstanceCreateInfo` 结构体的 `flags`
102+
成员,然后添加 `VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME` 到实例的启用扩展列表。
103+
104+
典型的代码应该像这样:
105+
106+
```c++
107+
...
108+
109+
std::vector<const char*> requiredExtensions;
110+
111+
for(uint32_t i = 0; i < glfwExtensionCount; i++) {
112+
requiredExtensions.emplace_back(glfwExtensions[i]);
113+
}
114+
115+
requiredExtensions.emplace_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
116+
117+
createInfo.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
118+
119+
createInfo.enabledExtensionCount = (uint32_t) requiredExtensions.size();
120+
createInfo.ppEnabledExtensionNames = requiredExtensions.data();
121+
122+
if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) {
123+
throw std::runtime_error("failed to create instance!");
124+
}
125+
```
126+
127+
## 检查插件是否受支持
128+
129+
如果你看过 `vkCreateInstance` 的文档,你就会看到有一个错误代码是 `VK_ERROR_EXTENSION_NOT_PRESENT`。我们可以简单地指定我们想用的扩展,
130+
如果返回了这个错误码就直接终止程序。如果要检查那些必要的扩展,例如窗口系统接口(window system interface, WSI),这么做还有点道理,但如果我
131+
们要检查那些可选的功能呢?
132+
133+
为了在创建实例之前得到所有受支持的扩展列表,可以用 `vkEnumerateInstanceExtensionProperties` 函数。它需要两个指针变量,一个指向受支持的扩
134+
展数量,另一个指向一个 `VkExtensionProperties` 类型的、存储着扩展的细节的数组。它的第一个参数是可选的,允许我们使用一个特殊的验证层来选择扩
135+
展,我们现在先忽略它。
136+
137+
为了分配那个存储着扩展的细节的数组,我们需要先知道扩展的数量。你可以通过把最后一个参数留空的方式来只请求扩展的数量:
138+
139+
```c++
140+
uint32_t extensionCount = 0;
141+
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, nullptr);
142+
```
143+
144+
现在来分配一个数组,保存扩展的细节(引入头文件 `#include <vector>`):
145+
146+
```c++
147+
std::vector<VkExtensionProperties> extensions(extensionCount);
148+
```
149+
150+
最后我们就可以查询扩展的细节了:
151+
152+
```c++
153+
vkEnumerateInstanceExtensionProperties(nullptr, &extensionCount, extensions.data());
154+
```
155+
156+
每个 `VkExtensionProperties` 结构体都包含着扩展的名称和版本。我们可以通过一个简单的循环来输出它们(`\t` 是一个制表符,用来缩进):
157+
158+
```c++
159+
std::cout << "available extensions:\n";
160+
161+
for (const auto& extension : extensions) {
162+
std::cout << '\t' << extension.extensionName << '\n';
163+
}
164+
```
165+
166+
如果你想输出 Vulkan 支持的详细信息,你可以把这段代码加到 `createInstance` 函数里。留一个课后练习,尝试创建一个函数,检查
167+
`glfwGetRequiredInstanceExtensions` 函数返回的所有扩展是不是都在受支持的扩展列表里。
168+
169+
## 清理
170+
171+
`VkInstance` 只应该在程序退出之前被销毁。可以在 `cleanup` 函数中用 `vkDestroyInstance` 函数销毁它:
172+
173+
```c++
174+
void cleanup() {
175+
vkDestroyInstance(instance, nullptr);
176+
177+
glfwDestroyWindow(window);
178+
179+
glfwTerminate();
180+
}
181+
```
182+
183+
`vkDestroyInstance` 函数的参数非常直接了当,就像在上一章里提过的那样,Vulkan 中的分配器和回收器都有一个可选的回调函数参数,这个参数被我们设
184+
置为 `nullptr` 以忽略它。在随后的章节中,我们创建的所有其它 Vulkan 资源都会在实例被销毁之前回收。
185+
186+
在创建了实例之后、开始进行更复杂的步骤之前,是时候看看我们的[验证层](!zh/Drawing_a_triangle/Setup/Validation_layers)来评估调试选项了。
187+
188+
[C++ 代码](https://vulkan-tutorial.com/code/01_instance_creation.cpp)

0 commit comments

Comments
 (0)