diff options
| author | Egor Yusov <egor.yusov@gmail.com> | 2018-05-30 15:20:35 +0000 |
|---|---|---|
| committer | Egor Yusov <egor.yusov@gmail.com> | 2018-05-30 15:20:35 +0000 |
| commit | 5a56e75e2f3c7243eb040590e73312c5fef98534 (patch) | |
| tree | e54be16682aed5f44db86484517606c64fb5ab01 /Graphics/GraphicsEngineVulkan | |
| parent | Added vertex buffer committing in Vk (diff) | |
| download | DiligentCore-5a56e75e2f3c7243eb040590e73312c5fef98534.tar.gz DiligentCore-5a56e75e2f3c7243eb040590e73312c5fef98534.zip | |
Implemented dynamic resource write-discard mapping
Diffstat (limited to 'Graphics/GraphicsEngineVulkan')
16 files changed, 600 insertions, 700 deletions
diff --git a/Graphics/GraphicsEngineVulkan/CMakeLists.txt b/Graphics/GraphicsEngineVulkan/CMakeLists.txt index fff5b003..ef9273ab 100644 --- a/Graphics/GraphicsEngineVulkan/CMakeLists.txt +++ b/Graphics/GraphicsEngineVulkan/CMakeLists.txt @@ -11,7 +11,7 @@ set(INCLUDE include/VulkanTypeConversions.h include/DescriptorPoolManager.h include/DeviceContextVkImpl.h - include/DynamicUploadHeap.h + include/VulkanDynamicHeap.h include/FramebufferCache.h include/GenerateMips.h include/pch.h @@ -68,7 +68,7 @@ set(SRC src/VulkanTypeConversions.cpp src/DescriptorPoolManager.cpp src/DeviceContextVkImpl.cpp - src/DynamicUploadHeap.cpp + src/VulkanDynamicHeap.cpp src/FramebufferCache.cpp src/GenerateMips.cpp src/PipelineLayout.cpp diff --git a/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h b/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h index 15c9b226..97a86bc5 100644 --- a/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h +++ b/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h @@ -30,7 +30,7 @@ #include "RenderDeviceVk.h" #include "BufferBase.h" #include "BufferViewVkImpl.h" -#include "DynamicUploadHeap.h" +#include "VulkanDynamicHeap.h" #include "VulkanUtilities/VulkanObjectWrappers.h" namespace Diligent @@ -66,27 +66,6 @@ public: void DbgVerifyDynamicAllocation(Uint32 ContextId); #endif - /*virtual IVkResource *GetVkBuffer(size_t &DataStartByteOffset, Uint32 ContextId)override final - { - auto *pVkResource = GetVkResource(); - if(pVkResource != nullptr) - { - VERIFY(m_Desc.Usage != USAGE_DYNAMIC || (m_Desc.BindFlags | (BIND_SHADER_RESOURCE|BIND_UNORDERED_ACCESS)) != 0, "Expected non-dynamic buffer or a buffer with SRV or UAV bind flags"); - DataStartByteOffset = 0; - return pVkResource; - } - else - { - VERIFY(m_Desc.Usage == USAGE_DYNAMIC, "Dynamic buffer is expected"); - -#ifdef _DEBUG - DbgVerifyDynamicAllocation(ContextId); -#endif - DataStartByteOffset = m_DynamicData[ContextId].Offset; - return m_DynamicData[ContextId].pBuffer; - } - } - */ VkBuffer GetVkBuffer()const override final { @@ -95,47 +74,22 @@ public: virtual void* GetNativeHandle()override final { - //VERIFY(GetVkResource() != nullptr, "The buffer is dynamic and has no pointer to Vk resource"); - //size_t DataStartByteOffset = 0; auto VkBuffer = GetVkBuffer(); - //VERIFY(DataStartByteOffset == 0, "0 offset expected"); return VkBuffer; } virtual void SetAccessFlags(VkAccessFlags AccessFlags )override final{ m_AccessFlags = AccessFlags; } VkAccessFlags GetAccessFlags()const{return m_AccessFlags;} -/* - Vk_GPU_VIRTUAL_ADDRESS GetGPUAddress(Uint32 ContextId) - { - if(m_Desc.Usage == USAGE_DYNAMIC) - { -#ifdef _DEBUG - DbgVerifyDynamicAllocation(ContextId); -#endif - return m_DynamicData[ContextId].GPUAddress; - } - else - { - return GetVkResource()->GetGPUVirtualAddress(); - } - } - - */ private: virtual void CreateViewInternal( const struct BufferViewDesc &ViewDesc, IBufferView **ppView, bool bIsDefaultView )override; VulkanUtilities::BufferViewWrapper CreateView(struct BufferViewDesc &ViewDesc); VkAccessFlags m_AccessFlags = 0; - /* #ifdef _DEBUG - std::vector< std::pair<MAP_TYPE, Uint32>, STDAllocatorRawMem<std::pair<MAP_TYPE, Uint32>> > m_DbgMapType; + std::vector< std::pair<MAP_TYPE, Uint32> > m_DbgMapType; #endif - - friend class DeviceContextVkImpl; - // Array of dynamic allocations for every device context - std::vector<DynamicAllocation, STDAllocatorRawMem<DynamicAllocation> > m_DynamicData;*/ VulkanUtilities::BufferWrapper m_VulkanBuffer; VulkanUtilities::DeviceMemoryWrapper m_BufferMemory; diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h index 17f8844c..799ccb31 100644 --- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h +++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h @@ -25,12 +25,14 @@ /// \file /// Declaration of Diligent::DeviceContextVkImpl class +#include <unordered_map> #include "DeviceContextVk.h" #include "DeviceContextBase.h" #include "GenerateMips.h" #include "VulkanUtilities/VulkanCommandBufferPool.h" #include "VulkanUtilities/VulkanCommandBuffer.h" +#include "VulkanDynamicHeap.h" #ifdef _DEBUG # define VERIFY_CONTEXT_BINDINGS @@ -103,8 +105,6 @@ public: } #if 0 - virtual void TransitionBufferState(IBuffer *pBuffer, Vk_RESOURCE_STATES State)override final; - ///// Clears the state caches. This function is called once per frame ///// (before present) to release all outstanding objects ///// that are only kept alive by references in the cache @@ -122,9 +122,9 @@ public: #endif void GenerateMips(class TextureViewVkImpl *pTexView); -#if 0 - struct DynamicAllocation AllocateDynamicSpace(size_t NumBytes); -#endif + void* AllocateDynamicUploadSpace(BufferVkImpl* pBuffer, size_t NumBytes, size_t Alignment); + void CopyAndFreeDynamicUploadData(BufferVkImpl* pBuffer); + Uint32 GetContextId()const{return m_ContextId;} size_t GetNumCommandsInCtx()const { return m_State.NumCommands; } @@ -137,7 +137,6 @@ public: private: void CommitRenderPassAndFramebuffer(class PipelineStateVkImpl *pPipelineStateVk); - void CommitVkIndexBuffer(VALUE_TYPE IndexType); void CommitVkVertexBuffers(); void TransitionVkVertexBuffers(); void CommitRenderTargets(); @@ -158,17 +157,8 @@ private: /// Flag indicating if currently committed index buffer is up to date bool CommittedIBUpToDate = false; - /// Format of the currently committed index buffer - VALUE_TYPE CommittedIBFormat = VT_UNDEFINED; - /// Offset in the currently committed index buffer - Uint32 CommittedVkIndexDataStartOffset = 0; - /// Currently committed index buffer - VkBuffer CommittedVkIndexBuffer = VK_NULL_HANDLE; - Uint32 NumCommands = 0; }m_State; - - #if 0 @@ -177,7 +167,6 @@ private: CComPtr<IVkCommandSignature> m_pDispatchIndirectSignature; GenerateMipsHelper m_MipsGenerator; - class DynamicUploadHeap* m_pUploadHeap = nullptr; #endif class ShaderResourceCacheVk *m_pCommittedResourceCache = nullptr; #if 0 @@ -191,6 +180,8 @@ private: std::vector<VkSemaphore> m_WaitSemaphores; std::vector<VkPipelineStageFlags> m_WaitDstStageMasks; std::vector<VkSemaphore> m_SignalSemaphores; + + std::unordered_map<BufferVkImpl*, VulkanDynamicAllocation> m_UploadAllocations; }; } diff --git a/Graphics/GraphicsEngineVulkan/include/DynamicUploadHeap.h b/Graphics/GraphicsEngineVulkan/include/DynamicUploadHeap.h deleted file mode 100644 index b6b24ed1..00000000 --- a/Graphics/GraphicsEngineVulkan/include/DynamicUploadHeap.h +++ /dev/null @@ -1,142 +0,0 @@ -/* Copyright 2015-2018 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#pragma once - -#include "RingBuffer.h" - -namespace Diligent -{ - -// Constant blocks must be multiples of 16 constants @ 16 bytes each -#define DEFAULT_ALIGN 256 - -struct DynamicAllocation -{ -#if 0 - DynamicAllocation(ID3D12Resource *pBuff = nullptr, size_t ThisOffset = 0, size_t ThisSize = 0) - : pBuffer(pBuff), Offset(ThisOffset), Size(ThisSize) {} - - //CComPtr<ID3D12Resource> pBuffer; // The D3D buffer associated with this memory. - ID3D12Resource *pBuffer = nullptr; // The D3D buffer associated with this memory. - size_t Offset = 0; // Offset from start of buffer resource - size_t Size = 0; // Reserved size of this allocation - void* CPUAddress = 0; // The CPU-writeable address - D3D12_GPU_VIRTUAL_ADDRESS GPUAddress = 0; // The GPU-visible address -#ifdef _DEBUG - Uint64 FrameNum = static_cast<Uint64>(-1); -#endif -#endif -}; - -class GPURingBuffer : public RingBuffer -{ -public: -#if 0 - GPURingBuffer(size_t MaxSize, IMemoryAllocator &Allocator, ID3D12Device *pd3d12Device, bool AllowCPUAccess); - - GPURingBuffer(GPURingBuffer&& rhs) : - RingBuffer(std::move(rhs)), - m_CpuVirtualAddress(rhs.m_CpuVirtualAddress), - m_GpuVirtualAddress(rhs.m_GpuVirtualAddress), - m_pBuffer(std::move(rhs.m_pBuffer)) - { - rhs.m_CpuVirtualAddress = nullptr; - rhs.m_GpuVirtualAddress = 0; - rhs.m_pBuffer.Release(); - } - - GPURingBuffer& operator =(GPURingBuffer&& rhs) - { - Destroy(); - - static_cast<RingBuffer&>(*this) = std::move(rhs); - m_CpuVirtualAddress = rhs.m_CpuVirtualAddress; - m_GpuVirtualAddress = rhs.m_GpuVirtualAddress; - m_pBuffer = std::move(rhs.m_pBuffer); - rhs.m_CpuVirtualAddress = 0; - rhs.m_GpuVirtualAddress = 0; - - return *this; - } - - ~GPURingBuffer(); - - DynamicAllocation Allocate(size_t SizeInBytes) - { - auto Offset = RingBuffer::Allocate(SizeInBytes); - if (Offset != RingBuffer::InvalidOffset) - { - DynamicAllocation DynAlloc(m_pBuffer, Offset, SizeInBytes); - DynAlloc.GPUAddress = m_GpuVirtualAddress + Offset; - DynAlloc.CPUAddress = m_CpuVirtualAddress; - if(DynAlloc.CPUAddress) - DynAlloc.CPUAddress = reinterpret_cast<char*>(DynAlloc.CPUAddress) + Offset; - return DynAlloc; - } - else - { - return DynamicAllocation(nullptr, 0, 0); - } - } - - GPURingBuffer(const GPURingBuffer&) = delete; - GPURingBuffer& operator =(GPURingBuffer&) = delete; - -private: - void Destroy(); - - void* m_CpuVirtualAddress; - D3D12_GPU_VIRTUAL_ADDRESS m_GpuVirtualAddress; - CComPtr<ID3D12Resource> m_pBuffer; -#endif -}; - -class DynamicUploadHeap -{ -public: -#if 0 - DynamicUploadHeap(IMemoryAllocator &Allocator, bool bIsCPUAccessible, class RenderDeviceD3D12Impl* pDevice, size_t InitialSize); - - DynamicUploadHeap(const DynamicUploadHeap&)=delete; - DynamicUploadHeap(DynamicUploadHeap&&)=delete; - DynamicUploadHeap& operator=(const DynamicUploadHeap&)=delete; - DynamicUploadHeap& operator=(DynamicUploadHeap&&)=delete; - - DynamicAllocation Allocate( size_t SizeInBytes, size_t Alignment = DEFAULT_ALIGN ); - - void FinishFrame(Uint64 FenceValue, Uint64 LastCompletedFenceValue); - -private: - const bool m_bIsCPUAccessible; - // When a chunk of dynamic memory is requested, the heap first tries to allocate the memory in the largest GPU buffer. - // If allocation fails, a new ring buffer is created that provides enough space and requests memory from that buffer. - // Only the largest buffer is used for allocation and all other buffers are released when GPU is done with corresponding frames - std::vector<GPURingBuffer, STDAllocatorRawMem<GPURingBuffer> > m_RingBuffers; - IMemoryAllocator &m_Allocator; - RenderDeviceD3D12Impl* m_pDeviceD3D12 = nullptr; - //std::mutex m_Mutex; -#endif -}; - -} diff --git a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.h b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.h index b857054c..44950419 100644 --- a/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.h +++ b/Graphics/GraphicsEngineVulkan/include/PipelineStateVkImpl.h @@ -133,6 +133,8 @@ private: VulkanUtilities::RenderPassWrapper m_RenderPass; VulkanUtilities::PipelineWrapper m_Pipeline; PipelineLayout m_PipelineLayout; + bool m_HasStaticResources = false; + bool m_HasNonStaticResources = false; }; } diff --git a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h index d89d6e71..c44b97f2 100644 --- a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h +++ b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h @@ -31,7 +31,7 @@ #include "RenderDeviceBase.h" #include "DescriptorPoolManager.h" #include "CommandContext.h" -#include "DynamicUploadHeap.h" +#include "VulkanDynamicHeap.h" #include "Atomics.h" #include "CommandQueueVk.h" #include "VulkanUtilities/VulkanInstance.h" @@ -105,10 +105,6 @@ public: void FinishFrame(bool ReleaseAllResources); virtual void FinishFrame()override final { FinishFrame(false); } - /* - DynamicUploadHeap* RequestUploadHeap(); - void ReleaseUploadHeap(DynamicUploadHeap* pUploadHeap); - */ DescriptorPoolAllocation AllocateDescriptorSet(VkDescriptorSetLayout SetLayout) { @@ -121,6 +117,10 @@ public: return m_DescriptorPools[1 + CtxId].Allocate(SetLayout); } + VulkanDynamicAllocation AllocateDynamicUploadSpace(Uint32 CtxId, size_t Size, size_t Alignment) + { + return m_UploadHeaps[CtxId]->Allocate(Size, Alignment); + } std::shared_ptr<const VulkanUtilities::VulkanInstance> GetVulkanInstance()const{return m_VulkanInstance;} const VulkanUtilities::VulkanPhysicalDevice &GetPhysicalDevice(){return *m_PhysicalDevice;} @@ -205,11 +205,8 @@ private: std::vector<DescriptorPoolManager, STDAllocatorRawMem<DescriptorPoolManager> > m_DescriptorPools; -#if 0 - std::mutex m_UploadHeapMutex; - typedef std::unique_ptr<DynamicUploadHeap, STDDeleterRawMem<DynamicUploadHeap> > UploadHeapPoolElemType; + typedef std::unique_ptr<VulkanDynamicHeap, STDDeleterRawMem<VulkanDynamicHeap> > UploadHeapPoolElemType; std::vector< UploadHeapPoolElemType, STDAllocatorRawMem<UploadHeapPoolElemType> > m_UploadHeaps; -#endif }; } diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h b/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h new file mode 100644 index 00000000..791afbf9 --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h @@ -0,0 +1,130 @@ +/* Copyright 2015-2018 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#pragma once + +#include "RingBuffer.h" +#include "VulkanUtilities/VulkanLogicalDevice.h" +#include "VulkanUtilities/VulkanObjectWrappers.h" + +namespace Diligent +{ + +// Constant blocks must be multiples of 16 constants @ 16 bytes each +#define DEFAULT_ALIGN 256 + +class RenderDeviceVkImpl; + +struct VulkanDynamicAllocation +{ + VulkanDynamicAllocation(VkBuffer _Buff, size_t _Offset, size_t _Size, void *_CPUAddress) + : vkBuffer(_Buff), Offset(_Offset), Size(_Size), CPUAddress(_CPUAddress) + {} + + VkBuffer vkBuffer = VK_NULL_HANDLE; // Vulkan buffer associated with this memory. + size_t Offset = 0; // Offset from the start of the buffer resource + size_t Size = 0; // Reserved size of this allocation + void *CPUAddress = nullptr; +#ifdef _DEBUG + Uint64 FrameNum = static_cast<Uint64>(-1); +#endif +}; + +class VulkanRingBuffer : public RingBuffer +{ +public: + VulkanRingBuffer(size_t MaxSize, IMemoryAllocator &Allocator, RenderDeviceVkImpl* pDeviceVk); + + VulkanRingBuffer(VulkanRingBuffer&& rhs)noexcept : + RingBuffer(std::move(rhs)), + m_pDeviceVk(rhs.m_pDeviceVk), + m_VkBuffer(std::move(rhs.m_VkBuffer)), + m_CPUAddress(rhs.m_CPUAddress) + { + rhs.m_CPUAddress = nullptr; + } + + VulkanRingBuffer (const VulkanRingBuffer&) = delete; + VulkanRingBuffer& operator= (VulkanRingBuffer&) = delete; + VulkanRingBuffer& operator= (VulkanRingBuffer&& rhs) + { + Destroy(); + + static_cast<RingBuffer&>(*this) = std::move(rhs); + m_pDeviceVk = rhs.m_pDeviceVk; + m_VkBuffer = std::move(rhs.m_VkBuffer); + m_CPUAddress = rhs.m_CPUAddress; + rhs.m_CPUAddress = nullptr; + + return *this; + } + + ~VulkanRingBuffer(); + + VulkanDynamicAllocation Allocate(size_t SizeInBytes) + { + auto Offset = RingBuffer::Allocate(SizeInBytes); + if (Offset != RingBuffer::InvalidOffset) + { + return VulkanDynamicAllocation {m_VkBuffer, Offset, SizeInBytes, m_CPUAddress + Offset}; + } + else + { + return VulkanDynamicAllocation {nullptr, 0, 0, nullptr}; + } + } + +private: + void Destroy(); + + RenderDeviceVkImpl* m_pDeviceVk; + VulkanUtilities::BufferWrapper m_VkBuffer; + VulkanUtilities::DeviceMemoryWrapper m_BufferMemory; + Uint8* m_CPUAddress; +}; + +class VulkanDynamicHeap +{ +public: + VulkanDynamicHeap(IMemoryAllocator &Allocator, class RenderDeviceVkImpl* pDeviceVk, size_t InitialSize); + + VulkanDynamicHeap (const VulkanDynamicHeap&) = delete; + VulkanDynamicHeap (VulkanDynamicHeap&&) = delete; + VulkanDynamicHeap& operator= (const VulkanDynamicHeap&) = delete; + VulkanDynamicHeap& operator= (VulkanDynamicHeap&&) = delete; + + VulkanDynamicAllocation Allocate( size_t SizeInBytes, size_t Alignment = DEFAULT_ALIGN ); + + void FinishFrame(Uint64 FenceValue, Uint64 LastCompletedFenceValue); + +private: + // When a chunk of dynamic memory is requested, the heap first tries to allocate the memory in the largest GPU buffer. + // If allocation fails, a new ring buffer is created that provides enough space and requests memory from that buffer. + // Only the largest buffer is used for allocation and all other buffers are released when GPU is done with corresponding frames + std::vector<VulkanRingBuffer, STDAllocatorRawMem<VulkanRingBuffer> > m_RingBuffers; + IMemoryAllocator &m_Allocator; + RenderDeviceVkImpl* m_pDeviceVk = nullptr; + //std::mutex m_Mutex; +}; + +} diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanTypeConversions.h b/Graphics/GraphicsEngineVulkan/include/VulkanTypeConversions.h index 10dec2a7..cc2f794a 100644 --- a/Graphics/GraphicsEngineVulkan/include/VulkanTypeConversions.h +++ b/Graphics/GraphicsEngineVulkan/include/VulkanTypeConversions.h @@ -44,7 +44,6 @@ void BlendStateDesc_To_VkBlendStateCI(const BlendStateDesc &BSDesc, std::vector<VkPipelineColorBlendAttachmentState> &ColorBlendAttachments); void InputLayoutDesc_To_VkVertexInputStateCI(const InputLayoutDesc& LayoutDesc, - const std::array<Uint32, MaxBufferSlots>& Strides, VkPipelineVertexInputStateCreateInfo &VertexInputStateCI, std::array<VkVertexInputBindingDescription, iMaxLayoutElements>& BindingDescriptions, std::array<VkVertexInputAttributeDescription, iMaxLayoutElements>& AttributeDescription); diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanCommandBuffer.h b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanCommandBuffer.h index 905e8400..22fa8286 100644 --- a/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanCommandBuffer.h +++ b/Graphics/GraphicsEngineVulkan/include/VulkanUtilities/VulkanCommandBuffer.h @@ -253,10 +253,15 @@ namespace VulkanUtilities void BindIndexBuffer(VkBuffer Buffer, VkDeviceSize Offset, VkIndexType IndexType) { VERIFY_EXPR(m_VkCmdBuffer != VK_NULL_HANDLE); - vkCmdBindIndexBuffer(m_VkCmdBuffer, Buffer, Offset, IndexType); - m_State.IndexBuffer = Buffer; - m_State.IndexBufferOffset = Offset; - m_State.IndexType = IndexType; + if(m_State.IndexBuffer != Buffer || + m_State.IndexBufferOffset != Offset || + m_State.IndexType != IndexType) + { + vkCmdBindIndexBuffer(m_VkCmdBuffer, Buffer, Offset, IndexType); + m_State.IndexBuffer = Buffer; + m_State.IndexBufferOffset = Offset; + m_State.IndexType = IndexType; + } } void BindVertexBuffers(uint32_t firstBinding, uint32_t bindingCount, const VkBuffer* pBuffers, const VkDeviceSize* pOffsets) @@ -326,6 +331,20 @@ namespace VulkanUtilities vkCmdBindDescriptorSets(m_VkCmdBuffer, pipelineBindPoint, layout, firstSet, descriptorSetCount, pDescriptorSets, dynamicOffsetCount, pDynamicOffsets); } + void CopyBuffer(VkBuffer srcBuffer, + VkBuffer dstBuffer, + uint32_t regionCount, + const VkBufferCopy* pRegions) + { + VERIFY_EXPR(m_VkCmdBuffer != VK_NULL_HANDLE); + if(m_State.RenderPass != VK_NULL_HANDLE) + { + // Copy buffer operation must be performed outside of render pass. + EndRenderPass(); + } + vkCmdCopyBuffer(m_VkCmdBuffer, srcBuffer, dstBuffer, regionCount, pRegions); + } + void FlushBarriers(); void SetVkCmdBuffer(VkCommandBuffer VkCmdBuffer) @@ -342,9 +361,9 @@ namespace VulkanUtilities VkPipeline ComputePipeline = VK_NULL_HANDLE; VkBuffer IndexBuffer = VK_NULL_HANDLE; VkDeviceSize IndexBufferOffset = 0; + VkIndexType IndexType = VK_INDEX_TYPE_MAX_ENUM; uint32_t FramebufferWidth = 0; uint32_t FramebufferHeight = 0; - VkIndexType IndexType = VK_INDEX_TYPE_MAX_ENUM; }; const StateCache& GetState()const{return m_State;} diff --git a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp index 8bd36c3e..32bf143c 100644 --- a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp @@ -40,12 +40,11 @@ BufferVkImpl :: BufferVkImpl(IReferenceCounters *pRefCounters, RenderDeviceVkImpl *pRenderDeviceVk, const BufferDesc& BuffDesc, const BufferData &BuffData /*= BufferData()*/) : - TBufferBase(pRefCounters, BuffViewObjMemAllocator, pRenderDeviceVk, BuffDesc, false)/*, + TBufferBase(pRefCounters, BuffViewObjMemAllocator, pRenderDeviceVk, BuffDesc, false), #ifdef _DEBUG - m_DbgMapType(1 + pRenderDeviceVk->GetNumDeferredContexts(), std::make_pair(static_cast<MAP_TYPE>(-1), static_cast<Uint32>(-1)), STD_ALLOCATOR_RAW_MEM(DynamicAllocation, GetRawAllocator(), "Allocator for vector<pair<MAP_TYPE,Uint32>>")), + m_DbgMapType(1 + pRenderDeviceVk->GetNumDeferredContexts()), #endif - m_DynamicData(BuffDesc.Usage == USAGE_DYNAMIC ? (1 + pRenderDeviceVk->GetNumDeferredContexts()) : 0, DynamicAllocation(), STD_ALLOCATOR_RAW_MEM(DynamicAllocation, GetRawAllocator(), "Allocator for vector<DynamicAllocation>")) - */ + m_AccessFlags(0) { #define LOG_BUFFER_ERROR_AND_THROW(...) LOG_ERROR_AND_THROW("Buffer \"", BuffDesc.Name ? BuffDesc.Name : "", "\": ", ##__VA_ARGS__); @@ -68,198 +67,179 @@ BufferVkImpl :: BufferVkImpl(IReferenceCounters *pRefCounters, } const auto& LogicalDevice = pRenderDeviceVk->GetLogicalDevice(); - if(m_Desc.Usage == USAGE_DYNAMIC && (m_Desc.BindFlags & (BIND_SHADER_RESOURCE|BIND_UNORDERED_ACCESS)) == 0) - { - UNSUPPORTED("Dynamic buffers are not yet implemented"); - // Dynamic constant/vertex/index buffers are suballocated in the upload heap when Map() is called. - // Dynamic buffers with SRV or UAV flags need to be allocated in GPU-only memory - // Dynamic upload heap buffer is always in Vk_RESOURCE_STATE_GENERIC_READ state -#if 0 - m_UsageState = Vk_RESOURCE_STATE_GENERIC_READ; - VERIFY_EXPR(m_DynamicData.size() == 1 + pRenderDeviceVk->GetNumDeferredContexts()); -#endif - } - else - { - VkBufferCreateInfo VkBuffCI = {}; - VkBuffCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; - VkBuffCI.pNext = nullptr; - VkBuffCI.flags = 0; // VK_BUFFER_CREATE_SPARSE_BINDING_BIT, VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT, VK_BUFFER_CREATE_SPARSE_ALIASED_BIT - VkBuffCI.size = m_Desc.uiSizeInBytes; - VkBuffCI.usage = - VK_BUFFER_USAGE_TRANSFER_SRC_BIT | // The buffer can be used as the source of a transfer command - VK_BUFFER_USAGE_TRANSFER_DST_BIT; // The buffer can be used as the destination of a transfer command - if (m_Desc.BindFlags & BIND_UNORDERED_ACCESS) - VkBuffCI.usage |= m_Desc.Mode == BUFFER_MODE_FORMATTED ? VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT : VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - if (m_Desc.BindFlags & BIND_SHADER_RESOURCE) - VkBuffCI.usage |= m_Desc.Mode == BUFFER_MODE_FORMATTED ? VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER : VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; - if (m_Desc.BindFlags & BIND_VERTEX_BUFFER) - VkBuffCI.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; - if (m_Desc.BindFlags & BIND_INDEX_BUFFER) - VkBuffCI.usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; - if (m_Desc.BindFlags & BIND_INDIRECT_DRAW_ARGS) - VkBuffCI.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; - if (m_Desc.BindFlags & BIND_UNIFORM_BUFFER) - VkBuffCI.usage |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; - - VkBuffCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; // sharing mode of the buffer when it will be accessed by multiple queue families. - VkBuffCI.queueFamilyIndexCount = 0; // number of entries in the pQueueFamilyIndices array - VkBuffCI.pQueueFamilyIndices = nullptr; // list of queue families that will access this buffer - // (ignored if sharingMode is not VK_SHARING_MODE_CONCURRENT). - - m_VulkanBuffer = LogicalDevice.CreateBuffer(VkBuffCI, m_Desc.Name); - - VkMemoryRequirements MemReqs = LogicalDevice.GetBufferMemoryRequirements(m_VulkanBuffer); - - VkMemoryAllocateInfo MemAlloc = {}; - MemAlloc.pNext = nullptr; - MemAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - MemAlloc.allocationSize = MemReqs.size; + + VkBufferCreateInfo VkBuffCI = {}; + VkBuffCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + VkBuffCI.pNext = nullptr; + VkBuffCI.flags = 0; // VK_BUFFER_CREATE_SPARSE_BINDING_BIT, VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT, VK_BUFFER_CREATE_SPARSE_ALIASED_BIT + VkBuffCI.size = m_Desc.uiSizeInBytes; + VkBuffCI.usage = + VK_BUFFER_USAGE_TRANSFER_SRC_BIT | // The buffer can be used as the source of a transfer command + VK_BUFFER_USAGE_TRANSFER_DST_BIT; // The buffer can be used as the destination of a transfer command + if (m_Desc.BindFlags & BIND_UNORDERED_ACCESS) + VkBuffCI.usage |= m_Desc.Mode == BUFFER_MODE_FORMATTED ? VK_BUFFER_USAGE_STORAGE_TEXEL_BUFFER_BIT : VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + if (m_Desc.BindFlags & BIND_SHADER_RESOURCE) + VkBuffCI.usage |= m_Desc.Mode == BUFFER_MODE_FORMATTED ? VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER : VK_BUFFER_USAGE_STORAGE_BUFFER_BIT; + if (m_Desc.BindFlags & BIND_VERTEX_BUFFER) + VkBuffCI.usage |= VK_BUFFER_USAGE_VERTEX_BUFFER_BIT; + if (m_Desc.BindFlags & BIND_INDEX_BUFFER) + VkBuffCI.usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT; + if (m_Desc.BindFlags & BIND_INDIRECT_DRAW_ARGS) + VkBuffCI.usage |= VK_BUFFER_USAGE_INDIRECT_BUFFER_BIT; + if (m_Desc.BindFlags & BIND_UNIFORM_BUFFER) + VkBuffCI.usage |= VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; + + VkBuffCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; // sharing mode of the buffer when it will be accessed by multiple queue families. + VkBuffCI.queueFamilyIndexCount = 0; // number of entries in the pQueueFamilyIndices array + VkBuffCI.pQueueFamilyIndices = nullptr; // list of queue families that will access this buffer + // (ignored if sharingMode is not VK_SHARING_MODE_CONCURRENT). + + m_VulkanBuffer = LogicalDevice.CreateBuffer(VkBuffCI, m_Desc.Name); + + VkMemoryRequirements MemReqs = LogicalDevice.GetBufferMemoryRequirements(m_VulkanBuffer); + + VkMemoryAllocateInfo MemAlloc = {}; + MemAlloc.pNext = nullptr; + MemAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + MemAlloc.allocationSize = MemReqs.size; - auto& PhysicalDevice = pRenderDeviceVk->GetPhysicalDevice(); + auto& PhysicalDevice = pRenderDeviceVk->GetPhysicalDevice(); - VkMemoryPropertyFlags BufferMemoryFlags = 0; - if (m_Desc.Usage == USAGE_CPU_ACCESSIBLE) - BufferMemoryFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; - else - BufferMemoryFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; + VkMemoryPropertyFlags BufferMemoryFlags = 0; + if (m_Desc.Usage == USAGE_CPU_ACCESSIBLE) + BufferMemoryFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_CACHED_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT; + else + BufferMemoryFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT; - // memoryTypeBits is a bitmask and contains one bit set for every supported memory type for the resource. - // Bit i is set if and only if the memory type i in the VkPhysicalDeviceMemoryProperties structure for the - // physical device is supported for the resource. - MemAlloc.memoryTypeIndex = PhysicalDevice.GetMemoryTypeIndex(MemReqs.memoryTypeBits, BufferMemoryFlags); - if(BufferMemoryFlags == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) - { - // There must be at least one memory type with the DEVICE_LOCAL_BIT bit set - VERIFY(MemAlloc.memoryTypeIndex != VulkanUtilities::VulkanPhysicalDevice::InvalidMemoryTypeIndex, - "Vulkan spec requires that memoryTypeBits member always contains " - "at least one bit set corresponding to a VkMemoryType with a propertyFlags that has the " - "VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT bit set (11.6)"); - } - else if(MemAlloc.memoryTypeIndex != VulkanUtilities::VulkanPhysicalDevice::InvalidMemoryTypeIndex) - { - LOG_ERROR_AND_THROW("Failed to find suitable device memory type for a buffer"); - } + // memoryTypeBits is a bitmask and contains one bit set for every supported memory type for the resource. + // Bit i is set if and only if the memory type i in the VkPhysicalDeviceMemoryProperties structure for the + // physical device is supported for the resource. + MemAlloc.memoryTypeIndex = PhysicalDevice.GetMemoryTypeIndex(MemReqs.memoryTypeBits, BufferMemoryFlags); + if(BufferMemoryFlags == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) + { + // There must be at least one memory type with the DEVICE_LOCAL_BIT bit set + VERIFY(MemAlloc.memoryTypeIndex != VulkanUtilities::VulkanPhysicalDevice::InvalidMemoryTypeIndex, + "Vulkan spec requires that memoryTypeBits member always contains " + "at least one bit set corresponding to a VkMemoryType with a propertyFlags that has the " + "VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT bit set (11.6)"); + } + else if(MemAlloc.memoryTypeIndex == VulkanUtilities::VulkanPhysicalDevice::InvalidMemoryTypeIndex) + { + LOG_ERROR_AND_THROW("Failed to find suitable device memory type for a buffer"); + } - { - std::string MemoryName("Device local memory for buffer '"); - MemoryName += m_Desc.Name; - MemoryName += '\''; - m_BufferMemory = LogicalDevice.AllocateDeviceMemory(MemAlloc, MemoryName.c_str()); - } + { + std::string MemoryName("Device local memory for buffer '"); + MemoryName += m_Desc.Name; + MemoryName += '\''; + m_BufferMemory = LogicalDevice.AllocateDeviceMemory(MemAlloc, MemoryName.c_str()); + } - auto err = LogicalDevice.BindBufferMemory(m_VulkanBuffer, m_BufferMemory, 0 /*offset*/); - CHECK_VK_ERROR_AND_THROW(err, "Failed to bind buffer memory"); + auto err = LogicalDevice.BindBufferMemory(m_VulkanBuffer, m_BufferMemory, 0 /*offset*/); + CHECK_VK_ERROR_AND_THROW(err, "Failed to bind buffer memory"); - bool bInitializeBuffer = (BuffData.pData != nullptr && BuffData.DataSize > 0); - //if(bInitializeBuffer) - // m_UsageState = Vk_RESOURCE_STATE_COPY_DEST; + bool bInitializeBuffer = (BuffData.pData != nullptr && BuffData.DataSize > 0); + //if(bInitializeBuffer) + // m_UsageState = Vk_RESOURCE_STATE_COPY_DEST; - if( bInitializeBuffer ) - { - m_AccessFlags = VK_ACCESS_TRANSFER_WRITE_BIT; + if( bInitializeBuffer ) + { + m_AccessFlags = VK_ACCESS_TRANSFER_WRITE_BIT; - VkBufferCreateInfo VkStaginBuffCI = VkBuffCI; - VkStaginBuffCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + VkBufferCreateInfo VkStaginBuffCI = VkBuffCI; + VkStaginBuffCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; - std::string StagingBufferName = "Staging buffer for '"; - StagingBufferName += m_Desc.Name; - StagingBufferName += '\''; - VulkanUtilities::BufferWrapper StagingBuffer = LogicalDevice.CreateBuffer(VkStaginBuffCI, StagingBufferName.c_str()); + std::string StagingBufferName = "Staging buffer for '"; + StagingBufferName += m_Desc.Name; + StagingBufferName += '\''; + VulkanUtilities::BufferWrapper StagingBuffer = LogicalDevice.CreateBuffer(VkStaginBuffCI, StagingBufferName.c_str()); - VkMemoryRequirements StagingBufferMemReqs = LogicalDevice.GetBufferMemoryRequirements(StagingBuffer); + VkMemoryRequirements StagingBufferMemReqs = LogicalDevice.GetBufferMemoryRequirements(StagingBuffer); - VkMemoryAllocateInfo StagingMemAlloc = {}; - StagingMemAlloc.pNext = nullptr; - StagingMemAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; - StagingMemAlloc.allocationSize = StagingBufferMemReqs.size; + VkMemoryAllocateInfo StagingMemAlloc = {}; + StagingMemAlloc.pNext = nullptr; + StagingMemAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + StagingMemAlloc.allocationSize = StagingBufferMemReqs.size; - // VK_MEMORY_PROPERTY_HOST_COHERENT_BIT bit specifies that the host cache management commands vkFlushMappedMemoryRanges - // and vkInvalidateMappedMemoryRanges are NOT needed to flush host writes to the device or make device writes visible - // to the host (10.2) - StagingMemAlloc.memoryTypeIndex = PhysicalDevice.GetMemoryTypeIndex(StagingBufferMemReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); - - VERIFY(StagingMemAlloc.memoryTypeIndex != VulkanUtilities::VulkanPhysicalDevice::InvalidMemoryTypeIndex, - "Vulkan spec requires that for a VkBuffer not created with the " - "VK_BUFFER_CREATE_SPARSE_BINDING_BIT bit set, the memoryTypeBits member always contains at least one bit set " - "corresponding to a VkMemoryType with a propertyFlags that has both the VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT bit " - "and the VK_MEMORY_PROPERTY_HOST_COHERENT_BIT bit set(11.6)"); - - std::string StagingMemoryName("Staging memory for buffer '"); - StagingMemoryName += m_Desc.Name; - StagingMemoryName += '\''; - VulkanUtilities::DeviceMemoryWrapper StagingBufferMemory = LogicalDevice.AllocateDeviceMemory(StagingMemAlloc, StagingMemoryName.c_str()); + // VK_MEMORY_PROPERTY_HOST_COHERENT_BIT bit specifies that the host cache management commands vkFlushMappedMemoryRanges + // and vkInvalidateMappedMemoryRanges are NOT needed to flush host writes to the device or make device writes visible + // to the host (10.2) + StagingMemAlloc.memoryTypeIndex = PhysicalDevice.GetMemoryTypeIndex(StagingBufferMemReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + VERIFY(StagingMemAlloc.memoryTypeIndex != VulkanUtilities::VulkanPhysicalDevice::InvalidMemoryTypeIndex, + "Vulkan spec requires that for a VkBuffer not created with the " + "VK_BUFFER_CREATE_SPARSE_BINDING_BIT bit set, the memoryTypeBits member always contains at least one bit set " + "corresponding to a VkMemoryType with a propertyFlags that has both the VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT bit " + "and the VK_MEMORY_PROPERTY_HOST_COHERENT_BIT bit set(11.6)"); + + std::string StagingMemoryName("Staging memory for buffer '"); + StagingMemoryName += m_Desc.Name; + StagingMemoryName += '\''; + VulkanUtilities::DeviceMemoryWrapper StagingBufferMemory = LogicalDevice.AllocateDeviceMemory(StagingMemAlloc, StagingMemoryName.c_str()); - { - void *StagingData = nullptr; - err = LogicalDevice.MapMemory(StagingBufferMemory, - 0, // offset - StagingMemAlloc.allocationSize, - 0, // flags, reserved for future use - &StagingData); - CHECK_VK_ERROR_AND_THROW(err, "Failed to map staging memory"); - memcpy(StagingData, BuffData.pData, BuffData.DataSize); - LogicalDevice.UnmapMemory(StagingBufferMemory); - } + { + void *StagingData = nullptr; + err = LogicalDevice.MapMemory(StagingBufferMemory, + 0, // offset + StagingMemAlloc.allocationSize, + 0, // flags, reserved for future use + &StagingData); + CHECK_VK_ERROR_AND_THROW(err, "Failed to map staging memory"); + memcpy(StagingData, BuffData.pData, BuffData.DataSize); + LogicalDevice.UnmapMemory(StagingBufferMemory); + } - err = LogicalDevice.BindBufferMemory(StagingBuffer, StagingBufferMemory, 0 /*offset*/); - CHECK_VK_ERROR_AND_THROW(err, "Failed to bind staging bufer memory"); + err = LogicalDevice.BindBufferMemory(StagingBuffer, StagingBufferMemory, 0 /*offset*/); + CHECK_VK_ERROR_AND_THROW(err, "Failed to bind staging bufer memory"); - auto vkCmdBuff = pRenderDeviceVk->AllocateCommandBuffer("Transient cmd buff to copy staging data to a device buffer"); + auto vkCmdBuff = pRenderDeviceVk->AllocateCommandBuffer("Transient cmd buff to copy staging data to a device buffer"); #if 0 - // copy data to the intermediate upload heap and then schedule a copy from the upload heap to the default buffer - VERIFY_EXPR(m_UsageState == Vk_RESOURCE_STATE_COPY_DEST); + // copy data to the intermediate upload heap and then schedule a copy from the upload heap to the default buffer + VERIFY_EXPR(m_UsageState == Vk_RESOURCE_STATE_COPY_DEST); #endif - // We MUST NOT call TransitionResource() from here, because - // it will call AddRef() and potentially Release(), while - // the object is not constructed yet - - // Copy commands MUST be recorded outside of a render pass instance. This is OK here - // as copy will be the only command in the cmd buffer - VkBufferCopy BuffCopy = {}; - BuffCopy.srcOffset = 0; - BuffCopy.dstOffset = 0; - BuffCopy.size = VkBuffCI.size; - vkCmdCopyBuffer(vkCmdBuff, StagingBuffer, m_VulkanBuffer, 1, &BuffCopy); - - // Command list fence should only be signaled when submitting cmd list - // from the immediate context, otherwise the basic requirement will be violated - // as in the scenario below - // See http://diligentgraphics.com/diligent-engine/architecture/Vk/managing-resource-lifetimes/ - // - // Signaled Fence | Immediate Context | InitContext | - // | | | - // N | Draw(ResourceX) | | - // | Release(ResourceX) | | - // | - (ResourceX, N) -> Release Queue | | - // | | CopyResource() | - // N+1 | | CloseAndExecuteCommandContext() | - // | | | - // N+2 | CloseAndExecuteCommandContext() | | - // | - Cmd list is submitted with number | | - // | N+1, but resource it references | | - // | was added to the delete queue | | - // | with value N | | - pRenderDeviceVk->ExecuteCommandBuffer(vkCmdBuff, false); - pRenderDeviceVk->DisposeCommandBuffer(vkCmdBuff); - - // Add reference to the object to the release queue to keep it alive - // until copy operation is complete. This must be done after - // submitting command list for execution! - pRenderDeviceVk->SafeReleaseVkObject(std::move(StagingBuffer)); - pRenderDeviceVk->SafeReleaseVkObject(std::move(StagingBufferMemory)); - } - else - { - m_AccessFlags = 0; - } - - //if (m_Desc.BindFlags & BIND_UNIFORM_BUFFER) - //{ - // m_CBVDescriptorAllocation = pRenderDeviceVk->AllocateDescriptor(Vk_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); - // CreateCBV(m_CBVDescriptorAllocation.GetCpuHandle()); - //} + // We MUST NOT call TransitionResource() from here, because + // it will call AddRef() and potentially Release(), while + // the object is not constructed yet + + // Copy commands MUST be recorded outside of a render pass instance. This is OK here + // as copy will be the only command in the cmd buffer + VkBufferCopy BuffCopy = {}; + BuffCopy.srcOffset = 0; + BuffCopy.dstOffset = 0; + BuffCopy.size = VkBuffCI.size; + vkCmdCopyBuffer(vkCmdBuff, StagingBuffer, m_VulkanBuffer, 1, &BuffCopy); + + // Command list fence should only be signaled when submitting cmd list + // from the immediate context, otherwise the basic requirement will be violated + // as in the scenario below + // See http://diligentgraphics.com/diligent-engine/architecture/Vk/managing-resource-lifetimes/ + // + // Signaled Fence | Immediate Context | InitContext | + // | | | + // N | Draw(ResourceX) | | + // | Release(ResourceX) | | + // | - (ResourceX, N) -> Release Queue | | + // | | CopyResource() | + // N+1 | | CloseAndExecuteCommandContext() | + // | | | + // N+2 | CloseAndExecuteCommandContext() | | + // | - Cmd list is submitted with number | | + // | N+1, but resource it references | | + // | was added to the delete queue | | + // | with value N | | + pRenderDeviceVk->ExecuteCommandBuffer(vkCmdBuff, false); + pRenderDeviceVk->DisposeCommandBuffer(vkCmdBuff); + + // Add reference to the object to the release queue to keep it alive + // until copy operation is complete. This must be done after + // submitting command list for execution! + pRenderDeviceVk->SafeReleaseVkObject(std::move(StagingBuffer)); + pRenderDeviceVk->SafeReleaseVkObject(std::move(StagingBufferMemory)); + } + else + { + m_AccessFlags = 0; } } @@ -307,16 +287,15 @@ static BufferDesc BufferDescFromVkResource(BufferDesc BuffDesc, void *pVkBuffer) } BufferVkImpl :: BufferVkImpl(IReferenceCounters *pRefCounters, - FixedBlockMemoryAllocator &BuffViewObjMemAllocator, - RenderDeviceVkImpl *pRenderDeviceVk, - const BufferDesc& BuffDesc, - void *pVkBuffer) : - TBufferBase(pRefCounters, BuffViewObjMemAllocator, pRenderDeviceVk, BufferDescFromVkResource(BuffDesc, pVkBuffer), false)/*, + FixedBlockMemoryAllocator &BuffViewObjMemAllocator, + RenderDeviceVkImpl *pRenderDeviceVk, + const BufferDesc& BuffDesc, + void *pVkBuffer) : + TBufferBase(pRefCounters, BuffViewObjMemAllocator, pRenderDeviceVk, BufferDescFromVkResource(BuffDesc, pVkBuffer), false), #ifdef _DEBUG - m_DbgMapType(1 + pRenderDeviceVk->GetNumDeferredContexts(), std::make_pair(static_cast<MAP_TYPE>(-1), static_cast<Uint32>(-1)), STD_ALLOCATOR_RAW_MEM(DynamicAllocation, GetRawAllocator(), "Allocator for vector<pair<MAP_TYPE,Uint32>>")), + m_DbgMapType(1 + pRenderDeviceVk->GetNumDeferredContexts()), #endif - m_DynamicData(BuffDesc.Usage == USAGE_DYNAMIC ? (1 + pRenderDeviceVk->GetNumDeferredContexts()) : 0, DynamicAllocation(), STD_ALLOCATOR_RAW_MEM(DynamicAllocation, GetRawAllocator(), "Allocator for vector<DynamicAllocation>")) - */ + m_AccessFlags(0) { #if 0 m_pVkResource = pVkBuffer; @@ -362,7 +341,7 @@ void BufferVkImpl :: CopyData(IDeviceContext *pContext, IBuffer *pSrcBuffer, Uin void BufferVkImpl :: Map(IDeviceContext *pContext, MAP_TYPE MapType, Uint32 MapFlags, PVoid &pMappedData) { TBufferBase::Map( pContext, MapType, MapFlags, pMappedData ); -#if 0 + auto *pDeviceContextVk = ValidatedCast<DeviceContextVkImpl>(pContext); #ifdef _DEBUG if(pDeviceContextVk != nullptr) @@ -370,6 +349,8 @@ void BufferVkImpl :: Map(IDeviceContext *pContext, MAP_TYPE MapType, Uint32 MapF #endif if (MapType == MAP_READ ) { + UNSUPPORTED("Mapping buffer for reading is not yet imlemented"); +#if 0 LOG_WARNING_MESSAGE_ONCE("Mapping CPU buffer for reading on Vk currently requires flushing context and idling GPU"); pDeviceContextVk->Flush(); auto *pDeviceVk = ValidatedCast<RenderDeviceVkImpl>(GetDevice()); @@ -380,25 +361,26 @@ void BufferVkImpl :: Map(IDeviceContext *pContext, MAP_TYPE MapType, Uint32 MapF MapRange.Begin = 0; MapRange.End = m_Desc.uiSizeInBytes; m_pVkResource->Map(0, &MapRange, &pMappedData); +#endif } else if(MapType == MAP_WRITE) { if (m_Desc.Usage == USAGE_CPU_ACCESSIBLE) { + UNSUPPORTED("Not implemented"); +#if 0 VERIFY(m_pVkResource != nullptr, "USAGE_CPU_ACCESSIBLE buffer mapped for writing must intialize Vk resource"); if (MapFlags & MAP_FLAG_DISCARD) { } m_pVkResource->Map(0, nullptr, &pMappedData); +#endif } else if (m_Desc.Usage == USAGE_DYNAMIC) { VERIFY(MapFlags & MAP_FLAG_DISCARD, "Vk buffer must be mapped for writing with MAP_FLAG_DISCARD flag"); - auto *pCtxVk = ValidatedCast<DeviceContextVkImpl>(pContext); - auto ContextId = pDeviceContextVk->GetContextId(); - m_DynamicData[ContextId] = pCtxVk->AllocateDynamicSpace(m_Desc.uiSizeInBytes); - pMappedData = m_DynamicData[ContextId].CPUAddress; + pMappedData = pDeviceContextVk->AllocateDynamicUploadSpace(this, m_Desc.uiSizeInBytes, 0); } else { @@ -413,17 +395,15 @@ void BufferVkImpl :: Map(IDeviceContext *pContext, MAP_TYPE MapType, Uint32 MapF { LOG_ERROR("Only MAP_WRITE_DISCARD and MAP_READ are currently implemented in Vk"); } -#endif } void BufferVkImpl::Unmap( IDeviceContext *pContext, MAP_TYPE MapType, Uint32 MapFlags ) { TBufferBase::Unmap( pContext, MapType, MapFlags ); -#if 0 auto *pDeviceContextVk = ValidatedCast<DeviceContextVkImpl>(pContext); - Uint32 CtxId = pDeviceContextVk != nullptr ? pDeviceContextVk->GetContextId() : static_cast<Uint32>(-1); #ifdef _DEBUG + Uint32 CtxId = pDeviceContextVk != nullptr ? pDeviceContextVk->GetContextId() : static_cast<Uint32>(-1); if (pDeviceContextVk != nullptr) { VERIFY(m_DbgMapType[CtxId].first == MapType, "Map type does not match the type provided to Map()"); @@ -433,27 +413,29 @@ void BufferVkImpl::Unmap( IDeviceContext *pContext, MAP_TYPE MapType, Uint32 Map if (MapType == MAP_READ ) { + UNSUPPORTED("This map type is not yet supported"); +#if 0 Vk_RANGE MapRange; // It is valid to specify the CPU didn't write any data by passing a range where End is less than or equal to Begin. MapRange.Begin = 1; MapRange.End = 0; m_pVkResource->Unmap(0, &MapRange); +#endif } else if(MapType == MAP_WRITE) { if (m_Desc.Usage == USAGE_CPU_ACCESSIBLE) { + UNSUPPORTED("This map type is not yet supported"); +#if 0 VERIFY(m_pVkResource != nullptr, "USAGE_CPU_ACCESSIBLE buffer mapped for writing must intialize Vk resource"); m_pVkResource->Unmap(0, nullptr); +#endif } else if (m_Desc.Usage == USAGE_DYNAMIC) { VERIFY(MapFlags & MAP_FLAG_DISCARD, "Vk buffer must be mapped for writing with MAP_FLAG_DISCARD flag"); - // Copy data into the resource - if (m_pVkResource) - { - pDeviceContextVk->UpdateBufferRegion(this, m_DynamicData[CtxId], 0, m_Desc.uiSizeInBytes); - } + pDeviceContextVk->CopyAndFreeDynamicUploadData(this); } } @@ -461,7 +443,6 @@ void BufferVkImpl::Unmap( IDeviceContext *pContext, MAP_TYPE MapType, Uint32 Map if(pDeviceContextVk != nullptr) m_DbgMapType[CtxId] = std::make_pair(static_cast<MAP_TYPE>(-1), static_cast<Uint32>(-1)); #endif -#endif } void BufferVkImpl::CreateViewInternal( const BufferViewDesc &OrigViewDesc, IBufferView **ppView, bool bIsDefaultView ) @@ -520,16 +501,4 @@ VulkanUtilities::BufferViewWrapper BufferVkImpl::CreateView(struct BufferViewDes return BuffView; } -#if 0 -#ifdef _DEBUG -void BufferVkImpl::DbgVerifyDynamicAllocation(Uint32 ContextId) -{ - VERIFY(m_DynamicData[ContextId].GPUAddress != 0, "Dynamic buffer must be mapped before the first use"); - auto CurrentFrame = ValidatedCast<RenderDeviceVkImpl>(GetDevice())->GetCurrentFrameNumber(); - VERIFY(m_DynamicData[ContextId].FrameNum == CurrentFrame, "Dynamic allocation is out-of-date. Dynamic buffer \"", m_Desc.Name, "\" must be mapped in the same frame it is used."); - VERIFY(GetState() == Vk_RESOURCE_STATE_GENERIC_READ, "Dynamic buffers are expected to always be in Vk_RESOURCE_STATE_GENERIC_READ state"); -} -#endif - -#endif } diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp index 78291140..f8e87c77 100644 --- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp @@ -30,7 +30,6 @@ #include "TextureVkImpl.h" #include "BufferVkImpl.h" #include "VulkanTypeConversions.h" -#include "DynamicUploadHeap.h" #include "CommandListVkImpl.h" namespace Diligent @@ -38,7 +37,6 @@ namespace Diligent DeviceContextVkImpl::DeviceContextVkImpl( IReferenceCounters *pRefCounters, RenderDeviceVkImpl *pDeviceVkImpl, bool bIsDeferred, const EngineVkAttribs &Attribs, Uint32 ContextId) : TDeviceContextBase(pRefCounters, pDeviceVkImpl, bIsDeferred), - //m_pUploadHeap(pDeviceVkImpl->RequestUploadHeap() ), m_NumCommandsToFlush(bIsDeferred ? std::numeric_limits<decltype(m_NumCommandsToFlush)>::max() : Attribs.NumCommandsToFlushCmdBuffer), /*m_MipsGenerator(pDeviceVkImpl->GetVkDevice()), m_CmdListAllocator(GetRawAllocator(), sizeof(CommandListVkImpl), 64 ),*/ @@ -300,47 +298,6 @@ namespace Diligent m_CommandBuffer.BeginRenderPass(Key.Pass, vkFramebuffer, m_FramebufferWidth, m_FramebufferHeight); } - void DeviceContextVkImpl::CommitVkIndexBuffer(VALUE_TYPE IndexType) - { - BufferVkImpl *pBuffVk = m_pIndexBuffer.RawPtr<BufferVkImpl>(); - - // Device context keeps strong reference to bound index buffer. - // When the buffer is unbound, the reference to the Vk resource - // is added to the context. There is no need to add reference here - -// bool IsDynamic = pBuffVk->GetDesc().Usage == USAGE_DYNAMIC; -//#ifdef _DEBUG -// if(IsDynamic) -// pBuffVk->DbgVerifyDynamicAllocation(m_ContextId); -//#endif - - size_t BuffDataStartByteOffset = 0; - auto *vkBuffer = pBuffVk->GetVkBuffer(); - - if( //IsDynamic || - m_State.CommittedVkIndexBuffer != vkBuffer || - m_State.CommittedIBFormat != IndexType || - m_State.CommittedVkIndexDataStartOffset != m_IndexDataStartOffset + BuffDataStartByteOffset) - { - m_State.CommittedVkIndexBuffer = vkBuffer; - m_State.CommittedIBFormat = IndexType; - m_State.CommittedVkIndexDataStartOffset = m_IndexDataStartOffset + static_cast<Uint32>(BuffDataStartByteOffset); - VkIndexType vkIndexType; - if( IndexType == VT_UINT32 ) - vkIndexType = VK_INDEX_TYPE_UINT32; - else - { - VERIFY(IndexType == VT_UINT16, "Unsupported index format. Only R16_UINT and R32_UINT are allowed."); - vkIndexType = VK_INDEX_TYPE_UINT16; - } - m_CommandBuffer.BindIndexBuffer(m_State.CommittedVkIndexBuffer, m_State.CommittedVkIndexDataStartOffset, vkIndexType); - } - - // GPU virtual address of a dynamic index buffer can change every time - // a draw command is invoked - m_State.CommittedIBUpToDate = true;//!IsDynamic; - } - void DeviceContextVkImpl::TransitionVkVertexBuffers() { for( UINT Buff = 0; Buff < m_NumVertexStreams; ++Buff ) @@ -419,22 +376,18 @@ namespace Diligent auto *pPipelineStateVk = m_pPipelineState.RawPtr<PipelineStateVkImpl>(); EnsureVkCmdBuffer(); - if(m_CommandBuffer.GetState().RenderPass == VK_NULL_HANDLE) - CommitRenderPassAndFramebuffer(pPipelineStateVk); if( DrawAttribs.IsIndexed ) { VERIFY( m_pIndexBuffer != nullptr, "Index buffer is not set up for indexed draw command" ); - if( m_State.CommittedIBFormat != DrawAttribs.IndexType ) - m_State.CommittedIBUpToDate = false; - BufferVkImpl *pBuffVk = m_pIndexBuffer.RawPtr<BufferVkImpl>(); if(pBuffVk->GetAccessFlags() != VK_ACCESS_INDEX_READ_BIT) BufferMemoryBarrier(*pBuffVk, VK_ACCESS_INDEX_READ_BIT); - if(!m_State.CommittedIBUpToDate) - CommitVkIndexBuffer(DrawAttribs.IndexType); + VERIFY(DrawAttribs.IndexType == VT_UINT16 || DrawAttribs.IndexType == VT_UINT32, "Unsupported index format. Only R16_UINT and R32_UINT are allowed."); + VkIndexType vkIndexType = DrawAttribs.IndexType == VT_UINT16 ? VK_INDEX_TYPE_UINT16 : VK_INDEX_TYPE_UINT32; + m_CommandBuffer.BindIndexBuffer(pBuffVk->GetVkBuffer(), m_IndexDataStartOffset, vkIndexType); } if(m_State.CommittedVBsUpToDate) @@ -457,6 +410,10 @@ namespace Diligent } #endif #endif + + if(m_CommandBuffer.GetState().RenderPass == VK_NULL_HANDLE) + CommitRenderPassAndFramebuffer(pPipelineStateVk); + if( DrawAttribs.IsIndirect ) { #if 0 @@ -1174,4 +1131,42 @@ namespace Diligent m_CommandBuffer.BufferMemoryBarrier(vkBuff, BufferVk.GetAccessFlags(), NewAccessFlags); BufferVk.SetAccessFlags(NewAccessFlags); } + + void* DeviceContextVkImpl::AllocateDynamicUploadSpace(BufferVkImpl* pBuffer, size_t NumBytes, size_t Alignment) + { + VERIFY(m_UploadAllocations.find(pBuffer) == m_UploadAllocations.end(), "Upload space has already been allocated for this buffer"); + auto UploadAllocation = m_pDevice.RawPtr<RenderDeviceVkImpl>()->AllocateDynamicUploadSpace(m_ContextId, NumBytes, Alignment); + auto *CPUAddress = UploadAllocation.CPUAddress; + m_UploadAllocations.emplace(pBuffer, std::move(UploadAllocation)); + return CPUAddress; + } + + void DeviceContextVkImpl::CopyAndFreeDynamicUploadData(BufferVkImpl* pBuffer) + { + auto it = m_UploadAllocations.find(pBuffer); + if(it != m_UploadAllocations.end()) + { + +#ifdef _DEBUG + auto CurrentFrame = m_pDevice.RawPtr<RenderDeviceVkImpl>()->GetCurrentFrameNumber(); + VERIFY(it->second.FrameNum == CurrentFrame, "Dynamic allocation is out-of-date. Dynamic buffer \"", pBuffer->GetDesc().Name, "\" must be unmapped in the same frame it is used."); +#endif + EnsureVkCmdBuffer(); + if(pBuffer->GetAccessFlags() != VK_ACCESS_TRANSFER_WRITE_BIT) + { + BufferMemoryBarrier(*pBuffer, VK_ACCESS_TRANSFER_WRITE_BIT); + } + VkBufferCopy CopyRegion; + CopyRegion.srcOffset = it->second.Offset; + CopyRegion.dstOffset = 0; + CopyRegion.size = it->second.Size; + m_CommandBuffer.CopyBuffer(it->second.vkBuffer, pBuffer->GetVkBuffer(), 1, &CopyRegion); + + m_UploadAllocations.erase(it); + } + else + { + UNEXPECTED("Unable to find dynamic allocation for this buffer"); + } + } } diff --git a/Graphics/GraphicsEngineVulkan/src/DynamicUploadHeap.cpp b/Graphics/GraphicsEngineVulkan/src/DynamicUploadHeap.cpp deleted file mode 100644 index ae01cc3e..00000000 --- a/Graphics/GraphicsEngineVulkan/src/DynamicUploadHeap.cpp +++ /dev/null @@ -1,171 +0,0 @@ -/* Copyright 2015-2018 Egor Yusov - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. - * - * In no event and under no legal theory, whether in tort (including negligence), - * contract, or otherwise, unless required by applicable law (such as deliberate - * and grossly negligent acts) or agreed to in writing, shall any Contributor be - * liable for any damages, including any direct, indirect, special, incidental, - * or consequential damages of any character arising as a result of this License or - * out of the use or inability to use the software (including but not limited to damages - * for loss of goodwill, work stoppage, computer failure or malfunction, or any and - * all other commercial damages or losses), even if such Contributor has been advised - * of the possibility of such damages. - */ - -#include "pch.h" -#include "DynamicUploadHeap.h" -#include "RenderDeviceVkImpl.h" - -namespace Diligent -{ -#if 0 - GPURingBuffer::GPURingBuffer(size_t MaxSize, IMemoryAllocator &Allocator, IVkDevice *pVkDevice, bool AllowCPUAccess) : - RingBuffer(MaxSize, Allocator), - m_CpuVirtualAddress(nullptr), - m_GpuVirtualAddress(0) - { - Vk_HEAP_PROPERTIES HeapProps; - HeapProps.CPUPageProperty = Vk_CPU_PAGE_PROPERTY_UNKNOWN; - HeapProps.MemoryPoolPreference = Vk_MEMORY_POOL_UNKNOWN; - HeapProps.CreationNodeMask = 1; - HeapProps.VisibleNodeMask = 1; - - Vk_RESOURCE_DESC ResourceDesc; - ResourceDesc.Dimension = Vk_RESOURCE_DIMENSION_BUFFER; - ResourceDesc.Alignment = 0; - ResourceDesc.Height = 1; - ResourceDesc.DepthOrArraySize = 1; - ResourceDesc.MipLevels = 1; - ResourceDesc.Format = DXGI_FORMAT_UNKNOWN; - ResourceDesc.SampleDesc.Count = 1; - ResourceDesc.SampleDesc.Quality = 0; - ResourceDesc.Layout = Vk_TEXTURE_LAYOUT_ROW_MAJOR; - - Vk_RESOURCE_STATES DefaultUsage; - if (AllowCPUAccess) - { - HeapProps.Type = Vk_HEAP_TYPE_UPLOAD; - ResourceDesc.Flags = Vk_RESOURCE_FLAG_NONE; - DefaultUsage = Vk_RESOURCE_STATE_GENERIC_READ; - } - else - { - HeapProps.Type = Vk_HEAP_TYPE_DEFAULT; - ResourceDesc.Flags = Vk_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; - DefaultUsage = Vk_RESOURCE_STATE_UNORDERED_ACCESS; - } - ResourceDesc.Width = MaxSize; - - auto hr = pVkDevice->CreateCommittedResource(&HeapProps, Vk_HEAP_FLAG_NONE, &ResourceDesc, - DefaultUsage, nullptr, __uuidof(m_pBuffer), reinterpret_cast<void**>(static_cast<IVkResource**>(&m_pBuffer)) ); - if(FAILED(hr)) - LOG_ERROR("Failed to create new upload ring buffer"); - - m_pBuffer->SetName(L"Upload Ring Buffer"); - - m_GpuVirtualAddress = m_pBuffer->GetGPUVirtualAddress(); - - if (AllowCPUAccess) - { - m_pBuffer->Map(0, nullptr, &m_CpuVirtualAddress); - } - - LOG_INFO_MESSAGE("GPU ring buffer created. Size: ", MaxSize, "; GPU virtual address 0x", std::hex, m_GpuVirtualAddress); - } - - void GPURingBuffer::Destroy() - { - if(m_pBuffer) - { - LOG_INFO_MESSAGE("Destroying GPU ring buffer. Size: ", m_pBuffer->GetDesc().Width, "; GPU virtual address 0x", std::hex, m_GpuVirtualAddress); - } - - if (m_CpuVirtualAddress) - { - m_pBuffer->Unmap(0, nullptr); - } - m_CpuVirtualAddress = 0; - m_GpuVirtualAddress = 0; - m_pBuffer.Release(); - } - - GPURingBuffer::~GPURingBuffer() - { - Destroy(); - } - - DynamicUploadHeap::DynamicUploadHeap(IMemoryAllocator &Allocator, bool bIsCPUAccessible, class RenderDeviceVkImpl* pDevice, size_t InitialSize) : - m_Allocator(Allocator), - m_pDeviceVk(pDevice), - m_bIsCPUAccessible(bIsCPUAccessible), - m_RingBuffers(STD_ALLOCATOR_RAW_MEM(GPURingBuffer, GetRawAllocator(), "Allocator for vector<GPURingBuffer>")) - { - m_RingBuffers.emplace_back(InitialSize, Allocator, pDevice->GetVkDevice(), m_bIsCPUAccessible); - } - - DynamicAllocation DynamicUploadHeap::Allocate(size_t SizeInBytes, size_t Alignment /*= DEFAULT_ALIGN*/) - { - // Every device context has its own upload heap, so there is no need to lock - - //std::lock_guard<std::mutex> Lock(m_Mutex); - - // - // Deferred contexts must not update resources or map dynamic buffers - // across several frames! - // - - const size_t AlignmentMask = Alignment - 1; - // Assert that it's a power of two. - VERIFY_EXPR((AlignmentMask & Alignment) == 0); - // Align the allocation - const size_t AlignedSize = (SizeInBytes + AlignmentMask) & ~AlignmentMask; - auto DynAlloc = m_RingBuffers.back().Allocate(AlignedSize); - if (!DynAlloc.pBuffer) - { - auto NewMaxSize = m_RingBuffers.back().GetMaxSize() * 2; - while(NewMaxSize < AlignedSize)NewMaxSize*=2; - m_RingBuffers.emplace_back(NewMaxSize, m_Allocator, m_pDeviceVk->GetVkDevice(), m_bIsCPUAccessible); - DynAlloc = m_RingBuffers.back().Allocate(AlignedSize); - } -#ifdef _DEBUG - DynAlloc.FrameNum = m_pDeviceVk->GetCurrentFrameNumber(); -#endif - return DynAlloc; - } - - void DynamicUploadHeap::FinishFrame(Uint64 FenceValue, Uint64 LastCompletedFenceValue) - { - // Every device context has its own upload heap, so there is no need to lock - //std::lock_guard<std::mutex> Lock(m_Mutex); - - // - // Deferred contexts must not update resources or map dynamic buffers - // across several frames! - // - - size_t NumBuffsToDelete = 0; - for(size_t Ind = 0; Ind < m_RingBuffers.size(); ++Ind) - { - auto &RingBuff = m_RingBuffers[Ind]; - RingBuff.FinishCurrentFrame(FenceValue); - RingBuff.ReleaseCompletedFrames(LastCompletedFenceValue); - if ( NumBuffsToDelete == Ind && Ind < m_RingBuffers.size()-1 && RingBuff.IsEmpty()) - { - ++NumBuffsToDelete; - } - } - - if(NumBuffsToDelete) - m_RingBuffers.erase(m_RingBuffers.begin(), m_RingBuffers.begin()+NumBuffsToDelete); - } -#endif -} diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp index ad575083..3cdd2b62 100644 --- a/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/PipelineStateVkImpl.cpp @@ -159,9 +159,9 @@ PipelineStateVkImpl :: PipelineStateVkImpl(IReferenceCounters *pRefCounters, Ren for(Uint32 s=0; s < m_NumShaders; ++s) new (m_ShaderResourceLayouts + s) ShaderResourceLayoutVk(*this, LogicalDevice, GetRawAllocator()); - if (PipelineDesc.IsComputePipeline) + if (m_Desc.IsComputePipeline) { - auto &ComputePipeline = PipelineDesc.ComputePipeline; + auto &ComputePipeline = m_Desc.ComputePipeline; if( ComputePipeline.pCS == nullptr ) LOG_ERROR_AND_THROW("Compute shader is not set in the pipeline desc"); @@ -196,7 +196,7 @@ PipelineStateVkImpl :: PipelineStateVkImpl(IReferenceCounters *pRefCounters, Ren { const auto &PhysicalDevice = pDeviceVk->GetPhysicalDevice(); - auto &GraphicsPipeline = PipelineDesc.GraphicsPipeline; + auto &GraphicsPipeline = m_Desc.GraphicsPipeline; CreateRenderPass(LogicalDevice); @@ -207,10 +207,6 @@ PipelineStateVkImpl :: PipelineStateVkImpl(IReferenceCounters *pRefCounters, Ren PipelineCI.flags = VK_PIPELINE_CREATE_DISABLE_OPTIMIZATION_BIT; #endif -#if 0 - m_PipelineLayout.AllocateStaticSamplers(GetShaders(), GetNumShaders()); -#endif - PipelineCI.stageCount = m_NumShaders; std::vector<VkPipelineShaderStageCreateInfo> Stages(PipelineCI.stageCount); for (Uint32 s = 0; s < m_NumShaders; ++s) @@ -247,7 +243,7 @@ PipelineStateVkImpl :: PipelineStateVkImpl(IReferenceCounters *pRefCounters, Ren VkPipelineVertexInputStateCreateInfo VertexInputStateCI = {}; std::array<VkVertexInputBindingDescription, iMaxLayoutElements> BindingDescriptions; std::array<VkVertexInputAttributeDescription, iMaxLayoutElements> AttributeDescription; - InputLayoutDesc_To_VkVertexInputStateCI(GraphicsPipeline.InputLayout, m_Strides, VertexInputStateCI, BindingDescriptions, AttributeDescription); + InputLayoutDesc_To_VkVertexInputStateCI(GraphicsPipeline.InputLayout, VertexInputStateCI, BindingDescriptions, AttributeDescription); PipelineCI.pVertexInputState = &VertexInputStateCI; @@ -372,18 +368,21 @@ PipelineStateVkImpl :: PipelineStateVkImpl(IReferenceCounters *pRefCounters, Ren if(PipelineDesc.SRBAllocationGranularity > 1) m_VariableDataAllocators.Init(m_NumShaders, PipelineDesc.SRBAllocationGranularity); - bool HasStaticResources = false; - bool HasNonStaticResources = false; + m_HasStaticResources = false; + m_HasNonStaticResources = false; for (Uint32 s=0; s < m_NumShaders; ++s) { const auto &Layout = m_ShaderResourceLayouts[s]; - HasStaticResources = Layout.GetResourceCount(SHADER_VARIABLE_TYPE_STATIC) != 0; - HasNonStaticResources = Layout.GetResourceCount(SHADER_VARIABLE_TYPE_MUTABLE) != 0 || - Layout.GetResourceCount(SHADER_VARIABLE_TYPE_DYNAMIC) != 0; + if(Layout.GetResourceCount(SHADER_VARIABLE_TYPE_STATIC) != 0) + m_HasStaticResources = true; + + if(Layout.GetResourceCount(SHADER_VARIABLE_TYPE_MUTABLE) != 0 || + Layout.GetResourceCount(SHADER_VARIABLE_TYPE_DYNAMIC) != 0) + m_HasNonStaticResources = true; } // If there are only static resources, create default shader resource binding - if(HasStaticResources && !HasNonStaticResources) + if(m_HasStaticResources && !m_HasNonStaticResources) { auto &SRBAllocator = pDeviceVk->GetSRBAllocator(); // Default shader resource binding must be initialized after resource layouts are parsed! @@ -497,8 +496,11 @@ ShaderResourceCacheVk* PipelineStateVkImpl::CommitAndTransitionShaderResources(I bool CommitResources, bool TransitionResources)const { + if(!m_HasStaticResources && !m_HasNonStaticResources) + return nullptr; + #ifdef VERIFY_SHADER_BINDINGS - if (pShaderResourceBinding == nullptr && !m_pDefaultShaderResBinding) + if (pShaderResourceBinding == nullptr && m_HasNonStaticResources) { LOG_ERROR_MESSAGE("Pipeline state \"", m_Desc.Name, "\" contains mutable/dynamic shader variables and requires shader resource binding to commit all resources, but none is provided."); } diff --git a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp index 899a72fa..b7ef1308 100644 --- a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp +++ b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp @@ -58,7 +58,7 @@ RenderDeviceVkImpl :: RenderDeviceVkImpl(IReferenceCounters *pRefCounters, m_VkObjReleaseQueue(STD_ALLOCATOR_RAW_MEM(ReleaseQueueElemType, GetRawAllocator(), "Allocator for queue<ReleaseQueueElemType>")), m_StaleVkObjects(STD_ALLOCATOR_RAW_MEM(ReleaseQueueElemType, GetRawAllocator(), "Allocator for queue<ReleaseQueueElemType>")), m_DescriptorPools(STD_ALLOCATOR_RAW_MEM(DescriptorPoolManager, GetRawAllocator(), "Allocator for vector<DescriptorPoolManager>")), - //m_UploadHeaps(STD_ALLOCATOR_RAW_MEM(UploadHeapPoolElemType, GetRawAllocator(), "Allocator for vector<unique_ptr<DynamicUploadHeap>>")), + m_UploadHeaps(STD_ALLOCATOR_RAW_MEM(UploadHeapPoolElemType, GetRawAllocator(), "Allocator for vector<unique_ptr<VulkanDynamicHeap>>")), m_CmdBufferPool(m_LogicalVkDevice->GetSharedPtr(), pCmdQueue->GetQueueFamilyIndex(), VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT), m_FramebufferCache(*this) { @@ -89,6 +89,7 @@ RenderDeviceVkImpl :: RenderDeviceVkImpl(IReferenceCounters *pRefCounters, true // Thread-safe ); + m_UploadHeaps.reserve(1 + NumDeferredContexts); for(Uint32 ctx = 0; ctx < 1 + NumDeferredContexts; ++ctx) { m_DescriptorPools.emplace_back( @@ -108,6 +109,18 @@ RenderDeviceVkImpl :: RenderDeviceVkImpl(IReferenceCounters *pRefCounters, CreationAttribs.DynamicDescriptorPoolSize.MaxDescriptorSets, false // Dynamic descriptor pools need not to be thread-safe ); + + { +#ifdef _DEBUG + size_t InitialSize = 1024+64; +#else + size_t InitialSize = 64<<10;//16<<20; +#endif + auto &UploadHeapAllocator = GetRawAllocator(); + auto *pRawMem = ALLOCATE(UploadHeapAllocator, "DynamicUploadHeap instance", sizeof(VulkanDynamicHeap)); + auto *pNewHeap = new (pRawMem) VulkanDynamicHeap(GetRawAllocator(), this, InitialSize); + m_UploadHeaps.emplace_back( pNewHeap, STDDeleterRawMem<VulkanDynamicHeap>(UploadHeapAllocator) ); + } } } @@ -306,7 +319,6 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources) Atomics::AtomicIncrement(m_NextCmdListNumber); } -#if 0 { // There is no need to lock as new heaps are only created during initialization // time for every context @@ -336,7 +348,6 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources) } } -#endif { // This is OK if other thread disposes descriptor heap allocation at this time @@ -353,30 +364,6 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources) Atomics::AtomicIncrement(m_FrameNumber); } -#if 0 -DynamicUploadHeap* RenderDeviceVkImpl::RequestUploadHeap() -{ - std::lock_guard<std::mutex> LockGuard(m_UploadHeapMutex); - -#ifdef _DEBUG - size_t InitialSize = 1024+64; -#else - size_t InitialSize = 64<<10;//16<<20; -#endif - - auto &UploadHeapAllocator = GetRawAllocator(); - auto *pRawMem = ALLOCATE(UploadHeapAllocator, "DynamicUploadHeap instance", sizeof(DynamicUploadHeap)); - auto *pNewHeap = new (pRawMem) DynamicUploadHeap(GetRawAllocator(), true, this, InitialSize); - m_UploadHeaps.emplace_back( pNewHeap, STDDeleterRawMem<DynamicUploadHeap>(UploadHeapAllocator) ); - return pNewHeap; -} - -void RenderDeviceVkImpl::ReleaseUploadHeap(DynamicUploadHeap* pUploadHeap) -{ - -} -#endif - VkCommandBuffer RenderDeviceVkImpl::AllocateCommandBuffer(const Char *DebugName) { std::lock_guard<std::mutex> LockGuard(m_CmdPoolMutex); diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp new file mode 100644 index 00000000..ae219873 --- /dev/null +++ b/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp @@ -0,0 +1,168 @@ +/* Copyright 2015-2018 Egor Yusov + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF ANY PROPRIETARY RIGHTS. + * + * In no event and under no legal theory, whether in tort (including negligence), + * contract, or otherwise, unless required by applicable law (such as deliberate + * and grossly negligent acts) or agreed to in writing, shall any Contributor be + * liable for any damages, including any direct, indirect, special, incidental, + * or consequential damages of any character arising as a result of this License or + * out of the use or inability to use the software (including but not limited to damages + * for loss of goodwill, work stoppage, computer failure or malfunction, or any and + * all other commercial damages or losses), even if such Contributor has been advised + * of the possibility of such damages. + */ + +#include "pch.h" +#include "VulkanDynamicHeap.h" +#include "RenderDeviceVkImpl.h" + +namespace Diligent +{ +VulkanRingBuffer::VulkanRingBuffer(size_t MaxSize, IMemoryAllocator &Allocator, RenderDeviceVkImpl* pDeviceVk) : + RingBuffer(MaxSize, Allocator), + m_pDeviceVk(pDeviceVk) +{ + VkBufferCreateInfo VkBuffCI = {}; + VkBuffCI.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; + VkBuffCI.pNext = nullptr; + VkBuffCI.flags = 0; // VK_BUFFER_CREATE_SPARSE_BINDING_BIT, VK_BUFFER_CREATE_SPARSE_RESIDENCY_BIT, VK_BUFFER_CREATE_SPARSE_ALIASED_BIT + VkBuffCI.size = MaxSize; + VkBuffCI.usage = VK_BUFFER_USAGE_TRANSFER_SRC_BIT; + VkBuffCI.sharingMode = VK_SHARING_MODE_EXCLUSIVE; + VkBuffCI.queueFamilyIndexCount = 0; + VkBuffCI.pQueueFamilyIndices = nullptr; + + const auto& LogicalDevice = pDeviceVk->GetLogicalDevice(); + m_VkBuffer = LogicalDevice.CreateBuffer(VkBuffCI, "Upload buffer"); + VkMemoryRequirements MemReqs = LogicalDevice.GetBufferMemoryRequirements(m_VkBuffer); + + const auto& PhysicalDevice = pDeviceVk->GetPhysicalDevice(); + + VkMemoryAllocateInfo MemAlloc = {}; + MemAlloc.pNext = nullptr; + MemAlloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; + MemAlloc.allocationSize = MemReqs.size; + + // VK_MEMORY_PROPERTY_HOST_COHERENT_BIT bit specifies that the host cache management commands vkFlushMappedMemoryRanges + // and vkInvalidateMappedMemoryRanges are NOT needed to flush host writes to the device or make device writes visible + // to the host (10.2) + MemAlloc.memoryTypeIndex = PhysicalDevice.GetMemoryTypeIndex(MemReqs.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); + + VERIFY(MemAlloc.memoryTypeIndex != VulkanUtilities::VulkanPhysicalDevice::InvalidMemoryTypeIndex, + "Vulkan spec requires that for a VkBuffer not created with the " + "VK_BUFFER_CREATE_SPARSE_BINDING_BIT bit set, the memoryTypeBits member always contains at least one bit set " + "corresponding to a VkMemoryType with a propertyFlags that has both the VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT bit " + "and the VK_MEMORY_PROPERTY_HOST_COHERENT_BIT bit set(11.6)"); + + m_BufferMemory = LogicalDevice.AllocateDeviceMemory(MemAlloc, "Host-visible memory for upload buffer"); + + void *Data = nullptr; + auto err = LogicalDevice.MapMemory(m_BufferMemory, + 0, // offset + MemAlloc.allocationSize, + 0, // flags, reserved for future use + &Data); + m_CPUAddress = reinterpret_cast<Uint8*>(Data); + CHECK_VK_ERROR_AND_THROW(err, "Failed to map memory"); + + err = LogicalDevice.BindBufferMemory(m_VkBuffer, m_BufferMemory, 0 /*offset*/); + CHECK_VK_ERROR_AND_THROW(err, "Failed to bind bufer memory"); + + LOG_INFO_MESSAGE("GPU ring buffer created. Size: ", MaxSize); +} + +void VulkanRingBuffer::Destroy() +{ + if(m_VkBuffer) + { + LOG_INFO_MESSAGE("Destroying GPU ring buffer. Size: ", GetMaxSize()); + m_pDeviceVk->GetLogicalDevice().UnmapMemory(m_BufferMemory); + m_pDeviceVk->SafeReleaseVkObject(std::move(m_VkBuffer)); + m_pDeviceVk->SafeReleaseVkObject(std::move(m_BufferMemory)); + } + m_CPUAddress = nullptr; +} + +VulkanRingBuffer::~VulkanRingBuffer() +{ + Destroy(); +} + + +VulkanDynamicHeap::VulkanDynamicHeap(IMemoryAllocator &Allocator, RenderDeviceVkImpl* pDevice, size_t InitialSize) : + m_Allocator(Allocator), + m_pDeviceVk(pDevice), + m_RingBuffers(STD_ALLOCATOR_RAW_MEM(VulkanRingBuffer, GetRawAllocator(), "Allocator for vector<VulkanRingBuffer>")) +{ + m_RingBuffers.emplace_back(InitialSize, Allocator, pDevice); +} + + +VulkanDynamicAllocation VulkanDynamicHeap::Allocate(size_t SizeInBytes, size_t Alignment /*= DEFAULT_ALIGN*/) +{ + if(Alignment == 0) + Alignment = DEFAULT_ALIGN; + // Every device context has its own upload heap, so there is no need to lock + + //std::lock_guard<std::mutex> Lock(m_Mutex); + + // + // Deferred contexts must not update resources or map dynamic buffers + // across several frames! + // + + const size_t AlignmentMask = Alignment - 1; + // Assert that it's a power of two. + VERIFY_EXPR((AlignmentMask & Alignment) == 0); + // Align the allocation + const size_t AlignedSize = (SizeInBytes + AlignmentMask) & ~AlignmentMask; + auto DynAlloc = m_RingBuffers.back().Allocate(AlignedSize); + if (DynAlloc.vkBuffer == VK_NULL_HANDLE) + { + auto NewMaxSize = m_RingBuffers.back().GetMaxSize() * 2; + while(NewMaxSize < AlignedSize)NewMaxSize*=2; + m_RingBuffers.emplace_back(NewMaxSize, m_Allocator, m_pDeviceVk); + DynAlloc = m_RingBuffers.back().Allocate(AlignedSize); + } +#ifdef _DEBUG + DynAlloc.FrameNum = m_pDeviceVk->GetCurrentFrameNumber(); +#endif + return DynAlloc; +} + +void VulkanDynamicHeap::FinishFrame(Uint64 FenceValue, Uint64 LastCompletedFenceValue) +{ + // Every device context has its own upload heap, so there is no need to lock + //std::lock_guard<std::mutex> Lock(m_Mutex); + + // + // Deferred contexts must not update resources or map dynamic buffers + // across several frames! + // + + size_t NumBuffsToDelete = 0; + for(size_t Ind = 0; Ind < m_RingBuffers.size(); ++Ind) + { + auto &RingBuff = m_RingBuffers[Ind]; + RingBuff.FinishCurrentFrame(FenceValue); + RingBuff.ReleaseCompletedFrames(LastCompletedFenceValue); + if ( NumBuffsToDelete == Ind && Ind < m_RingBuffers.size()-1 && RingBuff.IsEmpty()) + { + ++NumBuffsToDelete; + } + } + + if(NumBuffsToDelete) + m_RingBuffers.erase(m_RingBuffers.begin(), m_RingBuffers.begin()+NumBuffsToDelete); +} + +} diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp index ac04ec7c..95a64f3e 100644 --- a/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp +++ b/Graphics/GraphicsEngineVulkan/src/VulkanTypeConversions.cpp @@ -927,7 +927,6 @@ VkVertexInputRate LayoutElemFrequencyToVkInputRate(LayoutElement::FREQUENCY freq } void InputLayoutDesc_To_VkVertexInputStateCI(const InputLayoutDesc& LayoutDesc, - const std::array<Uint32, MaxBufferSlots>& Strides, VkPipelineVertexInputStateCreateInfo &VertexInputStateCI, std::array<VkVertexInputBindingDescription, iMaxLayoutElements>& BindingDescriptions, std::array<VkVertexInputAttributeDescription, iMaxLayoutElements>& AttributeDescription) @@ -951,12 +950,13 @@ void InputLayoutDesc_To_VkVertexInputStateCI(const InputLayoutDesc& LayoutDesc, BindingDescInd = VertexInputStateCI.vertexBindingDescriptionCount++; auto &BindingDesc = BindingDescriptions[BindingDescInd]; BindingDesc.binding = LayoutElem.BufferSlot; - BindingDesc.stride = Strides[LayoutElem.BufferSlot]; + BindingDesc.stride = LayoutElem.Stride; BindingDesc.inputRate = LayoutElemFrequencyToVkInputRate(LayoutElem.Frequency); } const auto &BindingDesc = BindingDescriptions[BindingDescInd]; VERIFY(BindingDesc.binding == LayoutElem.BufferSlot, "Inconsistent buffer slot"); + VERIFY(BindingDesc.stride == LayoutElem.Stride, "Inconsistent strides"); VERIFY(BindingDesc.inputRate == LayoutElemFrequencyToVkInputRate(LayoutElem.Frequency), "Incosistent layout element frequency"); auto &AttribDesc = AttributeDescription[elem]; |
