diff options
| author | Egor Yusov <egor.yusov@gmail.com> | 2018-06-17 02:21:46 +0000 |
|---|---|---|
| committer | Egor Yusov <egor.yusov@gmail.com> | 2018-06-17 02:21:46 +0000 |
| commit | 76bfae0b232a1c524b538960dc5b673fcec0b49f (patch) | |
| tree | a88350a430257467d1391cf7e8b5087e360d5829 /Graphics/GraphicsEngineVulkan | |
| parent | Improved dynamic heap memory usage stats reporting (diff) | |
| download | DiligentCore-76bfae0b232a1c524b538960dc5b673fcec0b49f.tar.gz DiligentCore-76bfae0b232a1c524b538960dc5b673fcec0b49f.zip | |
Implemented forced wait when allocation from dynamic heap fails in Vulkan
Diffstat (limited to 'Graphics/GraphicsEngineVulkan')
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; } |
