1 | /**************************************************************************/ |
2 | /* vulkan_context.cpp */ |
3 | /**************************************************************************/ |
4 | /* This file is part of: */ |
5 | /* GODOT ENGINE */ |
6 | /* https://godotengine.org */ |
7 | /**************************************************************************/ |
8 | /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ |
9 | /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ |
10 | /* */ |
11 | /* Permission is hereby granted, free of charge, to any person obtaining */ |
12 | /* a copy of this software and associated documentation files (the */ |
13 | /* "Software"), to deal in the Software without restriction, including */ |
14 | /* without limitation the rights to use, copy, modify, merge, publish, */ |
15 | /* distribute, sublicense, and/or sell copies of the Software, and to */ |
16 | /* permit persons to whom the Software is furnished to do so, subject to */ |
17 | /* the following conditions: */ |
18 | /* */ |
19 | /* The above copyright notice and this permission notice shall be */ |
20 | /* included in all copies or substantial portions of the Software. */ |
21 | /* */ |
22 | /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ |
23 | /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ |
24 | /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ |
25 | /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ |
26 | /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ |
27 | /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ |
28 | /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ |
29 | /**************************************************************************/ |
30 | |
31 | #include "vulkan_context.h" |
32 | |
33 | #include "core/config/engine.h" |
34 | #include "core/config/project_settings.h" |
35 | #include "core/string/ustring.h" |
36 | #include "core/templates/local_vector.h" |
37 | #include "core/version.h" |
38 | #include "servers/rendering/rendering_device.h" |
39 | |
40 | #include "vk_enum_string_helper.h" |
41 | |
42 | #include <stdio.h> |
43 | #include <stdlib.h> |
44 | #include <string.h> |
45 | |
46 | #define ARRAY_SIZE(a) (sizeof(a) / sizeof(a[0])) |
47 | #define APP_SHORT_NAME "GodotEngine" |
48 | |
49 | VulkanHooks *VulkanContext::vulkan_hooks = nullptr; |
50 | |
51 | Vector<VkAttachmentReference> VulkanContext::_convert_VkAttachmentReference2(uint32_t p_count, const VkAttachmentReference2 *p_refs) { |
52 | Vector<VkAttachmentReference> att_refs; |
53 | |
54 | if (p_refs != nullptr) { |
55 | for (uint32_t i = 0; i < p_count; i++) { |
56 | // We lose aspectMask in this conversion but we don't use it currently. |
57 | |
58 | VkAttachmentReference ref = { |
59 | p_refs[i].attachment, /* attachment */ |
60 | p_refs[i].layout /* layout */ |
61 | }; |
62 | |
63 | att_refs.push_back(ref); |
64 | } |
65 | } |
66 | |
67 | return att_refs; |
68 | } |
69 | |
70 | VkResult VulkanContext::vkCreateRenderPass2KHR(VkDevice p_device, const VkRenderPassCreateInfo2 *p_create_info, const VkAllocationCallbacks *p_allocator, VkRenderPass *p_render_pass) { |
71 | if (is_device_extension_enabled(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)) { |
72 | if (fpCreateRenderPass2KHR == nullptr) { |
73 | fpCreateRenderPass2KHR = (PFN_vkCreateRenderPass2KHR)vkGetDeviceProcAddr(p_device, "vkCreateRenderPass2KHR" ); |
74 | } |
75 | |
76 | if (fpCreateRenderPass2KHR == nullptr) { |
77 | return VK_ERROR_EXTENSION_NOT_PRESENT; |
78 | } else { |
79 | return (fpCreateRenderPass2KHR)(p_device, p_create_info, p_allocator, p_render_pass); |
80 | } |
81 | } else { |
82 | // need to fall back on vkCreateRenderPass |
83 | |
84 | const void *next = p_create_info->pNext; // ATM we only support multiview which should work if supported. |
85 | |
86 | Vector<VkAttachmentDescription> attachments; |
87 | for (uint32_t i = 0; i < p_create_info->attachmentCount; i++) { |
88 | // Basically the old layout just misses type and next. |
89 | VkAttachmentDescription att = { |
90 | p_create_info->pAttachments[i].flags, /* flags */ |
91 | p_create_info->pAttachments[i].format, /* format */ |
92 | p_create_info->pAttachments[i].samples, /* samples */ |
93 | p_create_info->pAttachments[i].loadOp, /* loadOp */ |
94 | p_create_info->pAttachments[i].storeOp, /* storeOp */ |
95 | p_create_info->pAttachments[i].stencilLoadOp, /* stencilLoadOp */ |
96 | p_create_info->pAttachments[i].stencilStoreOp, /* stencilStoreOp */ |
97 | p_create_info->pAttachments[i].initialLayout, /* initialLayout */ |
98 | p_create_info->pAttachments[i].finalLayout /* finalLayout */ |
99 | }; |
100 | |
101 | attachments.push_back(att); |
102 | } |
103 | |
104 | Vector<Vector<VkAttachmentReference>> attachment_references; |
105 | Vector<VkSubpassDescription> subpasses; |
106 | for (uint32_t i = 0; i < p_create_info->subpassCount; i++) { |
107 | // Here we need to do more, again it's just stripping out type and next |
108 | // but we have VkAttachmentReference2 to convert to VkAttachmentReference. |
109 | // Also viewmask is not supported but we don't use it outside of multiview. |
110 | |
111 | Vector<VkAttachmentReference> input_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].inputAttachmentCount, p_create_info->pSubpasses[i].pInputAttachments); |
112 | Vector<VkAttachmentReference> color_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].colorAttachmentCount, p_create_info->pSubpasses[i].pColorAttachments); |
113 | Vector<VkAttachmentReference> resolve_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].colorAttachmentCount, p_create_info->pSubpasses[i].pResolveAttachments); |
114 | Vector<VkAttachmentReference> depth_attachments = _convert_VkAttachmentReference2(p_create_info->pSubpasses[i].colorAttachmentCount, p_create_info->pSubpasses[i].pDepthStencilAttachment); |
115 | |
116 | VkSubpassDescription subpass = { |
117 | p_create_info->pSubpasses[i].flags, /* flags */ |
118 | p_create_info->pSubpasses[i].pipelineBindPoint, /* pipelineBindPoint */ |
119 | p_create_info->pSubpasses[i].inputAttachmentCount, /* inputAttachmentCount */ |
120 | input_attachments.size() == 0 ? nullptr : input_attachments.ptr(), /* pInputAttachments */ |
121 | p_create_info->pSubpasses[i].colorAttachmentCount, /* colorAttachmentCount */ |
122 | color_attachments.size() == 0 ? nullptr : color_attachments.ptr(), /* pColorAttachments */ |
123 | resolve_attachments.size() == 0 ? nullptr : resolve_attachments.ptr(), /* pResolveAttachments */ |
124 | depth_attachments.size() == 0 ? nullptr : depth_attachments.ptr(), /* pDepthStencilAttachment */ |
125 | p_create_info->pSubpasses[i].preserveAttachmentCount, /* preserveAttachmentCount */ |
126 | p_create_info->pSubpasses[i].pPreserveAttachments /* pPreserveAttachments */ |
127 | }; |
128 | attachment_references.push_back(input_attachments); |
129 | attachment_references.push_back(color_attachments); |
130 | attachment_references.push_back(resolve_attachments); |
131 | attachment_references.push_back(depth_attachments); |
132 | |
133 | subpasses.push_back(subpass); |
134 | } |
135 | |
136 | Vector<VkSubpassDependency> dependencies; |
137 | for (uint32_t i = 0; i < p_create_info->dependencyCount; i++) { |
138 | // We lose viewOffset here but again I don't believe we use this anywhere. |
139 | VkSubpassDependency dep = { |
140 | p_create_info->pDependencies[i].srcSubpass, /* srcSubpass */ |
141 | p_create_info->pDependencies[i].dstSubpass, /* dstSubpass */ |
142 | p_create_info->pDependencies[i].srcStageMask, /* srcStageMask */ |
143 | p_create_info->pDependencies[i].dstStageMask, /* dstStageMask */ |
144 | p_create_info->pDependencies[i].srcAccessMask, /* srcAccessMask */ |
145 | p_create_info->pDependencies[i].dstAccessMask, /* dstAccessMask */ |
146 | p_create_info->pDependencies[i].dependencyFlags, /* dependencyFlags */ |
147 | }; |
148 | |
149 | dependencies.push_back(dep); |
150 | } |
151 | |
152 | // CorrelatedViewMask is not supported in vkCreateRenderPass but we |
153 | // currently only use this for multiview. |
154 | // We'll need to look into this. |
155 | |
156 | VkRenderPassCreateInfo create_info = { |
157 | VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO, /* sType */ |
158 | next, /* pNext*/ |
159 | p_create_info->flags, /* flags */ |
160 | (uint32_t)attachments.size(), /* attachmentCount */ |
161 | attachments.ptr(), /* pAttachments */ |
162 | (uint32_t)subpasses.size(), /* subpassCount */ |
163 | subpasses.ptr(), /* pSubpasses */ |
164 | (uint32_t)dependencies.size(), /* */ |
165 | dependencies.ptr(), /* */ |
166 | }; |
167 | |
168 | return vkCreateRenderPass(device, &create_info, p_allocator, p_render_pass); |
169 | } |
170 | } |
171 | |
172 | VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_messenger_callback( |
173 | VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity, |
174 | VkDebugUtilsMessageTypeFlagsEXT messageType, |
175 | const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, |
176 | void *pUserData) { |
177 | // This error needs to be ignored because the AMD allocator will mix up memory types on IGP processors. |
178 | if (strstr(pCallbackData->pMessage, "Mapping an image with layout" ) != nullptr && |
179 | strstr(pCallbackData->pMessage, "can result in undefined behavior if this memory is used by the device" ) != nullptr) { |
180 | return VK_FALSE; |
181 | } |
182 | // This needs to be ignored because Validator is wrong here. |
183 | if (strstr(pCallbackData->pMessage, "Invalid SPIR-V binary version 1.3" ) != nullptr) { |
184 | return VK_FALSE; |
185 | } |
186 | // This needs to be ignored because Validator is wrong here. |
187 | if (strstr(pCallbackData->pMessage, "Shader requires flag" ) != nullptr) { |
188 | return VK_FALSE; |
189 | } |
190 | |
191 | // This needs to be ignored because Validator is wrong here. |
192 | if (strstr(pCallbackData->pMessage, "SPIR-V module not valid: Pointer operand" ) != nullptr && |
193 | strstr(pCallbackData->pMessage, "must be a memory object" ) != nullptr) { |
194 | return VK_FALSE; |
195 | } |
196 | |
197 | if (pCallbackData->pMessageIdName && strstr(pCallbackData->pMessageIdName, "UNASSIGNED-CoreValidation-DrawState-ClearCmdBeforeDraw" ) != nullptr) { |
198 | return VK_FALSE; |
199 | } |
200 | |
201 | String type_string; |
202 | switch (messageType) { |
203 | case (VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT): |
204 | type_string = "GENERAL" ; |
205 | break; |
206 | case (VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT): |
207 | type_string = "VALIDATION" ; |
208 | break; |
209 | case (VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT): |
210 | type_string = "PERFORMANCE" ; |
211 | break; |
212 | case (VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT & VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT): |
213 | type_string = "VALIDATION|PERFORMANCE" ; |
214 | break; |
215 | } |
216 | |
217 | String objects_string; |
218 | if (pCallbackData->objectCount > 0) { |
219 | objects_string = "\n\tObjects - " + String::num_int64(pCallbackData->objectCount); |
220 | for (uint32_t object = 0; object < pCallbackData->objectCount; ++object) { |
221 | objects_string += |
222 | "\n\t\tObject[" + String::num_int64(object) + "]" + |
223 | " - " + string_VkObjectType(pCallbackData->pObjects[object].objectType) + |
224 | ", Handle " + String::num_int64(pCallbackData->pObjects[object].objectHandle); |
225 | if (nullptr != pCallbackData->pObjects[object].pObjectName && strlen(pCallbackData->pObjects[object].pObjectName) > 0) { |
226 | objects_string += ", Name \"" + String(pCallbackData->pObjects[object].pObjectName) + "\"" ; |
227 | } |
228 | } |
229 | } |
230 | |
231 | String labels_string; |
232 | if (pCallbackData->cmdBufLabelCount > 0) { |
233 | labels_string = "\n\tCommand Buffer Labels - " + String::num_int64(pCallbackData->cmdBufLabelCount); |
234 | for (uint32_t cmd_buf_label = 0; cmd_buf_label < pCallbackData->cmdBufLabelCount; ++cmd_buf_label) { |
235 | labels_string += |
236 | "\n\t\tLabel[" + String::num_int64(cmd_buf_label) + "]" + |
237 | " - " + pCallbackData->pCmdBufLabels[cmd_buf_label].pLabelName + |
238 | "{ " ; |
239 | for (int color_idx = 0; color_idx < 4; ++color_idx) { |
240 | labels_string += String::num(pCallbackData->pCmdBufLabels[cmd_buf_label].color[color_idx]); |
241 | if (color_idx < 3) { |
242 | labels_string += ", " ; |
243 | } |
244 | } |
245 | labels_string += " }" ; |
246 | } |
247 | } |
248 | |
249 | String error_message(type_string + |
250 | " - Message Id Number: " + String::num_int64(pCallbackData->messageIdNumber) + |
251 | " | Message Id Name: " + pCallbackData->pMessageIdName + |
252 | "\n\t" + pCallbackData->pMessage + |
253 | objects_string + labels_string); |
254 | |
255 | // Convert VK severity to our own log macros. |
256 | switch (messageSeverity) { |
257 | case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT: |
258 | print_verbose(error_message); |
259 | break; |
260 | case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT: |
261 | print_line(error_message); |
262 | break; |
263 | case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT: |
264 | WARN_PRINT(error_message); |
265 | break; |
266 | case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT: |
267 | ERR_PRINT(error_message); |
268 | CRASH_COND_MSG(Engine::get_singleton()->is_abort_on_gpu_errors_enabled(), |
269 | "Crashing, because abort on GPU errors is enabled." ); |
270 | break; |
271 | case VK_DEBUG_UTILS_MESSAGE_SEVERITY_FLAG_BITS_MAX_ENUM_EXT: |
272 | break; // Shouldn't happen, only handling to make compilers happy. |
273 | } |
274 | |
275 | return VK_FALSE; |
276 | } |
277 | |
278 | VKAPI_ATTR VkBool32 VKAPI_CALL VulkanContext::_debug_report_callback( |
279 | VkDebugReportFlagsEXT flags, |
280 | VkDebugReportObjectTypeEXT objectType, |
281 | uint64_t object, |
282 | size_t location, |
283 | int32_t messageCode, |
284 | const char *pLayerPrefix, |
285 | const char *pMessage, |
286 | void *pUserData) { |
287 | String debugMessage = String("Vulkan Debug Report: object - " ) + |
288 | String::num_int64(object) + "\n" + pMessage; |
289 | |
290 | switch (flags) { |
291 | case VK_DEBUG_REPORT_DEBUG_BIT_EXT: |
292 | case VK_DEBUG_REPORT_INFORMATION_BIT_EXT: |
293 | print_line(debugMessage); |
294 | break; |
295 | case VK_DEBUG_REPORT_WARNING_BIT_EXT: |
296 | case VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT: |
297 | WARN_PRINT(debugMessage); |
298 | break; |
299 | case VK_DEBUG_REPORT_ERROR_BIT_EXT: |
300 | ERR_PRINT(debugMessage); |
301 | break; |
302 | } |
303 | |
304 | return VK_FALSE; |
305 | } |
306 | |
307 | VkBool32 VulkanContext::_check_layers(uint32_t check_count, const char *const *check_names, uint32_t layer_count, VkLayerProperties *layers) { |
308 | for (uint32_t i = 0; i < check_count; i++) { |
309 | VkBool32 found = 0; |
310 | for (uint32_t j = 0; j < layer_count; j++) { |
311 | if (!strcmp(check_names[i], layers[j].layerName)) { |
312 | found = 1; |
313 | break; |
314 | } |
315 | } |
316 | if (!found) { |
317 | WARN_PRINT("Can't find layer: " + String(check_names[i])); |
318 | return 0; |
319 | } |
320 | } |
321 | return 1; |
322 | } |
323 | |
324 | Error VulkanContext::_get_preferred_validation_layers(uint32_t *count, const char *const **names) { |
325 | static const LocalVector<LocalVector<const char *>> instance_validation_layers_alt{ |
326 | // Preferred set of validation layers. |
327 | { "VK_LAYER_KHRONOS_validation" }, |
328 | |
329 | // Alternative (deprecated, removed in SDK 1.1.126.0) set of validation layers. |
330 | { "VK_LAYER_LUNARG_standard_validation" }, |
331 | |
332 | // Alternative (deprecated, removed in SDK 1.1.121.1) set of validation layers. |
333 | { "VK_LAYER_GOOGLE_threading" , "VK_LAYER_LUNARG_parameter_validation" , "VK_LAYER_LUNARG_object_tracker" , "VK_LAYER_LUNARG_core_validation" , "VK_LAYER_GOOGLE_unique_objects" } |
334 | }; |
335 | |
336 | // Clear out-arguments. |
337 | *count = 0; |
338 | if (names != nullptr) { |
339 | *names = nullptr; |
340 | } |
341 | |
342 | VkResult err; |
343 | uint32_t instance_layer_count; |
344 | |
345 | err = vkEnumerateInstanceLayerProperties(&instance_layer_count, nullptr); |
346 | if (err) { |
347 | ERR_FAIL_V(ERR_CANT_CREATE); |
348 | } |
349 | |
350 | if (instance_layer_count < 1) { |
351 | return OK; |
352 | } |
353 | |
354 | VkLayerProperties *instance_layers = (VkLayerProperties *)malloc(sizeof(VkLayerProperties) * instance_layer_count); |
355 | err = vkEnumerateInstanceLayerProperties(&instance_layer_count, instance_layers); |
356 | if (err) { |
357 | free(instance_layers); |
358 | ERR_FAIL_V(ERR_CANT_CREATE); |
359 | } |
360 | |
361 | for (const LocalVector<const char *> &layer : instance_validation_layers_alt) { |
362 | if (_check_layers(layer.size(), layer.ptr(), instance_layer_count, instance_layers)) { |
363 | *count = layer.size(); |
364 | if (names != nullptr) { |
365 | *names = layer.ptr(); |
366 | } |
367 | break; |
368 | } |
369 | } |
370 | |
371 | free(instance_layers); |
372 | |
373 | return OK; |
374 | } |
375 | |
376 | typedef VkResult(VKAPI_PTR *_vkEnumerateInstanceVersion)(uint32_t *); |
377 | |
378 | Error VulkanContext::_obtain_vulkan_version() { |
379 | // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkApplicationInfo.html#_description |
380 | // For Vulkan 1.0 vkEnumerateInstanceVersion is not available, including not in the loader we compile against on Android. |
381 | _vkEnumerateInstanceVersion func = (_vkEnumerateInstanceVersion)vkGetInstanceProcAddr(nullptr, "vkEnumerateInstanceVersion" ); |
382 | if (func != nullptr) { |
383 | uint32_t api_version; |
384 | VkResult res = func(&api_version); |
385 | if (res == VK_SUCCESS) { |
386 | instance_api_version = api_version; |
387 | } else { |
388 | // According to the documentation this shouldn't fail with anything except a memory allocation error |
389 | // in which case we're in deep trouble anyway. |
390 | ERR_FAIL_V(ERR_CANT_CREATE); |
391 | } |
392 | } else { |
393 | print_line("vkEnumerateInstanceVersion not available, assuming Vulkan 1.0." ); |
394 | instance_api_version = VK_API_VERSION_1_0; |
395 | } |
396 | |
397 | return OK; |
398 | } |
399 | |
400 | bool VulkanContext::instance_extensions_initialized = false; |
401 | HashMap<CharString, bool> VulkanContext::requested_instance_extensions; |
402 | |
403 | void VulkanContext::register_requested_instance_extension(const CharString &extension_name, bool p_required) { |
404 | ERR_FAIL_COND_MSG(instance_extensions_initialized, "You can only registered extensions before the Vulkan instance is created" ); |
405 | ERR_FAIL_COND(requested_instance_extensions.has(extension_name)); |
406 | |
407 | requested_instance_extensions[extension_name] = p_required; |
408 | } |
409 | |
410 | Error VulkanContext::_initialize_instance_extensions() { |
411 | enabled_instance_extension_names.clear(); |
412 | |
413 | // Make sure our core extensions are here |
414 | register_requested_instance_extension(VK_KHR_SURFACE_EXTENSION_NAME, true); |
415 | register_requested_instance_extension(_get_platform_surface_extension(), true); |
416 | |
417 | if (_use_validation_layers()) { |
418 | register_requested_instance_extension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, false); |
419 | } |
420 | |
421 | // This extension allows us to use the properties2 features to query additional device capabilities |
422 | register_requested_instance_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, false); |
423 | |
424 | // Only enable debug utils in verbose mode or DEV_ENABLED. |
425 | // End users would get spammed with messages of varying verbosity due to the |
426 | // mess that thirdparty layers/extensions and drivers seem to leave in their |
427 | // wake, making the Windows registry a bottomless pit of broken layer JSON. |
428 | #ifdef DEV_ENABLED |
429 | bool want_debug_utils = true; |
430 | #else |
431 | bool want_debug_utils = OS::get_singleton()->is_stdout_verbose(); |
432 | #endif |
433 | if (want_debug_utils) { |
434 | register_requested_instance_extension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, false); |
435 | } |
436 | |
437 | // Load instance extensions that are available... |
438 | uint32_t instance_extension_count = 0; |
439 | VkResult err = vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, nullptr); |
440 | ERR_FAIL_COND_V(err != VK_SUCCESS && err != VK_INCOMPLETE, ERR_CANT_CREATE); |
441 | ERR_FAIL_COND_V_MSG(instance_extension_count == 0, ERR_CANT_CREATE, "No instance extensions found, is a driver installed?" ); |
442 | |
443 | VkExtensionProperties *instance_extensions = (VkExtensionProperties *)malloc(sizeof(VkExtensionProperties) * instance_extension_count); |
444 | err = vkEnumerateInstanceExtensionProperties(nullptr, &instance_extension_count, instance_extensions); |
445 | if (err != VK_SUCCESS && err != VK_INCOMPLETE) { |
446 | free(instance_extensions); |
447 | ERR_FAIL_V(ERR_CANT_CREATE); |
448 | } |
449 | #ifdef DEV_ENABLED |
450 | for (uint32_t i = 0; i < instance_extension_count; i++) { |
451 | print_verbose(String("VULKAN: Found instance extension " ) + String(instance_extensions[i].extensionName)); |
452 | } |
453 | #endif |
454 | |
455 | // Enable all extensions that are supported and requested |
456 | for (uint32_t i = 0; i < instance_extension_count; i++) { |
457 | CharString extension_name(instance_extensions[i].extensionName); |
458 | if (requested_instance_extensions.has(extension_name)) { |
459 | enabled_instance_extension_names.insert(extension_name); |
460 | } |
461 | } |
462 | |
463 | // Now check our requested extensions |
464 | for (KeyValue<CharString, bool> &requested_extension : requested_instance_extensions) { |
465 | if (!enabled_instance_extension_names.has(requested_extension.key)) { |
466 | if (requested_extension.value) { |
467 | free(instance_extensions); |
468 | ERR_FAIL_V_MSG(ERR_BUG, String("Required extension " ) + String(requested_extension.key) + String(" not found, is a driver installed?" )); |
469 | } else { |
470 | print_verbose(String("Optional extension " ) + String(requested_extension.key) + String(" not found" )); |
471 | } |
472 | } |
473 | } |
474 | |
475 | free(instance_extensions); |
476 | |
477 | instance_extensions_initialized = true; |
478 | return OK; |
479 | } |
480 | |
481 | bool VulkanContext::device_extensions_initialized = false; |
482 | HashMap<CharString, bool> VulkanContext::requested_device_extensions; |
483 | |
484 | void VulkanContext::register_requested_device_extension(const CharString &extension_name, bool p_required) { |
485 | ERR_FAIL_COND_MSG(device_extensions_initialized, "You can only registered extensions before the Vulkan instance is created" ); |
486 | ERR_FAIL_COND(requested_device_extensions.has(extension_name)); |
487 | |
488 | requested_device_extensions[extension_name] = p_required; |
489 | } |
490 | |
491 | Error VulkanContext::_initialize_device_extensions() { |
492 | // Look for device extensions. |
493 | enabled_device_extension_names.clear(); |
494 | |
495 | // Make sure our core extensions are here |
496 | register_requested_device_extension(VK_KHR_SWAPCHAIN_EXTENSION_NAME, true); |
497 | |
498 | register_requested_device_extension(VK_KHR_MULTIVIEW_EXTENSION_NAME, false); |
499 | register_requested_device_extension(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME, false); |
500 | register_requested_device_extension(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, false); |
501 | register_requested_device_extension(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false); |
502 | register_requested_device_extension(VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME, false); |
503 | register_requested_device_extension(VK_KHR_16BIT_STORAGE_EXTENSION_NAME, false); |
504 | register_requested_device_extension(VK_KHR_IMAGE_FORMAT_LIST_EXTENSION_NAME, false); |
505 | register_requested_device_extension(VK_KHR_MAINTENANCE_2_EXTENSION_NAME, false); |
506 | register_requested_device_extension(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME, false); |
507 | |
508 | if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) { |
509 | register_requested_device_extension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, true); |
510 | } |
511 | |
512 | // TODO consider the following extensions: |
513 | // - VK_KHR_spirv_1_4 |
514 | // - VK_KHR_swapchain_mutable_format |
515 | // - VK_EXT_full_screen_exclusive |
516 | // - VK_EXT_hdr_metadata |
517 | // - VK_KHR_depth_stencil_resolve |
518 | |
519 | // Even though the user "enabled" the extension via the command |
520 | // line, we must make sure that it's enumerated for use with the |
521 | // device. Therefore, disable it here, and re-enable it again if |
522 | // enumerated. |
523 | if (VK_KHR_incremental_present_enabled) { |
524 | register_requested_device_extension(VK_KHR_INCREMENTAL_PRESENT_EXTENSION_NAME, false); |
525 | } |
526 | if (VK_GOOGLE_display_timing_enabled) { |
527 | register_requested_device_extension(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME, false); |
528 | } |
529 | |
530 | // obtain available device extensions |
531 | uint32_t device_extension_count = 0; |
532 | VkResult err = vkEnumerateDeviceExtensionProperties(gpu, nullptr, &device_extension_count, nullptr); |
533 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
534 | ERR_FAIL_COND_V_MSG(device_extension_count == 0, ERR_CANT_CREATE, |
535 | "vkEnumerateDeviceExtensionProperties failed to find any extensions\n\n" |
536 | "Do you have a compatible Vulkan installable client driver (ICD) installed?\n" |
537 | "vkCreateInstance Failure" ); |
538 | |
539 | VkExtensionProperties *device_extensions = (VkExtensionProperties *)malloc(sizeof(VkExtensionProperties) * device_extension_count); |
540 | err = vkEnumerateDeviceExtensionProperties(gpu, nullptr, &device_extension_count, device_extensions); |
541 | if (err) { |
542 | free(device_extensions); |
543 | ERR_FAIL_V(ERR_CANT_CREATE); |
544 | } |
545 | |
546 | #ifdef DEV_ENABLED |
547 | for (uint32_t i = 0; i < device_extension_count; i++) { |
548 | print_verbose(String("VULKAN: Found device extension " ) + String(device_extensions[i].extensionName)); |
549 | } |
550 | #endif |
551 | |
552 | // Enable all extensions that are supported and requested |
553 | for (uint32_t i = 0; i < device_extension_count; i++) { |
554 | CharString extension_name(device_extensions[i].extensionName); |
555 | if (requested_device_extensions.has(extension_name)) { |
556 | enabled_device_extension_names.insert(extension_name); |
557 | } |
558 | } |
559 | |
560 | // Now check our requested extensions |
561 | for (KeyValue<CharString, bool> &requested_extension : requested_device_extensions) { |
562 | if (!enabled_device_extension_names.has(requested_extension.key)) { |
563 | if (requested_extension.value) { |
564 | free(device_extensions); |
565 | ERR_FAIL_V_MSG(ERR_BUG, |
566 | String("vkEnumerateDeviceExtensionProperties failed to find the " ) + String(requested_extension.key) + String(" extension.\n\nDo you have a compatible Vulkan installable client driver (ICD) installed?\nvkCreateInstance Failure" )); |
567 | } else { |
568 | print_verbose(String("Optional extension " ) + String(requested_extension.key) + String(" not found" )); |
569 | } |
570 | } |
571 | } |
572 | |
573 | free(device_extensions); |
574 | |
575 | device_extensions_initialized = true; |
576 | return OK; |
577 | } |
578 | |
579 | uint32_t VulkanContext::SubgroupCapabilities::supported_stages_flags_rd() const { |
580 | uint32_t flags = 0; |
581 | |
582 | if (supportedStages & VK_SHADER_STAGE_VERTEX_BIT) { |
583 | flags += RenderingDevice::ShaderStage::SHADER_STAGE_VERTEX_BIT; |
584 | } |
585 | if (supportedStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) { |
586 | flags += RenderingDevice::ShaderStage::SHADER_STAGE_TESSELATION_CONTROL_BIT; |
587 | } |
588 | if (supportedStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) { |
589 | flags += RenderingDevice::ShaderStage::SHADER_STAGE_TESSELATION_EVALUATION_BIT; |
590 | } |
591 | // if (supportedStages & VK_SHADER_STAGE_GEOMETRY_BIT) { |
592 | // flags += RenderingDevice::ShaderStage::SHADER_STAGE_GEOMETRY_BIT; |
593 | // } |
594 | if (supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) { |
595 | flags += RenderingDevice::ShaderStage::SHADER_STAGE_FRAGMENT_BIT; |
596 | } |
597 | if (supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) { |
598 | flags += RenderingDevice::ShaderStage::SHADER_STAGE_COMPUTE_BIT; |
599 | } |
600 | |
601 | return flags; |
602 | } |
603 | |
604 | String VulkanContext::SubgroupCapabilities::supported_stages_desc() const { |
605 | String res; |
606 | |
607 | if (supportedStages & VK_SHADER_STAGE_VERTEX_BIT) { |
608 | res += ", STAGE_VERTEX" ; |
609 | } |
610 | if (supportedStages & VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT) { |
611 | res += ", STAGE_TESSELLATION_CONTROL" ; |
612 | } |
613 | if (supportedStages & VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT) { |
614 | res += ", STAGE_TESSELLATION_EVALUATION" ; |
615 | } |
616 | if (supportedStages & VK_SHADER_STAGE_GEOMETRY_BIT) { |
617 | res += ", STAGE_GEOMETRY" ; |
618 | } |
619 | if (supportedStages & VK_SHADER_STAGE_FRAGMENT_BIT) { |
620 | res += ", STAGE_FRAGMENT" ; |
621 | } |
622 | if (supportedStages & VK_SHADER_STAGE_COMPUTE_BIT) { |
623 | res += ", STAGE_COMPUTE" ; |
624 | } |
625 | |
626 | // These are not defined on Android GRMBL. |
627 | if (supportedStages & 0x00000100 /* VK_SHADER_STAGE_RAYGEN_BIT_KHR */) { |
628 | res += ", STAGE_RAYGEN_KHR" ; |
629 | } |
630 | if (supportedStages & 0x00000200 /* VK_SHADER_STAGE_ANY_HIT_BIT_KHR */) { |
631 | res += ", STAGE_ANY_HIT_KHR" ; |
632 | } |
633 | if (supportedStages & 0x00000400 /* VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR */) { |
634 | res += ", STAGE_CLOSEST_HIT_KHR" ; |
635 | } |
636 | if (supportedStages & 0x00000800 /* VK_SHADER_STAGE_MISS_BIT_KHR */) { |
637 | res += ", STAGE_MISS_KHR" ; |
638 | } |
639 | if (supportedStages & 0x00001000 /* VK_SHADER_STAGE_INTERSECTION_BIT_KHR */) { |
640 | res += ", STAGE_INTERSECTION_KHR" ; |
641 | } |
642 | if (supportedStages & 0x00002000 /* VK_SHADER_STAGE_CALLABLE_BIT_KHR */) { |
643 | res += ", STAGE_CALLABLE_KHR" ; |
644 | } |
645 | if (supportedStages & 0x00000040 /* VK_SHADER_STAGE_TASK_BIT_NV */) { |
646 | res += ", STAGE_TASK_NV" ; |
647 | } |
648 | if (supportedStages & 0x00000080 /* VK_SHADER_STAGE_MESH_BIT_NV */) { |
649 | res += ", STAGE_MESH_NV" ; |
650 | } |
651 | |
652 | return res.substr(2); // Remove first ", ". |
653 | } |
654 | |
655 | uint32_t VulkanContext::SubgroupCapabilities::supported_operations_flags_rd() const { |
656 | uint32_t flags = 0; |
657 | |
658 | if (supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) { |
659 | flags += RenderingDevice::SubgroupOperations::SUBGROUP_BASIC_BIT; |
660 | } |
661 | if (supportedOperations & VK_SUBGROUP_FEATURE_VOTE_BIT) { |
662 | flags += RenderingDevice::SubgroupOperations::SUBGROUP_VOTE_BIT; |
663 | } |
664 | if (supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) { |
665 | flags += RenderingDevice::SubgroupOperations::SUBGROUP_ARITHMETIC_BIT; |
666 | } |
667 | if (supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) { |
668 | flags += RenderingDevice::SubgroupOperations::SUBGROUP_BALLOT_BIT; |
669 | } |
670 | if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) { |
671 | flags += RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_BIT; |
672 | } |
673 | if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) { |
674 | flags += RenderingDevice::SubgroupOperations::SUBGROUP_SHUFFLE_RELATIVE_BIT; |
675 | } |
676 | if (supportedOperations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) { |
677 | flags += RenderingDevice::SubgroupOperations::SUBGROUP_CLUSTERED_BIT; |
678 | } |
679 | if (supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) { |
680 | flags += RenderingDevice::SubgroupOperations::SUBGROUP_QUAD_BIT; |
681 | } |
682 | |
683 | return flags; |
684 | } |
685 | |
686 | String VulkanContext::SubgroupCapabilities::supported_operations_desc() const { |
687 | String res; |
688 | |
689 | if (supportedOperations & VK_SUBGROUP_FEATURE_BASIC_BIT) { |
690 | res += ", FEATURE_BASIC" ; |
691 | } |
692 | if (supportedOperations & VK_SUBGROUP_FEATURE_VOTE_BIT) { |
693 | res += ", FEATURE_VOTE" ; |
694 | } |
695 | if (supportedOperations & VK_SUBGROUP_FEATURE_ARITHMETIC_BIT) { |
696 | res += ", FEATURE_ARITHMETIC" ; |
697 | } |
698 | if (supportedOperations & VK_SUBGROUP_FEATURE_BALLOT_BIT) { |
699 | res += ", FEATURE_BALLOT" ; |
700 | } |
701 | if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_BIT) { |
702 | res += ", FEATURE_SHUFFLE" ; |
703 | } |
704 | if (supportedOperations & VK_SUBGROUP_FEATURE_SHUFFLE_RELATIVE_BIT) { |
705 | res += ", FEATURE_SHUFFLE_RELATIVE" ; |
706 | } |
707 | if (supportedOperations & VK_SUBGROUP_FEATURE_CLUSTERED_BIT) { |
708 | res += ", FEATURE_CLUSTERED" ; |
709 | } |
710 | if (supportedOperations & VK_SUBGROUP_FEATURE_QUAD_BIT) { |
711 | res += ", FEATURE_QUAD" ; |
712 | } |
713 | if (supportedOperations & VK_SUBGROUP_FEATURE_PARTITIONED_BIT_NV) { |
714 | res += ", FEATURE_PARTITIONED_NV" ; |
715 | } |
716 | |
717 | return res.substr(2); // Remove first ", ". |
718 | } |
719 | |
720 | Error VulkanContext::_check_capabilities() { |
721 | // https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_multiview.html |
722 | // https://www.khronos.org/blog/vulkan-subgroup-tutorial |
723 | |
724 | // For Vulkan 1.0 vkGetPhysicalDeviceProperties2 is not available, including not in the loader we compile against on Android. |
725 | |
726 | // So we check if the functions are accessible by getting their function pointers and skipping if not |
727 | // (note that the desktop loader does a better job here but the android loader doesn't.) |
728 | |
729 | // Assume not supported until proven otherwise. |
730 | vrs_capabilities.pipeline_vrs_supported = false; |
731 | vrs_capabilities.primitive_vrs_supported = false; |
732 | vrs_capabilities.attachment_vrs_supported = false; |
733 | vrs_capabilities.min_texel_size = Size2i(); |
734 | vrs_capabilities.max_texel_size = Size2i(); |
735 | vrs_capabilities.texel_size = Size2i(); |
736 | multiview_capabilities.is_supported = false; |
737 | multiview_capabilities.geometry_shader_is_supported = false; |
738 | multiview_capabilities.tessellation_shader_is_supported = false; |
739 | multiview_capabilities.max_view_count = 0; |
740 | multiview_capabilities.max_instance_count = 0; |
741 | subgroup_capabilities.size = 0; |
742 | subgroup_capabilities.supportedStages = 0; |
743 | subgroup_capabilities.supportedOperations = 0; |
744 | subgroup_capabilities.quadOperationsInAllStages = false; |
745 | shader_capabilities.shader_float16_is_supported = false; |
746 | shader_capabilities.shader_int8_is_supported = false; |
747 | storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = false; |
748 | storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported = false; |
749 | storage_buffer_capabilities.storage_push_constant_16_is_supported = false; |
750 | storage_buffer_capabilities.storage_input_output_16 = false; |
751 | |
752 | if (is_instance_extension_enabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME)) { |
753 | // Check for extended features. |
754 | PFN_vkGetPhysicalDeviceFeatures2 vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2" ); |
755 | if (vkGetPhysicalDeviceFeatures2_func == nullptr) { |
756 | // In Vulkan 1.0 might be accessible under its original extension name. |
757 | vkGetPhysicalDeviceFeatures2_func = (PFN_vkGetPhysicalDeviceFeatures2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceFeatures2KHR" ); |
758 | } |
759 | if (vkGetPhysicalDeviceFeatures2_func != nullptr) { |
760 | // Check our extended features. |
761 | void *next = nullptr; |
762 | |
763 | // We must check that the relative extension is present before assuming a |
764 | // feature as enabled. |
765 | // See also: https://github.com/godotengine/godot/issues/65409 |
766 | |
767 | VkPhysicalDeviceVulkan12Features device_features_vk12 = {}; |
768 | VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = {}; |
769 | VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features = {}; |
770 | VkPhysicalDevice16BitStorageFeaturesKHR storage_feature = {}; |
771 | VkPhysicalDeviceMultiviewFeatures multiview_features = {}; |
772 | |
773 | if (device_api_version >= VK_API_VERSION_1_2) { |
774 | device_features_vk12.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; |
775 | device_features_vk12.pNext = next; |
776 | next = &device_features_vk12; |
777 | } else { |
778 | if (is_device_extension_enabled(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) { |
779 | shader_features = { |
780 | /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR, |
781 | /*pNext*/ next, |
782 | /*shaderFloat16*/ false, |
783 | /*shaderInt8*/ false, |
784 | }; |
785 | next = &shader_features; |
786 | } |
787 | } |
788 | |
789 | if (is_device_extension_enabled(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) { |
790 | vrs_features = { |
791 | /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR, |
792 | /*pNext*/ next, |
793 | /*pipelineFragmentShadingRate*/ false, |
794 | /*primitiveFragmentShadingRate*/ false, |
795 | /*attachmentFragmentShadingRate*/ false, |
796 | }; |
797 | next = &vrs_features; |
798 | } |
799 | |
800 | if (is_device_extension_enabled(VK_KHR_16BIT_STORAGE_EXTENSION_NAME)) { |
801 | storage_feature = { |
802 | /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR, |
803 | /*pNext*/ next, |
804 | /*storageBuffer16BitAccess*/ false, |
805 | /*uniformAndStorageBuffer16BitAccess*/ false, |
806 | /*storagePushConstant16*/ false, |
807 | /*storageInputOutput16*/ false, |
808 | }; |
809 | next = &storage_feature; |
810 | } |
811 | |
812 | if (is_device_extension_enabled(VK_KHR_MULTIVIEW_EXTENSION_NAME)) { |
813 | multiview_features = { |
814 | /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES, |
815 | /*pNext*/ next, |
816 | /*multiview*/ false, |
817 | /*multiviewGeometryShader*/ false, |
818 | /*multiviewTessellationShader*/ false, |
819 | }; |
820 | next = &multiview_features; |
821 | } |
822 | |
823 | VkPhysicalDeviceFeatures2 device_features; |
824 | device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; |
825 | device_features.pNext = next; |
826 | |
827 | vkGetPhysicalDeviceFeatures2_func(gpu, &device_features); |
828 | |
829 | if (device_api_version >= VK_API_VERSION_1_2) { |
830 | #ifdef MACOS_ENABLED |
831 | ERR_FAIL_COND_V_MSG(!device_features_vk12.shaderSampledImageArrayNonUniformIndexing, ERR_CANT_CREATE, "Your GPU doesn't support shaderSampledImageArrayNonUniformIndexing which is required to use the Vulkan-based renderers in Godot." ); |
832 | #endif |
833 | |
834 | if (is_device_extension_enabled(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) { |
835 | shader_capabilities.shader_float16_is_supported = device_features_vk12.shaderFloat16; |
836 | shader_capabilities.shader_int8_is_supported = device_features_vk12.shaderInt8; |
837 | } |
838 | } else { |
839 | if (is_device_extension_enabled(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME)) { |
840 | shader_capabilities.shader_float16_is_supported = shader_features.shaderFloat16; |
841 | shader_capabilities.shader_int8_is_supported = shader_features.shaderInt8; |
842 | } |
843 | } |
844 | |
845 | if (is_device_extension_enabled(VK_KHR_FRAGMENT_SHADING_RATE_EXTENSION_NAME)) { |
846 | vrs_capabilities.pipeline_vrs_supported = vrs_features.pipelineFragmentShadingRate; |
847 | vrs_capabilities.primitive_vrs_supported = vrs_features.primitiveFragmentShadingRate; |
848 | vrs_capabilities.attachment_vrs_supported = vrs_features.attachmentFragmentShadingRate; |
849 | } |
850 | |
851 | if (is_device_extension_enabled(VK_KHR_MULTIVIEW_EXTENSION_NAME)) { |
852 | multiview_capabilities.is_supported = multiview_features.multiview; |
853 | multiview_capabilities.geometry_shader_is_supported = multiview_features.multiviewGeometryShader; |
854 | multiview_capabilities.tessellation_shader_is_supported = multiview_features.multiviewTessellationShader; |
855 | } |
856 | |
857 | if (is_device_extension_enabled(VK_KHR_16BIT_STORAGE_EXTENSION_NAME)) { |
858 | storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported = storage_feature.storageBuffer16BitAccess; |
859 | storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported = storage_feature.uniformAndStorageBuffer16BitAccess; |
860 | storage_buffer_capabilities.storage_push_constant_16_is_supported = storage_feature.storagePushConstant16; |
861 | storage_buffer_capabilities.storage_input_output_16 = storage_feature.storageInputOutput16; |
862 | } |
863 | } |
864 | |
865 | // Check extended properties. |
866 | PFN_vkGetPhysicalDeviceProperties2 device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2" ); |
867 | if (device_properties_func == nullptr) { |
868 | // In Vulkan 1.0 might be accessible under its original extension name. |
869 | device_properties_func = (PFN_vkGetPhysicalDeviceProperties2)vkGetInstanceProcAddr(inst, "vkGetPhysicalDeviceProperties2KHR" ); |
870 | } |
871 | if (device_properties_func != nullptr) { |
872 | VkPhysicalDeviceFragmentShadingRatePropertiesKHR vrsProperties{}; |
873 | VkPhysicalDeviceMultiviewProperties multiviewProperties{}; |
874 | VkPhysicalDeviceSubgroupProperties subgroupProperties{}; |
875 | VkPhysicalDeviceProperties2 physicalDeviceProperties{}; |
876 | void *nextptr = nullptr; |
877 | |
878 | if (device_api_version >= VK_API_VERSION_1_1) { // Vulkan 1.1 or higher |
879 | subgroupProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_PROPERTIES; |
880 | subgroupProperties.pNext = nextptr; |
881 | |
882 | nextptr = &subgroupProperties; |
883 | } |
884 | |
885 | if (multiview_capabilities.is_supported) { |
886 | multiviewProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_PROPERTIES; |
887 | multiviewProperties.pNext = nextptr; |
888 | |
889 | nextptr = &multiviewProperties; |
890 | } |
891 | |
892 | if (vrs_capabilities.attachment_vrs_supported) { |
893 | vrsProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_PROPERTIES_KHR; |
894 | vrsProperties.pNext = nextptr; |
895 | |
896 | nextptr = &vrsProperties; |
897 | } |
898 | |
899 | physicalDeviceProperties.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2; |
900 | physicalDeviceProperties.pNext = nextptr; |
901 | |
902 | device_properties_func(gpu, &physicalDeviceProperties); |
903 | |
904 | subgroup_capabilities.size = subgroupProperties.subgroupSize; |
905 | subgroup_capabilities.supportedStages = subgroupProperties.supportedStages; |
906 | subgroup_capabilities.supportedOperations = subgroupProperties.supportedOperations; |
907 | // Note: quadOperationsInAllStages will be true if: |
908 | // - supportedStages has VK_SHADER_STAGE_ALL_GRAPHICS + VK_SHADER_STAGE_COMPUTE_BIT. |
909 | // - supportedOperations has VK_SUBGROUP_FEATURE_QUAD_BIT. |
910 | subgroup_capabilities.quadOperationsInAllStages = subgroupProperties.quadOperationsInAllStages; |
911 | |
912 | if (vrs_capabilities.pipeline_vrs_supported || vrs_capabilities.primitive_vrs_supported || vrs_capabilities.attachment_vrs_supported) { |
913 | print_verbose("- Vulkan Variable Rate Shading supported:" ); |
914 | if (vrs_capabilities.pipeline_vrs_supported) { |
915 | print_verbose(" Pipeline fragment shading rate" ); |
916 | } |
917 | if (vrs_capabilities.primitive_vrs_supported) { |
918 | print_verbose(" Primitive fragment shading rate" ); |
919 | } |
920 | if (vrs_capabilities.attachment_vrs_supported) { |
921 | // TODO expose these somehow to the end user. |
922 | vrs_capabilities.min_texel_size.x = vrsProperties.minFragmentShadingRateAttachmentTexelSize.width; |
923 | vrs_capabilities.min_texel_size.y = vrsProperties.minFragmentShadingRateAttachmentTexelSize.height; |
924 | vrs_capabilities.max_texel_size.x = vrsProperties.maxFragmentShadingRateAttachmentTexelSize.width; |
925 | vrs_capabilities.max_texel_size.y = vrsProperties.maxFragmentShadingRateAttachmentTexelSize.height; |
926 | |
927 | // We'll attempt to default to a texel size of 16x16 |
928 | vrs_capabilities.texel_size.x = CLAMP(16, vrs_capabilities.min_texel_size.x, vrs_capabilities.max_texel_size.x); |
929 | vrs_capabilities.texel_size.y = CLAMP(16, vrs_capabilities.min_texel_size.y, vrs_capabilities.max_texel_size.y); |
930 | |
931 | print_verbose(String(" Attachment fragment shading rate" ) + String(", min texel size: (" ) + itos(vrs_capabilities.min_texel_size.x) + String(", " ) + itos(vrs_capabilities.min_texel_size.y) + String(")" ) + String(", max texel size: (" ) + itos(vrs_capabilities.max_texel_size.x) + String(", " ) + itos(vrs_capabilities.max_texel_size.y) + String(")" )); |
932 | } |
933 | |
934 | } else { |
935 | print_verbose("- Vulkan Variable Rate Shading not supported" ); |
936 | } |
937 | |
938 | if (multiview_capabilities.is_supported) { |
939 | multiview_capabilities.max_view_count = multiviewProperties.maxMultiviewViewCount; |
940 | multiview_capabilities.max_instance_count = multiviewProperties.maxMultiviewInstanceIndex; |
941 | |
942 | print_verbose("- Vulkan multiview supported:" ); |
943 | print_verbose(" max view count: " + itos(multiview_capabilities.max_view_count)); |
944 | print_verbose(" max instances: " + itos(multiview_capabilities.max_instance_count)); |
945 | } else { |
946 | print_verbose("- Vulkan multiview not supported" ); |
947 | } |
948 | |
949 | print_verbose("- Vulkan subgroup:" ); |
950 | print_verbose(" size: " + itos(subgroup_capabilities.size)); |
951 | print_verbose(" stages: " + subgroup_capabilities.supported_stages_desc()); |
952 | print_verbose(" supported ops: " + subgroup_capabilities.supported_operations_desc()); |
953 | if (subgroup_capabilities.quadOperationsInAllStages) { |
954 | print_verbose(" quad operations in all stages" ); |
955 | } |
956 | } else { |
957 | print_verbose("- Couldn't call vkGetPhysicalDeviceProperties2" ); |
958 | } |
959 | } |
960 | |
961 | return OK; |
962 | } |
963 | |
964 | Error VulkanContext::_create_instance() { |
965 | // Obtain Vulkan version. |
966 | _obtain_vulkan_version(); |
967 | |
968 | // Initialize extensions. |
969 | { |
970 | Error err = _initialize_instance_extensions(); |
971 | if (err != OK) { |
972 | return err; |
973 | } |
974 | } |
975 | |
976 | int enabled_extension_count = 0; |
977 | const char *enabled_extension_names[MAX_EXTENSIONS]; |
978 | ERR_FAIL_COND_V(enabled_instance_extension_names.size() > MAX_EXTENSIONS, ERR_CANT_CREATE); |
979 | for (const CharString &extension_name : enabled_instance_extension_names) { |
980 | enabled_extension_names[enabled_extension_count++] = extension_name.ptr(); |
981 | } |
982 | |
983 | // We'll set application version to the Vulkan version we're developing against, even if our instance is based on |
984 | // an older Vulkan version, devices can still support newer versions of Vulkan. |
985 | // The exception is when we're on Vulkan 1.0, we should not set this to anything but 1.0. |
986 | // Note that this value is only used by validation layers to warn us about version issues. |
987 | uint32_t application_api_version = instance_api_version == VK_API_VERSION_1_0 ? VK_API_VERSION_1_0 : VK_API_VERSION_1_2; |
988 | |
989 | CharString cs = GLOBAL_GET("application/config/name" ).operator String().utf8(); |
990 | const VkApplicationInfo app = { |
991 | /*sType*/ VK_STRUCTURE_TYPE_APPLICATION_INFO, |
992 | /*pNext*/ nullptr, |
993 | /*pApplicationName*/ cs.get_data(), |
994 | /*applicationVersion*/ 0, // It would be really nice if we store a version number in project settings, say "application/config/version" |
995 | /*pEngineName*/ VERSION_NAME, |
996 | /*engineVersion*/ VK_MAKE_VERSION(VERSION_MAJOR, VERSION_MINOR, VERSION_PATCH), |
997 | /*apiVersion*/ application_api_version |
998 | }; |
999 | VkInstanceCreateInfo inst_info{}; |
1000 | inst_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; |
1001 | inst_info.pApplicationInfo = &app; |
1002 | inst_info.enabledExtensionCount = enabled_extension_count; |
1003 | inst_info.ppEnabledExtensionNames = (const char *const *)enabled_extension_names; |
1004 | if (_use_validation_layers()) { |
1005 | _get_preferred_validation_layers(&inst_info.enabledLayerCount, &inst_info.ppEnabledLayerNames); |
1006 | } |
1007 | |
1008 | /* |
1009 | * This is info for a temp callback to use during CreateInstance. |
1010 | * After the instance is created, we use the instance-based |
1011 | * function to register the final callback. |
1012 | */ |
1013 | VkDebugUtilsMessengerCreateInfoEXT dbg_messenger_create_info = {}; |
1014 | VkDebugReportCallbackCreateInfoEXT dbg_report_callback_create_info = {}; |
1015 | if (is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { |
1016 | // VK_EXT_debug_utils style. |
1017 | dbg_messenger_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; |
1018 | dbg_messenger_create_info.pNext = nullptr; |
1019 | dbg_messenger_create_info.flags = 0; |
1020 | dbg_messenger_create_info.messageSeverity = |
1021 | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; |
1022 | dbg_messenger_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | |
1023 | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | |
1024 | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; |
1025 | dbg_messenger_create_info.pfnUserCallback = _debug_messenger_callback; |
1026 | dbg_messenger_create_info.pUserData = this; |
1027 | inst_info.pNext = &dbg_messenger_create_info; |
1028 | } else if (is_instance_extension_enabled(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) { |
1029 | dbg_report_callback_create_info.sType = VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT; |
1030 | dbg_report_callback_create_info.flags = VK_DEBUG_REPORT_INFORMATION_BIT_EXT | |
1031 | VK_DEBUG_REPORT_WARNING_BIT_EXT | |
1032 | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT | |
1033 | VK_DEBUG_REPORT_ERROR_BIT_EXT | |
1034 | VK_DEBUG_REPORT_DEBUG_BIT_EXT; |
1035 | dbg_report_callback_create_info.pfnCallback = _debug_report_callback; |
1036 | dbg_report_callback_create_info.pUserData = this; |
1037 | inst_info.pNext = &dbg_report_callback_create_info; |
1038 | } |
1039 | |
1040 | VkResult err; |
1041 | |
1042 | if (vulkan_hooks) { |
1043 | if (!vulkan_hooks->create_vulkan_instance(&inst_info, &inst)) { |
1044 | return ERR_CANT_CREATE; |
1045 | } |
1046 | } else { |
1047 | err = vkCreateInstance(&inst_info, nullptr, &inst); |
1048 | ERR_FAIL_COND_V_MSG(err == VK_ERROR_INCOMPATIBLE_DRIVER, ERR_CANT_CREATE, |
1049 | "Cannot find a compatible Vulkan installable client driver (ICD).\n\n" |
1050 | "vkCreateInstance Failure" ); |
1051 | ERR_FAIL_COND_V_MSG(err == VK_ERROR_EXTENSION_NOT_PRESENT, ERR_CANT_CREATE, |
1052 | "Cannot find a specified extension library.\n" |
1053 | "Make sure your layers path is set appropriately.\n" |
1054 | "vkCreateInstance Failure" ); |
1055 | ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, |
1056 | "vkCreateInstance failed.\n\n" |
1057 | "Do you have a compatible Vulkan installable client driver (ICD) installed?\n" |
1058 | "Please look at the Getting Started guide for additional information.\n" |
1059 | "vkCreateInstance Failure" ); |
1060 | } |
1061 | |
1062 | inst_initialized = true; |
1063 | |
1064 | #ifdef USE_VOLK |
1065 | volkLoadInstance(inst); |
1066 | #endif |
1067 | |
1068 | if (is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { |
1069 | // Setup VK_EXT_debug_utils function pointers always (we use them for debug labels and names). |
1070 | CreateDebugUtilsMessengerEXT = |
1071 | (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugUtilsMessengerEXT" ); |
1072 | DestroyDebugUtilsMessengerEXT = |
1073 | (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugUtilsMessengerEXT" ); |
1074 | SubmitDebugUtilsMessageEXT = |
1075 | (PFN_vkSubmitDebugUtilsMessageEXT)vkGetInstanceProcAddr(inst, "vkSubmitDebugUtilsMessageEXT" ); |
1076 | CmdBeginDebugUtilsLabelEXT = |
1077 | (PFN_vkCmdBeginDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdBeginDebugUtilsLabelEXT" ); |
1078 | CmdEndDebugUtilsLabelEXT = |
1079 | (PFN_vkCmdEndDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdEndDebugUtilsLabelEXT" ); |
1080 | CmdInsertDebugUtilsLabelEXT = |
1081 | (PFN_vkCmdInsertDebugUtilsLabelEXT)vkGetInstanceProcAddr(inst, "vkCmdInsertDebugUtilsLabelEXT" ); |
1082 | SetDebugUtilsObjectNameEXT = |
1083 | (PFN_vkSetDebugUtilsObjectNameEXT)vkGetInstanceProcAddr(inst, "vkSetDebugUtilsObjectNameEXT" ); |
1084 | if (nullptr == CreateDebugUtilsMessengerEXT || nullptr == DestroyDebugUtilsMessengerEXT || |
1085 | nullptr == SubmitDebugUtilsMessageEXT || nullptr == CmdBeginDebugUtilsLabelEXT || |
1086 | nullptr == CmdEndDebugUtilsLabelEXT || nullptr == CmdInsertDebugUtilsLabelEXT || |
1087 | nullptr == SetDebugUtilsObjectNameEXT) { |
1088 | ERR_FAIL_V_MSG(ERR_CANT_CREATE, |
1089 | "GetProcAddr: Failed to init VK_EXT_debug_utils\n" |
1090 | "GetProcAddr: Failure" ); |
1091 | } |
1092 | |
1093 | err = CreateDebugUtilsMessengerEXT(inst, &dbg_messenger_create_info, nullptr, &dbg_messenger); |
1094 | switch (err) { |
1095 | case VK_SUCCESS: |
1096 | break; |
1097 | case VK_ERROR_OUT_OF_HOST_MEMORY: |
1098 | ERR_FAIL_V_MSG(ERR_CANT_CREATE, |
1099 | "CreateDebugUtilsMessengerEXT: out of host memory\n" |
1100 | "CreateDebugUtilsMessengerEXT Failure" ); |
1101 | break; |
1102 | default: |
1103 | ERR_FAIL_V_MSG(ERR_CANT_CREATE, |
1104 | "CreateDebugUtilsMessengerEXT: unknown failure\n" |
1105 | "CreateDebugUtilsMessengerEXT Failure" ); |
1106 | ERR_FAIL_V(ERR_CANT_CREATE); |
1107 | break; |
1108 | } |
1109 | } else if (is_instance_extension_enabled(VK_EXT_DEBUG_REPORT_EXTENSION_NAME)) { |
1110 | CreateDebugReportCallbackEXT = (PFN_vkCreateDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkCreateDebugReportCallbackEXT" ); |
1111 | DebugReportMessageEXT = (PFN_vkDebugReportMessageEXT)vkGetInstanceProcAddr(inst, "vkDebugReportMessageEXT" ); |
1112 | DestroyDebugReportCallbackEXT = (PFN_vkDestroyDebugReportCallbackEXT)vkGetInstanceProcAddr(inst, "vkDestroyDebugReportCallbackEXT" ); |
1113 | |
1114 | if (nullptr == CreateDebugReportCallbackEXT || nullptr == DebugReportMessageEXT || nullptr == DestroyDebugReportCallbackEXT) { |
1115 | ERR_FAIL_V_MSG(ERR_CANT_CREATE, |
1116 | "GetProcAddr: Failed to init VK_EXT_debug_report\n" |
1117 | "GetProcAddr: Failure" ); |
1118 | } |
1119 | |
1120 | err = CreateDebugReportCallbackEXT(inst, &dbg_report_callback_create_info, nullptr, &dbg_debug_report); |
1121 | switch (err) { |
1122 | case VK_SUCCESS: |
1123 | break; |
1124 | case VK_ERROR_OUT_OF_HOST_MEMORY: |
1125 | ERR_FAIL_V_MSG(ERR_CANT_CREATE, |
1126 | "CreateDebugReportCallbackEXT: out of host memory\n" |
1127 | "CreateDebugReportCallbackEXT Failure" ); |
1128 | break; |
1129 | default: |
1130 | ERR_FAIL_V_MSG(ERR_CANT_CREATE, |
1131 | "CreateDebugReportCallbackEXT: unknown failure\n" |
1132 | "CreateDebugReportCallbackEXT Failure" ); |
1133 | ERR_FAIL_V(ERR_CANT_CREATE); |
1134 | break; |
1135 | } |
1136 | } |
1137 | |
1138 | return OK; |
1139 | } |
1140 | |
1141 | Error VulkanContext::_create_physical_device(VkSurfaceKHR p_surface) { |
1142 | // Make initial call to query gpu_count, then second call for gpu info. |
1143 | uint32_t gpu_count = 0; |
1144 | VkResult err = vkEnumeratePhysicalDevices(inst, &gpu_count, nullptr); |
1145 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
1146 | ERR_FAIL_COND_V_MSG(gpu_count == 0, ERR_CANT_CREATE, |
1147 | "vkEnumeratePhysicalDevices reported zero accessible devices.\n\n" |
1148 | "Do you have a compatible Vulkan installable client driver (ICD) installed?\n" |
1149 | "vkEnumeratePhysicalDevices Failure" ); |
1150 | |
1151 | VkPhysicalDevice *physical_devices = (VkPhysicalDevice *)malloc(sizeof(VkPhysicalDevice) * gpu_count); |
1152 | err = vkEnumeratePhysicalDevices(inst, &gpu_count, physical_devices); |
1153 | if (err) { |
1154 | free(physical_devices); |
1155 | ERR_FAIL_V(ERR_CANT_CREATE); |
1156 | } |
1157 | |
1158 | static const struct { |
1159 | uint32_t id; |
1160 | const char *name; |
1161 | } vendor_names[] = { |
1162 | { 0x1002, "AMD" }, |
1163 | { 0x1010, "ImgTec" }, |
1164 | { 0x106B, "Apple" }, |
1165 | { 0x10DE, "NVIDIA" }, |
1166 | { 0x13B5, "ARM" }, |
1167 | { 0x5143, "Qualcomm" }, |
1168 | { 0x8086, "Intel" }, |
1169 | { 0, nullptr }, |
1170 | }; |
1171 | |
1172 | int32_t device_index = -1; |
1173 | if (vulkan_hooks) { |
1174 | if (!vulkan_hooks->get_physical_device(&gpu)) { |
1175 | return ERR_CANT_CREATE; |
1176 | } |
1177 | |
1178 | // Not really needed but nice to print the correct entry. |
1179 | for (uint32_t i = 0; i < gpu_count; ++i) { |
1180 | if (physical_devices[i] == gpu) { |
1181 | device_index = i; |
1182 | break; |
1183 | } |
1184 | } |
1185 | } else { |
1186 | // TODO: At least on Linux Laptops integrated GPUs fail with Vulkan in many instances. |
1187 | // The device should really be a preference, but for now choosing a discrete GPU over the |
1188 | // integrated one is better than the default. |
1189 | |
1190 | int type_selected = -1; |
1191 | print_verbose("Vulkan devices:" ); |
1192 | for (uint32_t i = 0; i < gpu_count; ++i) { |
1193 | VkPhysicalDeviceProperties props; |
1194 | vkGetPhysicalDeviceProperties(physical_devices[i], &props); |
1195 | |
1196 | bool present_supported = false; |
1197 | |
1198 | uint32_t device_queue_family_count = 0; |
1199 | vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, nullptr); |
1200 | VkQueueFamilyProperties *device_queue_props = (VkQueueFamilyProperties *)malloc(device_queue_family_count * sizeof(VkQueueFamilyProperties)); |
1201 | vkGetPhysicalDeviceQueueFamilyProperties(physical_devices[i], &device_queue_family_count, device_queue_props); |
1202 | for (uint32_t j = 0; j < device_queue_family_count; j++) { |
1203 | if ((device_queue_props[j].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { |
1204 | VkBool32 supports; |
1205 | err = vkGetPhysicalDeviceSurfaceSupportKHR( |
1206 | physical_devices[i], j, p_surface, &supports); |
1207 | if (err == VK_SUCCESS && supports) { |
1208 | present_supported = true; |
1209 | } else { |
1210 | continue; |
1211 | } |
1212 | } |
1213 | } |
1214 | String name = props.deviceName; |
1215 | String vendor = "Unknown" ; |
1216 | String dev_type; |
1217 | switch (props.deviceType) { |
1218 | case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: { |
1219 | dev_type = "Discrete" ; |
1220 | } break; |
1221 | case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: { |
1222 | dev_type = "Integrated" ; |
1223 | } break; |
1224 | case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: { |
1225 | dev_type = "Virtual" ; |
1226 | } break; |
1227 | case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: { |
1228 | dev_type = "CPU" ; |
1229 | } break; |
1230 | default: { |
1231 | dev_type = "Other" ; |
1232 | } break; |
1233 | } |
1234 | uint32_t vendor_idx = 0; |
1235 | while (vendor_names[vendor_idx].name != nullptr) { |
1236 | if (props.vendorID == vendor_names[vendor_idx].id) { |
1237 | vendor = vendor_names[vendor_idx].name; |
1238 | break; |
1239 | } |
1240 | vendor_idx++; |
1241 | } |
1242 | free(device_queue_props); |
1243 | print_verbose(" #" + itos(i) + ": " + vendor + " " + name + " - " + (present_supported ? "Supported" : "Unsupported" ) + ", " + dev_type); |
1244 | |
1245 | if (present_supported) { // Select first supported device of preferred type: Discrete > Integrated > Virtual > CPU > Other. |
1246 | switch (props.deviceType) { |
1247 | case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU: { |
1248 | if (type_selected < 4) { |
1249 | type_selected = 4; |
1250 | device_index = i; |
1251 | } |
1252 | } break; |
1253 | case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: { |
1254 | if (type_selected < 3) { |
1255 | type_selected = 3; |
1256 | device_index = i; |
1257 | } |
1258 | } break; |
1259 | case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU: { |
1260 | if (type_selected < 2) { |
1261 | type_selected = 2; |
1262 | device_index = i; |
1263 | } |
1264 | } break; |
1265 | case VkPhysicalDeviceType::VK_PHYSICAL_DEVICE_TYPE_CPU: { |
1266 | if (type_selected < 1) { |
1267 | type_selected = 1; |
1268 | device_index = i; |
1269 | } |
1270 | } break; |
1271 | default: { |
1272 | if (type_selected < 0) { |
1273 | type_selected = 0; |
1274 | device_index = i; |
1275 | } |
1276 | } break; |
1277 | } |
1278 | } |
1279 | } |
1280 | |
1281 | int32_t user_device_index = Engine::get_singleton()->get_gpu_index(); // Force user selected GPU. |
1282 | if (user_device_index >= 0 && user_device_index < (int32_t)gpu_count) { |
1283 | device_index = user_device_index; |
1284 | } |
1285 | |
1286 | ERR_FAIL_COND_V_MSG(device_index == -1, ERR_CANT_CREATE, "None of Vulkan devices supports both graphics and present queues." ); |
1287 | |
1288 | gpu = physical_devices[device_index]; |
1289 | } |
1290 | |
1291 | free(physical_devices); |
1292 | |
1293 | // Get identifier properties. |
1294 | vkGetPhysicalDeviceProperties(gpu, &gpu_props); |
1295 | |
1296 | device_name = gpu_props.deviceName; |
1297 | device_type = gpu_props.deviceType; |
1298 | pipeline_cache_id = String::hex_encode_buffer(gpu_props.pipelineCacheUUID, VK_UUID_SIZE); |
1299 | pipeline_cache_id += "-driver-" + itos(gpu_props.driverVersion); |
1300 | { |
1301 | device_vendor = "Unknown" ; |
1302 | uint32_t vendor_idx = 0; |
1303 | while (vendor_names[vendor_idx].name != nullptr) { |
1304 | if (gpu_props.vendorID == vendor_names[vendor_idx].id) { |
1305 | device_vendor = vendor_names[vendor_idx].name; |
1306 | break; |
1307 | } |
1308 | vendor_idx++; |
1309 | } |
1310 | } |
1311 | |
1312 | // Get device version |
1313 | device_api_version = gpu_props.apiVersion; |
1314 | |
1315 | String rendering_method; |
1316 | if (OS::get_singleton()->get_current_rendering_method() == "mobile" ) { |
1317 | rendering_method = "Forward Mobile" ; |
1318 | } else { |
1319 | rendering_method = "Forward+" ; |
1320 | } |
1321 | |
1322 | // Output our device version |
1323 | print_line(vformat("Vulkan API %s - %s - Using Vulkan Device #%d: %s - %s" , get_device_api_version(), rendering_method, device_index, device_vendor, device_name)); |
1324 | |
1325 | { |
1326 | Error _err = _initialize_device_extensions(); |
1327 | if (_err != OK) { |
1328 | return _err; |
1329 | } |
1330 | } |
1331 | |
1332 | // Call with nullptr data to get count. |
1333 | vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queue_family_count, nullptr); |
1334 | ERR_FAIL_COND_V(queue_family_count == 0, ERR_CANT_CREATE); |
1335 | |
1336 | queue_props = (VkQueueFamilyProperties *)malloc(queue_family_count * sizeof(VkQueueFamilyProperties)); |
1337 | vkGetPhysicalDeviceQueueFamilyProperties(gpu, &queue_family_count, queue_props); |
1338 | // Query fine-grained feature support for this device. |
1339 | // If app has specific feature requirements it should check supported |
1340 | // features based on this query |
1341 | vkGetPhysicalDeviceFeatures(gpu, &physical_device_features); |
1342 | |
1343 | // Check required features and abort if any of them is missing. |
1344 | if (!physical_device_features.imageCubeArray || !physical_device_features.independentBlend) { |
1345 | String error_string = vformat("Your GPU (%s) does not support the following features which are required to use Vulkan-based renderers in Godot:\n\n" , device_name); |
1346 | if (!physical_device_features.imageCubeArray) { |
1347 | error_string += "- No support for image cube arrays.\n" ; |
1348 | } |
1349 | if (!physical_device_features.independentBlend) { |
1350 | error_string += "- No support for independentBlend.\n" ; |
1351 | } |
1352 | error_string += "\nThis is usually a hardware limitation, so updating graphics drivers won't help in most cases." ; |
1353 | |
1354 | #if defined(ANDROID_ENABLED) || defined(IOS_ENABLED) |
1355 | // Android/iOS platform ports currently don't exit themselves when this method returns `ERR_CANT_CREATE`. |
1356 | OS::get_singleton()->alert(error_string + "\nClick OK to exit (black screen will be visible)." ); |
1357 | #else |
1358 | OS::get_singleton()->alert(error_string + "\nClick OK to exit." ); |
1359 | #endif |
1360 | |
1361 | return ERR_CANT_CREATE; |
1362 | } |
1363 | |
1364 | physical_device_features.robustBufferAccess = false; // Turn off robust buffer access, which can hamper performance on some hardware. |
1365 | |
1366 | #define GET_INSTANCE_PROC_ADDR(inst, entrypoint) \ |
1367 | { \ |
1368 | fp##entrypoint = (PFN_vk##entrypoint)vkGetInstanceProcAddr(inst, "vk" #entrypoint); \ |
1369 | ERR_FAIL_COND_V_MSG(fp##entrypoint == nullptr, ERR_CANT_CREATE, \ |
1370 | "vkGetInstanceProcAddr failed to find vk" #entrypoint); \ |
1371 | } |
1372 | |
1373 | GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfaceSupportKHR); |
1374 | GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfaceCapabilitiesKHR); |
1375 | GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfaceFormatsKHR); |
1376 | GET_INSTANCE_PROC_ADDR(inst, GetPhysicalDeviceSurfacePresentModesKHR); |
1377 | GET_INSTANCE_PROC_ADDR(inst, GetSwapchainImagesKHR); |
1378 | |
1379 | // Gets capability info for current Vulkan driver. |
1380 | { |
1381 | Error res = _check_capabilities(); |
1382 | if (res != OK) { |
1383 | return res; |
1384 | } |
1385 | } |
1386 | |
1387 | device_initialized = true; |
1388 | return OK; |
1389 | } |
1390 | |
1391 | Error VulkanContext::_create_device() { |
1392 | VkResult err; |
1393 | float queue_priorities[1] = { 0.0 }; |
1394 | VkDeviceQueueCreateInfo queues[2]; |
1395 | queues[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; |
1396 | queues[0].pNext = nullptr; |
1397 | queues[0].queueFamilyIndex = graphics_queue_family_index; |
1398 | queues[0].queueCount = 1; |
1399 | queues[0].pQueuePriorities = queue_priorities; |
1400 | queues[0].flags = 0; |
1401 | |
1402 | // Before we retrieved what is supported, here we tell Vulkan we want to enable these features using the same structs. |
1403 | void *nextptr = nullptr; |
1404 | |
1405 | VkPhysicalDeviceShaderFloat16Int8FeaturesKHR shader_features = { |
1406 | /*sType*/ VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_FLOAT16_INT8_FEATURES_KHR, |
1407 | /*pNext*/ nextptr, |
1408 | /*shaderFloat16*/ shader_capabilities.shader_float16_is_supported, |
1409 | /*shaderInt8*/ shader_capabilities.shader_int8_is_supported, |
1410 | }; |
1411 | nextptr = &shader_features; |
1412 | |
1413 | VkPhysicalDeviceFragmentShadingRateFeaturesKHR vrs_features = {}; |
1414 | if (vrs_capabilities.pipeline_vrs_supported || vrs_capabilities.primitive_vrs_supported || vrs_capabilities.attachment_vrs_supported) { |
1415 | // Insert into our chain to enable these features if they are available. |
1416 | vrs_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FRAGMENT_SHADING_RATE_FEATURES_KHR; |
1417 | vrs_features.pNext = nextptr; |
1418 | vrs_features.pipelineFragmentShadingRate = vrs_capabilities.pipeline_vrs_supported; |
1419 | vrs_features.primitiveFragmentShadingRate = vrs_capabilities.primitive_vrs_supported; |
1420 | vrs_features.attachmentFragmentShadingRate = vrs_capabilities.attachment_vrs_supported; |
1421 | |
1422 | nextptr = &vrs_features; |
1423 | } |
1424 | |
1425 | VkPhysicalDeviceVulkan11Features vulkan11features = {}; |
1426 | VkPhysicalDevice16BitStorageFeaturesKHR storage_feature = {}; |
1427 | VkPhysicalDeviceMultiviewFeatures multiview_features = {}; |
1428 | if (device_api_version >= VK_API_VERSION_1_2) { |
1429 | // In Vulkan 1.2 and newer we use a newer struct to enable various features. |
1430 | |
1431 | vulkan11features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; |
1432 | vulkan11features.pNext = nextptr; |
1433 | vulkan11features.storageBuffer16BitAccess = storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported; |
1434 | vulkan11features.uniformAndStorageBuffer16BitAccess = storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported; |
1435 | vulkan11features.storagePushConstant16 = storage_buffer_capabilities.storage_push_constant_16_is_supported; |
1436 | vulkan11features.storageInputOutput16 = storage_buffer_capabilities.storage_input_output_16; |
1437 | vulkan11features.multiview = multiview_capabilities.is_supported; |
1438 | vulkan11features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; |
1439 | vulkan11features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; |
1440 | vulkan11features.variablePointersStorageBuffer = 0; |
1441 | vulkan11features.variablePointers = 0; |
1442 | vulkan11features.protectedMemory = 0; |
1443 | vulkan11features.samplerYcbcrConversion = 0; |
1444 | vulkan11features.shaderDrawParameters = 0; |
1445 | nextptr = &vulkan11features; |
1446 | } else { |
1447 | // On Vulkan 1.0 and 1.1 we use our older structs to initialize these features. |
1448 | storage_feature.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR; |
1449 | storage_feature.pNext = nextptr; |
1450 | storage_feature.storageBuffer16BitAccess = storage_buffer_capabilities.storage_buffer_16_bit_access_is_supported; |
1451 | storage_feature.uniformAndStorageBuffer16BitAccess = storage_buffer_capabilities.uniform_and_storage_buffer_16_bit_access_is_supported; |
1452 | storage_feature.storagePushConstant16 = storage_buffer_capabilities.storage_push_constant_16_is_supported; |
1453 | storage_feature.storageInputOutput16 = storage_buffer_capabilities.storage_input_output_16; |
1454 | nextptr = &storage_feature; |
1455 | |
1456 | if (device_api_version >= VK_API_VERSION_1_1) { // any Vulkan 1.1.x version |
1457 | multiview_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MULTIVIEW_FEATURES; |
1458 | multiview_features.pNext = nextptr; |
1459 | multiview_features.multiview = multiview_capabilities.is_supported; |
1460 | multiview_features.multiviewGeometryShader = multiview_capabilities.geometry_shader_is_supported; |
1461 | multiview_features.multiviewTessellationShader = multiview_capabilities.tessellation_shader_is_supported; |
1462 | nextptr = &multiview_features; |
1463 | } |
1464 | } |
1465 | |
1466 | uint32_t enabled_extension_count = 0; |
1467 | const char *enabled_extension_names[MAX_EXTENSIONS]; |
1468 | ERR_FAIL_COND_V(enabled_device_extension_names.size() > MAX_EXTENSIONS, ERR_CANT_CREATE); |
1469 | for (const CharString &extension_name : enabled_device_extension_names) { |
1470 | enabled_extension_names[enabled_extension_count++] = extension_name.ptr(); |
1471 | } |
1472 | |
1473 | VkDeviceCreateInfo sdevice = { |
1474 | /*sType*/ VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, |
1475 | /*pNext*/ nextptr, |
1476 | /*flags*/ 0, |
1477 | /*queueCreateInfoCount*/ 1, |
1478 | /*pQueueCreateInfos*/ queues, |
1479 | /*enabledLayerCount*/ 0, |
1480 | /*ppEnabledLayerNames*/ nullptr, |
1481 | /*enabledExtensionCount*/ enabled_extension_count, |
1482 | /*ppEnabledExtensionNames*/ (const char *const *)enabled_extension_names, |
1483 | /*pEnabledFeatures*/ &physical_device_features, // If specific features are required, pass them in here. |
1484 | }; |
1485 | if (separate_present_queue) { |
1486 | queues[1].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; |
1487 | queues[1].pNext = nullptr; |
1488 | queues[1].queueFamilyIndex = present_queue_family_index; |
1489 | queues[1].queueCount = 1; |
1490 | queues[1].pQueuePriorities = queue_priorities; |
1491 | queues[1].flags = 0; |
1492 | sdevice.queueCreateInfoCount = 2; |
1493 | } |
1494 | |
1495 | if (vulkan_hooks) { |
1496 | if (!vulkan_hooks->create_vulkan_device(&sdevice, &device)) { |
1497 | return ERR_CANT_CREATE; |
1498 | } |
1499 | } else { |
1500 | err = vkCreateDevice(gpu, &sdevice, nullptr, &device); |
1501 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
1502 | } |
1503 | |
1504 | return OK; |
1505 | } |
1506 | |
1507 | Error VulkanContext::_initialize_queues(VkSurfaceKHR p_surface) { |
1508 | // Iterate over each queue to learn whether it supports presenting: |
1509 | VkBool32 *supportsPresent = (VkBool32 *)malloc(queue_family_count * sizeof(VkBool32)); |
1510 | for (uint32_t i = 0; i < queue_family_count; i++) { |
1511 | fpGetPhysicalDeviceSurfaceSupportKHR(gpu, i, p_surface, &supportsPresent[i]); |
1512 | } |
1513 | |
1514 | // Search for a graphics and a present queue in the array of queue |
1515 | // families, try to find one that supports both. |
1516 | uint32_t graphicsQueueFamilyIndex = UINT32_MAX; |
1517 | uint32_t presentQueueFamilyIndex = UINT32_MAX; |
1518 | for (uint32_t i = 0; i < queue_family_count; i++) { |
1519 | if ((queue_props[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) != 0) { |
1520 | if (graphicsQueueFamilyIndex == UINT32_MAX) { |
1521 | graphicsQueueFamilyIndex = i; |
1522 | } |
1523 | |
1524 | if (supportsPresent[i] == VK_TRUE) { |
1525 | graphicsQueueFamilyIndex = i; |
1526 | presentQueueFamilyIndex = i; |
1527 | break; |
1528 | } |
1529 | } |
1530 | } |
1531 | |
1532 | if (presentQueueFamilyIndex == UINT32_MAX) { |
1533 | // If didn't find a queue that supports both graphics and present, then |
1534 | // find a separate present queue. |
1535 | for (uint32_t i = 0; i < queue_family_count; ++i) { |
1536 | if (supportsPresent[i] == VK_TRUE) { |
1537 | presentQueueFamilyIndex = i; |
1538 | break; |
1539 | } |
1540 | } |
1541 | } |
1542 | |
1543 | free(supportsPresent); |
1544 | |
1545 | // Generate error if could not find both a graphics and a present queue. |
1546 | ERR_FAIL_COND_V_MSG(graphicsQueueFamilyIndex == UINT32_MAX || presentQueueFamilyIndex == UINT32_MAX, ERR_CANT_CREATE, |
1547 | "Could not find both graphics and present queues\n" ); |
1548 | |
1549 | graphics_queue_family_index = graphicsQueueFamilyIndex; |
1550 | present_queue_family_index = presentQueueFamilyIndex; |
1551 | separate_present_queue = (graphics_queue_family_index != present_queue_family_index); |
1552 | |
1553 | _create_device(); |
1554 | |
1555 | static PFN_vkGetDeviceProcAddr g_gdpa = nullptr; |
1556 | #define GET_DEVICE_PROC_ADDR(dev, entrypoint) \ |
1557 | { \ |
1558 | if (!g_gdpa) \ |
1559 | g_gdpa = (PFN_vkGetDeviceProcAddr)vkGetInstanceProcAddr(inst, "vkGetDeviceProcAddr"); \ |
1560 | fp##entrypoint = (PFN_vk##entrypoint)g_gdpa(dev, "vk" #entrypoint); \ |
1561 | ERR_FAIL_COND_V_MSG(fp##entrypoint == nullptr, ERR_CANT_CREATE, \ |
1562 | "vkGetDeviceProcAddr failed to find vk" #entrypoint); \ |
1563 | } |
1564 | |
1565 | GET_DEVICE_PROC_ADDR(device, CreateSwapchainKHR); |
1566 | GET_DEVICE_PROC_ADDR(device, DestroySwapchainKHR); |
1567 | GET_DEVICE_PROC_ADDR(device, GetSwapchainImagesKHR); |
1568 | GET_DEVICE_PROC_ADDR(device, AcquireNextImageKHR); |
1569 | GET_DEVICE_PROC_ADDR(device, QueuePresentKHR); |
1570 | if (is_device_extension_enabled(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME)) { |
1571 | GET_DEVICE_PROC_ADDR(device, GetRefreshCycleDurationGOOGLE); |
1572 | GET_DEVICE_PROC_ADDR(device, GetPastPresentationTimingGOOGLE); |
1573 | } |
1574 | |
1575 | vkGetDeviceQueue(device, graphics_queue_family_index, 0, &graphics_queue); |
1576 | |
1577 | if (!separate_present_queue) { |
1578 | present_queue = graphics_queue; |
1579 | } else { |
1580 | vkGetDeviceQueue(device, present_queue_family_index, 0, &present_queue); |
1581 | } |
1582 | |
1583 | // Get the list of VkFormat's that are supported: |
1584 | uint32_t formatCount; |
1585 | VkResult err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, p_surface, &formatCount, nullptr); |
1586 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
1587 | VkSurfaceFormatKHR *surfFormats = (VkSurfaceFormatKHR *)malloc(formatCount * sizeof(VkSurfaceFormatKHR)); |
1588 | err = fpGetPhysicalDeviceSurfaceFormatsKHR(gpu, p_surface, &formatCount, surfFormats); |
1589 | if (err) { |
1590 | free(surfFormats); |
1591 | ERR_FAIL_V(ERR_CANT_CREATE); |
1592 | } |
1593 | // If the format list includes just one entry of VK_FORMAT_UNDEFINED, |
1594 | // the surface has no preferred format. Otherwise, at least one |
1595 | // supported format will be returned. |
1596 | if (formatCount == 1 && surfFormats[0].format == VK_FORMAT_UNDEFINED) { |
1597 | format = VK_FORMAT_B8G8R8A8_UNORM; |
1598 | color_space = surfFormats[0].colorSpace; |
1599 | } else { |
1600 | // These should be ordered with the ones we want to use on top and fallback modes further down |
1601 | // we want a 32bit RGBA unsigned normalized buffer or similar. |
1602 | const VkFormat allowed_formats[] = { |
1603 | VK_FORMAT_B8G8R8A8_UNORM, |
1604 | VK_FORMAT_R8G8B8A8_UNORM |
1605 | }; |
1606 | uint32_t allowed_formats_count = sizeof(allowed_formats) / sizeof(VkFormat); |
1607 | |
1608 | if (formatCount < 1) { |
1609 | free(surfFormats); |
1610 | ERR_FAIL_V_MSG(ERR_CANT_CREATE, "formatCount less than 1" ); |
1611 | } |
1612 | |
1613 | // Find the first format that we support. |
1614 | format = VK_FORMAT_UNDEFINED; |
1615 | for (uint32_t af = 0; af < allowed_formats_count && format == VK_FORMAT_UNDEFINED; af++) { |
1616 | for (uint32_t sf = 0; sf < formatCount && format == VK_FORMAT_UNDEFINED; sf++) { |
1617 | if (surfFormats[sf].format == allowed_formats[af]) { |
1618 | format = surfFormats[sf].format; |
1619 | color_space = surfFormats[sf].colorSpace; |
1620 | } |
1621 | } |
1622 | } |
1623 | |
1624 | if (format == VK_FORMAT_UNDEFINED) { |
1625 | free(surfFormats); |
1626 | ERR_FAIL_V_MSG(ERR_CANT_CREATE, "No usable surface format found." ); |
1627 | } |
1628 | } |
1629 | |
1630 | free(surfFormats); |
1631 | |
1632 | Error serr = _create_semaphores(); |
1633 | if (serr) { |
1634 | return serr; |
1635 | } |
1636 | |
1637 | queues_initialized = true; |
1638 | return OK; |
1639 | } |
1640 | |
1641 | Error VulkanContext::_create_semaphores() { |
1642 | VkResult err; |
1643 | |
1644 | // Create semaphores to synchronize acquiring presentable buffers before |
1645 | // rendering and waiting for drawing to be complete before presenting. |
1646 | VkSemaphoreCreateInfo semaphoreCreateInfo = { |
1647 | /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, |
1648 | /*pNext*/ nullptr, |
1649 | /*flags*/ 0, |
1650 | }; |
1651 | |
1652 | // Create fences that we can use to throttle if we get too far |
1653 | // ahead of the image presents. |
1654 | VkFenceCreateInfo fence_ci = { |
1655 | /*sType*/ VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, |
1656 | /*pNext*/ nullptr, |
1657 | /*flags*/ VK_FENCE_CREATE_SIGNALED_BIT |
1658 | }; |
1659 | for (uint32_t i = 0; i < FRAME_LAG; i++) { |
1660 | err = vkCreateFence(device, &fence_ci, nullptr, &fences[i]); |
1661 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
1662 | |
1663 | err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &draw_complete_semaphores[i]); |
1664 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
1665 | |
1666 | if (separate_present_queue) { |
1667 | err = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &image_ownership_semaphores[i]); |
1668 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
1669 | } |
1670 | } |
1671 | frame_index = 0; |
1672 | |
1673 | // Get Memory information and properties. |
1674 | vkGetPhysicalDeviceMemoryProperties(gpu, &memory_properties); |
1675 | |
1676 | return OK; |
1677 | } |
1678 | |
1679 | bool VulkanContext::_use_validation_layers() { |
1680 | return Engine::get_singleton()->is_validation_layers_enabled(); |
1681 | } |
1682 | |
1683 | VkExtent2D VulkanContext::_compute_swapchain_extent(const VkSurfaceCapabilitiesKHR &p_surf_capabilities, int *p_window_width, int *p_window_height) const { |
1684 | // Width and height are either both 0xFFFFFFFF, or both not 0xFFFFFFFF. |
1685 | if (p_surf_capabilities.currentExtent.width == 0xFFFFFFFF) { |
1686 | // If the surface size is undefined, the size is set to the size |
1687 | // of the images requested, which must fit within the minimum and |
1688 | // maximum values. |
1689 | VkExtent2D extent = {}; |
1690 | extent.width = CLAMP((uint32_t)(*p_window_width), p_surf_capabilities.minImageExtent.width, p_surf_capabilities.maxImageExtent.width); |
1691 | extent.height = CLAMP((uint32_t)(*p_window_height), p_surf_capabilities.minImageExtent.height, p_surf_capabilities.maxImageExtent.height); |
1692 | return extent; |
1693 | } else { |
1694 | // If the surface size is defined, the swap chain size must match. |
1695 | *p_window_width = p_surf_capabilities.currentExtent.width; |
1696 | *p_window_height = p_surf_capabilities.currentExtent.height; |
1697 | return p_surf_capabilities.currentExtent; |
1698 | } |
1699 | } |
1700 | |
1701 | Error VulkanContext::_window_create(DisplayServer::WindowID p_window_id, DisplayServer::VSyncMode p_vsync_mode, VkSurfaceKHR p_surface, int p_width, int p_height) { |
1702 | ERR_FAIL_COND_V(windows.has(p_window_id), ERR_INVALID_PARAMETER); |
1703 | |
1704 | if (!device_initialized) { |
1705 | Error err = _create_physical_device(p_surface); |
1706 | ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); |
1707 | } |
1708 | |
1709 | if (!queues_initialized) { |
1710 | // We use a single GPU, but we need a surface to initialize the |
1711 | // queues, so this process must be deferred until a surface |
1712 | // is created. |
1713 | Error err = _initialize_queues(p_surface); |
1714 | ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); |
1715 | } |
1716 | |
1717 | Window window; |
1718 | window.surface = p_surface; |
1719 | window.width = p_width; |
1720 | window.height = p_height; |
1721 | window.vsync_mode = p_vsync_mode; |
1722 | Error err = _update_swap_chain(&window); |
1723 | ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); |
1724 | |
1725 | windows[p_window_id] = window; |
1726 | return OK; |
1727 | } |
1728 | |
1729 | void VulkanContext::window_resize(DisplayServer::WindowID p_window, int p_width, int p_height) { |
1730 | ERR_FAIL_COND(!windows.has(p_window)); |
1731 | windows[p_window].width = p_width; |
1732 | windows[p_window].height = p_height; |
1733 | _update_swap_chain(&windows[p_window]); |
1734 | } |
1735 | |
1736 | int VulkanContext::window_get_width(DisplayServer::WindowID p_window) { |
1737 | ERR_FAIL_COND_V(!windows.has(p_window), -1); |
1738 | return windows[p_window].width; |
1739 | } |
1740 | |
1741 | int VulkanContext::window_get_height(DisplayServer::WindowID p_window) { |
1742 | ERR_FAIL_COND_V(!windows.has(p_window), -1); |
1743 | return windows[p_window].height; |
1744 | } |
1745 | |
1746 | bool VulkanContext::window_is_valid_swapchain(DisplayServer::WindowID p_window) { |
1747 | ERR_FAIL_COND_V(!windows.has(p_window), false); |
1748 | Window *w = &windows[p_window]; |
1749 | return w->swapchain_image_resources != VK_NULL_HANDLE; |
1750 | } |
1751 | |
1752 | VkRenderPass VulkanContext::window_get_render_pass(DisplayServer::WindowID p_window) { |
1753 | ERR_FAIL_COND_V(!windows.has(p_window), VK_NULL_HANDLE); |
1754 | Window *w = &windows[p_window]; |
1755 | // Vulkan use of currentbuffer. |
1756 | return w->render_pass; |
1757 | } |
1758 | |
1759 | VkFramebuffer VulkanContext::window_get_framebuffer(DisplayServer::WindowID p_window) { |
1760 | ERR_FAIL_COND_V(!windows.has(p_window), VK_NULL_HANDLE); |
1761 | ERR_FAIL_COND_V(!buffers_prepared, VK_NULL_HANDLE); |
1762 | Window *w = &windows[p_window]; |
1763 | // Vulkan use of currentbuffer. |
1764 | if (w->swapchain_image_resources != VK_NULL_HANDLE) { |
1765 | return w->swapchain_image_resources[w->current_buffer].framebuffer; |
1766 | } else { |
1767 | return VK_NULL_HANDLE; |
1768 | } |
1769 | } |
1770 | |
1771 | void VulkanContext::window_destroy(DisplayServer::WindowID p_window_id) { |
1772 | ERR_FAIL_COND(!windows.has(p_window_id)); |
1773 | _clean_up_swap_chain(&windows[p_window_id]); |
1774 | |
1775 | vkDestroySurfaceKHR(inst, windows[p_window_id].surface, nullptr); |
1776 | windows.erase(p_window_id); |
1777 | } |
1778 | |
1779 | Error VulkanContext::_clean_up_swap_chain(Window *window) { |
1780 | if (!window->swapchain) { |
1781 | return OK; |
1782 | } |
1783 | vkDeviceWaitIdle(device); |
1784 | |
1785 | // This destroys images associated it seems. |
1786 | fpDestroySwapchainKHR(device, window->swapchain, nullptr); |
1787 | window->swapchain = VK_NULL_HANDLE; |
1788 | vkDestroyRenderPass(device, window->render_pass, nullptr); |
1789 | window->render_pass = VK_NULL_HANDLE; |
1790 | if (window->swapchain_image_resources) { |
1791 | for (uint32_t i = 0; i < swapchainImageCount; i++) { |
1792 | vkDestroyImageView(device, window->swapchain_image_resources[i].view, nullptr); |
1793 | vkDestroyFramebuffer(device, window->swapchain_image_resources[i].framebuffer, nullptr); |
1794 | } |
1795 | |
1796 | free(window->swapchain_image_resources); |
1797 | window->swapchain_image_resources = nullptr; |
1798 | swapchainImageCount = 0; |
1799 | } |
1800 | if (separate_present_queue) { |
1801 | vkDestroyCommandPool(device, window->present_cmd_pool, nullptr); |
1802 | } |
1803 | |
1804 | for (uint32_t i = 0; i < FRAME_LAG; i++) { |
1805 | // Destroy the semaphores now (we'll re-create it later if we have to). |
1806 | // We must do this because the semaphore cannot be reused if it's in a signaled state |
1807 | // (which happens if vkAcquireNextImageKHR returned VK_ERROR_OUT_OF_DATE_KHR or VK_SUBOPTIMAL_KHR) |
1808 | // The only way to reset it would be to present the swapchain... the one we just destroyed. |
1809 | // And the API has no way to "unsignal" the semaphore. |
1810 | vkDestroySemaphore(device, window->image_acquired_semaphores[i], nullptr); |
1811 | window->image_acquired_semaphores[i] = 0; |
1812 | } |
1813 | |
1814 | return OK; |
1815 | } |
1816 | |
1817 | Error VulkanContext::_update_swap_chain(Window *window) { |
1818 | VkResult err; |
1819 | |
1820 | if (window->swapchain) { |
1821 | _clean_up_swap_chain(window); |
1822 | } |
1823 | |
1824 | // Check the surface capabilities and formats. |
1825 | VkSurfaceCapabilitiesKHR surfCapabilities; |
1826 | err = fpGetPhysicalDeviceSurfaceCapabilitiesKHR(gpu, window->surface, &surfCapabilities); |
1827 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
1828 | |
1829 | { |
1830 | VkBool32 supports = VK_FALSE; |
1831 | err = vkGetPhysicalDeviceSurfaceSupportKHR( |
1832 | gpu, present_queue_family_index, window->surface, &supports); |
1833 | ERR_FAIL_COND_V_MSG(err != VK_SUCCESS || supports == false, ERR_CANT_CREATE, |
1834 | "Window's surface is not supported by device. Did the GPU go offline? Was the window " |
1835 | "created on another monitor? Check previous errors & try launching with " |
1836 | "--gpu-validation." ); |
1837 | } |
1838 | |
1839 | uint32_t presentModeCount; |
1840 | err = fpGetPhysicalDeviceSurfacePresentModesKHR(gpu, window->surface, &presentModeCount, nullptr); |
1841 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
1842 | VkPresentModeKHR *presentModes = (VkPresentModeKHR *)malloc(presentModeCount * sizeof(VkPresentModeKHR)); |
1843 | ERR_FAIL_COND_V(!presentModes, ERR_CANT_CREATE); |
1844 | err = fpGetPhysicalDeviceSurfacePresentModesKHR(gpu, window->surface, &presentModeCount, presentModes); |
1845 | if (err) { |
1846 | free(presentModes); |
1847 | ERR_FAIL_V(ERR_CANT_CREATE); |
1848 | } |
1849 | |
1850 | VkExtent2D swapchainExtent = _compute_swapchain_extent(surfCapabilities, &window->width, &window->height); |
1851 | |
1852 | if (window->width == 0 || window->height == 0) { |
1853 | free(presentModes); |
1854 | // Likely window minimized, no swapchain created. |
1855 | return ERR_SKIP; |
1856 | } |
1857 | // The FIFO present mode is guaranteed by the spec to be supported |
1858 | // and to have no tearing. It's a great default present mode to use. |
1859 | |
1860 | // There are times when you may wish to use another present mode. The |
1861 | // following code shows how to select them, and the comments provide some |
1862 | // reasons you may wish to use them. |
1863 | // |
1864 | // It should be noted that Vulkan 1.0 doesn't provide a method for |
1865 | // synchronizing rendering with the presentation engine's display. There |
1866 | // is a method provided for throttling rendering with the display, but |
1867 | // there are some presentation engines for which this method will not work. |
1868 | // If an application doesn't throttle its rendering, and if it renders much |
1869 | // faster than the refresh rate of the display, this can waste power on |
1870 | // mobile devices. That is because power is being spent rendering images |
1871 | // that may never be seen. |
1872 | |
1873 | // VK_PRESENT_MODE_IMMEDIATE_KHR is for applications that don't care about |
1874 | // tearing, or have some way of synchronizing their rendering with the |
1875 | // display. |
1876 | // VK_PRESENT_MODE_MAILBOX_KHR may be useful for applications that |
1877 | // generally render a new presentable image every refresh cycle, but are |
1878 | // occasionally early. In this case, the application wants the new image |
1879 | // to be displayed instead of the previously-queued-for-presentation image |
1880 | // that has not yet been displayed. |
1881 | // VK_PRESENT_MODE_FIFO_RELAXED_KHR is for applications that generally |
1882 | // render a new presentable image every refresh cycle, but are occasionally |
1883 | // late. In this case (perhaps because of stuttering/latency concerns), |
1884 | // the application wants the late image to be immediately displayed, even |
1885 | // though that may mean some tearing. |
1886 | |
1887 | VkPresentModeKHR requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR; |
1888 | switch (window->vsync_mode) { |
1889 | case DisplayServer::VSYNC_MAILBOX: |
1890 | requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_MAILBOX_KHR; |
1891 | break; |
1892 | case DisplayServer::VSYNC_ADAPTIVE: |
1893 | requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_RELAXED_KHR; |
1894 | break; |
1895 | case DisplayServer::VSYNC_ENABLED: |
1896 | requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_FIFO_KHR; |
1897 | break; |
1898 | case DisplayServer::VSYNC_DISABLED: |
1899 | requested_present_mode = VkPresentModeKHR::VK_PRESENT_MODE_IMMEDIATE_KHR; |
1900 | break; |
1901 | } |
1902 | |
1903 | // Check if the requested mode is available. |
1904 | bool present_mode_available = false; |
1905 | for (uint32_t i = 0; i < presentModeCount; i++) { |
1906 | if (presentModes[i] == requested_present_mode) { |
1907 | present_mode_available = true; |
1908 | } |
1909 | } |
1910 | |
1911 | // Set the windows present mode if it is available, otherwise FIFO is used (guaranteed supported). |
1912 | if (present_mode_available) { |
1913 | if (window->presentMode != requested_present_mode) { |
1914 | window->presentMode = requested_present_mode; |
1915 | print_verbose("Using present mode: " + String(string_VkPresentModeKHR(window->presentMode))); |
1916 | } |
1917 | } else { |
1918 | String present_mode_string; |
1919 | switch (window->vsync_mode) { |
1920 | case DisplayServer::VSYNC_MAILBOX: |
1921 | present_mode_string = "Mailbox" ; |
1922 | break; |
1923 | case DisplayServer::VSYNC_ADAPTIVE: |
1924 | present_mode_string = "Adaptive" ; |
1925 | break; |
1926 | case DisplayServer::VSYNC_ENABLED: |
1927 | present_mode_string = "Enabled" ; |
1928 | break; |
1929 | case DisplayServer::VSYNC_DISABLED: |
1930 | present_mode_string = "Disabled" ; |
1931 | break; |
1932 | } |
1933 | WARN_PRINT(vformat("The requested V-Sync mode %s is not available. Falling back to V-Sync mode Enabled." , present_mode_string)); |
1934 | window->vsync_mode = DisplayServer::VSYNC_ENABLED; // Set to default. |
1935 | } |
1936 | |
1937 | free(presentModes); |
1938 | |
1939 | // Determine the number of VkImages to use in the swap chain. |
1940 | // Application desires to acquire 3 images at a time for triple |
1941 | // buffering. |
1942 | uint32_t desiredNumOfSwapchainImages = 3; |
1943 | if (desiredNumOfSwapchainImages < surfCapabilities.minImageCount) { |
1944 | desiredNumOfSwapchainImages = surfCapabilities.minImageCount; |
1945 | } |
1946 | // If maxImageCount is 0, we can ask for as many images as we want; |
1947 | // otherwise we're limited to maxImageCount. |
1948 | if ((surfCapabilities.maxImageCount > 0) && (desiredNumOfSwapchainImages > surfCapabilities.maxImageCount)) { |
1949 | // Application must settle for fewer images than desired. |
1950 | desiredNumOfSwapchainImages = surfCapabilities.maxImageCount; |
1951 | } |
1952 | |
1953 | VkSurfaceTransformFlagsKHR preTransform; |
1954 | if (surfCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { |
1955 | preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; |
1956 | } else { |
1957 | preTransform = surfCapabilities.currentTransform; |
1958 | } |
1959 | |
1960 | VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; |
1961 | |
1962 | if (OS::get_singleton()->is_layered_allowed() || !(surfCapabilities.supportedCompositeAlpha & compositeAlpha)) { |
1963 | // Find a supported composite alpha mode - one of these is guaranteed to be set. |
1964 | VkCompositeAlphaFlagBitsKHR compositeAlphaFlags[4] = { |
1965 | VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR, |
1966 | VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR, |
1967 | VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, |
1968 | VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR, |
1969 | }; |
1970 | |
1971 | for (uint32_t i = 0; i < ARRAY_SIZE(compositeAlphaFlags); i++) { |
1972 | if (surfCapabilities.supportedCompositeAlpha & compositeAlphaFlags[i]) { |
1973 | compositeAlpha = compositeAlphaFlags[i]; |
1974 | break; |
1975 | } |
1976 | } |
1977 | } |
1978 | |
1979 | VkSwapchainCreateInfoKHR swapchain_ci = { |
1980 | /*sType*/ VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR, |
1981 | /*pNext*/ nullptr, |
1982 | /*flags*/ 0, |
1983 | /*surface*/ window->surface, |
1984 | /*minImageCount*/ desiredNumOfSwapchainImages, |
1985 | /*imageFormat*/ format, |
1986 | /*imageColorSpace*/ color_space, |
1987 | /*imageExtent*/ { |
1988 | /*width*/ swapchainExtent.width, |
1989 | /*height*/ swapchainExtent.height, |
1990 | }, |
1991 | /*imageArrayLayers*/ 1, |
1992 | /*imageUsage*/ VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, |
1993 | /*imageSharingMode*/ VK_SHARING_MODE_EXCLUSIVE, |
1994 | /*queueFamilyIndexCount*/ 0, |
1995 | /*pQueueFamilyIndices*/ nullptr, |
1996 | /*preTransform*/ (VkSurfaceTransformFlagBitsKHR)preTransform, |
1997 | /*compositeAlpha*/ compositeAlpha, |
1998 | /*presentMode*/ window->presentMode, |
1999 | /*clipped*/ true, |
2000 | /*oldSwapchain*/ VK_NULL_HANDLE, |
2001 | }; |
2002 | |
2003 | err = fpCreateSwapchainKHR(device, &swapchain_ci, nullptr, &window->swapchain); |
2004 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
2005 | |
2006 | uint32_t sp_image_count; |
2007 | err = fpGetSwapchainImagesKHR(device, window->swapchain, &sp_image_count, nullptr); |
2008 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
2009 | |
2010 | if (swapchainImageCount == 0) { |
2011 | // Assign here for the first time. |
2012 | swapchainImageCount = sp_image_count; |
2013 | } else { |
2014 | ERR_FAIL_COND_V(swapchainImageCount != sp_image_count, ERR_BUG); |
2015 | } |
2016 | |
2017 | VkImage *swapchainImages = (VkImage *)malloc(swapchainImageCount * sizeof(VkImage)); |
2018 | ERR_FAIL_COND_V(!swapchainImages, ERR_CANT_CREATE); |
2019 | err = fpGetSwapchainImagesKHR(device, window->swapchain, &swapchainImageCount, swapchainImages); |
2020 | if (err) { |
2021 | free(swapchainImages); |
2022 | ERR_FAIL_V(ERR_CANT_CREATE); |
2023 | } |
2024 | |
2025 | window->swapchain_image_resources = |
2026 | (SwapchainImageResources *)malloc(sizeof(SwapchainImageResources) * swapchainImageCount); |
2027 | if (!window->swapchain_image_resources) { |
2028 | free(swapchainImages); |
2029 | ERR_FAIL_V(ERR_CANT_CREATE); |
2030 | } |
2031 | |
2032 | for (uint32_t i = 0; i < swapchainImageCount; i++) { |
2033 | VkImageViewCreateInfo color_image_view = { |
2034 | /*sType*/ VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO, |
2035 | /*pNext*/ nullptr, |
2036 | /*flags*/ 0, |
2037 | /*image*/ swapchainImages[i], |
2038 | /*viewType*/ VK_IMAGE_VIEW_TYPE_2D, |
2039 | /*format*/ format, |
2040 | /*components*/ { |
2041 | /*r*/ VK_COMPONENT_SWIZZLE_R, |
2042 | /*g*/ VK_COMPONENT_SWIZZLE_G, |
2043 | /*b*/ VK_COMPONENT_SWIZZLE_B, |
2044 | /*a*/ VK_COMPONENT_SWIZZLE_A, |
2045 | }, |
2046 | /*subresourceRange*/ { /*aspectMask*/ VK_IMAGE_ASPECT_COLOR_BIT, |
2047 | /*baseMipLevel*/ 0, |
2048 | /*levelCount*/ 1, |
2049 | /*baseArrayLayer*/ 0, |
2050 | /*layerCount*/ 1 }, |
2051 | }; |
2052 | |
2053 | window->swapchain_image_resources[i].image = swapchainImages[i]; |
2054 | |
2055 | color_image_view.image = window->swapchain_image_resources[i].image; |
2056 | |
2057 | err = vkCreateImageView(device, &color_image_view, nullptr, &window->swapchain_image_resources[i].view); |
2058 | if (err) { |
2059 | free(swapchainImages); |
2060 | ERR_FAIL_V(ERR_CANT_CREATE); |
2061 | } |
2062 | } |
2063 | |
2064 | free(swapchainImages); |
2065 | |
2066 | /******** FRAMEBUFFER ************/ |
2067 | |
2068 | { |
2069 | const VkAttachmentDescription2KHR attachment = { |
2070 | /*sType*/ VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR, |
2071 | /*pNext*/ nullptr, |
2072 | /*flags*/ 0, |
2073 | /*format*/ format, |
2074 | /*samples*/ VK_SAMPLE_COUNT_1_BIT, |
2075 | /*loadOp*/ VK_ATTACHMENT_LOAD_OP_CLEAR, |
2076 | /*storeOp*/ VK_ATTACHMENT_STORE_OP_STORE, |
2077 | /*stencilLoadOp*/ VK_ATTACHMENT_LOAD_OP_DONT_CARE, |
2078 | /*stencilStoreOp*/ VK_ATTACHMENT_STORE_OP_DONT_CARE, |
2079 | /*initialLayout*/ VK_IMAGE_LAYOUT_UNDEFINED, |
2080 | /*finalLayout*/ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, |
2081 | |
2082 | }; |
2083 | const VkAttachmentReference2KHR color_reference = { |
2084 | /*sType*/ VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR, |
2085 | /*pNext*/ nullptr, |
2086 | /*attachment*/ 0, |
2087 | /*layout*/ VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL, |
2088 | /*aspectMask*/ 0, |
2089 | }; |
2090 | |
2091 | const VkSubpassDescription2KHR subpass = { |
2092 | /*sType*/ VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR, |
2093 | /*pNext*/ nullptr, |
2094 | /*flags*/ 0, |
2095 | /*pipelineBindPoint*/ VK_PIPELINE_BIND_POINT_GRAPHICS, |
2096 | /*viewMask*/ 0, |
2097 | /*inputAttachmentCount*/ 0, |
2098 | /*pInputAttachments*/ nullptr, |
2099 | /*colorAttachmentCount*/ 1, |
2100 | /*pColorAttachments*/ &color_reference, |
2101 | /*pResolveAttachments*/ nullptr, |
2102 | /*pDepthStencilAttachment*/ nullptr, |
2103 | /*preserveAttachmentCount*/ 0, |
2104 | /*pPreserveAttachments*/ nullptr, |
2105 | }; |
2106 | |
2107 | const VkRenderPassCreateInfo2KHR rp_info = { |
2108 | /*sType*/ VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR, |
2109 | /*pNext*/ nullptr, |
2110 | /*flags*/ 0, |
2111 | /*attachmentCount*/ 1, |
2112 | /*pAttachments*/ &attachment, |
2113 | /*subpassCount*/ 1, |
2114 | /*pSubpasses*/ &subpass, |
2115 | /*dependencyCount*/ 0, |
2116 | /*pDependencies*/ nullptr, |
2117 | /*correlatedViewMaskCount*/ 0, |
2118 | /*pCorrelatedViewMasks*/ nullptr, |
2119 | }; |
2120 | |
2121 | err = vkCreateRenderPass2KHR(device, &rp_info, nullptr, &window->render_pass); |
2122 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
2123 | |
2124 | for (uint32_t i = 0; i < swapchainImageCount; i++) { |
2125 | const VkFramebufferCreateInfo fb_info = { |
2126 | /*sType*/ VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO, |
2127 | /*pNext*/ nullptr, |
2128 | /*flags*/ 0, |
2129 | /*renderPass*/ window->render_pass, |
2130 | /*attachmentCount*/ 1, |
2131 | /*pAttachments*/ &window->swapchain_image_resources[i].view, |
2132 | /*width*/ (uint32_t)window->width, |
2133 | /*height*/ (uint32_t)window->height, |
2134 | /*layers*/ 1, |
2135 | }; |
2136 | |
2137 | err = vkCreateFramebuffer(device, &fb_info, nullptr, &window->swapchain_image_resources[i].framebuffer); |
2138 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
2139 | } |
2140 | } |
2141 | |
2142 | /******** SEPARATE PRESENT QUEUE ************/ |
2143 | |
2144 | if (separate_present_queue) { |
2145 | const VkCommandPoolCreateInfo present_cmd_pool_info = { |
2146 | /*sType*/ VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO, |
2147 | /*pNext*/ nullptr, |
2148 | /*flags*/ 0, |
2149 | /*queueFamilyIndex*/ present_queue_family_index, |
2150 | }; |
2151 | err = vkCreateCommandPool(device, &present_cmd_pool_info, nullptr, &window->present_cmd_pool); |
2152 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
2153 | const VkCommandBufferAllocateInfo present_cmd_info = { |
2154 | /*sType*/ VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO, |
2155 | /*pNext*/ nullptr, |
2156 | /*commandPool*/ window->present_cmd_pool, |
2157 | /*level*/ VK_COMMAND_BUFFER_LEVEL_PRIMARY, |
2158 | /*commandBufferCount*/ 1, |
2159 | }; |
2160 | for (uint32_t i = 0; i < swapchainImageCount; i++) { |
2161 | err = vkAllocateCommandBuffers(device, &present_cmd_info, |
2162 | &window->swapchain_image_resources[i].graphics_to_present_cmd); |
2163 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
2164 | |
2165 | const VkCommandBufferBeginInfo cmd_buf_info = { |
2166 | /*sType*/ VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, |
2167 | /*pNext*/ nullptr, |
2168 | /*flags*/ VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT, |
2169 | /*pInheritanceInfo*/ nullptr, |
2170 | }; |
2171 | err = vkBeginCommandBuffer(window->swapchain_image_resources[i].graphics_to_present_cmd, &cmd_buf_info); |
2172 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
2173 | |
2174 | VkImageMemoryBarrier image_ownership_barrier = { |
2175 | /*sType*/ VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER, |
2176 | /*pNext*/ nullptr, |
2177 | /*srcAccessMask*/ 0, |
2178 | /*dstAccessMask*/ VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, |
2179 | /*oldLayout*/ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, |
2180 | /*newLayout*/ VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, |
2181 | /*srcQueueFamilyIndex*/ graphics_queue_family_index, |
2182 | /*dstQueueFamilyIndex*/ present_queue_family_index, |
2183 | /*image*/ window->swapchain_image_resources[i].image, |
2184 | /*subresourceRange*/ { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } |
2185 | }; |
2186 | |
2187 | vkCmdPipelineBarrier(window->swapchain_image_resources[i].graphics_to_present_cmd, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, |
2188 | VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_ownership_barrier); |
2189 | err = vkEndCommandBuffer(window->swapchain_image_resources[i].graphics_to_present_cmd); |
2190 | ERR_FAIL_COND_V(err, ERR_CANT_CREATE); |
2191 | } |
2192 | } |
2193 | |
2194 | // Reset current buffer. |
2195 | window->current_buffer = 0; |
2196 | |
2197 | VkSemaphoreCreateInfo semaphoreCreateInfo = { |
2198 | /*sType*/ VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, |
2199 | /*pNext*/ nullptr, |
2200 | /*flags*/ 0, |
2201 | }; |
2202 | |
2203 | for (uint32_t i = 0; i < FRAME_LAG; i++) { |
2204 | VkResult vkerr = vkCreateSemaphore(device, &semaphoreCreateInfo, nullptr, &window->image_acquired_semaphores[i]); |
2205 | ERR_FAIL_COND_V(vkerr, ERR_CANT_CREATE); |
2206 | } |
2207 | |
2208 | return OK; |
2209 | } |
2210 | |
2211 | Error VulkanContext::initialize() { |
2212 | #ifdef USE_VOLK |
2213 | if (volkInitialize() != VK_SUCCESS) { |
2214 | return FAILED; |
2215 | } |
2216 | #endif |
2217 | |
2218 | Error err = _create_instance(); |
2219 | if (err != OK) { |
2220 | return err; |
2221 | } |
2222 | |
2223 | return OK; |
2224 | } |
2225 | |
2226 | void VulkanContext::set_setup_buffer(VkCommandBuffer p_command_buffer) { |
2227 | command_buffer_queue.write[0] = p_command_buffer; |
2228 | } |
2229 | |
2230 | void VulkanContext::append_command_buffer(VkCommandBuffer p_command_buffer) { |
2231 | if (command_buffer_queue.size() <= command_buffer_count) { |
2232 | command_buffer_queue.resize(command_buffer_count + 1); |
2233 | } |
2234 | |
2235 | command_buffer_queue.write[command_buffer_count] = p_command_buffer; |
2236 | command_buffer_count++; |
2237 | } |
2238 | |
2239 | void VulkanContext::flush(bool p_flush_setup, bool p_flush_pending) { |
2240 | // Ensure everything else pending is executed. |
2241 | vkDeviceWaitIdle(device); |
2242 | |
2243 | // Flush the pending setup buffer. |
2244 | |
2245 | bool setup_flushable = p_flush_setup && command_buffer_queue[0]; |
2246 | bool pending_flushable = p_flush_pending && command_buffer_count > 1; |
2247 | |
2248 | if (setup_flushable) { |
2249 | // Use a fence to wait for everything done. |
2250 | VkSubmitInfo submit_info; |
2251 | submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
2252 | submit_info.pNext = nullptr; |
2253 | submit_info.pWaitDstStageMask = nullptr; |
2254 | submit_info.waitSemaphoreCount = 0; |
2255 | submit_info.pWaitSemaphores = nullptr; |
2256 | submit_info.commandBufferCount = 1; |
2257 | submit_info.pCommandBuffers = command_buffer_queue.ptr(); |
2258 | submit_info.signalSemaphoreCount = pending_flushable ? 1 : 0; |
2259 | submit_info.pSignalSemaphores = pending_flushable ? &draw_complete_semaphores[frame_index] : nullptr; |
2260 | VkResult err = vkQueueSubmit(graphics_queue, 1, &submit_info, VK_NULL_HANDLE); |
2261 | command_buffer_queue.write[0] = nullptr; |
2262 | ERR_FAIL_COND(err); |
2263 | } |
2264 | |
2265 | if (pending_flushable) { |
2266 | // Use a fence to wait for everything to finish. |
2267 | |
2268 | VkSubmitInfo submit_info; |
2269 | submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
2270 | submit_info.pNext = nullptr; |
2271 | VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; |
2272 | submit_info.pWaitDstStageMask = setup_flushable ? &wait_stage_mask : nullptr; |
2273 | submit_info.waitSemaphoreCount = setup_flushable ? 1 : 0; |
2274 | submit_info.pWaitSemaphores = setup_flushable ? &draw_complete_semaphores[frame_index] : nullptr; |
2275 | submit_info.commandBufferCount = command_buffer_count - 1; |
2276 | submit_info.pCommandBuffers = command_buffer_queue.ptr() + 1; |
2277 | submit_info.signalSemaphoreCount = 0; |
2278 | submit_info.pSignalSemaphores = nullptr; |
2279 | VkResult err = vkQueueSubmit(graphics_queue, 1, &submit_info, VK_NULL_HANDLE); |
2280 | command_buffer_count = 1; |
2281 | ERR_FAIL_COND(err); |
2282 | } |
2283 | |
2284 | vkDeviceWaitIdle(device); |
2285 | } |
2286 | |
2287 | Error VulkanContext::prepare_buffers() { |
2288 | if (!queues_initialized) { |
2289 | return OK; |
2290 | } |
2291 | |
2292 | VkResult err; |
2293 | |
2294 | // Ensure no more than FRAME_LAG renderings are outstanding. |
2295 | vkWaitForFences(device, 1, &fences[frame_index], VK_TRUE, UINT64_MAX); |
2296 | vkResetFences(device, 1, &fences[frame_index]); |
2297 | |
2298 | for (KeyValue<int, Window> &E : windows) { |
2299 | Window *w = &E.value; |
2300 | |
2301 | w->semaphore_acquired = false; |
2302 | |
2303 | if (w->swapchain == VK_NULL_HANDLE) { |
2304 | continue; |
2305 | } |
2306 | |
2307 | do { |
2308 | // Get the index of the next available swapchain image. |
2309 | err = |
2310 | fpAcquireNextImageKHR(device, w->swapchain, UINT64_MAX, |
2311 | w->image_acquired_semaphores[frame_index], VK_NULL_HANDLE, &w->current_buffer); |
2312 | |
2313 | if (err == VK_ERROR_OUT_OF_DATE_KHR) { |
2314 | // Swapchain is out of date (e.g. the window was resized) and |
2315 | // must be recreated. |
2316 | print_verbose("Vulkan: Early out of date swapchain, recreating." ); |
2317 | // resize_notify(); |
2318 | _update_swap_chain(w); |
2319 | } else if (err == VK_SUBOPTIMAL_KHR) { |
2320 | // Swapchain is not as optimal as it could be, but the platform's |
2321 | // presentation engine will still present the image correctly. |
2322 | print_verbose("Vulkan: Early suboptimal swapchain, recreating." ); |
2323 | Error swap_chain_err = _update_swap_chain(w); |
2324 | if (swap_chain_err == ERR_SKIP) { |
2325 | break; |
2326 | } |
2327 | } else if (err != VK_SUCCESS) { |
2328 | ERR_BREAK_MSG(err != VK_SUCCESS, "Vulkan: Did not create swapchain successfully. Error code: " + String(string_VkResult(err))); |
2329 | } else { |
2330 | w->semaphore_acquired = true; |
2331 | } |
2332 | } while (err != VK_SUCCESS); |
2333 | } |
2334 | |
2335 | buffers_prepared = true; |
2336 | |
2337 | return OK; |
2338 | } |
2339 | |
2340 | Error VulkanContext::swap_buffers() { |
2341 | if (!queues_initialized) { |
2342 | return OK; |
2343 | } |
2344 | |
2345 | // print_line("swapbuffers?"); |
2346 | VkResult err; |
2347 | |
2348 | #if 0 |
2349 | if (is_device_extension_enabled(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME)) { |
2350 | // Look at what happened to previous presents, and make appropriate |
2351 | // adjustments in timing. |
2352 | DemoUpdateTargetIPD(demo); |
2353 | |
2354 | // Note: a real application would position its geometry to that it's in |
2355 | // the correct location for when the next image is presented. It might |
2356 | // also wait, so that there's less latency between any input and when |
2357 | // the next image is rendered/presented. This demo program is so |
2358 | // simple that it doesn't do either of those. |
2359 | } |
2360 | #endif |
2361 | // Wait for the image acquired semaphore to be signaled to ensure |
2362 | // that the image won't be rendered to until the presentation |
2363 | // engine has fully released ownership to the application, and it is |
2364 | // okay to render to the image. |
2365 | |
2366 | const VkCommandBuffer *commands_ptr = nullptr; |
2367 | uint32_t commands_to_submit = 0; |
2368 | |
2369 | if (command_buffer_queue[0] == nullptr) { |
2370 | // No setup command, but commands to submit, submit from the first and skip command. |
2371 | if (command_buffer_count > 1) { |
2372 | commands_ptr = command_buffer_queue.ptr() + 1; |
2373 | commands_to_submit = command_buffer_count - 1; |
2374 | } |
2375 | } else { |
2376 | commands_ptr = command_buffer_queue.ptr(); |
2377 | commands_to_submit = command_buffer_count; |
2378 | } |
2379 | |
2380 | VkSemaphore *semaphores_to_acquire = (VkSemaphore *)alloca(windows.size() * sizeof(VkSemaphore)); |
2381 | VkPipelineStageFlags *pipe_stage_flags = (VkPipelineStageFlags *)alloca(windows.size() * sizeof(VkPipelineStageFlags)); |
2382 | uint32_t semaphores_to_acquire_count = 0; |
2383 | |
2384 | for (KeyValue<int, Window> &E : windows) { |
2385 | Window *w = &E.value; |
2386 | |
2387 | if (w->semaphore_acquired) { |
2388 | semaphores_to_acquire[semaphores_to_acquire_count] = w->image_acquired_semaphores[frame_index]; |
2389 | pipe_stage_flags[semaphores_to_acquire_count] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
2390 | semaphores_to_acquire_count++; |
2391 | } |
2392 | } |
2393 | |
2394 | VkSubmitInfo submit_info; |
2395 | submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
2396 | submit_info.pNext = nullptr; |
2397 | submit_info.waitSemaphoreCount = semaphores_to_acquire_count; |
2398 | submit_info.pWaitSemaphores = semaphores_to_acquire; |
2399 | submit_info.pWaitDstStageMask = pipe_stage_flags; |
2400 | submit_info.commandBufferCount = commands_to_submit; |
2401 | submit_info.pCommandBuffers = commands_ptr; |
2402 | submit_info.signalSemaphoreCount = 1; |
2403 | submit_info.pSignalSemaphores = &draw_complete_semaphores[frame_index]; |
2404 | err = vkQueueSubmit(graphics_queue, 1, &submit_info, fences[frame_index]); |
2405 | ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Vulkan: Cannot submit graphics queue. Error code: " + String(string_VkResult(err))); |
2406 | |
2407 | command_buffer_queue.write[0] = nullptr; |
2408 | command_buffer_count = 1; |
2409 | |
2410 | if (separate_present_queue) { |
2411 | // If we are using separate queues, change image ownership to the |
2412 | // present queue before presenting, waiting for the draw complete |
2413 | // semaphore and signaling the ownership released semaphore when finished. |
2414 | VkFence nullFence = VK_NULL_HANDLE; |
2415 | pipe_stage_flags[0] = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; |
2416 | submit_info.waitSemaphoreCount = 1; |
2417 | submit_info.pWaitSemaphores = &draw_complete_semaphores[frame_index]; |
2418 | submit_info.commandBufferCount = 0; |
2419 | |
2420 | VkCommandBuffer *cmdbufptr = (VkCommandBuffer *)alloca(sizeof(VkCommandBuffer *) * windows.size()); |
2421 | submit_info.pCommandBuffers = cmdbufptr; |
2422 | |
2423 | for (KeyValue<int, Window> &E : windows) { |
2424 | Window *w = &E.value; |
2425 | |
2426 | if (w->swapchain == VK_NULL_HANDLE) { |
2427 | continue; |
2428 | } |
2429 | cmdbufptr[submit_info.commandBufferCount] = w->swapchain_image_resources[w->current_buffer].graphics_to_present_cmd; |
2430 | submit_info.commandBufferCount++; |
2431 | } |
2432 | |
2433 | submit_info.signalSemaphoreCount = 1; |
2434 | submit_info.pSignalSemaphores = &image_ownership_semaphores[frame_index]; |
2435 | err = vkQueueSubmit(present_queue, 1, &submit_info, nullFence); |
2436 | ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Vulkan: Cannot submit present queue. Error code: " + String(string_VkResult(err))); |
2437 | } |
2438 | |
2439 | // If we are using separate queues, we have to wait for image ownership, |
2440 | // otherwise wait for draw complete. |
2441 | VkPresentInfoKHR present = { |
2442 | /*sType*/ VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, |
2443 | /*pNext*/ nullptr, |
2444 | /*waitSemaphoreCount*/ 1, |
2445 | /*pWaitSemaphores*/ (separate_present_queue) ? &image_ownership_semaphores[frame_index] : &draw_complete_semaphores[frame_index], |
2446 | /*swapchainCount*/ 0, |
2447 | /*pSwapchain*/ nullptr, |
2448 | /*pImageIndices*/ nullptr, |
2449 | /*pResults*/ nullptr, |
2450 | }; |
2451 | |
2452 | VkSwapchainKHR *pSwapchains = (VkSwapchainKHR *)alloca(sizeof(VkSwapchainKHR *) * windows.size()); |
2453 | uint32_t *pImageIndices = (uint32_t *)alloca(sizeof(uint32_t *) * windows.size()); |
2454 | |
2455 | present.pSwapchains = pSwapchains; |
2456 | present.pImageIndices = pImageIndices; |
2457 | |
2458 | for (KeyValue<int, Window> &E : windows) { |
2459 | Window *w = &E.value; |
2460 | |
2461 | if (w->swapchain == VK_NULL_HANDLE) { |
2462 | continue; |
2463 | } |
2464 | pSwapchains[present.swapchainCount] = w->swapchain; |
2465 | pImageIndices[present.swapchainCount] = w->current_buffer; |
2466 | present.swapchainCount++; |
2467 | } |
2468 | |
2469 | #if 0 |
2470 | if (is_device_extension_enabled(VK_KHR_incremental_present_enabled)) { |
2471 | // If using VK_KHR_incremental_present, we provide a hint of the region |
2472 | // that contains changed content relative to the previously-presented |
2473 | // image. The implementation can use this hint in order to save |
2474 | // work/power (by only copying the region in the hint). The |
2475 | // implementation is free to ignore the hint though, and so we must |
2476 | // ensure that the entire image has the correctly-drawn content. |
2477 | uint32_t eighthOfWidth = width / 8; |
2478 | uint32_t eighthOfHeight = height / 8; |
2479 | VkRectLayerKHR rect = { |
2480 | /*offset.x*/ eighthOfWidth, |
2481 | /*offset.y*/ eighthOfHeight, |
2482 | /*extent.width*/ eighthOfWidth * 6, |
2483 | /*extent.height*/ eighthOfHeight * 6, |
2484 | /*layer*/ 0, |
2485 | }; |
2486 | VkPresentRegionKHR region = { |
2487 | /*rectangleCount*/ 1, |
2488 | /*pRectangles*/ &rect, |
2489 | }; |
2490 | VkPresentRegionsKHR regions = { |
2491 | /*sType*/ VK_STRUCTURE_TYPE_PRESENT_REGIONS_KHR, |
2492 | /*pNext*/ present.pNext, |
2493 | /*swapchainCount*/ present.swapchainCount, |
2494 | /*pRegions*/ ®ion, |
2495 | }; |
2496 | present.pNext = ®ions; |
2497 | } |
2498 | #endif |
2499 | |
2500 | #if 0 |
2501 | if (is_device_extension_enabled(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME)) { |
2502 | VkPresentTimeGOOGLE ptime; |
2503 | if (prev_desired_present_time == 0) { |
2504 | // This must be the first present for this swapchain. |
2505 | // |
2506 | // We don't know where we are relative to the presentation engine's |
2507 | // display's refresh cycle. We also don't know how long rendering |
2508 | // takes. Let's make a grossly-simplified assumption that the |
2509 | // desiredPresentTime should be half way between now and |
2510 | // now+target_IPD. We will adjust over time. |
2511 | uint64_t curtime = getTimeInNanoseconds(); |
2512 | if (curtime == 0) { |
2513 | // Since we didn't find out the current time, don't give a |
2514 | // desiredPresentTime. |
2515 | ptime.desiredPresentTime = 0; |
2516 | } else { |
2517 | ptime.desiredPresentTime = curtime + (target_IPD >> 1); |
2518 | } |
2519 | } else { |
2520 | ptime.desiredPresentTime = (prev_desired_present_time + target_IPD); |
2521 | } |
2522 | ptime.presentID = next_present_id++; |
2523 | prev_desired_present_time = ptime.desiredPresentTime; |
2524 | |
2525 | VkPresentTimesInfoGOOGLE present_time = { |
2526 | /*sType*/ VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE, |
2527 | /*pNext*/ present.pNext, |
2528 | /*swapchainCount*/ present.swapchainCount, |
2529 | /*pTimes*/ &ptime, |
2530 | }; |
2531 | if (is_device_extension_enabled(VK_GOOGLE_DISPLAY_TIMING_EXTENSION_NAME)) { |
2532 | present.pNext = &present_time; |
2533 | } |
2534 | } |
2535 | #endif |
2536 | // print_line("current buffer: " + itos(current_buffer)); |
2537 | err = fpQueuePresentKHR(present_queue, &present); |
2538 | |
2539 | frame_index += 1; |
2540 | frame_index %= FRAME_LAG; |
2541 | |
2542 | if (err == VK_ERROR_OUT_OF_DATE_KHR) { |
2543 | // Swapchain is out of date (e.g. the window was resized) and |
2544 | // must be recreated. |
2545 | print_verbose("Vulkan queue submit: Swapchain is out of date, recreating." ); |
2546 | resize_notify(); |
2547 | } else if (err == VK_SUBOPTIMAL_KHR) { |
2548 | // Swapchain is not as optimal as it could be, but the platform's |
2549 | // presentation engine will still present the image correctly. |
2550 | print_verbose("Vulkan queue submit: Swapchain is suboptimal." ); |
2551 | } else { |
2552 | ERR_FAIL_COND_V_MSG(err, ERR_CANT_CREATE, "Error code: " + String(string_VkResult(err))); |
2553 | } |
2554 | |
2555 | buffers_prepared = false; |
2556 | return OK; |
2557 | } |
2558 | |
2559 | void VulkanContext::resize_notify() { |
2560 | } |
2561 | |
2562 | VkDevice VulkanContext::get_device() { |
2563 | return device; |
2564 | } |
2565 | |
2566 | VkPhysicalDevice VulkanContext::get_physical_device() { |
2567 | return gpu; |
2568 | } |
2569 | |
2570 | int VulkanContext::get_swapchain_image_count() const { |
2571 | return swapchainImageCount; |
2572 | } |
2573 | |
2574 | VkQueue VulkanContext::get_graphics_queue() const { |
2575 | return graphics_queue; |
2576 | } |
2577 | |
2578 | uint32_t VulkanContext::get_graphics_queue_family_index() const { |
2579 | return graphics_queue_family_index; |
2580 | } |
2581 | |
2582 | VkFormat VulkanContext::get_screen_format() const { |
2583 | return format; |
2584 | } |
2585 | |
2586 | VkPhysicalDeviceLimits VulkanContext::get_device_limits() const { |
2587 | return gpu_props.limits; |
2588 | } |
2589 | |
2590 | RID VulkanContext::local_device_create() { |
2591 | LocalDevice ld; |
2592 | |
2593 | { // Create device. |
2594 | VkResult err; |
2595 | float queue_priorities[1] = { 0.0 }; |
2596 | VkDeviceQueueCreateInfo queues[2]; |
2597 | queues[0].sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; |
2598 | queues[0].pNext = nullptr; |
2599 | queues[0].queueFamilyIndex = graphics_queue_family_index; |
2600 | queues[0].queueCount = 1; |
2601 | queues[0].pQueuePriorities = queue_priorities; |
2602 | queues[0].flags = 0; |
2603 | |
2604 | uint32_t enabled_extension_count = 0; |
2605 | const char *enabled_extension_names[MAX_EXTENSIONS]; |
2606 | ERR_FAIL_COND_V(enabled_device_extension_names.size() > MAX_EXTENSIONS, RID()); |
2607 | for (const CharString &extension_name : enabled_device_extension_names) { |
2608 | enabled_extension_names[enabled_extension_count++] = extension_name.ptr(); |
2609 | } |
2610 | |
2611 | VkDeviceCreateInfo sdevice = { |
2612 | /*sType =*/VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO, |
2613 | /*pNext */ nullptr, |
2614 | /*flags */ 0, |
2615 | /*queueCreateInfoCount */ 1, |
2616 | /*pQueueCreateInfos */ queues, |
2617 | /*enabledLayerCount */ 0, |
2618 | /*ppEnabledLayerNames */ nullptr, |
2619 | /*enabledExtensionCount */ enabled_extension_count, |
2620 | /*ppEnabledExtensionNames */ (const char *const *)enabled_extension_names, |
2621 | /*pEnabledFeatures */ &physical_device_features, // If specific features are required, pass them in here. |
2622 | }; |
2623 | err = vkCreateDevice(gpu, &sdevice, nullptr, &ld.device); |
2624 | ERR_FAIL_COND_V(err, RID()); |
2625 | } |
2626 | |
2627 | { // Create graphics queue. |
2628 | |
2629 | vkGetDeviceQueue(ld.device, graphics_queue_family_index, 0, &ld.queue); |
2630 | } |
2631 | |
2632 | return local_device_owner.make_rid(ld); |
2633 | } |
2634 | |
2635 | VkDevice VulkanContext::local_device_get_vk_device(RID p_local_device) { |
2636 | LocalDevice *ld = local_device_owner.get_or_null(p_local_device); |
2637 | return ld->device; |
2638 | } |
2639 | |
2640 | void VulkanContext::local_device_push_command_buffers(RID p_local_device, const VkCommandBuffer *p_buffers, int p_count) { |
2641 | LocalDevice *ld = local_device_owner.get_or_null(p_local_device); |
2642 | ERR_FAIL_COND(ld->waiting); |
2643 | |
2644 | VkSubmitInfo submit_info; |
2645 | submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; |
2646 | submit_info.pNext = nullptr; |
2647 | submit_info.pWaitDstStageMask = nullptr; |
2648 | submit_info.waitSemaphoreCount = 0; |
2649 | submit_info.pWaitSemaphores = nullptr; |
2650 | submit_info.commandBufferCount = p_count; |
2651 | submit_info.pCommandBuffers = p_buffers; |
2652 | submit_info.signalSemaphoreCount = 0; |
2653 | submit_info.pSignalSemaphores = nullptr; |
2654 | |
2655 | VkResult err = vkQueueSubmit(ld->queue, 1, &submit_info, VK_NULL_HANDLE); |
2656 | if (err == VK_ERROR_OUT_OF_HOST_MEMORY) { |
2657 | print_line("Vulkan: Out of host memory!" ); |
2658 | } |
2659 | if (err == VK_ERROR_OUT_OF_DEVICE_MEMORY) { |
2660 | print_line("Vulkan: Out of device memory!" ); |
2661 | } |
2662 | if (err == VK_ERROR_DEVICE_LOST) { |
2663 | print_line("Vulkan: Device lost!" ); |
2664 | } |
2665 | ERR_FAIL_COND(err); |
2666 | |
2667 | ld->waiting = true; |
2668 | } |
2669 | |
2670 | void VulkanContext::local_device_sync(RID p_local_device) { |
2671 | LocalDevice *ld = local_device_owner.get_or_null(p_local_device); |
2672 | ERR_FAIL_COND(!ld->waiting); |
2673 | |
2674 | vkDeviceWaitIdle(ld->device); |
2675 | ld->waiting = false; |
2676 | } |
2677 | |
2678 | void VulkanContext::local_device_free(RID p_local_device) { |
2679 | LocalDevice *ld = local_device_owner.get_or_null(p_local_device); |
2680 | vkDestroyDevice(ld->device, nullptr); |
2681 | local_device_owner.free(p_local_device); |
2682 | } |
2683 | |
2684 | void VulkanContext::command_begin_label(VkCommandBuffer p_command_buffer, String p_label_name, const Color p_color) { |
2685 | if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { |
2686 | return; |
2687 | } |
2688 | |
2689 | CharString cs = p_label_name.utf8(); |
2690 | VkDebugUtilsLabelEXT label; |
2691 | label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; |
2692 | label.pNext = nullptr; |
2693 | label.pLabelName = cs.get_data(); |
2694 | label.color[0] = p_color[0]; |
2695 | label.color[1] = p_color[1]; |
2696 | label.color[2] = p_color[2]; |
2697 | label.color[3] = p_color[3]; |
2698 | CmdBeginDebugUtilsLabelEXT(p_command_buffer, &label); |
2699 | } |
2700 | |
2701 | void VulkanContext::command_insert_label(VkCommandBuffer p_command_buffer, String p_label_name, const Color p_color) { |
2702 | if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { |
2703 | return; |
2704 | } |
2705 | CharString cs = p_label_name.utf8(); |
2706 | VkDebugUtilsLabelEXT label; |
2707 | label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; |
2708 | label.pNext = nullptr; |
2709 | label.pLabelName = cs.get_data(); |
2710 | label.color[0] = p_color[0]; |
2711 | label.color[1] = p_color[1]; |
2712 | label.color[2] = p_color[2]; |
2713 | label.color[3] = p_color[3]; |
2714 | CmdInsertDebugUtilsLabelEXT(p_command_buffer, &label); |
2715 | } |
2716 | |
2717 | void VulkanContext::command_end_label(VkCommandBuffer p_command_buffer) { |
2718 | if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { |
2719 | return; |
2720 | } |
2721 | CmdEndDebugUtilsLabelEXT(p_command_buffer); |
2722 | } |
2723 | |
2724 | void VulkanContext::set_object_name(VkObjectType p_object_type, uint64_t p_object_handle, String p_object_name) { |
2725 | if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { |
2726 | return; |
2727 | } |
2728 | CharString obj_data = p_object_name.utf8(); |
2729 | VkDebugUtilsObjectNameInfoEXT name_info; |
2730 | name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT; |
2731 | name_info.pNext = nullptr; |
2732 | name_info.objectType = p_object_type; |
2733 | name_info.objectHandle = p_object_handle; |
2734 | name_info.pObjectName = obj_data.get_data(); |
2735 | SetDebugUtilsObjectNameEXT(device, &name_info); |
2736 | } |
2737 | |
2738 | String VulkanContext::get_device_vendor_name() const { |
2739 | return device_vendor; |
2740 | } |
2741 | |
2742 | String VulkanContext::get_device_name() const { |
2743 | return device_name; |
2744 | } |
2745 | |
2746 | RenderingDevice::DeviceType VulkanContext::get_device_type() const { |
2747 | return RenderingDevice::DeviceType(device_type); |
2748 | } |
2749 | |
2750 | String VulkanContext::get_device_api_version() const { |
2751 | return vformat("%d.%d.%d" , VK_API_VERSION_MAJOR(device_api_version), VK_API_VERSION_MINOR(device_api_version), VK_API_VERSION_PATCH(device_api_version)); |
2752 | } |
2753 | |
2754 | String VulkanContext::get_device_pipeline_cache_uuid() const { |
2755 | return pipeline_cache_id; |
2756 | } |
2757 | |
2758 | DisplayServer::VSyncMode VulkanContext::get_vsync_mode(DisplayServer::WindowID p_window) const { |
2759 | ERR_FAIL_COND_V_MSG(!windows.has(p_window), DisplayServer::VSYNC_ENABLED, "Could not get V-Sync mode for window with WindowID " + itos(p_window) + " because it does not exist." ); |
2760 | return windows[p_window].vsync_mode; |
2761 | } |
2762 | |
2763 | void VulkanContext::set_vsync_mode(DisplayServer::WindowID p_window, DisplayServer::VSyncMode p_mode) { |
2764 | ERR_FAIL_COND_MSG(!windows.has(p_window), "Could not set V-Sync mode for window with WindowID " + itos(p_window) + " because it does not exist." ); |
2765 | windows[p_window].vsync_mode = p_mode; |
2766 | _update_swap_chain(&windows[p_window]); |
2767 | } |
2768 | |
2769 | VulkanContext::VulkanContext() { |
2770 | command_buffer_queue.resize(1); // First one is always the setup command. |
2771 | command_buffer_queue.write[0] = nullptr; |
2772 | } |
2773 | |
2774 | VulkanContext::~VulkanContext() { |
2775 | if (queue_props) { |
2776 | free(queue_props); |
2777 | } |
2778 | if (device_initialized) { |
2779 | for (uint32_t i = 0; i < FRAME_LAG; i++) { |
2780 | vkDestroyFence(device, fences[i], nullptr); |
2781 | vkDestroySemaphore(device, draw_complete_semaphores[i], nullptr); |
2782 | if (separate_present_queue) { |
2783 | vkDestroySemaphore(device, image_ownership_semaphores[i], nullptr); |
2784 | } |
2785 | } |
2786 | if (inst_initialized && is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { |
2787 | DestroyDebugUtilsMessengerEXT(inst, dbg_messenger, nullptr); |
2788 | } |
2789 | if (inst_initialized && dbg_debug_report != VK_NULL_HANDLE) { |
2790 | DestroyDebugReportCallbackEXT(inst, dbg_debug_report, nullptr); |
2791 | } |
2792 | vkDestroyDevice(device, nullptr); |
2793 | } |
2794 | if (inst_initialized) { |
2795 | vkDestroyInstance(inst, nullptr); |
2796 | } |
2797 | } |
2798 | |