LCOV - code coverage report
Current view: top level - source/renderer/backend/vulkan - Device.cpp (source / functions) Hit Total Coverage
Test: 0 A.D. test coverage report Lines: 1 538 0.2 %
Date: 2023-01-19 00:18:29 Functions: 2 44 4.5 %

          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

Generated by: LCOV version 1.13