Line |
Branch |
Exec |
Source |
1 |
|
|
/* |
2 |
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated |
3 |
|
|
* documentation files (the “Software”), to deal in the Software without restriction, including without |
4 |
|
|
* limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies |
5 |
|
|
* of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: |
6 |
|
|
* |
7 |
|
|
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. |
8 |
|
|
* |
9 |
|
|
* THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT |
10 |
|
|
* LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. |
11 |
|
|
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, |
12 |
|
|
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. |
13 |
|
|
* |
14 |
|
|
* Copyright © 2020 Charles Giessen (charles@lunarg.com) |
15 |
|
|
*/ |
16 |
|
|
|
17 |
|
|
#pragma once |
18 |
|
|
|
19 |
|
|
#include <cassert> |
20 |
|
|
|
21 |
|
|
#include <vector> |
22 |
|
|
#include <system_error> |
23 |
|
|
|
24 |
|
|
#include <vulkan/vulkan.h> |
25 |
|
|
|
26 |
|
|
|
27 |
|
|
namespace vkb { |
28 |
|
|
|
29 |
|
|
namespace detail { |
30 |
|
|
|
31 |
|
|
struct Error { |
32 |
|
|
std::error_code type; |
33 |
|
|
VkResult vk_result = VK_SUCCESS; // optional error value if a vulkan call failed |
34 |
|
|
}; |
35 |
|
|
|
36 |
|
|
template <typename T> class Result { |
37 |
|
|
public: |
38 |
|
|
Result (const T& value) : m_value{ value }, m_init{ true } {} |
39 |
|
|
Result (T&& value) : m_value{ std::move (value) }, m_init{ true } {} |
40 |
|
|
|
41 |
|
|
Result (Error error) : m_error{ error }, m_init{ false } {} |
42 |
|
|
|
43 |
|
|
Result (std::error_code error_code, VkResult result = VK_SUCCESS) |
44 |
|
✗ |
: m_error{ error_code, result }, m_init{ false } {} |
45 |
|
|
|
46 |
|
|
~Result () { destroy (); } |
47 |
|
|
Result (Result const& expected) : m_init (expected.m_init) { |
48 |
|
|
if (m_init) |
49 |
|
|
new (&m_value) T{ expected.m_value }; |
50 |
|
|
else |
51 |
|
|
m_error = expected.m_error; |
52 |
|
|
} |
53 |
|
|
Result (Result&& expected) : m_init (expected.m_init) { |
54 |
|
|
if (m_init) |
55 |
|
|
new (&m_value) T{ std::move (expected.m_value) }; |
56 |
|
|
else |
57 |
|
|
m_error = std::move (expected.m_error); |
58 |
|
|
expected.destroy (); |
59 |
|
|
} |
60 |
|
|
|
61 |
|
|
Result& operator= (const T& expect) { |
62 |
|
|
destroy (); |
63 |
|
|
m_init = true; |
64 |
|
|
new (&m_value) T{ expect }; |
65 |
|
|
return *this; |
66 |
|
|
} |
67 |
|
|
Result& operator= (T&& expect) { |
68 |
|
|
destroy (); |
69 |
|
|
m_init = true; |
70 |
|
|
new (&m_value) T{ std::move (expect) }; |
71 |
|
|
return *this; |
72 |
|
|
} |
73 |
|
|
Result& operator= (const Error& error) { |
74 |
|
|
destroy (); |
75 |
|
|
m_init = false; |
76 |
|
|
m_error = error; |
77 |
|
|
return *this; |
78 |
|
|
} |
79 |
|
|
Result& operator= (Error&& error) { |
80 |
|
|
destroy (); |
81 |
|
|
m_init = false; |
82 |
|
|
m_error = error; |
83 |
|
|
return *this; |
84 |
|
|
} |
85 |
|
|
// clang-format off |
86 |
|
|
const T* operator-> () const { assert (m_init); return &m_value; } |
87 |
|
|
T* operator-> () { assert (m_init); return &m_value; } |
88 |
|
|
const T& operator* () const& { assert (m_init); return m_value; } |
89 |
|
|
T& operator* () & { assert (m_init); return m_value; } |
90 |
|
|
T&& operator* () && { assert (m_init); return std::move (m_value); } |
91 |
|
|
const T& value () const& { assert (m_init); return m_value; } |
92 |
|
|
T& value () & { assert (m_init); return m_value; } |
93 |
|
|
const T&& value () const&& { assert (m_init); return std::move (m_value); } |
94 |
|
|
T&& value () && { assert (m_init); return std::move (m_value); } |
95 |
|
|
|
96 |
|
|
std::error_code error() const { assert (!m_init); return m_error.type; } |
97 |
|
|
VkResult vk_result() const { assert (!m_init); return m_error.vk_result; } |
98 |
|
|
// clang-format on |
99 |
|
|
|
100 |
|
|
|
101 |
|
|
bool has_value () const { return m_init; } |
102 |
|
|
explicit operator bool () const { return m_init; } |
103 |
|
|
|
104 |
|
|
private: |
105 |
|
|
void destroy () { |
106 |
|
✗ |
if (m_init) m_value.~T (); |
107 |
|
✗ |
} |
108 |
|
|
union { |
109 |
|
|
T m_value; |
110 |
|
|
Error m_error; |
111 |
|
|
}; |
112 |
|
|
bool m_init; |
113 |
|
|
}; |
114 |
|
|
|
115 |
|
|
} // namespace detail |
116 |
|
|
|
117 |
|
|
enum class InstanceError { |
118 |
|
|
vulkan_unavailable, |
119 |
|
|
vulkan_version_unavailable, |
120 |
|
|
vulkan_version_1_1_unavailable, |
121 |
|
|
vulkan_version_1_2_unavailable, |
122 |
|
|
failed_create_instance, |
123 |
|
|
failed_create_debug_messenger, |
124 |
|
|
requested_layers_not_present, |
125 |
|
|
requested_extensions_not_present, |
126 |
|
|
windowing_extensions_not_present, |
127 |
|
|
}; |
128 |
|
|
enum class PhysicalDeviceError { |
129 |
|
|
no_surface_provided, |
130 |
|
|
failed_enumerate_physical_devices, |
131 |
|
|
no_physical_devices_found, |
132 |
|
|
no_suitable_device, |
133 |
|
|
}; |
134 |
|
|
enum class QueueError { |
135 |
|
|
present_unavailable, |
136 |
|
|
graphics_unavailable, |
137 |
|
|
compute_unavailable, |
138 |
|
|
transfer_unavailable, |
139 |
|
|
queue_index_out_of_range, |
140 |
|
|
invalid_queue_family_index |
141 |
|
|
}; |
142 |
|
|
enum class DeviceError { |
143 |
|
|
failed_create_device, |
144 |
|
|
}; |
145 |
|
|
enum class SwapchainError { |
146 |
|
|
surface_handle_not_provided, |
147 |
|
|
failed_query_surface_support_details, |
148 |
|
|
failed_create_swapchain, |
149 |
|
|
failed_get_swapchain_images, |
150 |
|
|
failed_create_swapchain_image_views, |
151 |
|
|
}; |
152 |
|
|
|
153 |
|
|
std::error_code make_error_code (InstanceError instance_error); |
154 |
|
|
std::error_code make_error_code (PhysicalDeviceError physical_device_error); |
155 |
|
|
std::error_code make_error_code (QueueError queue_error); |
156 |
|
|
std::error_code make_error_code (DeviceError device_error); |
157 |
|
|
std::error_code make_error_code (SwapchainError swapchain_error); |
158 |
|
|
|
159 |
|
|
const char* to_string_message_severity (VkDebugUtilsMessageSeverityFlagBitsEXT s); |
160 |
|
|
const char* to_string_message_type (VkDebugUtilsMessageTypeFlagsEXT s); |
161 |
|
|
|
162 |
|
|
const char* to_string (InstanceError err); |
163 |
|
|
const char* to_string (PhysicalDeviceError err); |
164 |
|
|
const char* to_string (QueueError err); |
165 |
|
|
const char* to_string (DeviceError err); |
166 |
|
|
const char* to_string (SwapchainError err); |
167 |
|
|
|
168 |
|
|
// Gathers useful information about the available vulkan capabilities, like layers and instance extensions. |
169 |
|
|
// Use this for enabling features conditionally, ie if you would like an extension but can use a fallback if |
170 |
|
|
// it isn't supported but need to know if support is available first. |
171 |
|
|
struct SystemInfo { |
172 |
|
|
private: |
173 |
|
|
SystemInfo (); |
174 |
|
|
|
175 |
|
|
public: |
176 |
|
|
// Use get_system_info to create a SystemInfo struct. This is because loading vulkan could fail. |
177 |
|
|
static detail::Result<SystemInfo> get_system_info (); |
178 |
|
|
static detail::Result<SystemInfo> get_system_info (PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr); |
179 |
|
|
|
180 |
|
|
// Returns true if a layer is available |
181 |
|
|
bool is_layer_available (const char* layer_name) const; |
182 |
|
|
// Returns true if an extension is available |
183 |
|
|
bool is_extension_available (const char* extension_name) const; |
184 |
|
|
|
185 |
|
|
std::vector<VkLayerProperties> available_layers; |
186 |
|
|
std::vector<VkExtensionProperties> available_extensions; |
187 |
|
|
bool validation_layers_available = false; |
188 |
|
|
bool debug_utils_available = false; |
189 |
|
|
}; |
190 |
|
|
|
191 |
|
|
|
192 |
|
|
class InstanceBuilder; |
193 |
|
|
class PhysicalDeviceSelector; |
194 |
|
|
|
195 |
|
|
struct Instance { |
196 |
|
|
VkInstance instance = VK_NULL_HANDLE; |
197 |
|
|
VkDebugUtilsMessengerEXT debug_messenger = VK_NULL_HANDLE; |
198 |
|
|
VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; |
199 |
|
|
|
200 |
|
|
PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr = nullptr; |
201 |
|
|
|
202 |
|
|
private: |
203 |
|
|
bool headless = false; |
204 |
|
|
uint32_t instance_version = VK_MAKE_VERSION (1, 0, 0); |
205 |
|
|
|
206 |
|
|
friend class InstanceBuilder; |
207 |
|
|
friend class PhysicalDeviceSelector; |
208 |
|
|
}; |
209 |
|
|
|
210 |
|
|
void destroy_instance (Instance instance); // release instance resources |
211 |
|
|
|
212 |
|
|
class InstanceBuilder { |
213 |
|
|
public: |
214 |
|
|
// Default constructor, will load vulkan. |
215 |
|
|
explicit InstanceBuilder (); |
216 |
|
|
// Optional: Can use your own PFN_vkGetInstanceProcAddr |
217 |
|
|
explicit InstanceBuilder (PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr); |
218 |
|
|
|
219 |
|
|
// Create a VkInstance. Return an error if it failed. |
220 |
|
|
detail::Result<Instance> build () const; |
221 |
|
|
|
222 |
|
|
// Sets the name of the application. Defaults to "" if none is provided. |
223 |
|
|
InstanceBuilder& set_app_name (const char* app_name); |
224 |
|
|
// Sets the name of the engine. Defaults to "" if none is provided. |
225 |
|
|
InstanceBuilder& set_engine_name (const char* engine_name); |
226 |
|
|
// Sets the (major, minor, patch) version of the application. |
227 |
|
|
InstanceBuilder& set_app_version (uint32_t major, uint32_t minor, uint32_t patch = 0); |
228 |
|
|
// Sets the (major, minor, patch) version of the engine. |
229 |
|
|
InstanceBuilder& set_engine_version (uint32_t major, uint32_t minor, uint32_t patch = 0); |
230 |
|
|
// Require a vulkan instance API version. Will fail to create if this version isn't available. |
231 |
|
|
InstanceBuilder& require_api_version (uint32_t major, uint32_t minor, uint32_t patch = 0); |
232 |
|
|
// Prefer a vulkan instance API version. If the desired version isn't available, it will use the highest version available. |
233 |
|
|
InstanceBuilder& desire_api_version (uint32_t major, uint32_t minor, uint32_t patch = 0); |
234 |
|
|
|
235 |
|
|
// Adds a layer to be enabled. Will fail to create an instance if the layer isn't available. |
236 |
|
|
InstanceBuilder& enable_layer (const char* layer_name); |
237 |
|
|
// Adds an extension to be enabled. Will fail to create an instance if the extension isn't available. |
238 |
|
|
InstanceBuilder& enable_extension (const char* extension_name); |
239 |
|
|
|
240 |
|
|
// Headless Mode does not load the required extensions for presentation. Defaults to true. |
241 |
|
|
InstanceBuilder& set_headless (bool headless = true); |
242 |
|
|
|
243 |
|
|
// Enables the validation layers. Will fail to create an instance if the validation layers aren't available. |
244 |
|
|
InstanceBuilder& enable_validation_layers (bool require_validation = true); |
245 |
|
|
// Checks if the validation layers are available and loads them if they are. |
246 |
|
|
InstanceBuilder& request_validation_layers (bool enable_validation = true); |
247 |
|
|
|
248 |
|
|
// Use a default debug callback that prints to standard out. |
249 |
|
|
InstanceBuilder& use_default_debug_messenger (); |
250 |
|
|
// Provide a user defined debug callback. |
251 |
|
|
InstanceBuilder& set_debug_callback (PFN_vkDebugUtilsMessengerCallbackEXT callback); |
252 |
|
|
// Set what message severity is needed to trigger the callback. |
253 |
|
|
InstanceBuilder& set_debug_messenger_severity (VkDebugUtilsMessageSeverityFlagsEXT severity); |
254 |
|
|
// Add a message severity to the list that triggers the callback. |
255 |
|
|
InstanceBuilder& add_debug_messenger_severity (VkDebugUtilsMessageSeverityFlagsEXT severity); |
256 |
|
|
// Set what message type triggers the callback. |
257 |
|
|
InstanceBuilder& set_debug_messenger_type (VkDebugUtilsMessageTypeFlagsEXT type); |
258 |
|
|
// Add a message type to the list of that triggers the callback. |
259 |
|
|
InstanceBuilder& add_debug_messenger_type (VkDebugUtilsMessageTypeFlagsEXT type); |
260 |
|
|
|
261 |
|
|
// Disable some validation checks. |
262 |
|
|
// Checks: All, and Shaders |
263 |
|
|
InstanceBuilder& add_validation_disable (VkValidationCheckEXT check); |
264 |
|
|
|
265 |
|
|
// Enables optional parts of the validation layers. |
266 |
|
|
// Parts: best practices, gpu assisted, and gpu assisted reserve binding slot. |
267 |
|
|
InstanceBuilder& add_validation_feature_enable (VkValidationFeatureEnableEXT enable); |
268 |
|
|
|
269 |
|
|
// Disables sections of the validation layers. |
270 |
|
|
// Options: All, shaders, thread safety, api parameters, object lifetimes, core checks, and unique handles. |
271 |
|
|
InstanceBuilder& add_validation_feature_disable (VkValidationFeatureDisableEXT disable); |
272 |
|
|
|
273 |
|
|
// Provide custom allocation callbacks. |
274 |
|
|
InstanceBuilder& set_allocation_callbacks (VkAllocationCallbacks* callbacks); |
275 |
|
|
|
276 |
|
|
private: |
277 |
|
|
struct InstanceInfo { |
278 |
|
|
// VkApplicationInfo |
279 |
|
|
const char* app_name = nullptr; |
280 |
|
|
const char* engine_name = nullptr; |
281 |
|
|
uint32_t application_version = 0; |
282 |
|
|
uint32_t engine_version = 0; |
283 |
|
|
uint32_t required_api_version = VK_MAKE_VERSION (1, 0, 0); |
284 |
|
|
uint32_t desired_api_version = VK_MAKE_VERSION (1, 0, 0); |
285 |
|
|
|
286 |
|
|
// VkInstanceCreateInfo |
287 |
|
|
std::vector<const char*> layers; |
288 |
|
|
std::vector<const char*> extensions; |
289 |
|
|
VkInstanceCreateFlags flags = 0; |
290 |
|
|
std::vector<VkBaseOutStructure*> pNext_elements; |
291 |
|
|
|
292 |
|
|
// debug callback |
293 |
|
|
PFN_vkDebugUtilsMessengerCallbackEXT debug_callback = nullptr; |
294 |
|
|
VkDebugUtilsMessageSeverityFlagsEXT debug_message_severity = |
295 |
|
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; |
296 |
|
|
VkDebugUtilsMessageTypeFlagsEXT debug_message_type = |
297 |
|
|
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | |
298 |
|
|
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; |
299 |
|
|
|
300 |
|
|
// validation features |
301 |
|
|
std::vector<VkValidationCheckEXT> disabled_validation_checks; |
302 |
|
|
std::vector<VkValidationFeatureEnableEXT> enabled_validation_features; |
303 |
|
|
std::vector<VkValidationFeatureDisableEXT> disabled_validation_features; |
304 |
|
|
|
305 |
|
|
// Custom allocator |
306 |
|
|
VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; |
307 |
|
|
|
308 |
|
|
bool request_validation_layers = false; |
309 |
|
|
bool enable_validation_layers = false; |
310 |
|
|
bool use_debug_messenger = false; |
311 |
|
|
bool headless_context = false; |
312 |
|
|
|
313 |
|
|
PFN_vkGetInstanceProcAddr fp_vkGetInstanceProcAddr = nullptr; |
314 |
|
|
} info; |
315 |
|
|
}; |
316 |
|
|
|
317 |
|
|
VKAPI_ATTR VkBool32 VKAPI_CALL default_debug_callback (VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, |
318 |
|
|
VkDebugUtilsMessageTypeFlagsEXT messageType, |
319 |
|
|
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData, |
320 |
|
|
void* pUserData); |
321 |
|
|
|
322 |
|
|
void destroy_debug_utils_messenger(VkInstance const instance, VkDebugUtilsMessengerEXT const messenger, VkAllocationCallbacks* allocation_callbacks = nullptr); |
323 |
|
|
|
324 |
|
|
// ---- Physical Device ---- // |
325 |
|
|
class PhysicalDeviceSelector; |
326 |
|
|
class DeviceBuilder; |
327 |
|
|
|
328 |
|
|
struct PhysicalDevice { |
329 |
|
|
VkPhysicalDevice physical_device = VK_NULL_HANDLE; |
330 |
|
|
VkSurfaceKHR surface = VK_NULL_HANDLE; |
331 |
|
|
|
332 |
|
|
VkPhysicalDeviceFeatures features{}; |
333 |
|
|
VkPhysicalDeviceProperties properties{}; |
334 |
|
|
VkPhysicalDeviceMemoryProperties memory_properties{}; |
335 |
|
|
|
336 |
|
|
// Has a queue family that supports compute operations but not graphics nor transfer. |
337 |
|
|
bool has_dedicated_compute_queue () const; |
338 |
|
|
// Has a queue family that supports transfer operations but not graphics nor compute. |
339 |
|
|
bool has_dedicated_transfer_queue () const; |
340 |
|
|
|
341 |
|
|
// Has a queue family that supports transfer operations but not graphics. |
342 |
|
|
bool has_separate_compute_queue () const; |
343 |
|
|
// Has a queue family that supports transfer operations but not graphics. |
344 |
|
|
bool has_separate_transfer_queue () const; |
345 |
|
|
|
346 |
|
|
// Advanced: Get the VkQueueFamilyProperties of the device if special queue setup is needed |
347 |
|
|
std::vector<VkQueueFamilyProperties> get_queue_families () const; |
348 |
|
|
|
349 |
|
|
private: |
350 |
|
|
std::vector<const char*> extensions_to_enable; |
351 |
|
|
std::vector<VkQueueFamilyProperties> queue_families; |
352 |
|
|
bool defer_surface_initialization = false; |
353 |
|
|
friend class PhysicalDeviceSelector; |
354 |
|
|
friend class DeviceBuilder; |
355 |
|
|
}; |
356 |
|
|
|
357 |
|
|
enum class PreferredDeviceType { |
358 |
|
|
other = 0, |
359 |
|
|
integrated = 1, |
360 |
|
|
discrete = 2, |
361 |
|
|
virtual_gpu = 3, |
362 |
|
|
cpu = 4 |
363 |
|
|
}; |
364 |
|
|
|
365 |
|
|
class PhysicalDeviceSelector { |
366 |
|
|
public: |
367 |
|
|
// Requires a vkb::Instance to construct, needed to pass instance creation info. |
368 |
|
|
explicit PhysicalDeviceSelector (Instance const& instance); |
369 |
|
|
|
370 |
|
|
detail::Result<PhysicalDevice> select () const; |
371 |
|
|
|
372 |
|
|
// Set the surface in which the physical device should render to. |
373 |
|
|
PhysicalDeviceSelector& set_surface (VkSurfaceKHR surface); |
374 |
|
|
// Set the desired physical device type to select. Defaults to PreferredDeviceType::discrete. |
375 |
|
|
PhysicalDeviceSelector& prefer_gpu_device_type (PreferredDeviceType type = PreferredDeviceType::discrete); |
376 |
|
|
// Allow selection of a gpu device type that isn't the preferred physical device type. Defaults to true. |
377 |
|
|
PhysicalDeviceSelector& allow_any_gpu_device_type (bool allow_any_type = true); |
378 |
|
|
|
379 |
|
|
// Require that a physical device supports presentation. Defaults to true. |
380 |
|
|
PhysicalDeviceSelector& require_present (bool require = true); |
381 |
|
|
|
382 |
|
|
// Require a queue family that supports compute operations but not graphics nor transfer. |
383 |
|
|
PhysicalDeviceSelector& require_dedicated_compute_queue (); |
384 |
|
|
// Require a queue family that supports transfer operations but not graphics nor compute. |
385 |
|
|
PhysicalDeviceSelector& require_dedicated_transfer_queue (); |
386 |
|
|
|
387 |
|
|
// Require a queue family that supports compute operations but not graphics. |
388 |
|
|
PhysicalDeviceSelector& require_separate_compute_queue (); |
389 |
|
|
// Require a queue family that supports transfer operations but not graphics. |
390 |
|
|
PhysicalDeviceSelector& require_separate_transfer_queue (); |
391 |
|
|
|
392 |
|
|
// Require a memory heap from VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT with `size` memory available. |
393 |
|
|
PhysicalDeviceSelector& required_device_memory_size (VkDeviceSize size); |
394 |
|
|
// Prefer a memory heap from VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT with `size` memory available. |
395 |
|
|
PhysicalDeviceSelector& desired_device_memory_size (VkDeviceSize size); |
396 |
|
|
|
397 |
|
|
// Require a physical device which supports a specific extension. |
398 |
|
|
PhysicalDeviceSelector& add_required_extension (const char* extension); |
399 |
|
|
// Require a physical device which supports a set of extensions. |
400 |
|
|
PhysicalDeviceSelector& add_required_extensions (std::vector<const char*> extensions); |
401 |
|
|
|
402 |
|
|
// Prefer a physical device which supports a specific extension. |
403 |
|
|
PhysicalDeviceSelector& add_desired_extension (const char* extension); |
404 |
|
|
// Prefer a physical device which supports a set of extensions. |
405 |
|
|
PhysicalDeviceSelector& add_desired_extensions (std::vector<const char*> extensions); |
406 |
|
|
|
407 |
|
|
// Prefer a physical device that supports a (major, minor) version of vulkan. |
408 |
|
|
PhysicalDeviceSelector& set_desired_version (uint32_t major, uint32_t minor); |
409 |
|
|
// Require a physical device that supports a (major, minor) version of vulkan. |
410 |
|
|
PhysicalDeviceSelector& set_minimum_version (uint32_t major, uint32_t minor); |
411 |
|
|
|
412 |
|
|
// Require a physical device which supports the features in VkPhysicalDeviceFeatures. |
413 |
|
|
PhysicalDeviceSelector& set_required_features (VkPhysicalDeviceFeatures features); |
414 |
|
|
|
415 |
|
|
// Used when surface creation happens after physical device selection. |
416 |
|
|
// Warning: This disables checking if the physical device supports a given surface. |
417 |
|
|
PhysicalDeviceSelector& defer_surface_initialization (); |
418 |
|
|
|
419 |
|
|
// Ignore all criteria and choose the first physical device that is available. |
420 |
|
|
// Only use when: The first gpu in the list may be set by global user preferences and an application may wish to respect it. |
421 |
|
|
PhysicalDeviceSelector& select_first_device_unconditionally (bool unconditionally = true); |
422 |
|
|
|
423 |
|
|
private: |
424 |
|
|
struct SystemInfo { |
425 |
|
|
VkInstance instance = VK_NULL_HANDLE; |
426 |
|
|
VkSurfaceKHR surface = VK_NULL_HANDLE; |
427 |
|
|
bool headless = false; |
428 |
|
|
} system_info; |
429 |
|
|
|
430 |
|
|
struct PhysicalDeviceDesc { |
431 |
|
|
VkPhysicalDevice phys_device = VK_NULL_HANDLE; |
432 |
|
|
std::vector<VkQueueFamilyProperties> queue_families; |
433 |
|
|
|
434 |
|
|
VkPhysicalDeviceFeatures device_features{}; |
435 |
|
|
VkPhysicalDeviceProperties device_properties{}; |
436 |
|
|
VkPhysicalDeviceMemoryProperties mem_properties{}; |
437 |
|
|
}; |
438 |
|
|
PhysicalDeviceDesc populate_device_details (VkPhysicalDevice phys_device) const; |
439 |
|
|
|
440 |
|
|
struct SelectionCriteria { |
441 |
|
|
PreferredDeviceType preferred_type = PreferredDeviceType::discrete; |
442 |
|
|
bool allow_any_type = true; |
443 |
|
|
bool require_present = true; |
444 |
|
|
bool require_dedicated_transfer_queue = false; |
445 |
|
|
bool require_dedicated_compute_queue = false; |
446 |
|
|
bool require_separate_transfer_queue = false; |
447 |
|
|
bool require_separate_compute_queue = false; |
448 |
|
|
VkDeviceSize required_mem_size = 0; |
449 |
|
|
VkDeviceSize desired_mem_size = 0; |
450 |
|
|
|
451 |
|
|
std::vector<const char*> required_extensions; |
452 |
|
|
std::vector<const char*> desired_extensions; |
453 |
|
|
|
454 |
|
|
uint32_t required_version = VK_MAKE_VERSION (1, 0, 0); |
455 |
|
|
uint32_t desired_version = VK_MAKE_VERSION (1, 0, 0); |
456 |
|
|
|
457 |
|
|
VkPhysicalDeviceFeatures required_features{}; |
458 |
|
|
|
459 |
|
|
bool defer_surface_initialization = false; |
460 |
|
|
bool use_first_gpu_unconditionally = false; |
461 |
|
|
} criteria; |
462 |
|
|
|
463 |
|
|
enum class Suitable { yes, partial, no }; |
464 |
|
|
|
465 |
|
|
Suitable is_device_suitable (PhysicalDeviceDesc phys_device) const; |
466 |
|
|
}; |
467 |
|
|
|
468 |
|
|
// ---- Queue ---- // |
469 |
|
|
enum class QueueType { present, graphics, compute, transfer }; |
470 |
|
|
|
471 |
|
|
// ---- Device ---- // |
472 |
|
|
|
473 |
|
|
struct Device { |
474 |
|
|
VkDevice device = VK_NULL_HANDLE; |
475 |
|
|
PhysicalDevice physical_device; |
476 |
|
|
VkSurfaceKHR surface = VK_NULL_HANDLE; |
477 |
|
|
std::vector<VkQueueFamilyProperties> queue_families; |
478 |
|
|
VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; |
479 |
|
|
|
480 |
|
|
detail::Result<uint32_t> get_queue_index (QueueType type) const; |
481 |
|
|
// Only a compute or transfer queue type is valid. All other queue types do not support a 'dedicated' queue index |
482 |
|
|
detail::Result<uint32_t> get_dedicated_queue_index (QueueType type) const; |
483 |
|
|
|
484 |
|
|
detail::Result<VkQueue> get_queue (QueueType type) const; |
485 |
|
|
// Only a compute or transfer queue type is valid. All other queue types do not support a 'dedicated' queue |
486 |
|
|
detail::Result<VkQueue> get_dedicated_queue (QueueType type) const; |
487 |
|
|
}; |
488 |
|
|
|
489 |
|
|
// For advanced device queue setup |
490 |
|
|
struct CustomQueueDescription { |
491 |
|
|
explicit CustomQueueDescription (uint32_t index, uint32_t count, std::vector<float> priorities); |
492 |
|
|
uint32_t index = 0; |
493 |
|
|
uint32_t count = 0; |
494 |
|
|
std::vector<float> priorities; |
495 |
|
|
}; |
496 |
|
|
|
497 |
|
|
void destroy_device (Device device); |
498 |
|
|
|
499 |
|
|
class DeviceBuilder { |
500 |
|
|
public: |
501 |
|
|
// Any features and extensions that are requested/required in PhysicalDeviceSelector are automatically enabled. |
502 |
|
|
explicit DeviceBuilder (PhysicalDevice physical_device); |
503 |
|
|
|
504 |
|
|
detail::Result<Device> build () const; |
505 |
|
|
|
506 |
|
|
// For Advanced Users: specify the exact list of VkDeviceQueueCreateInfo's needed for the application. |
507 |
|
|
// If a custom queue setup is provided, getting the queues and queue indexes is up to the application. |
508 |
|
|
DeviceBuilder& custom_queue_setup (std::vector<CustomQueueDescription> queue_descriptions); |
509 |
|
|
|
510 |
|
|
// Add a structure to the pNext chain of VkDeviceCreateInfo. |
511 |
|
|
// The structure must be valid when DeviceBuilder::build() is called. |
512 |
|
|
template <typename T> DeviceBuilder& add_pNext (T* structure) { |
513 |
|
|
info.pNext_chain.push_back (reinterpret_cast<VkBaseOutStructure*> (structure)); |
514 |
|
|
return *this; |
515 |
|
|
} |
516 |
|
|
|
517 |
|
|
// Provide custom allocation callbacks. |
518 |
|
|
DeviceBuilder& set_allocation_callbacks (VkAllocationCallbacks* callbacks); |
519 |
|
|
|
520 |
|
|
private: |
521 |
|
|
struct DeviceInfo { |
522 |
|
|
VkDeviceCreateFlags flags = 0; |
523 |
|
|
std::vector<VkBaseOutStructure*> pNext_chain; |
524 |
|
|
PhysicalDevice physical_device; |
525 |
|
|
VkSurfaceKHR surface = VK_NULL_HANDLE; |
526 |
|
|
bool defer_surface_initialization = false; |
527 |
|
|
std::vector<VkQueueFamilyProperties> queue_families; |
528 |
|
|
VkPhysicalDeviceFeatures features{}; |
529 |
|
|
std::vector<const char*> extensions_to_enable; |
530 |
|
|
std::vector<CustomQueueDescription> queue_descriptions; |
531 |
|
|
VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; |
532 |
|
|
} info; |
533 |
|
|
}; |
534 |
|
|
|
535 |
|
|
// ---- Swapchain ---- // |
536 |
|
|
struct Swapchain { |
537 |
|
|
VkDevice device = VK_NULL_HANDLE; |
538 |
|
|
VkSwapchainKHR swapchain = VK_NULL_HANDLE; |
539 |
|
|
uint32_t image_count = 0; |
540 |
|
|
VkFormat image_format = VK_FORMAT_UNDEFINED; |
541 |
|
|
VkExtent2D extent = { 0, 0 }; |
542 |
|
|
VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; |
543 |
|
|
|
544 |
|
|
// Returns a vector of VkImage handles to the swapchain. |
545 |
|
|
detail::Result<std::vector<VkImage>> get_images (); |
546 |
|
|
|
547 |
|
|
// Returns a vector of VkImageView's to the VkImage's of the swapchain. |
548 |
|
|
// VkImageViews must be destroyed. |
549 |
|
|
detail::Result<std::vector<VkImageView>> get_image_views (); |
550 |
|
|
void destroy_image_views (std::vector<VkImageView> const& image_views); |
551 |
|
|
}; |
552 |
|
|
|
553 |
|
|
void destroy_swapchain (Swapchain const& swapchain); |
554 |
|
|
|
555 |
|
|
class SwapchainBuilder { |
556 |
|
|
public: |
557 |
|
|
explicit SwapchainBuilder (Device const& device); |
558 |
|
|
explicit SwapchainBuilder (Device const& device, VkSurfaceKHR const surface); |
559 |
|
|
explicit SwapchainBuilder (VkPhysicalDevice const physical_device, VkDevice const device, VkSurfaceKHR const surface, int32_t graphics_queue_index = -1, int32_t present_queue_index = -1); |
560 |
|
|
|
561 |
|
|
detail::Result<Swapchain> build () const; |
562 |
|
|
|
563 |
|
|
// Set the oldSwapchain member of VkSwapchainCreateInfoKHR. |
564 |
|
|
// For use in rebuilding a swapchain. |
565 |
|
|
SwapchainBuilder& set_old_swapchain (VkSwapchainKHR old_swapchain); |
566 |
|
|
SwapchainBuilder& set_old_swapchain (Swapchain const& swapchain); |
567 |
|
|
|
568 |
|
|
|
569 |
|
|
// Desired size of the swapchain. By default, the swapchain will use the size |
570 |
|
|
// of the window being drawn to. |
571 |
|
|
SwapchainBuilder& set_desired_extent (uint32_t width, uint32_t height); |
572 |
|
|
|
573 |
|
|
// When determining the surface format, make this the first to be used if supported. |
574 |
|
|
SwapchainBuilder& set_desired_format (VkSurfaceFormatKHR format); |
575 |
|
|
// Add this swapchain format to the end of the list of formats selected from. |
576 |
|
|
SwapchainBuilder& add_fallback_format (VkSurfaceFormatKHR format); |
577 |
|
|
// Use the default swapchain formats. This is done if no formats are provided. |
578 |
|
|
SwapchainBuilder& use_default_format_selection (); |
579 |
|
|
|
580 |
|
|
// When determining the present mode, make this the first to be used if supported. |
581 |
|
|
SwapchainBuilder& set_desired_present_mode (VkPresentModeKHR present_mode); |
582 |
|
|
// Add this present mode to the end of the list of present modes selected from. |
583 |
|
|
SwapchainBuilder& add_fallback_present_mode (VkPresentModeKHR present_mode); |
584 |
|
|
// Use the default presentation mode. This is done if no present modes are provided. |
585 |
|
|
SwapchainBuilder& use_default_present_mode_selection (); |
586 |
|
|
|
587 |
|
|
// Set the bitmask of the image usage for acquired swapchain images. |
588 |
|
|
SwapchainBuilder& set_image_usage_flags (VkImageUsageFlags usage_flags); |
589 |
|
|
// Add a image usage to the bitmask for acquired swapchain images. |
590 |
|
|
SwapchainBuilder& add_image_usage_flags (VkImageUsageFlags usage_flags); |
591 |
|
|
// Use the default image usage bitmask values. This is the default if no image usages |
592 |
|
|
// are provided. The default is VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT |
593 |
|
|
SwapchainBuilder& use_default_image_usage_flags (); |
594 |
|
|
|
595 |
|
|
// Set the number of views in for multiview/stereo surface |
596 |
|
|
SwapchainBuilder& set_image_array_layer_count (uint32_t array_layer_count); |
597 |
|
|
|
598 |
|
|
// Set whether the Vulkan implementation is allowed to discard rendering operations that |
599 |
|
|
// affect regions of the surface that are not visible. Default is true. |
600 |
|
|
// Note: Applications should use the default of true if they do not expect to read back the content |
601 |
|
|
// of presentable images before presenting them or after reacquiring them, and if their fragment |
602 |
|
|
// shaders do not have any side effects that require them to run for all pixels in the presentable image. |
603 |
|
|
SwapchainBuilder& set_clipped (bool clipped = true); |
604 |
|
|
|
605 |
|
|
// Set the VkSwapchainCreateFlagBitsKHR. |
606 |
|
|
SwapchainBuilder& set_create_flags (VkSwapchainCreateFlagBitsKHR create_flags); |
607 |
|
|
// Set the transform to be applied, like a 90 degree rotation. Default is the current transform. |
608 |
|
|
SwapchainBuilder& set_pre_transform_flags (VkSurfaceTransformFlagBitsKHR pre_transform_flags); |
609 |
|
|
// Set the alpha channel to be used with other windows in on the system. Default is VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR. |
610 |
|
|
SwapchainBuilder& set_composite_alpha_flags (VkCompositeAlphaFlagBitsKHR composite_alpha_flags); |
611 |
|
|
|
612 |
|
|
// Add a structure to the pNext chain of VkSwapchainCreateInfoKHR. |
613 |
|
|
// The structure must be valid when SwapchainBuilder::build() is called. |
614 |
|
|
template <typename T> SwapchainBuilder& add_pNext (T* structure) { |
615 |
|
|
info.pNext_chain.push_back (reinterpret_cast<VkBaseOutStructure*> (structure)); |
616 |
|
|
return *this; |
617 |
|
|
} |
618 |
|
|
|
619 |
|
|
// Provide custom allocation callbacks. |
620 |
|
|
SwapchainBuilder& set_allocation_callbacks (VkAllocationCallbacks* callbacks); |
621 |
|
|
|
622 |
|
|
private: |
623 |
|
|
void add_desired_formats (std::vector<VkSurfaceFormatKHR>& formats) const; |
624 |
|
|
void add_desired_present_modes (std::vector<VkPresentModeKHR>& modes) const; |
625 |
|
|
|
626 |
|
|
struct SwapchainInfo { |
627 |
|
|
VkPhysicalDevice physical_device = VK_NULL_HANDLE; |
628 |
|
|
VkDevice device = VK_NULL_HANDLE; |
629 |
|
|
std::vector<VkBaseOutStructure*> pNext_chain; |
630 |
|
|
VkSwapchainCreateFlagBitsKHR create_flags = static_cast<VkSwapchainCreateFlagBitsKHR> (0); |
631 |
|
|
VkSurfaceKHR surface = VK_NULL_HANDLE; |
632 |
|
|
std::vector<VkSurfaceFormatKHR> desired_formats; |
633 |
|
|
uint32_t desired_width = 256; |
634 |
|
|
uint32_t desired_height = 256; |
635 |
|
|
uint32_t array_layer_count = 1; |
636 |
|
|
VkImageUsageFlags image_usage_flags = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; |
637 |
|
|
uint32_t graphics_queue_index = 0; |
638 |
|
|
uint32_t present_queue_index = 0; |
639 |
|
|
VkSurfaceTransformFlagBitsKHR pre_transform = static_cast<VkSurfaceTransformFlagBitsKHR> (0); |
640 |
|
|
VkCompositeAlphaFlagBitsKHR composite_alpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; |
641 |
|
|
std::vector<VkPresentModeKHR> desired_present_modes; |
642 |
|
|
bool clipped = true; |
643 |
|
|
VkSwapchainKHR old_swapchain = VK_NULL_HANDLE; |
644 |
|
|
VkAllocationCallbacks* allocation_callbacks = VK_NULL_HANDLE; |
645 |
|
|
} info; |
646 |
|
|
}; |
647 |
|
|
|
648 |
|
|
} // namespace vkb |
649 |
|
|
|
650 |
|
|
|
651 |
|
|
namespace std { |
652 |
|
|
template <> struct is_error_code_enum<vkb::InstanceError> : true_type {}; |
653 |
|
|
template <> struct is_error_code_enum<vkb::PhysicalDeviceError> : true_type {}; |
654 |
|
|
template <> struct is_error_code_enum<vkb::QueueError> : true_type {}; |
655 |
|
|
template <> struct is_error_code_enum<vkb::DeviceError> : true_type {}; |
656 |
|
|
template <> struct is_error_code_enum<vkb::SwapchainError> : true_type {}; |
657 |
|
|
} // namespace std |
658 |
|
|
|