-
Notifications
You must be signed in to change notification settings - Fork 815
Expand file tree
/
Copy pathinstance.h
More file actions
334 lines (298 loc) · 13.6 KB
/
instance.h
File metadata and controls
334 lines (298 loc) · 13.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
/* Copyright (c) 2018-2026, Arm Limited and Contributors
* Copyright (c) 2022-2026, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "common/helpers.h"
#include "structure_chain_builder.h"
#include <vulkan/vulkan.hpp>
namespace vkb
{
namespace core
{
namespace
{
template <vkb::BindingType bindingType>
typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::InstanceCreateFlags, VkInstanceCreateFlags>::type get_default_create_flags(std::vector<std::string> const &)
{
if constexpr (bindingType == vkb::BindingType::Cpp)
{
return vk::InstanceCreateFlags{};
}
else
{
return 0;
}
}
} // namespace
/**
* @brief A wrapper class for InstanceType
*
* This class is responsible for checking the API version, checking for required and optional extensions and layers, creating the Vulkan
* instance and initializing the default dispatcher.
*/
template <vkb::BindingType bindingType>
class Instance
{
public:
using InstanceCreateFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::InstanceCreateFlags, VkInstanceCreateFlags>::type;
using InstanceCreateInfoType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::InstanceCreateInfo, VkInstanceCreateInfo>::type;
using InstanceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Instance, VkInstance>::type;
public:
/**
* @brief Initializes the connection to Vulkan
* @param application_name The name of the application
* @param api_version The Vulkan API version that the instance will be using
* @param requested_layers The requested layers to be enabled
* @param requested_extensions The requested extensions to be enabled
* @param get_create_flags A function pointer returning the InstanceCreateFlags for the InstanceCreateInfo
* @param extend_instance_create_info A function pointer to extend the InstanceCreateInfo with additional structures in the pNext chain
* @throws runtime_error if a required layer or extension is not available
*/
Instance(
std::string const &application_name,
uint32_t api_version = VK_API_VERSION_1_1,
std::unordered_map<std::string, vkb::RequestMode> const &requested_layers = {},
std::unordered_map<std::string, vkb::RequestMode> const &requested_extensions = {},
std::function<InstanceCreateFlagsType(std::vector<std::string> const &)> const &get_create_flags = get_default_create_flags,
std::function<void(vkb::StructureChainBuilder<bindingType, InstanceCreateInfoType> &)> const &extend_instance_create_info = [](vkb::StructureChainBuilder<bindingType, InstanceCreateInfoType> const &) {});
Instance(vk::Instance instance, std::vector<char const *> const &externally_enabled_extensions = {}, bool needsToInitializeDispatcher = false);
Instance(VkInstance instance, std::vector<char const *> const &externally_enabled_extensions = {});
Instance(Instance const &) = delete;
Instance(Instance &&) = delete;
~Instance();
Instance &operator=(Instance const &) = delete;
Instance &operator=(Instance &&) = delete;
std::vector<std::string> const &get_enabled_extensions();
InstanceType get_handle() const;
/**
* @brief Checks if the given extension is enabled in the InstanceType
* @param extension An extension to check
*/
bool is_extension_enabled(char const *extension) const;
private:
std::vector<std::string> enabled_extensions; // The enabled extensions
vk::Instance handle; // The Vulkan instance
};
using InstanceC = Instance<vkb::BindingType::C>;
using InstanceCpp = Instance<vkb::BindingType::Cpp>;
namespace
{
inline bool enable_extension(std::string const &requested_extension,
std::vector<vk::ExtensionProperties> const &available_extensions,
std::vector<std::string> &enabled_extensions)
{
bool is_available = std::ranges::any_of(
available_extensions, [&requested_extension](auto const &available_extension) { return requested_extension == available_extension.extensionName; });
if (is_available)
{
bool is_already_enabled =
std::ranges::any_of(enabled_extensions, [&requested_extension](auto const &enabled_extension) { return requested_extension == enabled_extension; });
if (!is_already_enabled)
{
LOGI("Extension {} available, enabling it", requested_extension);
enabled_extensions.emplace_back(requested_extension);
}
}
else
{
LOGI("Extension {} not available", requested_extension);
}
return is_available;
}
inline bool
enable_layer(std::string const &requested_layer, std::vector<vk::LayerProperties> const &available_layers, std::vector<std::string> &enabled_layers)
{
bool is_available =
std::ranges::any_of(available_layers, [&requested_layer](auto const &available_layer) { return requested_layer == available_layer.layerName; });
if (is_available)
{
bool is_already_enabled =
std::ranges::any_of(enabled_layers, [&requested_layer](auto const &enabled_layer) { return requested_layer == enabled_layer; });
if (!is_already_enabled)
{
LOGI("Layer {} available, enabling it", requested_layer);
enabled_layers.emplace_back(requested_layer.c_str());
}
}
else
{
LOGI("Layer {} not available", requested_layer);
}
return is_available;
}
} // namespace
template <vkb::BindingType bindingType>
inline Instance<bindingType>::Instance(std::string const &application_name,
uint32_t api_version,
std::unordered_map<std::string, vkb::RequestMode> const &requested_layers,
std::unordered_map<std::string, vkb::RequestMode> const &requested_extensions,
std::function<InstanceCreateFlagsType(std::vector<std::string> const &)> const &get_create_flags,
std::function<void(vkb::StructureChainBuilder<bindingType, InstanceCreateInfoType> &)> const &extend_instance_create_info)
{
// check API version
LOGI("Requesting Vulkan API version {}.{}", VK_VERSION_MAJOR(api_version), VK_VERSION_MINOR(api_version));
if (api_version < VK_API_VERSION_1_1)
{
LOGE("Vulkan API version {}.{} is requested but version 1.1 or higher is required.", VK_VERSION_MAJOR(api_version), VK_VERSION_MINOR(api_version));
throw std::runtime_error("Requested Vulkan API version is too low.");
}
uint32_t instance_api_version = vk::enumerateInstanceVersion();
LOGI("Vulkan instance supports API version {}.{}", VK_VERSION_MAJOR(instance_api_version), VK_VERSION_MINOR(instance_api_version));
if (instance_api_version < api_version)
{
LOGE("Vulkan API version {}.{} is requested but only version {}.{} is supported.",
VK_VERSION_MAJOR(api_version),
VK_VERSION_MINOR(api_version),
VK_VERSION_MAJOR(instance_api_version),
VK_VERSION_MINOR(instance_api_version));
throw std::runtime_error("Requested Vulkan API version is too high.");
}
// Check for optional and required layers
std::vector<vk::LayerProperties> available_layers = vk::enumerateInstanceLayerProperties();
std::vector<std::string> enabled_layers;
for (auto const &requested_layer : requested_layers)
{
if (!enable_layer(requested_layer.first, available_layers, enabled_layers))
{
if (requested_layer.second == vkb::RequestMode::Optional)
{
LOGW("Optional layer {} not available, some features may be disabled", requested_layer.first);
}
else
{
LOGE("Required layer {} not available, cannot run", requested_layer.first);
throw std::runtime_error("Required layers are missing.");
}
}
}
std::vector<char const *> enabled_layers_cstr;
for (auto &layer : enabled_layers)
{
enabled_layers_cstr.push_back(layer.c_str());
}
// Check for optional and required extensions
std::vector<vk::ExtensionProperties> available_extensions = vk::enumerateInstanceExtensionProperties();
if (contains(enabled_layers, "VK_LAYER_KHRONOS_validation"))
{
std::string const validation_layer_name = "VK_LAYER_KHRONOS_validation";
std::vector<vk::ExtensionProperties> available_layer_instance_extensions = vk::enumerateInstanceExtensionProperties(validation_layer_name);
available_extensions.insert(available_extensions.end(),
available_layer_instance_extensions.begin(),
available_layer_instance_extensions.end());
}
for (auto const &requested_extension : requested_extensions)
{
if (!enable_extension(requested_extension.first, available_extensions, enabled_extensions))
{
if (requested_extension.second == vkb::RequestMode::Optional)
{
LOGW("Optional instance extension {} not available, some features may be disabled", requested_extension.first);
}
else
{
LOGE("Required instance extension {} not available, cannot run", requested_extension.first);
throw std::runtime_error("Required instance extensions are missing.");
}
}
}
std::vector<char const *> enabled_extensions_cstr;
for (auto &extension : enabled_extensions)
{
enabled_extensions_cstr.push_back(extension.c_str());
}
vk::ApplicationInfo app_info{.pApplicationName = application_name.c_str(), .pEngineName = "Vulkan Samples", .apiVersion = api_version};
vk::InstanceCreateInfo create_info{.flags = static_cast<vk::InstanceCreateFlags>(get_create_flags(enabled_extensions)),
.pApplicationInfo = &app_info,
.enabledLayerCount = static_cast<uint32_t>(enabled_layers_cstr.size()),
.ppEnabledLayerNames = enabled_layers_cstr.data(),
.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions_cstr.size()),
.ppEnabledExtensionNames = enabled_extensions_cstr.data()};
vkb::StructureChainBuilder<vkb::BindingType::Cpp, vk::InstanceCreateInfo> scb;
scb.set_anchor_struct(create_info);
if constexpr (bindingType == vkb::BindingType::Cpp)
{
extend_instance_create_info(scb);
}
else
{
extend_instance_create_info(reinterpret_cast<vkb::StructureChainBuilder<vkb::BindingType::C, VkInstanceCreateInfo> &>(scb));
}
// Create the Vulkan instance
handle = vk::createInstance(*scb.get_struct<vk::InstanceCreateInfo>());
// initialize the Vulkan-Hpp default dispatcher on the instance
VULKAN_HPP_DEFAULT_DISPATCHER.init(handle);
// Need to load volk for all the not-yet Vulkan-Hpp calls
volkLoadInstance(handle);
}
template <vkb::BindingType bindingType>
inline Instance<bindingType>::Instance(vk::Instance instance, std::vector<char const *> const &externally_enabled_extensions, bool needsToInitializeDispatcher) :
handle{instance}
{
if (needsToInitializeDispatcher)
{
#if defined(_HPP_VULKAN_LIBRARY)
static vk::detail::DynamicLoader dl(_HPP_VULKAN_LIBRARY);
#else
static vk::detail::DynamicLoader dl;
#endif
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);
}
// Some parts of the framework will check for certain extensions to be enabled
// To make those work we need to copy over externally enabled extensions into this class
for (auto extension : externally_enabled_extensions)
{
enabled_extensions.push_back(extension);
}
}
template <vkb::BindingType bindingType>
inline Instance<bindingType>::Instance(VkInstance instance, std::vector<char const *> const &externally_enabled_extensions) :
Instance<bindingType>(static_cast<vk::Instance>(instance), externally_enabled_extensions, true)
{}
template <vkb::BindingType bindingType>
inline Instance<bindingType>::~Instance()
{
if (handle)
{
handle.destroy();
}
}
template <vkb::BindingType bindingType>
inline typename Instance<bindingType>::InstanceType Instance<bindingType>::get_handle() const
{
if constexpr (bindingType == BindingType::Cpp)
{
return handle;
}
else
{
return static_cast<VkInstance>(handle);
}
}
template <vkb::BindingType bindingType>
inline bool Instance<bindingType>::is_extension_enabled(char const *extension) const
{
return std::ranges::any_of(enabled_extensions, [extension](std::string const &enabled_extension) { return enabled_extension == extension; });
}
template <vkb::BindingType bindingType>
inline std::vector<std::string> const &Instance<bindingType>::get_enabled_extensions()
{
return enabled_extensions;
}
} // namespace core
} // namespace vkb