diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index c935f4717..85666d7c5 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -110,6 +110,7 @@ jobs: - name: Run Test Suite (vulkan) env: LOVE_GRAPHICS_DEBUG: 1 + LOVE_GRAPHICS_VULKAN_ALLOW_SOFTWARE: 1 run: | ./love-${{ github.sha }}.AppImage love2d-${{ github.sha }}/testing/main.lua --all --isRunner --renderers vulkan - name: Love Test Report (vulkan) @@ -390,6 +391,7 @@ jobs: # if: steps.vars.outputs.arch != 'ARM64' # env: # LOVE_GRAPHICS_DEBUG: 1 +# LOVE_GRAPHICS_VULKAN_ALLOW_SOFTWARE: 1 # run: | # powershell.exe ./install/lovec.exe ./megasource/libs/love/testing/main.lua --all --isRunner --renderers vulkan # - name: Love Test Report (vulkan) diff --git a/src/modules/graphics/vulkan/Graphics.cpp b/src/modules/graphics/vulkan/Graphics.cpp index 4229d4bd2..ba9fe9991 100644 --- a/src/modules/graphics/vulkan/Graphics.cpp +++ b/src/modules/graphics/vulkan/Graphics.cpp @@ -30,6 +30,7 @@ #include "Vulkan.h" #include +#include #include #include @@ -100,7 +101,10 @@ Graphics::Graphics() volkInitializeCustom((PFN_vkGetInstanceProcAddr)SDL_Vulkan_GetVkGetInstanceProcAddr()); if (isDebugEnabled() && !checkValidationSupport()) + { + SDL_Vulkan_UnloadLibrary(); throw love::Exception("validation layers requested, but not available"); + } VkApplicationInfo appInfo{}; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; @@ -120,7 +124,10 @@ Graphics::Graphics() unsigned int count = 0; char const* const* extensions_string = SDL_Vulkan_GetInstanceExtensions(&count); if (extensions_string == nullptr) + { + SDL_Vulkan_UnloadLibrary(); throw love::Exception("couldn't retrieve sdl vulkan extensions"); + } std::vector extensions(extensions_string, extensions_string + count); @@ -141,9 +148,38 @@ Graphics::Graphics() } if (vkCreateInstance(&createInfo, nullptr, &instance) != VK_SUCCESS) + { + SDL_Vulkan_UnloadLibrary(); throw love::Exception("couldn't create vulkan instance"); + } volkLoadInstance(instance); + + uint32_t deviceCount = 0; + vkEnumeratePhysicalDevices(instance, &deviceCount, nullptr); + + int maxScore = 0; + + if (deviceCount > 0) + { + std::vector devices(deviceCount); + vkEnumeratePhysicalDevices(instance, &deviceCount, devices.data()); + + // This is imperfect because we can't query a swap chain at this point. + // In theory it could cause a device to have a non-zero score here and + // no devices to have a non-zero score later in setMode, but hopefully + // that won't happen in practice... + for (const auto &device : devices) + maxScore = std::max(maxScore, rateDeviceSuitability(device, false)); + } + + // Exit here if there are no suitable devices, to let other backends take over. + if (maxScore == 0) + { + vkDestroyInstance(instance, nullptr); + SDL_Vulkan_UnloadLibrary(); + throw love::Exception("no suitable vulkan physical devices found"); + } } Graphics::~Graphics() @@ -1535,7 +1571,7 @@ void Graphics::pickPhysicalDevice() for (const auto &device : devices) { - int score = rateDeviceSuitability(device); + int score = rateDeviceSuitability(device, true); candidates.insert(std::make_pair(score, device)); } @@ -1583,7 +1619,7 @@ bool Graphics::checkDeviceExtensionSupport(VkPhysicalDevice device) // if the score is nonzero then the device is suitable. // A higher rating means generally better performance // if the score is 0 the device is unsuitable -int Graphics::rateDeviceSuitability(VkPhysicalDevice device) +int Graphics::rateDeviceSuitability(VkPhysicalDevice device, bool querySwapChain) { VkPhysicalDeviceProperties deviceProperties; VkPhysicalDeviceFeatures deviceFeatures; @@ -1603,15 +1639,21 @@ int Graphics::rateDeviceSuitability(VkPhysicalDevice device) // definitely needed + if (deviceProperties.deviceType == VK_PHYSICAL_DEVICE_TYPE_CPU) + { + if (!SDL_GetHintBoolean("LOVE_GRAPHICS_VULKAN_ALLOW_SOFTWARE", false)) + score = 0; + } + QueueFamilyIndices indices = findQueueFamilies(device); - if (!indices.isComplete()) + if (!indices.isComplete() && (querySwapChain || !indices.graphicsFamily.hasValue)) score = 0; bool extensionsSupported = checkDeviceExtensionSupport(device); if (!extensionsSupported) score = 0; - if (extensionsSupported) + if (extensionsSupported && querySwapChain) { auto swapChainSupport = querySwapChainSupport(device); bool swapChainAdequate = !swapChainSupport.formats.empty() && !swapChainSupport.presentModes.empty(); @@ -1644,11 +1686,14 @@ QueueFamilyIndices Graphics::findQueueFamilies(VkPhysicalDevice device) if ((queueFamily.queueFlags & VK_QUEUE_GRAPHICS_BIT) && (queueFamily.queueFlags & VK_QUEUE_COMPUTE_BIT)) indices.graphicsFamily = i; - VkBool32 presentSupport = false; - vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); + if (surface != VK_NULL_HANDLE) + { + VkBool32 presentSupport = false; + vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface, &presentSupport); - if (presentSupport) - indices.presentFamily = i; + if (presentSupport) + indices.presentFamily = i; + } if (indices.isComplete()) break; diff --git a/src/modules/graphics/vulkan/Graphics.h b/src/modules/graphics/vulkan/Graphics.h index c4c1ec143..173cbd9b5 100644 --- a/src/modules/graphics/vulkan/Graphics.h +++ b/src/modules/graphics/vulkan/Graphics.h @@ -300,7 +300,7 @@ protected: private: bool checkValidationSupport(); void pickPhysicalDevice(); - int rateDeviceSuitability(VkPhysicalDevice device); + int rateDeviceSuitability(VkPhysicalDevice device, bool querySwapChain); QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device); void createLogicalDevice(); void createPipelineCache();