[Improvement] (MG_Backend/DirectVulkan): Do not crash when validation layers are unavailable.

This commit is contained in:
BZLZHH
2026-02-15 20:20:16 +08:00
parent 4bcedc84b9
commit 1a91e48219
@@ -60,7 +60,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return VK_FALSE; return VK_FALSE;
} }
VulkanRenderer::VulkanRenderer(NativeWindowType window, const RendererConfig& cfg) : m_window(window), m_config(cfg) { VulkanRenderer::VulkanRenderer(NativeWindowType window, const RendererConfig& cfg)
: m_window(window), m_config(cfg) {
// Initialize(); // Initialize();
} }
@@ -255,8 +256,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VK_VERIFY(vkWaitForFences(m_device, 1, &m_imageInFlightFences[m_currentFrameIndex], VK_TRUE, UINT64_MAX)); VK_VERIFY(vkWaitForFences(m_device, 1, &m_imageInFlightFences[m_currentFrameIndex], VK_TRUE, UINT64_MAX));
VK_VERIFY(vkResetFences(m_device, 1, &m_imageInFlightFences[m_currentFrameIndex])); VK_VERIFY(vkResetFences(m_device, 1, &m_imageInFlightFences[m_currentFrameIndex]));
VK_VERIFY(vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, VK_VERIFY(vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX,
m_imageAvailableSemaphores[m_currentFrameIndex], m_imageAvailableSemaphores[m_currentFrameIndex], VK_NULL_HANDLE,
VK_NULL_HANDLE, &m_imageIndexAcquired)); &m_imageIndexAcquired));
MGLOG_D("VulkanRenderer initialized"); MGLOG_D("VulkanRenderer initialized");
} }
@@ -402,10 +403,11 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_currentFrameIndex = (m_currentFrameIndex + 1) % m_config.MaxFramesInFlight; m_currentFrameIndex = (m_currentFrameIndex + 1) % m_config.MaxFramesInFlight;
// 4) Wait/reset/acquire for next frame. // 4) Wait/reset/acquire for next frame.
VK_VERIFY(vkWaitForFences(m_device, 1, &m_imageInFlightFences[m_currentFrameIndex], VK_TRUE, UINT64_MAX), "Present, vkWaitForFences"); VK_VERIFY(vkWaitForFences(m_device, 1, &m_imageInFlightFences[m_currentFrameIndex], VK_TRUE, UINT64_MAX),
"Present, vkWaitForFences");
VK_VERIFY(vkResetFences(m_device, 1, &m_imageInFlightFences[m_currentFrameIndex]), "Present, vkResetFences"); VK_VERIFY(vkResetFences(m_device, 1, &m_imageInFlightFences[m_currentFrameIndex]), "Present, vkResetFences");
result = vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, result =
m_imageAvailableSemaphores[m_currentFrameIndex], vkAcquireNextImageKHR(m_device, m_swapchain, UINT64_MAX, m_imageAvailableSemaphores[m_currentFrameIndex],
VK_NULL_HANDLE, &m_imageIndexAcquired); VK_NULL_HANDLE, &m_imageIndexAcquired);
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) {
MGLOG_D("Present, vkAcquireNextImageKHR got %d, recreating swapchain", result); MGLOG_D("Present, vkAcquireNextImageKHR got %d, recreating swapchain", result);
@@ -427,7 +429,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
MGLOG_I("Validation layers %s.", m_config.EnableValidationLayers ? "requested" : "not requested"); MGLOG_I("Validation layers %s.", m_config.EnableValidationLayers ? "requested" : "not requested");
if (m_config.EnableValidationLayers && !validationLayerAvailable) { if (m_config.EnableValidationLayers && !validationLayerAvailable) {
MOBILEGL_ASSERT(false, "Validation layers requested but not available!"); MGLOG_I("Validation layers not available! Disabling validation layers.");
} }
m_validationLayersEnabled = m_config.EnableValidationLayers && validationLayerAvailable; m_validationLayersEnabled = m_config.EnableValidationLayers && validationLayerAvailable;
@@ -445,7 +447,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
appInfo.apiVersion = VK_API_VERSION_1_1; appInfo.apiVersion = VK_API_VERSION_1_1;
#endif #endif
// ---------------- Instance info ------------------- // ---------------- Instance info -------------------
VkInstanceCreateInfo instanceInfo = {}; VkInstanceCreateInfo instanceInfo = {};
instanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; instanceInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
@@ -490,22 +491,22 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VK_VERIFY(vkCreateInstance(&instanceInfo, nullptr, &m_instance), "vkCreateInstance failed"); VK_VERIFY(vkCreateInstance(&instanceInfo, nullptr, &m_instance), "vkCreateInstance failed");
if (m_validationLayersEnabled) if (m_validationLayersEnabled) VK_VERIFY(SetupDebugMessenger());
VK_VERIFY(SetupDebugMessenger());
} }
VkResult VulkanRenderer::SetupDebugMessenger() { VkResult VulkanRenderer::SetupDebugMessenger() {
auto createInfo = PopulateDebugMessengerCreateInfo(); auto createInfo = PopulateDebugMessengerCreateInfo();
auto vkCreateDebugUtilsMessengerEXT = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(m_instance, "vkCreateDebugUtilsMessengerEXT"); auto vkCreateDebugUtilsMessengerEXT =
if (!vkCreateDebugUtilsMessengerEXT) (PFN_vkCreateDebugUtilsMessengerEXT)vkGetInstanceProcAddr(m_instance, "vkCreateDebugUtilsMessengerEXT");
return VK_ERROR_EXTENSION_NOT_PRESENT; if (!vkCreateDebugUtilsMessengerEXT) return VK_ERROR_EXTENSION_NOT_PRESENT;
VK_VERIFY(vkCreateDebugUtilsMessengerEXT(m_instance, &createInfo, nullptr, &m_debugMessenger)); VK_VERIFY(vkCreateDebugUtilsMessengerEXT(m_instance, &createInfo, nullptr, &m_debugMessenger));
return VK_SUCCESS; return VK_SUCCESS;
} }
VkResult VulkanRenderer::DestroyDebugMessenger() { VkResult VulkanRenderer::DestroyDebugMessenger() {
if (m_debugMessenger != VK_NULL_HANDLE) { if (m_debugMessenger != VK_NULL_HANDLE) {
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(m_instance, "vkDestroyDebugUtilsMessengerEXT"); auto func = (PFN_vkDestroyDebugUtilsMessengerEXT)vkGetInstanceProcAddr(m_instance,
"vkDestroyDebugUtilsMessengerEXT");
if (func != nullptr) { if (func != nullptr) {
func(m_instance, m_debugMessenger, nullptr); func(m_instance, m_debugMessenger, nullptr);
} else { } else {
@@ -518,8 +519,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkDebugUtilsMessengerCreateInfoEXT VulkanRenderer::PopulateDebugMessengerCreateInfo() { VkDebugUtilsMessengerCreateInfoEXT VulkanRenderer::PopulateDebugMessengerCreateInfo() {
VkDebugUtilsMessengerCreateInfoEXT createInfo{}; VkDebugUtilsMessengerCreateInfoEXT createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; createInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT |
createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
createInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
createInfo.pfnUserCallback = DebugCallback; createInfo.pfnUserCallback = DebugCallback;
createInfo.pUserData = this; createInfo.pUserData = this;
return createInfo; return createInfo;
@@ -551,8 +556,9 @@ namespace MobileGL::MG_Backend::DirectVulkan {
} }
} }
Bool VulkanRenderer::GetMoreCapablePhysicalDevice( Bool VulkanRenderer::GetMoreCapablePhysicalDevice(VkPhysicalDevice newVkDevice, VkSurfaceKHR surface,
VkPhysicalDevice newVkDevice, VkSurfaceKHR surface, const PhysicalDevice& otherDevice, PhysicalDevice& outBetterDevice) { const PhysicalDevice& otherDevice,
PhysicalDevice& outBetterDevice) {
const auto deviceTypeToStr = [](VkPhysicalDeviceType type) { const auto deviceTypeToStr = [](VkPhysicalDeviceType type) {
switch (type) { switch (type) {
case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU: case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU:
@@ -576,16 +582,16 @@ namespace MobileGL::MG_Backend::DirectVulkan {
vkGetPhysicalDeviceProperties(newVkDevice, &newDevice.properties); vkGetPhysicalDeviceProperties(newVkDevice, &newDevice.properties);
const auto& deviceProperties = newDevice.properties; const auto& deviceProperties = newDevice.properties;
auto apiVersion = deviceProperties.apiVersion; auto apiVersion = deviceProperties.apiVersion;
MGLOG_I(" %s (Vulkan %d.%d.%d, %s)", MGLOG_I(" %s (Vulkan %d.%d.%d, %s)", deviceProperties.deviceName, VK_VERSION_MAJOR(apiVersion),
deviceProperties.deviceName, VK_VERSION_MINOR(apiVersion), VK_VERSION_PATCH(apiVersion),
VK_VERSION_MAJOR(apiVersion), VK_VERSION_MINOR(apiVersion), VK_VERSION_PATCH(apiVersion),
deviceTypeToStr(deviceProperties.deviceType)); deviceTypeToStr(deviceProperties.deviceType));
// Check device extensions (including swapchain extension) // Check device extensions (including swapchain extension)
Bool deviceExtSupported = IsNecessaryDeviceExtensionSupported(newVkDevice); Bool deviceExtSupported = IsNecessaryDeviceExtensionSupported(newVkDevice);
if (!deviceExtSupported) { if (!deviceExtSupported) {
outBetterDevice = otherDevice; outBetterDevice = otherDevice;
MGLOG_I(" Ignored physical device. (Reason: Some of the required device extension not supported on this device)"); MGLOG_I(" Ignored physical device. (Reason: Some of the required device extension not supported on this "
"device)");
return false; return false;
} }
@@ -644,7 +650,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VkSurfaceFormatKHR VulkanRenderer::ChooseSwapchainSurfaceFormat( VkSurfaceFormatKHR VulkanRenderer::ChooseSwapchainSurfaceFormat(
const Vector<VkSurfaceFormatKHR>& availableFormats) { const Vector<VkSurfaceFormatKHR>& availableFormats) {
for (const auto& availableFormat : availableFormats) { for (const auto& availableFormat : availableFormats) {
if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB && availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) { if (availableFormat.format == VK_FORMAT_B8G8R8A8_SRGB &&
availableFormat.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
return availableFormat; return availableFormat;
} }
} }
@@ -653,8 +660,7 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return availableFormats[0]; return availableFormats[0];
} }
VkPresentModeKHR VulkanRenderer::ChooseSwapchainPresentMode( VkPresentModeKHR VulkanRenderer::ChooseSwapchainPresentMode(const Vector<VkPresentModeKHR>& availablePresentModes) {
const Vector<VkPresentModeKHR>& availablePresentModes) {
for (auto desiredPresentMode : s_desiredPresentModes) { for (auto desiredPresentMode : s_desiredPresentModes) {
for (const auto& presentMode : availablePresentModes) { for (const auto& presentMode : availablePresentModes) {
if (presentMode == desiredPresentMode) { if (presentMode == desiredPresentMode) {
@@ -697,7 +703,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return true; return true;
} }
VulkanRenderer::SwapchainCapabilities VulkanRenderer::GetSwapchainCapabilities(VkPhysicalDevice device, VkSurfaceKHR surface) { VulkanRenderer::SwapchainCapabilities VulkanRenderer::GetSwapchainCapabilities(VkPhysicalDevice device,
VkSurfaceKHR surface) {
SwapchainCapabilities swapchainCapabilities; SwapchainCapabilities swapchainCapabilities;
VK_VERIFY(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &swapchainCapabilities.capabilities)); VK_VERIFY(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface, &swapchainCapabilities.capabilities));
@@ -706,7 +713,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (formatCount != 0) { if (formatCount != 0) {
swapchainCapabilities.surfaceFormats.resize(formatCount); swapchainCapabilities.surfaceFormats.resize(formatCount);
VK_VERIFY(vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount, swapchainCapabilities.surfaceFormats.data())); VK_VERIFY(vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface, &formatCount,
swapchainCapabilities.surfaceFormats.data()));
} }
Uint32 presentModeCount; Uint32 presentModeCount;
@@ -714,7 +722,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
if (presentModeCount != 0) { if (presentModeCount != 0) {
swapchainCapabilities.presentModes.resize(presentModeCount); swapchainCapabilities.presentModes.resize(presentModeCount);
VK_VERIFY(vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount, swapchainCapabilities.presentModes.data())); VK_VERIFY(vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface, &presentModeCount,
swapchainCapabilities.presentModes.data()));
} }
return swapchainCapabilities; return swapchainCapabilities;
@@ -765,8 +774,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
Uint32 extensionCount = 0; Uint32 extensionCount = 0;
VK_VERIFY(vkEnumerateDeviceExtensionProperties(m_physicalDevice.handle, nullptr, &extensionCount, nullptr)); VK_VERIFY(vkEnumerateDeviceExtensionProperties(m_physicalDevice.handle, nullptr, &extensionCount, nullptr));
Vector<VkExtensionProperties> availableExtensions(extensionCount); Vector<VkExtensionProperties> availableExtensions(extensionCount);
VK_VERIFY(vkEnumerateDeviceExtensionProperties( VK_VERIFY(vkEnumerateDeviceExtensionProperties(m_physicalDevice.handle, nullptr, &extensionCount,
m_physicalDevice.handle, nullptr, &extensionCount, availableExtensions.data())); availableExtensions.data()));
for (const auto& extension : availableExtensions) { for (const auto& extension : availableExtensions) {
if (strcmp(extension.extensionName, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) == 0) { if (strcmp(extension.extensionName, VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME) == 0) {
@@ -1102,10 +1111,12 @@ namespace MobileGL::MG_Backend::DirectVulkan {
auto& supportedSurfaceFormats = m_physicalDevice.swapchainCapabilities.surfaceFormats; auto& supportedSurfaceFormats = m_physicalDevice.swapchainCapabilities.surfaceFormats;
MGLOG_I("Got %d surface formats: ", supportedSurfaceFormats.size()); MGLOG_I("Got %d surface formats: ", supportedSurfaceFormats.size());
for (auto& surfaceFormat : supportedSurfaceFormats) { for (auto& surfaceFormat : supportedSurfaceFormats) {
MGLOG_I(" [%s, %s]", surfaceFormatToStr(surfaceFormat.format), colorSpaceToStr(surfaceFormat.colorSpace)); MGLOG_I(" [%s, %s]", surfaceFormatToStr(surfaceFormat.format),
colorSpaceToStr(surfaceFormat.colorSpace));
} }
m_swapchainSurfaceFormat = ChooseSwapchainSurfaceFormat(supportedSurfaceFormats); m_swapchainSurfaceFormat = ChooseSwapchainSurfaceFormat(supportedSurfaceFormats);
MGLOG_I("Picked surface format: [%s, %s]", surfaceFormatToStr(m_swapchainSurfaceFormat.format), colorSpaceToStr(m_swapchainSurfaceFormat.colorSpace)); MGLOG_I("Picked surface format: [%s, %s]", surfaceFormatToStr(m_swapchainSurfaceFormat.format),
colorSpaceToStr(m_swapchainSurfaceFormat.colorSpace));
auto& supportedPresentModes = m_physicalDevice.swapchainCapabilities.presentModes; auto& supportedPresentModes = m_physicalDevice.swapchainCapabilities.presentModes;
MGLOG_I("Got %d present mode: ", supportedPresentModes.size()); MGLOG_I("Got %d present mode: ", supportedPresentModes.size());
@@ -1127,7 +1138,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
sci.imageExtent = swapchainCaps.currentExtent; sci.imageExtent = swapchainCaps.currentExtent;
sci.imageArrayLayers = 1; sci.imageArrayLayers = 1;
sci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; sci.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
Uint32 queueFamilyIndices[] = { (Uint)m_physicalDevice.queueFamilies.graphicsFamily, (Uint)m_physicalDevice.queueFamilies.presentFamily }; Uint32 queueFamilyIndices[] = {(Uint)m_physicalDevice.queueFamilies.graphicsFamily,
(Uint)m_physicalDevice.queueFamilies.presentFamily};
if (m_physicalDevice.queueFamilies.graphicsFamily != m_physicalDevice.queueFamilies.presentFamily) { if (m_physicalDevice.queueFamilies.graphicsFamily != m_physicalDevice.queueFamilies.presentFamily) {
sci.imageSharingMode = VK_SHARING_MODE_CONCURRENT; sci.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
sci.queueFamilyIndexCount = 2; sci.queueFamilyIndexCount = 2;
@@ -1152,7 +1164,8 @@ namespace MobileGL::MG_Backend::DirectVulkan {
VK_VERIFY(vkGetSwapchainImagesKHR(m_device, m_swapchain, &gotImageCount, m_swapchainImages.data())); VK_VERIFY(vkGetSwapchainImagesKHR(m_device, m_swapchain, &gotImageCount, m_swapchainImages.data()));
m_swapchainExtent = swapchainCaps.currentExtent; m_swapchainExtent = swapchainCaps.currentExtent;
MGLOG_I("Swapchain created, extent = %dx%d, swapchain imageCount = %d", m_swapchainExtent.width, m_swapchainExtent.height, gotImageCount); MGLOG_I("Swapchain created, extent = %dx%d, swapchain imageCount = %d", m_swapchainExtent.width,
m_swapchainExtent.height, gotImageCount);
} }
void VulkanRenderer::CreateSwapchainImageViews() { void VulkanRenderer::CreateSwapchainImageViews() {
@@ -1290,21 +1303,20 @@ namespace MobileGL::MG_Backend::DirectVulkan {
return -1; return -1;
} }
Int VulkanRenderer::GetPresentQueueFamilyIndex( Int VulkanRenderer::GetPresentQueueFamilyIndex(const PhysicalDevice& physicalDevice, VkSurfaceKHR surface,
const PhysicalDevice& physicalDevice, VkSurfaceKHR surface, const Vector<VkQueueFamilyProperties>& queueFamilies,
const Vector<VkQueueFamilyProperties>& queueFamilies, Int preferredFamilyIndex) { Int preferredFamilyIndex) {
if (preferredFamilyIndex != -1) { if (preferredFamilyIndex != -1) {
VkBool32 supportsPresent = false; VkBool32 supportsPresent = false;
vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice.handle, preferredFamilyIndex, surface, &supportsPresent); vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice.handle, preferredFamilyIndex, surface,
if (supportsPresent) &supportsPresent);
return preferredFamilyIndex; if (supportsPresent) return preferredFamilyIndex;
} }
for (Uint32 i = 0; i < queueFamilies.size(); i++) { for (Uint32 i = 0; i < queueFamilies.size(); i++) {
VkBool32 supportsPresent = false; VkBool32 supportsPresent = false;
vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice.handle, i, surface, &supportsPresent); vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice.handle, i, surface, &supportsPresent);
if (supportsPresent) if (supportsPresent) return i;
return i;
} }
return -1; return -1;
} }
@@ -1345,7 +1357,6 @@ namespace MobileGL::MG_Backend::DirectVulkan {
m_renderPass = VK_NULL_HANDLE; m_renderPass = VK_NULL_HANDLE;
} }
for (auto imageView : m_swapchainImageViews) { for (auto imageView : m_swapchainImageViews) {
vkDestroyImageView(m_device, imageView, nullptr); vkDestroyImageView(m_device, imageView, nullptr);
} }