Line data Source code
1 : /* Copyright (C) 2023 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "Device.h"
21 :
22 : #include "lib/external_libraries/libsdl.h"
23 : #include "lib/hash.h"
24 : #include "maths/MathUtil.h"
25 : #include "ps/CLogger.h"
26 : #include "ps/ConfigDB.h"
27 : #include "ps/Profile.h"
28 : #include "renderer/backend/vulkan/Buffer.h"
29 : #include "renderer/backend/vulkan/DescriptorManager.h"
30 : #include "renderer/backend/vulkan/DeviceCommandContext.h"
31 : #include "renderer/backend/vulkan/DeviceSelection.h"
32 : #include "renderer/backend/vulkan/Framebuffer.h"
33 : #include "renderer/backend/vulkan/Mapping.h"
34 : #include "renderer/backend/vulkan/PipelineState.h"
35 : #include "renderer/backend/vulkan/RenderPassManager.h"
36 : #include "renderer/backend/vulkan/RingCommandContext.h"
37 : #include "renderer/backend/vulkan/SamplerManager.h"
38 : #include "renderer/backend/vulkan/ShaderProgram.h"
39 : #include "renderer/backend/vulkan/SubmitScheduler.h"
40 : #include "renderer/backend/vulkan/SwapChain.h"
41 : #include "renderer/backend/vulkan/Texture.h"
42 : #include "renderer/backend/vulkan/Utilities.h"
43 : #include "scriptinterface/JSON.h"
44 : #include "scriptinterface/Object.h"
45 : #include "scriptinterface/ScriptInterface.h"
46 : #include "scriptinterface/ScriptRequest.h"
47 :
48 : #include <algorithm>
49 : #include <iterator>
50 : #include <limits>
51 : #include <string>
52 : #include <string_view>
53 : #include <type_traits>
54 : #include <vector>
55 :
56 : // According to https://wiki.libsdl.org/SDL_Vulkan_LoadLibrary the following
57 : // functionality is supported since SDL 2.0.6.
58 : #if SDL_VERSION_ATLEAST(2, 0, 6)
59 : #include <SDL_vulkan.h>
60 : #endif
61 :
62 : namespace Renderer
63 : {
64 :
65 : namespace Backend
66 : {
67 :
68 : namespace Vulkan
69 : {
70 :
71 : namespace
72 : {
73 :
74 0 : std::vector<const char*> GetRequiredSDLExtensions(SDL_Window* window)
75 : {
76 0 : if (!window)
77 0 : return {};
78 :
79 0 : const size_t MAX_EXTENSION_COUNT = 16;
80 0 : unsigned int SDLExtensionCount = MAX_EXTENSION_COUNT;
81 : const char* SDLExtensions[MAX_EXTENSION_COUNT];
82 0 : ENSURE(SDL_Vulkan_GetInstanceExtensions(window, &SDLExtensionCount, SDLExtensions));
83 0 : std::vector<const char*> requiredExtensions;
84 0 : requiredExtensions.reserve(SDLExtensionCount);
85 0 : std::copy_n(SDLExtensions, SDLExtensionCount, std::back_inserter(requiredExtensions));
86 0 : return requiredExtensions;
87 : }
88 :
89 0 : std::vector<std::string> GetAvailableValidationLayers()
90 : {
91 0 : uint32_t layerCount = 0;
92 0 : ENSURE_VK_SUCCESS(vkEnumerateInstanceLayerProperties(&layerCount, nullptr));
93 :
94 0 : std::vector<VkLayerProperties> availableLayers(layerCount);
95 0 : ENSURE_VK_SUCCESS(vkEnumerateInstanceLayerProperties(&layerCount, availableLayers.data()));
96 :
97 0 : for (const VkLayerProperties& layer : availableLayers)
98 : {
99 0 : LOGMESSAGE("Vulkan validation layer: '%s' (%s) v%u.%u.%u.%u",
100 : layer.layerName, layer.description,
101 : VK_API_VERSION_VARIANT(layer.specVersion),
102 : VK_API_VERSION_MAJOR(layer.specVersion),
103 : VK_API_VERSION_MINOR(layer.specVersion),
104 : VK_API_VERSION_PATCH(layer.specVersion));
105 : }
106 :
107 0 : std::vector<std::string> availableValidationLayers;
108 0 : availableValidationLayers.reserve(layerCount);
109 0 : for (const VkLayerProperties& layer : availableLayers)
110 0 : availableValidationLayers.emplace_back(layer.layerName);
111 0 : return availableValidationLayers;
112 : }
113 :
114 0 : std::vector<std::string> GetAvailableInstanceExtensions(const char* layerName = nullptr)
115 : {
116 0 : uint32_t extensionCount = 0;
117 0 : ENSURE_VK_SUCCESS(vkEnumerateInstanceExtensionProperties(layerName, &extensionCount, nullptr));
118 0 : std::vector<VkExtensionProperties> extensions(extensionCount);
119 0 : ENSURE_VK_SUCCESS(vkEnumerateInstanceExtensionProperties(layerName, &extensionCount, extensions.data()));
120 :
121 0 : std::vector<std::string> availableExtensions;
122 0 : for (const VkExtensionProperties& extension : extensions)
123 0 : availableExtensions.emplace_back(extension.extensionName);
124 0 : return availableExtensions;
125 : }
126 :
127 0 : VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallback(
128 : VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
129 : VkDebugUtilsMessageTypeFlagsEXT messageType,
130 : const VkDebugUtilsMessengerCallbackDataEXT* callbackData,
131 : void* UNUSED(userData))
132 : {
133 0 : if ((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT) || (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT))
134 0 : LOGMESSAGE("Vulkan: %s", callbackData->pMessage);
135 0 : else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT)
136 : {
137 : struct HideRule
138 : {
139 : VkDebugUtilsMessageTypeFlagsEXT flags;
140 : std::string_view pattern;
141 : bool skip;
142 : };
143 0 : constexpr HideRule hideRules[] =
144 : {
145 : // Not consumed shader output is a known problem which produces too
146 : // many warning.
147 : {VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT, "OutputNotConsumed", false},
148 : // TODO: check vkGetImageMemoryRequirements2 for prefersDedicatedAllocation.
149 : {VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, "vkBindMemory-small-dedicated-allocation", false},
150 : {VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, "vkAllocateMemory-small-allocation", false},
151 : // We have some unnecessary clears which were needed for GL.
152 : // Ignore message for now, because they're spawned each frame.
153 : {VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, "ClearCmdBeforeDraw", true},
154 : {VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT, "vkCmdClearAttachments-clear-after-load", true},
155 : // TODO: investigate probably false-positive report.
156 : {VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT, "vkCmdBeginRenderPass-StoreOpDontCareThenLoadOpLoad", true},
157 : };
158 0 : const auto it = std::find_if(std::begin(hideRules), std::end(hideRules),
159 0 : [messageType, message = std::string_view{callbackData->pMessage}](const HideRule& hideRule) -> bool
160 0 : {
161 0 : return (hideRule.flags & messageType) && message.find(hideRule.pattern) != std::string_view::npos;
162 0 : });
163 0 : if (it == std::end(hideRules))
164 0 : LOGWARNING("Vulkan: %s", callbackData->pMessage);
165 0 : else if (!it->skip)
166 0 : LOGMESSAGE("Vulkan: %s", callbackData->pMessage);
167 : }
168 0 : else if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT)
169 0 : LOGERROR("Vulkan: %s", callbackData->pMessage);
170 :
171 0 : return VK_FALSE;
172 : }
173 :
174 : // A workaround function to meet calling conventions of Vulkan, SDL and GLAD.
175 0 : GLADapiproc GetInstanceProcAddr(VkInstance instance, const char* name)
176 : {
177 : #if SDL_VERSION_ATLEAST(2, 0, 6)
178 0 : PFN_vkGetInstanceProcAddr function = reinterpret_cast<PFN_vkGetInstanceProcAddr>(SDL_Vulkan_GetVkGetInstanceProcAddr());
179 0 : return reinterpret_cast<GLADapiproc>(function(instance, name));
180 : #else
181 : return nullptr;
182 : #endif
183 : }
184 :
185 : } // anonymous namespace
186 :
187 : // static
188 0 : std::unique_ptr<CDevice> CDevice::Create(SDL_Window* window)
189 : {
190 0 : if (!window)
191 : {
192 0 : LOGERROR("Can't create Vulkan device without window.");
193 0 : return nullptr;
194 : }
195 :
196 0 : GLADuserptrloadfunc gladLoadFunction = reinterpret_cast<GLADuserptrloadfunc>(GetInstanceProcAddr);
197 :
198 0 : std::unique_ptr<CDevice> device(new CDevice());
199 0 : device->m_Window = window;
200 :
201 : #ifdef NDEBUG
202 : bool enableDebugMessages = false;
203 : CFG_GET_VAL("renderer.backend.debugmessages", enableDebugMessages);
204 : bool enableDebugLabels = false;
205 : CFG_GET_VAL("renderer.backend.debuglabels", enableDebugLabels);
206 : bool enableDebugScopedLabels = false;
207 : CFG_GET_VAL("renderer.backend.debugscopedlabels", enableDebugScopedLabels);
208 : #else
209 0 : bool enableDebugMessages = true;
210 0 : bool enableDebugLabels = true;
211 0 : bool enableDebugScopedLabels = true;
212 : #endif
213 :
214 0 : int gladVulkanVersion = gladLoadVulkanUserPtr(nullptr, gladLoadFunction, nullptr);
215 0 : if (!gladVulkanVersion)
216 : {
217 0 : LOGERROR("GLAD unable to load vulkan.");
218 0 : return nullptr;
219 : }
220 :
221 0 : VkApplicationInfo applicationInfo{};
222 0 : applicationInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
223 0 : applicationInfo.pApplicationName = "0 A.D.";
224 0 : applicationInfo.applicationVersion = VK_MAKE_VERSION(0, 0, 27);
225 0 : applicationInfo.pEngineName = "Pyrogenesis";
226 0 : applicationInfo.engineVersion = applicationInfo.applicationVersion;
227 0 : applicationInfo.apiVersion = VK_API_VERSION_1_1;
228 :
229 0 : std::vector<const char*> requiredInstanceExtensions = GetRequiredSDLExtensions(window);
230 :
231 0 : device->m_ValidationLayers = GetAvailableValidationLayers();
232 0 : auto hasValidationLayer = [&layers = device->m_ValidationLayers](const char* name) -> bool
233 0 : {
234 0 : return std::find(layers.begin(), layers.end(), name) != layers.end();
235 0 : };
236 0 : device->m_InstanceExtensions = GetAvailableInstanceExtensions();
237 0 : auto hasInstanceExtension = [&extensions = device->m_InstanceExtensions](const char* name) -> bool
238 0 : {
239 0 : return std::find(extensions.begin(), extensions.end(), name) != extensions.end();
240 0 : };
241 :
242 : #ifdef NDEBUG
243 : bool enableDebugContext = false;
244 : CFG_GET_VAL("renderer.backend.debugcontext", enableDebugContext);
245 : #else
246 0 : bool enableDebugContext = true;
247 : #endif
248 :
249 0 : if (!hasInstanceExtension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
250 0 : enableDebugMessages = enableDebugLabels = enableDebugScopedLabels = false;
251 0 : const bool enableDebugLayers = enableDebugContext || enableDebugMessages || enableDebugLabels || enableDebugScopedLabels;
252 0 : if (enableDebugLayers)
253 0 : requiredInstanceExtensions.emplace_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
254 :
255 0 : std::vector<const char*> requestedValidationLayers;
256 0 : const bool enableValidationFeatures = enableDebugMessages && hasValidationLayer("VK_LAYER_KHRONOS_validation");
257 0 : if (enableValidationFeatures)
258 0 : requestedValidationLayers.emplace_back("VK_LAYER_KHRONOS_validation");
259 :
260 : // https://github.com/KhronosGroup/Vulkan-ValidationLayers/blob/master/docs/synchronization_usage.md
261 0 : VkValidationFeatureEnableEXT validationFeatureEnables[] =
262 : {
263 : VK_VALIDATION_FEATURE_ENABLE_BEST_PRACTICES_EXT,
264 : VK_VALIDATION_FEATURE_ENABLE_SYNCHRONIZATION_VALIDATION_EXT
265 : };
266 0 : VkValidationFeaturesEXT validationFeatures{};
267 0 : validationFeatures.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT;
268 0 : validationFeatures.enabledValidationFeatureCount = std::size(validationFeatureEnables);
269 0 : validationFeatures.pEnabledValidationFeatures = validationFeatureEnables;
270 :
271 0 : VkInstanceCreateInfo instanceCreateInfo{};
272 0 : instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
273 0 : instanceCreateInfo.pApplicationInfo = &applicationInfo;
274 0 : instanceCreateInfo.enabledExtensionCount = requiredInstanceExtensions.size();
275 0 : instanceCreateInfo.ppEnabledExtensionNames = requiredInstanceExtensions.data();
276 0 : if (requestedValidationLayers.empty())
277 : {
278 0 : instanceCreateInfo.enabledLayerCount = 0;
279 0 : instanceCreateInfo.ppEnabledLayerNames = nullptr;
280 : }
281 : else
282 : {
283 0 : instanceCreateInfo.enabledLayerCount = requestedValidationLayers.size();
284 0 : instanceCreateInfo.ppEnabledLayerNames = requestedValidationLayers.data();
285 : }
286 :
287 : // Enabling validation features might significantly reduce performance,
288 : // even more than the standard validation layer.
289 0 : if (enableValidationFeatures && enableDebugContext)
290 : {
291 0 : instanceCreateInfo.pNext = &validationFeatures;
292 : }
293 :
294 0 : const VkResult createInstanceResult = vkCreateInstance(&instanceCreateInfo, nullptr, &device->m_Instance);
295 0 : if (createInstanceResult != VK_SUCCESS)
296 : {
297 0 : if (createInstanceResult == VK_ERROR_INCOMPATIBLE_DRIVER)
298 0 : LOGERROR("Can't create Vulkan instance: incompatible driver.");
299 0 : else if (createInstanceResult == VK_ERROR_EXTENSION_NOT_PRESENT)
300 0 : LOGERROR("Can't create Vulkan instance: extension not present.");
301 0 : else if (createInstanceResult == VK_ERROR_LAYER_NOT_PRESENT)
302 0 : LOGERROR("Can't create Vulkan instance: layer not present.");
303 : else
304 0 : LOGERROR("Unknown error during Vulkan instance creation: %d", static_cast<int>(createInstanceResult));
305 0 : return nullptr;
306 : }
307 :
308 0 : gladVulkanVersion = gladLoadVulkanUserPtr(nullptr, gladLoadFunction, device->m_Instance);
309 0 : if (!gladVulkanVersion)
310 : {
311 0 : LOGERROR("GLAD unable to re-load vulkan after its instance creation.");
312 0 : return nullptr;
313 : }
314 :
315 0 : if (GLAD_VK_EXT_debug_utils && enableDebugMessages)
316 : {
317 0 : VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
318 0 : debugCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
319 0 : debugCreateInfo.messageSeverity =
320 : VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
321 : VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
322 : VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
323 0 : debugCreateInfo.messageType =
324 : VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
325 : VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
326 : VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
327 0 : debugCreateInfo.pfnUserCallback = DebugCallback;
328 0 : debugCreateInfo.pUserData = nullptr;
329 :
330 0 : ENSURE_VK_SUCCESS(vkCreateDebugUtilsMessengerEXT(
331 : device->m_Instance, &debugCreateInfo, nullptr, &device->m_DebugMessenger));
332 : }
333 :
334 0 : if (window)
335 0 : ENSURE(SDL_Vulkan_CreateSurface(window, device->m_Instance, &device->m_Surface));
336 :
337 : const std::vector<const char*> requiredDeviceExtensions =
338 : {
339 : VK_KHR_SWAPCHAIN_EXTENSION_NAME
340 0 : };
341 : std::vector<SAvailablePhysicalDevice> availablePhyscialDevices =
342 0 : GetAvailablePhysicalDevices(device->m_Instance, device->m_Surface, requiredDeviceExtensions);
343 0 : for (const SAvailablePhysicalDevice& device : availablePhyscialDevices)
344 : {
345 0 : LOGMESSAGE("Vulkan available device: '%s' Type: %u Supported: %c",
346 : device.properties.deviceName, static_cast<uint32_t>(device.properties.deviceType),
347 : IsPhysicalDeviceUnsupported(device) ? 'N' : 'Y');
348 0 : LOGMESSAGE(" ID: %u VendorID: %u API Version: %u Driver Version: %u",
349 : device.properties.deviceID, device.properties.vendorID,
350 : device.properties.apiVersion, device.properties.driverVersion);
351 0 : for (uint32_t memoryTypeIndex = 0; memoryTypeIndex < device.memoryProperties.memoryTypeCount; ++memoryTypeIndex)
352 : {
353 0 : const VkMemoryType& type = device.memoryProperties.memoryTypes[memoryTypeIndex];
354 0 : LOGMESSAGE(" Memory Type Index: %u Flags: %u Heap Index: %u",
355 : memoryTypeIndex, static_cast<uint32_t>(type.propertyFlags), type.heapIndex);
356 : }
357 0 : for (uint32_t memoryHeapIndex = 0; memoryHeapIndex < device.memoryProperties.memoryHeapCount; ++memoryHeapIndex)
358 : {
359 0 : const VkMemoryHeap& heap = device.memoryProperties.memoryHeaps[memoryHeapIndex];
360 0 : LOGMESSAGE(" Memory Heap Index: %u Size: %zu Flags: %u",
361 : memoryHeapIndex, static_cast<size_t>(heap.size / 1024), static_cast<uint32_t>(heap.flags));
362 : }
363 : }
364 0 : device->m_AvailablePhysicalDevices = availablePhyscialDevices;
365 : // We need to remove unsupported devices first.
366 0 : availablePhyscialDevices.erase(
367 0 : std::remove_if(
368 : availablePhyscialDevices.begin(), availablePhyscialDevices.end(),
369 0 : IsPhysicalDeviceUnsupported),
370 0 : availablePhyscialDevices.end());
371 0 : if (availablePhyscialDevices.empty())
372 : {
373 0 : LOGERROR("Vulkan can not find any supported and suitable device.");
374 0 : return nullptr;
375 : }
376 :
377 0 : int deviceIndexOverride = -1;
378 0 : CFG_GET_VAL("renderer.backend.vulkan.deviceindexoverride", deviceIndexOverride);
379 0 : auto choosedDeviceIt = device->m_AvailablePhysicalDevices.end();
380 0 : if (deviceIndexOverride >= 0)
381 : {
382 0 : choosedDeviceIt = std::find_if(
383 0 : device->m_AvailablePhysicalDevices.begin(), device->m_AvailablePhysicalDevices.end(),
384 0 : [deviceIndexOverride](const SAvailablePhysicalDevice& availableDevice)
385 0 : {
386 0 : return availableDevice.index == static_cast<uint32_t>(deviceIndexOverride);
387 0 : });
388 0 : if (choosedDeviceIt == device->m_AvailablePhysicalDevices.end())
389 0 : LOGWARNING("Device with override index %d not found.", deviceIndexOverride);
390 : }
391 0 : if (choosedDeviceIt == device->m_AvailablePhysicalDevices.end())
392 : {
393 : // We need to choose the best available device fits our needs.
394 0 : choosedDeviceIt = min_element(
395 : availablePhyscialDevices.begin(), availablePhyscialDevices.end(),
396 0 : ComparePhysicalDevices);
397 : }
398 0 : device->m_ChoosenDevice = *choosedDeviceIt;
399 0 : const SAvailablePhysicalDevice& choosenDevice = device->m_ChoosenDevice;
400 0 : device->m_AvailablePhysicalDevices.erase(std::remove_if(
401 0 : device->m_AvailablePhysicalDevices.begin(), device->m_AvailablePhysicalDevices.end(),
402 0 : [physicalDevice = choosenDevice.device](const SAvailablePhysicalDevice& device)
403 0 : {
404 0 : return physicalDevice == device.device;
405 0 : }), device->m_AvailablePhysicalDevices.end());
406 :
407 0 : gladVulkanVersion = gladLoadVulkanUserPtr(choosenDevice.device, gladLoadFunction, device->m_Instance);
408 0 : if (!gladVulkanVersion)
409 : {
410 0 : LOGERROR("GLAD unable to re-load vulkan after choosing its physical device.");
411 0 : return nullptr;
412 : }
413 :
414 0 : auto hasDeviceExtension = [&extensions = choosenDevice.extensions](const char* name) -> bool
415 0 : {
416 0 : return std::find(extensions.begin(), extensions.end(), name) != extensions.end();
417 0 : };
418 0 : const bool hasDescriptorIndexing = hasDeviceExtension(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
419 0 : const bool hasNeededDescriptorIndexingFeatures =
420 0 : hasDescriptorIndexing &&
421 0 : choosenDevice.descriptorIndexingProperties.maxUpdateAfterBindDescriptorsInAllPools >= 65536 &&
422 0 : choosenDevice.descriptorIndexingFeatures.shaderSampledImageArrayNonUniformIndexing &&
423 0 : choosenDevice.descriptorIndexingFeatures.runtimeDescriptorArray &&
424 0 : choosenDevice.descriptorIndexingFeatures.descriptorBindingVariableDescriptorCount &&
425 0 : choosenDevice.descriptorIndexingFeatures.descriptorBindingPartiallyBound &&
426 0 : choosenDevice.descriptorIndexingFeatures.descriptorBindingUpdateUnusedWhilePending &&
427 0 : choosenDevice.descriptorIndexingFeatures.descriptorBindingSampledImageUpdateAfterBind;
428 :
429 0 : std::vector<const char*> deviceExtensions = requiredDeviceExtensions;
430 0 : if (hasDescriptorIndexing)
431 0 : deviceExtensions.emplace_back(VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME);
432 :
433 0 : device->m_GraphicsQueueFamilyIndex = choosenDevice.graphicsQueueFamilyIndex;
434 : const std::array<size_t, 1> queueFamilyIndices{{
435 0 : choosenDevice.graphicsQueueFamilyIndex
436 0 : }};
437 :
438 0 : PS::StaticVector<VkDeviceQueueCreateInfo, 1> queueCreateInfos;
439 0 : const float queuePriority = 1.0f;
440 0 : std::transform(queueFamilyIndices.begin(), queueFamilyIndices.end(),
441 : std::back_inserter(queueCreateInfos),
442 0 : [&queuePriority](const size_t queueFamilyIndex)
443 0 : {
444 0 : VkDeviceQueueCreateInfo queueCreateInfo{};
445 0 : queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
446 0 : queueCreateInfo.pQueuePriorities = &queuePriority;
447 0 : queueCreateInfo.queueCount = 1;
448 0 : queueCreateInfo.queueFamilyIndex = queueFamilyIndex;
449 0 : return queueCreateInfo;
450 0 : });
451 :
452 : // https://github.com/KhronosGroup/Vulkan-Guide/blob/master/chapters/enabling_features.adoc
453 0 : VkPhysicalDeviceFeatures deviceFeatures{};
454 0 : VkPhysicalDeviceFeatures2 deviceFeatures2{};
455 0 : VkPhysicalDeviceDescriptorIndexingFeaturesEXT descriptorIndexingFeatures{};
456 :
457 0 : deviceFeatures.textureCompressionBC = choosenDevice.features.textureCompressionBC;
458 0 : deviceFeatures.samplerAnisotropy = choosenDevice.features.samplerAnisotropy;
459 :
460 0 : descriptorIndexingFeatures.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT;
461 0 : descriptorIndexingFeatures.shaderSampledImageArrayNonUniformIndexing =
462 0 : choosenDevice.descriptorIndexingFeatures.shaderSampledImageArrayNonUniformIndexing;
463 0 : descriptorIndexingFeatures.runtimeDescriptorArray =
464 0 : choosenDevice.descriptorIndexingFeatures.runtimeDescriptorArray;
465 0 : descriptorIndexingFeatures.descriptorBindingVariableDescriptorCount =
466 0 : choosenDevice.descriptorIndexingFeatures.descriptorBindingVariableDescriptorCount;
467 0 : descriptorIndexingFeatures.descriptorBindingPartiallyBound =
468 0 : choosenDevice.descriptorIndexingFeatures.descriptorBindingPartiallyBound;
469 0 : descriptorIndexingFeatures.descriptorBindingUpdateUnusedWhilePending =
470 0 : choosenDevice.descriptorIndexingFeatures.descriptorBindingUpdateUnusedWhilePending;
471 0 : descriptorIndexingFeatures.descriptorBindingSampledImageUpdateAfterBind =
472 0 : choosenDevice.descriptorIndexingFeatures.descriptorBindingSampledImageUpdateAfterBind;
473 :
474 0 : deviceFeatures2.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
475 0 : deviceFeatures2.features = deviceFeatures;
476 0 : if (hasNeededDescriptorIndexingFeatures)
477 0 : deviceFeatures2.pNext = &descriptorIndexingFeatures;
478 :
479 0 : VkDeviceCreateInfo deviceCreateInfo{};
480 0 : deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
481 0 : deviceCreateInfo.queueCreateInfoCount = queueCreateInfos.size();
482 0 : deviceCreateInfo.pQueueCreateInfos = queueCreateInfos.data();
483 0 : deviceCreateInfo.enabledExtensionCount = deviceExtensions.size();
484 0 : deviceCreateInfo.ppEnabledExtensionNames = deviceExtensions.data();
485 0 : deviceCreateInfo.pEnabledFeatures = nullptr;
486 0 : deviceCreateInfo.pNext = &deviceFeatures2;
487 0 : deviceCreateInfo.enabledLayerCount = 0;
488 0 : deviceCreateInfo.ppEnabledLayerNames = nullptr;
489 :
490 0 : const VkResult createDeviceResult = vkCreateDevice(
491 0 : choosenDevice.device, &deviceCreateInfo, nullptr, &device->m_Device);
492 0 : if (createDeviceResult != VK_SUCCESS)
493 : {
494 0 : if (createDeviceResult == VK_ERROR_FEATURE_NOT_PRESENT)
495 0 : LOGERROR("Can't create Vulkan device: feature not present.");
496 0 : else if (createDeviceResult == VK_ERROR_EXTENSION_NOT_PRESENT)
497 0 : LOGERROR("Can't create Vulkan device: extension not present.");
498 : else
499 0 : LOGERROR("Unknown error during Vulkan device creation: %d",
500 : static_cast<int>(createDeviceResult));
501 0 : return nullptr;
502 : }
503 :
504 0 : VmaVulkanFunctions vulkanFunctions{};
505 0 : vulkanFunctions.vkGetInstanceProcAddr = vkGetInstanceProcAddr;
506 0 : vulkanFunctions.vkGetDeviceProcAddr = vkGetDeviceProcAddr;
507 0 : vulkanFunctions.vkGetPhysicalDeviceProperties = vkGetPhysicalDeviceProperties;
508 0 : vulkanFunctions.vkGetPhysicalDeviceMemoryProperties = vkGetPhysicalDeviceMemoryProperties;
509 0 : vulkanFunctions.vkAllocateMemory = vkAllocateMemory;
510 0 : vulkanFunctions.vkFreeMemory = vkFreeMemory;
511 0 : vulkanFunctions.vkMapMemory = vkMapMemory;
512 0 : vulkanFunctions.vkUnmapMemory = vkUnmapMemory;
513 0 : vulkanFunctions.vkFlushMappedMemoryRanges = vkFlushMappedMemoryRanges;
514 0 : vulkanFunctions.vkInvalidateMappedMemoryRanges = vkInvalidateMappedMemoryRanges;
515 0 : vulkanFunctions.vkBindBufferMemory = vkBindBufferMemory;
516 0 : vulkanFunctions.vkBindImageMemory = vkBindImageMemory;
517 0 : vulkanFunctions.vkGetBufferMemoryRequirements = vkGetBufferMemoryRequirements;
518 0 : vulkanFunctions.vkGetImageMemoryRequirements = vkGetImageMemoryRequirements;
519 0 : vulkanFunctions.vkCreateBuffer = vkCreateBuffer;
520 0 : vulkanFunctions.vkDestroyBuffer = vkDestroyBuffer;
521 0 : vulkanFunctions.vkCreateImage = vkCreateImage;
522 0 : vulkanFunctions.vkDestroyImage = vkDestroyImage;
523 0 : vulkanFunctions.vkCmdCopyBuffer = vkCmdCopyBuffer;
524 :
525 0 : VmaAllocatorCreateInfo allocatorCreateInfo{};
526 0 : allocatorCreateInfo.instance = device->m_Instance;
527 0 : allocatorCreateInfo.physicalDevice = choosenDevice.device;
528 0 : allocatorCreateInfo.device = device->m_Device;
529 0 : allocatorCreateInfo.vulkanApiVersion = applicationInfo.apiVersion;
530 0 : allocatorCreateInfo.pVulkanFunctions = &vulkanFunctions;
531 : const VkResult createVMAAllocatorResult =
532 0 : vmaCreateAllocator(&allocatorCreateInfo, &device->m_VMAAllocator);
533 0 : if (createVMAAllocatorResult != VK_SUCCESS)
534 : {
535 0 : LOGERROR("Failed to create VMA allocator: %d",
536 : static_cast<int>(createDeviceResult));
537 0 : return nullptr;
538 : }
539 :
540 : // We need to use VK_SHARING_MODE_CONCURRENT if we have graphics and present
541 : // in different queues.
542 0 : vkGetDeviceQueue(device->m_Device, choosenDevice.graphicsQueueFamilyIndex,
543 0 : 0, &device->m_GraphicsQueue);
544 0 : ENSURE(device->m_GraphicsQueue != VK_NULL_HANDLE);
545 :
546 0 : Capabilities& capabilities = device->m_Capabilities;
547 :
548 0 : capabilities.debugLabels = enableDebugLabels;
549 0 : capabilities.debugScopedLabels = enableDebugScopedLabels;
550 0 : capabilities.S3TC = choosenDevice.features.textureCompressionBC;
551 0 : capabilities.ARBShaders = false;
552 0 : capabilities.ARBShadersShadow = false;
553 0 : capabilities.computeShaders = true;
554 0 : capabilities.instancing = true;
555 0 : capabilities.maxSampleCount = 1;
556 0 : const VkSampleCountFlags sampleCountFlags =
557 0 : choosenDevice.properties.limits.framebufferColorSampleCounts
558 0 : & choosenDevice.properties.limits.framebufferDepthSampleCounts
559 0 : & choosenDevice.properties.limits.framebufferStencilSampleCounts;
560 0 : const std::array<VkSampleCountFlagBits, 5> allowedSampleCountBits =
561 : {
562 : VK_SAMPLE_COUNT_1_BIT,
563 : VK_SAMPLE_COUNT_2_BIT,
564 : VK_SAMPLE_COUNT_4_BIT,
565 : VK_SAMPLE_COUNT_8_BIT,
566 : VK_SAMPLE_COUNT_16_BIT,
567 : };
568 0 : for (size_t index = 0; index < allowedSampleCountBits.size(); ++index)
569 0 : if (sampleCountFlags & allowedSampleCountBits[index])
570 0 : device->m_Capabilities.maxSampleCount = 1u << index;
571 0 : capabilities.multisampling = device->m_Capabilities.maxSampleCount > 1;
572 0 : capabilities.anisotropicFiltering = choosenDevice.features.samplerAnisotropy;
573 0 : capabilities.maxAnisotropy = choosenDevice.properties.limits.maxSamplerAnisotropy;
574 0 : capabilities.maxTextureSize =
575 0 : choosenDevice.properties.limits.maxImageDimension2D;
576 :
577 0 : device->m_RenderPassManager =
578 0 : std::make_unique<CRenderPassManager>(device.get());
579 0 : device->m_SamplerManager = std::make_unique<CSamplerManager>(device.get());
580 0 : device->m_SubmitScheduler =
581 0 : std::make_unique<CSubmitScheduler>(
582 0 : device.get(), device->m_GraphicsQueueFamilyIndex, device->m_GraphicsQueue);
583 :
584 0 : bool disableDescriptorIndexing = false;
585 0 : CFG_GET_VAL("renderer.backend.vulkan.disabledescriptorindexing", disableDescriptorIndexing);
586 0 : const bool useDescriptorIndexing = hasNeededDescriptorIndexingFeatures && !disableDescriptorIndexing;
587 0 : device->m_DescriptorManager =
588 0 : std::make_unique<CDescriptorManager>(device.get(), useDescriptorIndexing);
589 :
590 0 : device->RecreateSwapChain();
591 :
592 0 : device->m_Name = choosenDevice.properties.deviceName;
593 0 : device->m_Version =
594 0 : std::to_string(VK_API_VERSION_VARIANT(choosenDevice.properties.apiVersion)) +
595 0 : "." + std::to_string(VK_API_VERSION_MAJOR(choosenDevice.properties.apiVersion)) +
596 0 : "." + std::to_string(VK_API_VERSION_MINOR(choosenDevice.properties.apiVersion)) +
597 0 : "." + std::to_string(VK_API_VERSION_PATCH(choosenDevice.properties.apiVersion));
598 :
599 0 : device->m_DriverInformation = std::to_string(choosenDevice.properties.driverVersion);
600 :
601 : // Refs:
602 : // * https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceProperties.html
603 : // * https://pcisig.com/membership/member-companies
604 0 : device->m_VendorID = std::to_string(choosenDevice.properties.vendorID);
605 :
606 0 : device->m_Extensions = choosenDevice.extensions;
607 :
608 0 : return device;
609 : }
610 :
611 : CDevice::CDevice() = default;
612 :
613 0 : CDevice::~CDevice()
614 : {
615 0 : if (m_Device)
616 0 : vkDeviceWaitIdle(m_Device);
617 :
618 : // The order of destroying does matter to avoid use-after-free and validation
619 : // layers complaints.
620 :
621 0 : m_SubmitScheduler.reset();
622 :
623 0 : ProcessTextureToDestroyQueue(true);
624 :
625 0 : m_RenderPassManager.reset();
626 0 : m_SamplerManager.reset();
627 0 : m_DescriptorManager.reset();
628 0 : m_SwapChain.reset();
629 :
630 0 : ProcessObjectToDestroyQueue(true);
631 :
632 0 : if (m_VMAAllocator != VK_NULL_HANDLE)
633 0 : vmaDestroyAllocator(m_VMAAllocator);
634 :
635 0 : if (m_Device != VK_NULL_HANDLE)
636 0 : vkDestroyDevice(m_Device, nullptr);
637 :
638 0 : if (m_Surface != VK_NULL_HANDLE)
639 0 : vkDestroySurfaceKHR(m_Instance, m_Surface, nullptr);
640 :
641 0 : if (GLAD_VK_EXT_debug_utils && m_DebugMessenger)
642 0 : vkDestroyDebugUtilsMessengerEXT(m_Instance, m_DebugMessenger, nullptr);
643 :
644 0 : if (m_Instance != VK_NULL_HANDLE)
645 0 : vkDestroyInstance(m_Instance, nullptr);
646 0 : }
647 :
648 0 : void CDevice::Report(const ScriptRequest& rq, JS::HandleValue settings)
649 : {
650 0 : Script::SetProperty(rq, settings, "name", "vulkan");
651 :
652 0 : Script::SetProperty(rq, settings, "extensions", m_Extensions);
653 :
654 0 : JS::RootedValue device(rq.cx);
655 0 : Script::CreateObject(rq, &device);
656 0 : ReportAvailablePhysicalDevice(m_ChoosenDevice, rq, device);
657 0 : Script::SetProperty(rq, settings, "choosen_device", device);
658 :
659 0 : JS::RootedValue availableDevices(rq.cx);
660 0 : Script::CreateArray(rq, &availableDevices, m_AvailablePhysicalDevices.size());
661 0 : for (size_t index = 0; index < m_AvailablePhysicalDevices.size(); ++index)
662 : {
663 0 : JS::RootedValue device(rq.cx);
664 0 : Script::CreateObject(rq, &device);
665 0 : ReportAvailablePhysicalDevice(m_AvailablePhysicalDevices[index], rq, device);
666 0 : Script::SetPropertyInt(rq, availableDevices, index, device);
667 : }
668 0 : Script::SetProperty(rq, settings, "available_device", availableDevices);
669 :
670 0 : Script::SetProperty(rq, settings, "instance_extensions", m_InstanceExtensions);
671 0 : Script::SetProperty(rq, settings, "validation_layers", m_ValidationLayers);
672 0 : }
673 :
674 0 : std::unique_ptr<IGraphicsPipelineState> CDevice::CreateGraphicsPipelineState(
675 : const SGraphicsPipelineStateDesc& pipelineStateDesc)
676 : {
677 0 : return CGraphicsPipelineState::Create(this, pipelineStateDesc);
678 : }
679 :
680 0 : std::unique_ptr<IVertexInputLayout> CDevice::CreateVertexInputLayout(
681 : const PS::span<const SVertexAttributeFormat> attributes)
682 : {
683 0 : return std::make_unique<CVertexInputLayout>(this, attributes);
684 : }
685 :
686 0 : std::unique_ptr<ITexture> CDevice::CreateTexture(
687 : const char* name, const ITexture::Type type, const uint32_t usage,
688 : const Format format, const uint32_t width, const uint32_t height,
689 : const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount)
690 : {
691 0 : return CTexture::Create(
692 : this, name, type, usage, format, width, height,
693 0 : defaultSamplerDesc, MIPLevelCount, sampleCount);
694 : }
695 :
696 0 : std::unique_ptr<ITexture> CDevice::CreateTexture2D(
697 : const char* name, const uint32_t usage,
698 : const Format format, const uint32_t width, const uint32_t height,
699 : const Sampler::Desc& defaultSamplerDesc, const uint32_t MIPLevelCount, const uint32_t sampleCount)
700 : {
701 : return CreateTexture(
702 : name, ITexture::Type::TEXTURE_2D, usage, format,
703 0 : width, height, defaultSamplerDesc, MIPLevelCount, sampleCount);
704 : }
705 :
706 0 : std::unique_ptr<IFramebuffer> CDevice::CreateFramebuffer(
707 : const char* name, SColorAttachment* colorAttachment,
708 : SDepthStencilAttachment* depthStencilAttachment)
709 : {
710 0 : return CFramebuffer::Create(
711 0 : this, name, colorAttachment, depthStencilAttachment);
712 : }
713 :
714 0 : std::unique_ptr<IBuffer> CDevice::CreateBuffer(
715 : const char* name, const IBuffer::Type type, const uint32_t size, const bool dynamic)
716 : {
717 0 : return CreateCBuffer(name, type, size, dynamic);
718 : }
719 :
720 0 : std::unique_ptr<CBuffer> CDevice::CreateCBuffer(
721 : const char* name, const IBuffer::Type type, const uint32_t size, const bool dynamic)
722 : {
723 0 : return CBuffer::Create(this, name, type, size, dynamic);
724 : }
725 :
726 0 : std::unique_ptr<IShaderProgram> CDevice::CreateShaderProgram(
727 : const CStr& name, const CShaderDefines& defines)
728 : {
729 0 : return CShaderProgram::Create(this, name, defines);
730 : }
731 :
732 0 : std::unique_ptr<IDeviceCommandContext> CDevice::CreateCommandContext()
733 : {
734 0 : return CDeviceCommandContext::Create(this);
735 : }
736 :
737 0 : bool CDevice::AcquireNextBackbuffer()
738 : {
739 0 : if (!IsSwapChainValid())
740 : {
741 0 : vkDeviceWaitIdle(m_Device);
742 :
743 0 : RecreateSwapChain();
744 0 : if (!IsSwapChainValid())
745 0 : return false;
746 : }
747 :
748 0 : PROFILE3("AcquireNextBackbuffer");
749 0 : return m_SubmitScheduler->AcquireNextImage(*m_SwapChain);
750 : }
751 :
752 0 : IFramebuffer* CDevice::GetCurrentBackbuffer(
753 : const AttachmentLoadOp colorAttachmentLoadOp,
754 : const AttachmentStoreOp colorAttachmentStoreOp,
755 : const AttachmentLoadOp depthStencilAttachmentLoadOp,
756 : const AttachmentStoreOp depthStencilAttachmentStoreOp)
757 : {
758 0 : return IsSwapChainValid() ? m_SwapChain->GetCurrentBackbuffer(
759 : colorAttachmentLoadOp, colorAttachmentStoreOp,
760 0 : depthStencilAttachmentLoadOp, depthStencilAttachmentStoreOp) : nullptr;
761 : }
762 :
763 0 : void CDevice::Present()
764 : {
765 0 : if (!IsSwapChainValid())
766 0 : return;
767 :
768 0 : PROFILE3("Present");
769 :
770 0 : m_SubmitScheduler->Present(*m_SwapChain);
771 :
772 0 : ProcessObjectToDestroyQueue();
773 0 : ProcessTextureToDestroyQueue();
774 :
775 0 : ++m_FrameID;
776 : }
777 :
778 0 : void CDevice::OnWindowResize(const uint32_t width, const uint32_t height)
779 : {
780 0 : if (!IsSwapChainValid() ||
781 0 : width != m_SwapChain->GetDepthTexture()->GetWidth() ||
782 0 : height != m_SwapChain->GetDepthTexture()->GetHeight())
783 : {
784 0 : RecreateSwapChain();
785 : }
786 0 : }
787 :
788 0 : bool CDevice::IsTextureFormatSupported(const Format format) const
789 : {
790 0 : switch (format)
791 : {
792 0 : case Format::UNDEFINED:
793 0 : return false;
794 :
795 0 : case Format::BC1_RGB_UNORM: FALLTHROUGH;
796 : case Format::BC1_RGBA_UNORM: FALLTHROUGH;
797 : case Format::BC2_UNORM: FALLTHROUGH;
798 : case Format::BC3_UNORM:
799 0 : return m_Capabilities.S3TC;
800 :
801 0 : default:
802 0 : break;
803 : }
804 :
805 0 : VkFormatProperties formatProperties{};
806 0 : vkGetPhysicalDeviceFormatProperties(
807 0 : m_ChoosenDevice.device, Mapping::FromFormat(format), &formatProperties);
808 0 : return formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
809 : }
810 :
811 0 : bool CDevice::IsFramebufferFormatSupported(const Format format) const
812 : {
813 0 : VkFormatProperties formatProperties{};
814 0 : vkGetPhysicalDeviceFormatProperties(
815 0 : m_ChoosenDevice.device, Mapping::FromFormat(format), &formatProperties);
816 0 : if (IsDepthFormat(format))
817 0 : return formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
818 0 : return formatProperties.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
819 : }
820 :
821 0 : Format CDevice::GetPreferredDepthStencilFormat(
822 : const uint32_t usage, const bool depth, const bool stencil) const
823 : {
824 0 : ENSURE(depth || stencil);
825 0 : Format format = Format::UNDEFINED;
826 0 : if (stencil)
827 : {
828 : // https://github.com/KhronosGroup/Vulkan-Guide/blob/main/chapters/depth.adoc#depth-formats
829 : // At least one of VK_FORMAT_D24_UNORM_S8_UINT or VK_FORMAT_D32_SFLOAT_S8_UINT
830 : // must also be supported.
831 0 : if (IsFormatSupportedForUsage(Format::D24_UNORM_S8_UINT, usage))
832 0 : format = Format::D24_UNORM_S8_UINT;
833 : else
834 0 : format = Format::D32_SFLOAT_S8_UINT;
835 : }
836 : else
837 : {
838 : std::array<Format, 3> formatRequestOrder;
839 : // TODO: add most known vendors to enum.
840 : // https://developer.nvidia.com/blog/vulkan-dos-donts/
841 0 : if (m_ChoosenDevice.properties.vendorID == 0x10DE)
842 0 : formatRequestOrder = {Format::D24_UNORM, Format::D32_SFLOAT, Format::D16_UNORM};
843 : else
844 0 : formatRequestOrder = {Format::D32_SFLOAT, Format::D24_UNORM, Format::D16_UNORM};
845 0 : for (const Format formatRequest : formatRequestOrder)
846 0 : if (IsFormatSupportedForUsage(formatRequest, usage))
847 : {
848 0 : format = formatRequest;
849 0 : break;
850 : }
851 : }
852 0 : return format;
853 : }
854 :
855 0 : bool CDevice::IsFormatSupportedForUsage(const Format format, const uint32_t usage) const
856 : {
857 0 : VkFormatProperties formatProperties{};
858 0 : vkGetPhysicalDeviceFormatProperties(
859 0 : m_ChoosenDevice.device, Mapping::FromFormat(format), &formatProperties);
860 0 : VkFormatFeatureFlags expectedFeatures = 0;
861 0 : if (usage & ITexture::Usage::COLOR_ATTACHMENT)
862 0 : expectedFeatures |= VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BIT;
863 0 : if (usage & ITexture::Usage::DEPTH_STENCIL_ATTACHMENT)
864 0 : expectedFeatures |= VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT;
865 0 : if (usage & ITexture::Usage::SAMPLED)
866 0 : expectedFeatures |= VK_FORMAT_FEATURE_SAMPLED_IMAGE_BIT;
867 0 : if (usage & ITexture::Usage::TRANSFER_SRC)
868 0 : expectedFeatures |= VK_FORMAT_FEATURE_TRANSFER_SRC_BIT;
869 0 : if (usage & ITexture::Usage::TRANSFER_DST)
870 0 : expectedFeatures |= VK_FORMAT_FEATURE_TRANSFER_DST_BIT;
871 0 : return (formatProperties.optimalTilingFeatures & expectedFeatures) == expectedFeatures;
872 : }
873 :
874 0 : void CDevice::ScheduleObjectToDestroy(
875 : VkObjectType type, const uint64_t handle, const VmaAllocation allocation)
876 : {
877 0 : m_ObjectToDestroyQueue.push({m_FrameID, type, handle, allocation});
878 0 : }
879 :
880 0 : void CDevice::ScheduleTextureToDestroy(const CTexture::UID uid)
881 : {
882 0 : m_TextureToDestroyQueue.push({m_FrameID, uid});
883 0 : }
884 :
885 0 : void CDevice::SetObjectName(VkObjectType type, const uint64_t handle, const char* name)
886 : {
887 0 : if (!m_Capabilities.debugLabels)
888 0 : return;
889 0 : VkDebugUtilsObjectNameInfoEXT nameInfo{};
890 0 : nameInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
891 0 : nameInfo.objectType = type;
892 0 : nameInfo.objectHandle = handle;
893 0 : nameInfo.pObjectName = name;
894 0 : vkSetDebugUtilsObjectNameEXT(m_Device, &nameInfo);
895 : }
896 :
897 0 : std::unique_ptr<CRingCommandContext> CDevice::CreateRingCommandContext(const size_t size)
898 : {
899 : return std::make_unique<CRingCommandContext>(
900 0 : this, size, m_GraphicsQueueFamilyIndex, *m_SubmitScheduler);
901 : }
902 :
903 0 : void CDevice::RecreateSwapChain()
904 : {
905 0 : int surfaceDrawableWidth = 0, surfaceDrawableHeight = 0;
906 0 : SDL_Vulkan_GetDrawableSize(m_Window, &surfaceDrawableWidth, &surfaceDrawableHeight);
907 0 : m_SwapChain = CSwapChain::Create(
908 0 : this, m_Surface, surfaceDrawableWidth, surfaceDrawableHeight, std::move(m_SwapChain));
909 0 : }
910 :
911 0 : bool CDevice::IsSwapChainValid()
912 : {
913 0 : return m_SwapChain && m_SwapChain->IsValid();
914 : }
915 :
916 0 : void CDevice::ProcessObjectToDestroyQueue(const bool ignoreFrameID)
917 : {
918 0 : while (!m_ObjectToDestroyQueue.empty() &&
919 0 : (ignoreFrameID || m_ObjectToDestroyQueue.front().frameID + NUMBER_OF_FRAMES_IN_FLIGHT < m_FrameID))
920 : {
921 0 : ObjectToDestroy& object = m_ObjectToDestroyQueue.front();
922 : #if VK_USE_64_BIT_PTR_DEFINES
923 0 : void* handle = reinterpret_cast<void*>(object.handle);
924 : #else
925 : const uint64_t handle = object.handle;
926 : #endif
927 0 : switch (object.type)
928 : {
929 0 : case VK_OBJECT_TYPE_IMAGE:
930 0 : vmaDestroyImage(GetVMAAllocator(), static_cast<VkImage>(handle), object.allocation);
931 0 : break;
932 0 : case VK_OBJECT_TYPE_BUFFER:
933 0 : vmaDestroyBuffer(GetVMAAllocator(), static_cast<VkBuffer>(handle), object.allocation);
934 0 : break;
935 0 : case VK_OBJECT_TYPE_IMAGE_VIEW:
936 0 : vkDestroyImageView(m_Device, static_cast<VkImageView>(handle), nullptr);
937 0 : break;
938 0 : case VK_OBJECT_TYPE_BUFFER_VIEW:
939 0 : vkDestroyBufferView(m_Device, static_cast<VkBufferView>(handle), nullptr);
940 0 : break;
941 0 : case VK_OBJECT_TYPE_FRAMEBUFFER:
942 0 : vkDestroyFramebuffer(m_Device, static_cast<VkFramebuffer>(handle), nullptr);
943 0 : break;
944 0 : case VK_OBJECT_TYPE_RENDER_PASS:
945 0 : vkDestroyRenderPass(m_Device, static_cast<VkRenderPass>(handle), nullptr);
946 0 : break;
947 0 : case VK_OBJECT_TYPE_SAMPLER:
948 0 : vkDestroySampler(m_Device, static_cast<VkSampler>(handle), nullptr);
949 0 : break;
950 0 : case VK_OBJECT_TYPE_SHADER_MODULE:
951 0 : vkDestroyShaderModule(m_Device, static_cast<VkShaderModule>(handle), nullptr);
952 0 : break;
953 0 : case VK_OBJECT_TYPE_PIPELINE_LAYOUT:
954 0 : vkDestroyPipelineLayout(m_Device, static_cast<VkPipelineLayout>(handle), nullptr);
955 0 : break;
956 0 : case VK_OBJECT_TYPE_PIPELINE:
957 0 : vkDestroyPipeline(m_Device, static_cast<VkPipeline>(handle), nullptr);
958 0 : break;
959 0 : default:
960 0 : debug_warn("Unsupported object to destroy type.");
961 : }
962 0 : m_ObjectToDestroyQueue.pop();
963 : }
964 0 : }
965 :
966 0 : void CDevice::ProcessTextureToDestroyQueue(const bool ignoreFrameID)
967 : {
968 0 : while (!m_TextureToDestroyQueue.empty() &&
969 0 : (ignoreFrameID || m_TextureToDestroyQueue.front().first + NUMBER_OF_FRAMES_IN_FLIGHT < m_FrameID))
970 : {
971 0 : GetDescriptorManager().OnTextureDestroy(m_TextureToDestroyQueue.front().second);
972 0 : m_TextureToDestroyQueue.pop();
973 : }
974 0 : }
975 :
976 0 : std::unique_ptr<IDevice> CreateDevice(SDL_Window* window)
977 : {
978 0 : return Vulkan::CDevice::Create(window);
979 : }
980 :
981 : } // namespace Vulkan
982 :
983 : } // namespace Backend
984 :
985 3 : } // namespace Renderer
|