Avoids assertions on Vulkan backend device creation in case of driver bugs.

This commit is contained in:
Vladislav Belov
2024-12-05 18:32:15 +01:00
parent adcb9755ff
commit 60b4072b29
8 changed files with 88 additions and 39 deletions
+10 -4
View File
@@ -606,9 +606,11 @@ std::unique_ptr<CDevice> CDevice::Create(SDL_Window* window)
device->m_RenderPassManager =
std::make_unique<CRenderPassManager>(device.get());
device->m_SamplerManager = std::make_unique<CSamplerManager>(device.get());
device->m_SubmitScheduler =
std::make_unique<CSubmitScheduler>(
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> CDevice::Create(SDL_Window* window)
std::make_unique<CDescriptorManager>(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<CRingCommandContext> CDevice::CreateRingCommandContext(const size_t size)
{
return std::make_unique<CRingCommandContext>(
return CRingCommandContext::Create(
this, size, m_GraphicsQueueFamilyIndex, *m_SubmitScheduler);
}
@@ -318,8 +318,10 @@ std::unique_ptr<IDeviceCommandContext> 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<CUploadRing>(
device, IBuffer::Type::VERTEX, FRAME_INPLACE_BUFFER_INITIAL_SIZE);
@@ -49,31 +49,33 @@ constexpr uint32_t INVALID_OFFSET = std::numeric_limits<uint32_t>::max();
} // anonymous namespace
CRingCommandContext::CRingCommandContext(
std::unique_ptr<CRingCommandContext> 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<uint32_t>(m_Device->GetChoosenPhysicalDevice().properties.limits.optimalBufferCopyOffsetAlignment));
std::unique_ptr<CRingCommandContext> ringCommandContext{
new CRingCommandContext{device, submitScheduler}};
ringCommandContext->m_OptimalBufferCopyOffsetAlignment = std::max(
1u, static_cast<uint32_t>(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()
@@ -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<CRingCommandContext> 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();
@@ -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> CSubmitScheduler::Create(
CDevice* device, const uint32_t queueFamilyIndex, VkQueue queue)
: m_Device(device), m_Queue(queue)
{
std::unique_ptr<CSubmitScheduler> 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<CRingCommandContext>(
device, NUMBER_OF_FRAMES_IN_FLIGHT, queueFamilyIndex, *this);
m_PresentCommandContext = std::make_unique<CRingCommandContext>(
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()
@@ -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<CSubmitScheduler> 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;
+2 -1
View File
@@ -182,7 +182,8 @@ std::unique_ptr<CSwapChain> CSwapChain::Create(
std::unique_ptr<CSwapChain> 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];
+12 -1
View File
@@ -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<int>(result), Utilities::GetVkResultName(result), __func__, __LINE__); \
return nullptr; \
} \
} while (0)
namespace Renderer
{