From 60b4072b2909cc72172c948a014069405fc5ac15 Mon Sep 17 00:00:00 2001 From: Vladislav Belov Date: Thu, 5 Dec 2024 18:32:15 +0100 Subject: [PATCH] Avoids assertions on Vulkan backend device creation in case of driver bugs. --- source/renderer/backend/vulkan/Device.cpp | 14 ++++-- .../backend/vulkan/DeviceCommandContext.cpp | 2 + .../backend/vulkan/RingCommandContext.cpp | 36 +++++++++------ .../backend/vulkan/RingCommandContext.h | 8 +++- .../backend/vulkan/SubmitScheduler.cpp | 46 ++++++++++++------- .../renderer/backend/vulkan/SubmitScheduler.h | 5 +- source/renderer/backend/vulkan/SwapChain.cpp | 3 +- source/renderer/backend/vulkan/Utilities.h | 13 +++++- 8 files changed, 88 insertions(+), 39 deletions(-) diff --git a/source/renderer/backend/vulkan/Device.cpp b/source/renderer/backend/vulkan/Device.cpp index 1beabbb3fe..ad8780d55e 100644 --- a/source/renderer/backend/vulkan/Device.cpp +++ b/source/renderer/backend/vulkan/Device.cpp @@ -606,9 +606,11 @@ std::unique_ptr CDevice::Create(SDL_Window* window) device->m_RenderPassManager = std::make_unique(device.get()); device->m_SamplerManager = std::make_unique(device.get()); - device->m_SubmitScheduler = - std::make_unique( - device.get(), device->m_GraphicsQueueFamilyIndex, device->m_GraphicsQueue); + + device->m_SubmitScheduler = CSubmitScheduler::Create( + device.get(), device->m_GraphicsQueueFamilyIndex, device->m_GraphicsQueue); + if (!device->m_SubmitScheduler) + return nullptr; bool disableDescriptorIndexing = false; CFG_GET_VAL("renderer.backend.vulkan.disabledescriptorindexing", disableDescriptorIndexing); @@ -617,6 +619,10 @@ std::unique_ptr CDevice::Create(SDL_Window* window) std::make_unique(device.get(), useDescriptorIndexing); device->RecreateSwapChain(); + // Currently we assume that we should have a valid swapchain on the device + // creation. + if (!device->m_SwapChain) + return nullptr; device->m_Name = choosenDevice.properties.deviceName; device->m_Version = @@ -936,7 +942,7 @@ void CDevice::SetObjectName(VkObjectType type, const uint64_t handle, const char std::unique_ptr CDevice::CreateRingCommandContext(const size_t size) { - return std::make_unique( + return CRingCommandContext::Create( this, size, m_GraphicsQueueFamilyIndex, *m_SubmitScheduler); } diff --git a/source/renderer/backend/vulkan/DeviceCommandContext.cpp b/source/renderer/backend/vulkan/DeviceCommandContext.cpp index ff737106f8..054520749c 100644 --- a/source/renderer/backend/vulkan/DeviceCommandContext.cpp +++ b/source/renderer/backend/vulkan/DeviceCommandContext.cpp @@ -318,8 +318,10 @@ std::unique_ptr CDeviceCommandContext::Create(CDevice* de deviceCommandContext->m_DebugScopedLabels = device->GetCapabilities().debugScopedLabels; deviceCommandContext->m_PrependCommandContext = device->CreateRingCommandContext(NUMBER_OF_FRAMES_IN_FLIGHT); + ENSURE(deviceCommandContext->m_PrependCommandContext); deviceCommandContext->m_CommandContext = device->CreateRingCommandContext(NUMBER_OF_FRAMES_IN_FLIGHT); + ENSURE(deviceCommandContext->m_CommandContext); deviceCommandContext->m_VertexUploadRing = std::make_unique( device, IBuffer::Type::VERTEX, FRAME_INPLACE_BUFFER_INITIAL_SIZE); diff --git a/source/renderer/backend/vulkan/RingCommandContext.cpp b/source/renderer/backend/vulkan/RingCommandContext.cpp index 4eda35fc4c..eea12e7f0a 100644 --- a/source/renderer/backend/vulkan/RingCommandContext.cpp +++ b/source/renderer/backend/vulkan/RingCommandContext.cpp @@ -49,31 +49,33 @@ constexpr uint32_t INVALID_OFFSET = std::numeric_limits::max(); } // anonymous namespace -CRingCommandContext::CRingCommandContext( +std::unique_ptr CRingCommandContext::Create( CDevice* device, const size_t size, const uint32_t queueFamilyIndex, CSubmitScheduler& submitScheduler) - : m_Device(device), m_SubmitScheduler(submitScheduler) { - ENSURE(m_Device); + ENSURE(device); - m_OptimalBufferCopyOffsetAlignment = std::max( - 1u, static_cast(m_Device->GetChoosenPhysicalDevice().properties.limits.optimalBufferCopyOffsetAlignment)); + std::unique_ptr ringCommandContext{ + new CRingCommandContext{device, submitScheduler}}; + + ringCommandContext->m_OptimalBufferCopyOffsetAlignment = std::max( + 1u, static_cast(device->GetChoosenPhysicalDevice().properties.limits.optimalBufferCopyOffsetAlignment)); // In case of small amount of host memory it's better to make uploading // slower rather than crashing due to OOM, because memory for a // staging buffer is allocated in the host memory. - m_MaxStagingBufferCapacity = - m_Device->GetChoosenPhysicalDevice().hostTotalMemory <= SMALL_HOST_TOTAL_MEMORY_THRESHOLD + ringCommandContext->m_MaxStagingBufferCapacity = + device->GetChoosenPhysicalDevice().hostTotalMemory <= SMALL_HOST_TOTAL_MEMORY_THRESHOLD ? MAX_SMALL_STAGING_BUFFER_CAPACITY : MAX_STAGING_BUFFER_CAPACITY; - m_Ring.resize(size); - for (RingItem& item : m_Ring) + ringCommandContext->m_Ring.resize(size); + for (RingItem& item : ringCommandContext->m_Ring) { VkCommandPoolCreateInfo commandPoolCreateInfoInfo{}; commandPoolCreateInfoInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; commandPoolCreateInfoInfo.queueFamilyIndex = queueFamilyIndex; - ENSURE_VK_SUCCESS(vkCreateCommandPool( - m_Device->GetVkDevice(), &commandPoolCreateInfoInfo, + RETURN_NULLPTR_IF_NOT_VK_SUCCESS(vkCreateCommandPool( + device->GetVkDevice(), &commandPoolCreateInfoInfo, nullptr, &item.commandPool)); VkCommandBufferAllocateInfo allocateInfo{}; @@ -81,11 +83,19 @@ CRingCommandContext::CRingCommandContext( allocateInfo.commandPool = item.commandPool; allocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; allocateInfo.commandBufferCount = 1; - ENSURE_VK_SUCCESS(vkAllocateCommandBuffers( - m_Device->GetVkDevice(), &allocateInfo, &item.commandBuffer)); + RETURN_NULLPTR_IF_NOT_VK_SUCCESS(vkAllocateCommandBuffers( + device->GetVkDevice(), &allocateInfo, &item.commandBuffer)); device->SetObjectName( VK_OBJECT_TYPE_COMMAND_BUFFER, item.commandBuffer, "RingCommandBuffer"); } + + return ringCommandContext; +} + +CRingCommandContext::CRingCommandContext( + CDevice* device, CSubmitScheduler& submitScheduler) + : m_Device(device), m_SubmitScheduler(submitScheduler) +{ } CRingCommandContext::~CRingCommandContext() diff --git a/source/renderer/backend/vulkan/RingCommandContext.h b/source/renderer/backend/vulkan/RingCommandContext.h index 0af96da9c8..f96b0bbd19 100644 --- a/source/renderer/backend/vulkan/RingCommandContext.h +++ b/source/renderer/backend/vulkan/RingCommandContext.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Wildfire Games. +/* Copyright (C) 2024 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -47,9 +47,10 @@ class CDevice; class CRingCommandContext { public: - CRingCommandContext( + static std::unique_ptr Create( CDevice* device, const size_t size, const uint32_t queueFamilyIndex, CSubmitScheduler& submitScheduler); + ~CRingCommandContext(); /** @@ -95,6 +96,9 @@ public: const UploadBufferFunction& uploadFunction); private: + CRingCommandContext( + CDevice* device, CSubmitScheduler& submitScheduler); + void Begin(); void End(); diff --git a/source/renderer/backend/vulkan/SubmitScheduler.cpp b/source/renderer/backend/vulkan/SubmitScheduler.cpp index cdaf5de8e7..4bb6f4937d 100644 --- a/source/renderer/backend/vulkan/SubmitScheduler.cpp +++ b/source/renderer/backend/vulkan/SubmitScheduler.cpp @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Wildfire Games. +/* Copyright (C) 2024 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -34,43 +34,55 @@ namespace Backend namespace Vulkan { -CSubmitScheduler::CSubmitScheduler( +std::unique_ptr CSubmitScheduler::Create( CDevice* device, const uint32_t queueFamilyIndex, VkQueue queue) - : m_Device(device), m_Queue(queue) { + std::unique_ptr submitScheduler{new CSubmitScheduler{device, queue}}; + // Currently we need exactly NUMBER_OF_FRAMES_IN_FLIGHT fences to avoid // possible overlapping of different work between frames. constexpr size_t numberOfFences = NUMBER_OF_FRAMES_IN_FLIGHT; - m_Fences.reserve(numberOfFences); + submitScheduler->m_Fences.reserve(numberOfFences); for (size_t index = 0; index < numberOfFences; ++index) { VkFenceCreateInfo fenceCreateInfo{}; fenceCreateInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; VkFence fence = VK_NULL_HANDLE; - ENSURE_VK_SUCCESS(vkCreateFence( - m_Device->GetVkDevice(), &fenceCreateInfo, nullptr, &fence)); - m_Fences.push_back({fence, INVALID_SUBMIT_HANDLE}); + RETURN_NULLPTR_IF_NOT_VK_SUCCESS(vkCreateFence( + device->GetVkDevice(), &fenceCreateInfo, nullptr, &fence)); + submitScheduler->m_Fences.push_back({fence, INVALID_SUBMIT_HANDLE}); } - for (FrameObject& frameObject : m_FrameObjects) + for (FrameObject& frameObject : submitScheduler->m_FrameObjects) { VkSemaphoreCreateInfo semaphoreCreateInfo{}; semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; - ENSURE_VK_SUCCESS(vkCreateSemaphore( + RETURN_NULLPTR_IF_NOT_VK_SUCCESS(vkCreateSemaphore( device->GetVkDevice(), &semaphoreCreateInfo, nullptr, &frameObject.acquireImageSemaphore)); - ENSURE_VK_SUCCESS(vkCreateSemaphore( + RETURN_NULLPTR_IF_NOT_VK_SUCCESS(vkCreateSemaphore( device->GetVkDevice(), &semaphoreCreateInfo, nullptr, &frameObject.submitDone)); } - m_AcquireCommandContext = std::make_unique( - device, NUMBER_OF_FRAMES_IN_FLIGHT, queueFamilyIndex, *this); - m_PresentCommandContext = std::make_unique( - device, NUMBER_OF_FRAMES_IN_FLIGHT, queueFamilyIndex, *this); + submitScheduler->m_AcquireCommandContext = CRingCommandContext::Create( + device, NUMBER_OF_FRAMES_IN_FLIGHT, queueFamilyIndex, *submitScheduler); + if (!submitScheduler->m_AcquireCommandContext) + return nullptr; + submitScheduler->m_PresentCommandContext = CRingCommandContext::Create( + device, NUMBER_OF_FRAMES_IN_FLIGHT, queueFamilyIndex, *submitScheduler); + if (!submitScheduler->m_PresentCommandContext) + return nullptr; - CFG_GET_VAL("renderer.backend.vulkan.debugwaitidlebeforeacquire", m_DebugWaitIdleBeforeAcquire); - CFG_GET_VAL("renderer.backend.vulkan.debugwaitidlebeforepresent", m_DebugWaitIdleBeforePresent); - CFG_GET_VAL("renderer.backend.vulkan.debugwaitidleafterpresent", m_DebugWaitIdleAfterPresent); + CFG_GET_VAL("renderer.backend.vulkan.debugwaitidlebeforeacquire", submitScheduler->m_DebugWaitIdleBeforeAcquire); + CFG_GET_VAL("renderer.backend.vulkan.debugwaitidlebeforepresent", submitScheduler->m_DebugWaitIdleBeforePresent); + CFG_GET_VAL("renderer.backend.vulkan.debugwaitidleafterpresent", submitScheduler->m_DebugWaitIdleAfterPresent); + + return submitScheduler; +} + +CSubmitScheduler::CSubmitScheduler(CDevice* device, VkQueue queue) + : m_Device(device), m_Queue(queue) +{ } CSubmitScheduler::~CSubmitScheduler() diff --git a/source/renderer/backend/vulkan/SubmitScheduler.h b/source/renderer/backend/vulkan/SubmitScheduler.h index 325fdea15f..8746058a76 100644 --- a/source/renderer/backend/vulkan/SubmitScheduler.h +++ b/source/renderer/backend/vulkan/SubmitScheduler.h @@ -49,7 +49,8 @@ public: using SubmitHandle = uint32_t; static constexpr SubmitHandle INVALID_SUBMIT_HANDLE = 0; - CSubmitScheduler(CDevice* device, const uint32_t queueFamilyIndex, VkQueue queue); + static std::unique_ptr Create( + CDevice* device, const uint32_t queueFamilyIndex, VkQueue queue); ~CSubmitScheduler(); bool AcquireNextImage(CSwapChain& swapChain); @@ -65,6 +66,8 @@ public: void Flush(); private: + CSubmitScheduler(CDevice* device, VkQueue queue); + CDevice* m_Device = nullptr; VkQueue m_Queue = VK_NULL_HANDLE; diff --git a/source/renderer/backend/vulkan/SwapChain.cpp b/source/renderer/backend/vulkan/SwapChain.cpp index 3578207017..f84af98d20 100644 --- a/source/renderer/backend/vulkan/SwapChain.cpp +++ b/source/renderer/backend/vulkan/SwapChain.cpp @@ -182,7 +182,8 @@ std::unique_ptr CSwapChain::Create( std::unique_ptr swapChain(new CSwapChain()); swapChain->m_Device = device; - ENSURE_VK_SUCCESS(vkCreateSwapchainKHR( + + RETURN_NULLPTR_IF_NOT_VK_SUCCESS(vkCreateSwapchainKHR( device->GetVkDevice(), &swapChainCreateInfo, nullptr, &swapChain->m_SwapChain)); char nameBuffer[64]; diff --git a/source/renderer/backend/vulkan/Utilities.h b/source/renderer/backend/vulkan/Utilities.h index 6c7cde761f..27b4bf59cf 100644 --- a/source/renderer/backend/vulkan/Utilities.h +++ b/source/renderer/backend/vulkan/Utilities.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2023 Wildfire Games. +/* Copyright (C) 2024 Wildfire Games. * This file is part of 0 A.D. * * 0 A.D. is free software: you can redistribute it and/or modify @@ -33,6 +33,17 @@ } \ } while (0) +#define RETURN_NULLPTR_IF_NOT_VK_SUCCESS(EXPR) \ + do \ + { \ + const VkResult result = (EXPR); \ + if (result != VK_SUCCESS) \ + { \ + LOGERROR(#EXPR " returned %d (%s) instead of VK_SUCCESS (%s:%d)", static_cast(result), Utilities::GetVkResultName(result), __func__, __LINE__); \ + return nullptr; \ + } \ + } while (0) + namespace Renderer {