summaryrefslogtreecommitdiffstats
path: root/Graphics/GraphicsEngineVulkan
diff options
context:
space:
mode:
authorEgor Yusov <egor.yusov@gmail.com>2018-06-17 02:21:46 +0000
committerEgor Yusov <egor.yusov@gmail.com>2018-06-17 02:21:46 +0000
commit76bfae0b232a1c524b538960dc5b673fcec0b49f (patch)
treea88350a430257467d1391cf7e8b5087e360d5829 /Graphics/GraphicsEngineVulkan
parentImproved dynamic heap memory usage stats reporting (diff)
downloadDiligentCore-76bfae0b232a1c524b538960dc5b673fcec0b49f.tar.gz
DiligentCore-76bfae0b232a1c524b538960dc5b673fcec0b49f.zip
Implemented forced wait when allocation from dynamic heap fails in Vulkan
Diffstat (limited to 'Graphics/GraphicsEngineVulkan')
-rw-r--r--Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h8
-rw-r--r--Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp15
-rw-r--r--Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp9
-rw-r--r--Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp4
-rw-r--r--Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp31
-rw-r--r--Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp71
6 files changed, 89 insertions, 49 deletions
diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h b/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h
index d36aa26a..a36cb3ba 100644
--- a/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h
+++ b/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h
@@ -99,7 +99,7 @@ class VulkanRingBuffer
{
public:
VulkanRingBuffer(IMemoryAllocator& Allocator,
- class RenderDeviceVkImpl* pDeviceVk,
+ class RenderDeviceVkImpl& DeviceVk,
Uint32 Size);
~VulkanRingBuffer();
@@ -120,9 +120,9 @@ private:
static constexpr const Uint32 MinAlignment = 1024;
RingBuffer::OffsetType Allocate(size_t SizeInBytes);
- std::mutex m_RingBuffMtx;
- RingBuffer m_RingBuffer;
- RenderDeviceVkImpl* const m_pDeviceVk;
+ std::mutex m_RingBuffMtx;
+ RingBuffer m_RingBuffer;
+ RenderDeviceVkImpl& m_DeviceVk;
VulkanUtilities::BufferWrapper m_VkBuffer;
VulkanUtilities::DeviceMemoryWrapper m_BufferMemory;
diff --git a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp
index f199d80f..8120e819 100644
--- a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp
@@ -354,10 +354,17 @@ void BufferVkImpl :: Map(IDeviceContext *pContext, MAP_TYPE MapType, Uint32 MapF
{
VERIFY(MapFlags & MAP_FLAG_DISCARD, "Vk buffer must be mapped for writing with MAP_FLAG_DISCARD flag");
auto DynAlloc = pDeviceContextVk->AllocateDynamicSpace(m_Desc.uiSizeInBytes);
- const auto& DynamicHeap = pDeviceVk->GetDynamicHeapRingBuffer();
- auto* CPUAddress = DynamicHeap.GetCPUAddress();
- pMappedData = CPUAddress + DynAlloc.Offset;
- m_DynamicAllocations[pDeviceContextVk->GetContextId()] = std::move(DynAlloc);
+ if(DynAlloc.pParentDynamicHeap != nullptr)
+ {
+ const auto& DynamicHeap = pDeviceVk->GetDynamicHeapRingBuffer();
+ auto* CPUAddress = DynamicHeap.GetCPUAddress();
+ pMappedData = CPUAddress + DynAlloc.Offset;
+ m_DynamicAllocations[pDeviceContextVk->GetContextId()] = std::move(DynAlloc);
+ }
+ else
+ {
+ pMappedData = nullptr;
+ }
}
else
{
diff --git a/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp
index e884b273..35bd779f 100644
--- a/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp
@@ -60,7 +60,12 @@ Uint64 CommandQueueVkImpl::ExecuteCommandBuffer(const VkSubmitInfo& SubmitInfo)
{
std::lock_guard<std::mutex> Lock(m_QueueMutex);
auto Fence = m_FencePool.GetFence();
- auto err = vkQueueSubmit(m_VkQueue, 1, &SubmitInfo, Fence);
+ bool SubmitCount =
+ (SubmitInfo.waitSemaphoreCount != 0 ||
+ SubmitInfo.commandBufferCount != 0 ||
+ SubmitInfo.signalSemaphoreCount != 0) ?
+ 1 : 0;
+ auto err = vkQueueSubmit(m_VkQueue, SubmitCount, &SubmitInfo, Fence);
VERIFY(err == VK_SUCCESS, "Failed to submit command buffer to the command queue");
// We must atomically place the (value, fence) pair into the deque
@@ -76,7 +81,7 @@ Uint64 CommandQueueVkImpl::ExecuteCommandBuffer(VkCommandBuffer cmdBuffer)
{
VkSubmitInfo SubmitInfo = {};
SubmitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
- SubmitInfo.commandBufferCount = 1;
+ SubmitInfo.commandBufferCount = cmdBuffer != VK_NULL_HANDLE ? 1 : 0;
SubmitInfo.pCommandBuffers = &cmdBuffer;
SubmitInfo.waitSemaphoreCount = 0; // the number of semaphores upon which to wait before executing the command buffers
SubmitInfo.pWaitSemaphores = nullptr; // a pointer to an array of semaphores upon which to wait before the command
diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
index 39be6239..7709a821 100644
--- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
@@ -1292,10 +1292,6 @@ namespace Diligent
auto *pDeviceVkImpl = m_pDevice.RawPtr<RenderDeviceVkImpl>();
auto DynAlloc = m_DynamicHeap.Allocate(SizeInBytes, 0);
- if (DynAlloc.pParentDynamicHeap == nullptr)
- {
- UNSUPPORTED("Failed to allocate dynamic memory");
- }
#ifdef _DEBUG
DynAlloc.dbgFrameNumber = pDeviceVkImpl->GetCurrentFrameNumber();
#endif
diff --git a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp
index cb30f577..3526631d 100644
--- a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp
@@ -91,7 +91,7 @@ RenderDeviceVkImpl :: RenderDeviceVkImpl(IReferenceCounters*
m_DynamicHeapRingBuffer
{
GetRawAllocator(),
- this,
+ *this,
CreationAttribs.DynamicHeapSize
}
{
@@ -318,30 +318,19 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources)
}
}
- // We must use NextFenceValue here, NOT current value, because the
- // fence value may or may not have been incremented when the last
- // command list was submitted for execution (Unity only
- // increments fence value once per frame)
- Uint64 NextFenceValue = 0;
+ Uint64 SubmittedFenceValue = 0;
Uint64 SubmittedCmdBuffNumber = 0;
- {
- // Lock the command queue to avoid other threads interfering with the GPU
- std::lock_guard<std::mutex> LockGuard(m_CmdQueueMutex);
- NextFenceValue = m_pCommandQueue->GetNextFenceValue();
- // Increment cmd buffer number while keeping queue locked.
- // This guarantees that any Vk object released after the lock
- // is released, will be associated with the incremented cmd list number
- SubmittedCmdBuffNumber = m_NextCmdBuffNumber;
- Atomics::AtomicIncrement(m_NextCmdBuffNumber);
- }
-
+ VkSubmitInfo DummySubmitInfo = {};
+ // Submit empty command buffer to set a fence on the GPU
+ SubmitCommandBuffer(DummySubmitInfo, SubmittedCmdBuffNumber, SubmittedFenceValue);
+
// Discard all remaining objects. This is important to do if there were
// no command lists submitted during the frame. All stale resources will
- // be associated with the next fence value and thus will not be released
- // until the next command buffer is finished by the GPU
- ProcessStaleResources(SubmittedCmdBuffNumber, NextFenceValue, CompletedFenceValue);
+ // be associated with the submitted fence value and thus will not be released
+ // until the GPU is finished with the current frame
+ ProcessStaleResources(SubmittedCmdBuffNumber, SubmittedFenceValue, CompletedFenceValue);
- m_DynamicHeapRingBuffer.FinishFrame(NextFenceValue, CompletedFenceValue);
+ m_DynamicHeapRingBuffer.FinishFrame(SubmittedFenceValue, CompletedFenceValue);
Atomics::AtomicIncrement(m_FrameNumber);
}
diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp
index 80322bcd..60f5368c 100644
--- a/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp
@@ -22,6 +22,8 @@
*/
#include "pch.h"
+#include <chrono>
+#include <thread>
#include "VulkanDynamicHeap.h"
#include "RenderDeviceVkImpl.h"
@@ -36,11 +38,11 @@ static VkDeviceSize GetDefaultAlignment(const VulkanUtilities::VulkanPhysicalDev
}
VulkanRingBuffer::VulkanRingBuffer(IMemoryAllocator& Allocator,
- RenderDeviceVkImpl* pDeviceVk,
- Uint32 Size) :
+ RenderDeviceVkImpl& DeviceVk,
+ Uint32 Size) :
m_RingBuffer(Size, Allocator),
- m_pDeviceVk(pDeviceVk),
- m_DefaultAlignment(GetDefaultAlignment(pDeviceVk->GetPhysicalDevice()))
+ m_DeviceVk(DeviceVk),
+ m_DefaultAlignment(GetDefaultAlignment(DeviceVk.GetPhysicalDevice()))
{
VERIFY( (Size & (MinAlignment-1)) == 0, "Heap size is not min aligned");
VkBufferCreateInfo VkBuffCI = {};
@@ -59,11 +61,11 @@ VulkanRingBuffer::VulkanRingBuffer(IMemoryAllocator& Allocator,
VkBuffCI.queueFamilyIndexCount = 0;
VkBuffCI.pQueueFamilyIndices = nullptr;
- const auto& LogicalDevice = pDeviceVk->GetLogicalDevice();
+ const auto& LogicalDevice = DeviceVk.GetLogicalDevice();
m_VkBuffer = LogicalDevice.CreateBuffer(VkBuffCI, "Dynamic heap buffer");
VkMemoryRequirements MemReqs = LogicalDevice.GetBufferMemoryRequirements(m_VkBuffer);
- const auto& PhysicalDevice = pDeviceVk->GetPhysicalDevice();
+ const auto& PhysicalDevice = DeviceVk.GetPhysicalDevice();
VkMemoryAllocateInfo MemAlloc = {};
MemAlloc.pNext = nullptr;
@@ -102,9 +104,9 @@ void VulkanRingBuffer::Destroy()
{
if (m_VkBuffer)
{
- m_pDeviceVk->GetLogicalDevice().UnmapMemory(m_BufferMemory);
- m_pDeviceVk->SafeReleaseVkObject(std::move(m_VkBuffer));
- m_pDeviceVk->SafeReleaseVkObject(std::move(m_BufferMemory));
+ m_DeviceVk.GetLogicalDevice().UnmapMemory(m_BufferMemory);
+ m_DeviceVk.SafeReleaseVkObject(std::move(m_VkBuffer));
+ m_DeviceVk.SafeReleaseVkObject(std::move(m_BufferMemory));
}
m_CPUAddress = nullptr;
}
@@ -130,14 +132,55 @@ RingBuffer::OffsetType VulkanRingBuffer::Allocate(size_t SizeInBytes)
}
std::lock_guard<std::mutex> Lock(m_RingBuffMtx);
- auto Offset = m_RingBuffer.Allocate(SizeInBytes);
+ RingBuffer::OffsetType Offset = m_RingBuffer.Allocate(SizeInBytes);
if(Offset == RingBuffer::InvalidOffset)
{
- UNEXPECTED("Allocation failed");
+ // Failed to allocate space in the ring buffer. Try to wait for GPU to finish pening frames
+ // to release some space
+ auto StartIdleTime = std::chrono::high_resolution_clock::now();
+ static constexpr const auto SleepPeriod = std::chrono::milliseconds(1);
+ static constexpr const auto MaxIdleDuration = std::chrono::duration<double>{60.0 / 1000.0}; // 60 ms
+ std::chrono::duration<double> IdleDuration;
+ Uint32 SleepIterations = 0;
+ while(Offset == RingBuffer::InvalidOffset && IdleDuration < MaxIdleDuration)
+ {
+ auto LastCompletedFenceValue = m_DeviceVk.GetCompletedFenceValue();
+ m_RingBuffer.ReleaseCompletedFrames(LastCompletedFenceValue);
+
+ Offset = m_RingBuffer.Allocate(SizeInBytes);
+ if (Offset == RingBuffer::InvalidOffset)
+ {
+ std::this_thread::sleep_for(SleepPeriod);
+ ++SleepIterations;
+ }
+
+ auto CurrTime = std::chrono::high_resolution_clock::now();
+ IdleDuration = std::chrono::duration_cast<std::chrono::duration<double>>(CurrTime - StartIdleTime);
+ }
+
+ if(Offset == RingBuffer::InvalidOffset)
+ {
+ LOG_ERROR_MESSAGE("Space in dynamic heap is exausted! After idling for ", std::fixed, std::setprecision(1), IdleDuration.count()*1000.0, " ms still no space is available. Increase the size of the ring buffer by setting EngineVkAttribs::DynamicHeapSize to a greater value or optimize dynamic resource usage");
+ }
+ else
+ {
+ if(SleepIterations == 0)
+ {
+ LOG_WARNING_MESSAGE("Space in dynamic heap is almost exausted forcing mid-frame ring buffer shrinkage. Increase the size of the ring buffer by setting EngineVkAttribs::DynamicHeapSize to a greater value or optimize dynamic resource usage");
+ }
+ else
+ {
+ LOG_WARNING_MESSAGE("Space in dynamic heap is almost exausted. Allocation from the ring buffer forced idle time of ", std::fixed, std::setprecision(1), IdleDuration.count()*1000.0, " ms. Increase the size of the ring buffer by setting EngineVkAttribs::DynamicHeapSize to a greater value or optimize dynamic resource usage");
+ }
+ }
+ }
+
+ if (Offset != RingBuffer::InvalidOffset)
+ {
+ m_CurrentFrameSize += SizeInBytes;
+ m_FramePeakSize = std::max(m_FramePeakSize, m_CurrentFrameSize);
+ m_TotalPeakSize = std::max(m_TotalPeakSize, m_RingBuffer.GetUsedSize());
}
- m_CurrentFrameSize += SizeInBytes;
- m_FramePeakSize = std::max(m_FramePeakSize, m_CurrentFrameSize);
- m_TotalPeakSize = std::max(m_TotalPeakSize, m_RingBuffer.GetUsedSize());
return Offset;
}