summaryrefslogtreecommitdiffstats
path: root/Graphics
diff options
context:
space:
mode:
authorEgor Yusov <egor.yusov@gmail.com>2018-07-31 03:50:15 +0000
committerEgor Yusov <egor.yusov@gmail.com>2018-07-31 03:50:15 +0000
commitc3c38e981fe078d2a83d23283c54f0a0f113ce93 (patch)
treeb99e049dbb8fc66a6f4e20e395504a4c582002b7 /Graphics
parentFixed initialization of DebugMessageCallback to work in Release mode (diff)
downloadDiligentCore-c3c38e981fe078d2a83d23283c54f0a0f113ce93.tar.gz
DiligentCore-c3c38e981fe078d2a83d23283c54f0a0f113ce93.zip
Reworked dynamic resource management in Vulkan to not rely on RingBuffer; added FinishFrame() to device context
Diffstat (limited to 'Graphics')
-rw-r--r--Graphics/GraphicsAccessories/interface/RingBuffer.h2
-rw-r--r--Graphics/GraphicsEngine/interface/DeviceContext.h140
-rw-r--r--Graphics/GraphicsEngineD3D11/include/DeviceContextD3D11Impl.h2
-rw-r--r--Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp4
-rw-r--r--Graphics/GraphicsEngineD3D12/include/DeviceContextD3D12Impl.h2
-rw-r--r--Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp4
-rw-r--r--Graphics/GraphicsEngineD3D12/src/DynamicUploadHeap.cpp4
-rw-r--r--Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.h2
-rw-r--r--Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp4
-rw-r--r--Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h7
-rw-r--r--Graphics/GraphicsEngineVulkan/include/CommandQueueVkImpl.h2
-rw-r--r--Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h4
-rw-r--r--Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h8
-rw-r--r--Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h2
-rw-r--r--Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h256
-rw-r--r--Graphics/GraphicsEngineVulkan/interface/CommandQueueVk.h4
-rw-r--r--Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp22
-rw-r--r--Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp3
-rw-r--r--Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp59
-rw-r--r--Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp2
-rw-r--r--Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp37
-rw-r--r--Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp8
-rw-r--r--Graphics/GraphicsEngineVulkan/src/SwapChainVkImpl.cpp1
-rw-r--r--Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp127
-rw-r--r--Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp4
25 files changed, 434 insertions, 276 deletions
diff --git a/Graphics/GraphicsAccessories/interface/RingBuffer.h b/Graphics/GraphicsAccessories/interface/RingBuffer.h
index 70d87c04..8d21d38f 100644
--- a/Graphics/GraphicsAccessories/interface/RingBuffer.h
+++ b/Graphics/GraphicsAccessories/interface/RingBuffer.h
@@ -53,7 +53,7 @@ namespace Diligent
OffsetType Offset;
OffsetType Size;
};
- static const OffsetType InvalidOffset = static_cast<OffsetType>(-1);
+ static constexpr const OffsetType InvalidOffset = static_cast<OffsetType>(-1);
RingBuffer(OffsetType MaxSize, IMemoryAllocator &Allocator)noexcept :
m_CompletedFrameTails(0, FrameTailAttribs(0,0,0), STD_ALLOCATOR_RAW_MEM(FrameTailAttribs, Allocator, "Allocator for vector<FrameNumOffsetPair>" )),
diff --git a/Graphics/GraphicsEngine/interface/DeviceContext.h b/Graphics/GraphicsEngine/interface/DeviceContext.h
index 7ce56ccd..72904ecd 100644
--- a/Graphics/GraphicsEngine/interface/DeviceContext.h
+++ b/Graphics/GraphicsEngine/interface/DeviceContext.h
@@ -59,40 +59,40 @@ struct DrawAttribs
union
{
/// For a non-indexed draw call, number of vertices to draw
- Uint32 NumVertices;
+ Uint32 NumVertices = 0;
/// For an indexed draw call, number of indices to draw
Uint32 NumIndices;
};
/// For an indexed draw call, type of elements in the index buffer.
/// Allowed values: VT_UINT16 and VT_UINT32. Ignored if DrawAttribs::IsIndexed is False.
- VALUE_TYPE IndexType;
+ VALUE_TYPE IndexType = VT_UNDEFINED;
/// Indicates if index buffer will be used to index input vertices
- Bool IsIndexed;
+ Bool IsIndexed = False;
/// Number of instances to draw. If more than one instance is specified,
/// instanced draw call will be performed.
- Uint32 NumInstances;
+ Uint32 NumInstances = 1;
/// Indicates if indirect draw call will be performed. If set to True,
/// pIndirectDrawAttribs must contain valid pointer to the buffer, from which
/// draw attributes will be read.
- Bool IsIndirect;
+ Bool IsIndirect = False;
/// For indexed rendering, a constant which is added to each index before
/// accessing the vertex buffer.
- Uint32 BaseVertex;
+ Uint32 BaseVertex = 0;
/// For indirect rendering, offset from the beginning of the buffer to the
/// location of draw command attributes. Ignored if DrawAttribs::IsIndirect is False.
- Uint32 IndirectDrawArgsOffset;
+ Uint32 IndirectDrawArgsOffset = 0;
union
{
/// For non-indexed rendering, LOCATION (or INDEX, but NOT the byte offset) of the
/// first vertex in the vertex buffer to start reading vertices from
- Uint32 StartVertexLocation;
+ Uint32 StartVertexLocation = 0;
/// For indexed rendering, LOCATION (NOT the byte offset) of the first index in
/// the index buffer to start reading indices from
@@ -100,11 +100,11 @@ struct DrawAttribs
};
/// For instanced rendering, LOCATION (or INDEX, but NOT the byte offset) in the vertex
/// buffer to start reading instance data from
- Uint32 FirstInstanceLocation;
+ Uint32 FirstInstanceLocation = 0;
/// For indirect rendering, pointer to the buffer, from which
/// draw attributes will be read. Ignored if DrawAttribs::IsIndirect is False.
- IBuffer* pIndirectDrawAttribs;
+ IBuffer* pIndirectDrawAttribs = nullptr;
/// Initializes the structure members with default values
@@ -122,18 +122,7 @@ struct DrawAttribs
/// StartVertexLocation | 0
/// FirstInstanceLocation | 0
/// pIndirectDrawAttribs | nullptr
- DrawAttribs() :
- NumVertices(0),
- IndexType(VT_UNDEFINED),
- IsIndexed(False),
- NumInstances(1),
- IsIndirect(False),
- BaseVertex(0),
- IndirectDrawArgsOffset(0),
- StartVertexLocation(0),
- FirstInstanceLocation(0),
- pIndirectDrawAttribs(nullptr)
- {}
+ DrawAttribs()noexcept{}
};
/// Defines which parts of the depth-stencil buffer to clear.
@@ -141,7 +130,7 @@ struct DrawAttribs
/// These flags are used by IDeviceContext::ClearDepthStencil().
enum CLEAR_DEPTH_STENCIL_FLAGS : Int32
{
- CLEAR_DEPTH_FLAG = 0x01, ///< Clear depth part of the buffer
+ CLEAR_DEPTH_FLAG = 0x01, ///< Clear depth part of the buffer
CLEAR_STENCIL_FLAG = 0x02 ///< Clear stencil part of the buffer
};
@@ -170,12 +159,13 @@ struct DispatchComputeAttribs
/// \param [in] GroupsX - Number of groups dispatched in X direction. Default value is 1.
/// \param [in] GroupsY - Number of groups dispatched in Y direction. Default value is 1.
/// \param [in] GroupsZ - Number of groups dispatched in Z direction. Default value is 1.
- DispatchComputeAttribs( Uint32 GroupsX = 1, Uint32 GroupsY = 1, Uint32 GroupsZ = 1 ) :
- ThreadGroupCountX( GroupsX ),
- ThreadGroupCountY( GroupsY ),
- ThreadGroupCountZ( GroupsZ ),
+ explicit
+ DispatchComputeAttribs(Uint32 GroupsX = 1, Uint32 GroupsY = 1, Uint32 GroupsZ = 1) :
+ ThreadGroupCountX (GroupsX),
+ ThreadGroupCountY (GroupsY),
+ ThreadGroupCountZ (GroupsZ),
pIndirectDispatchAttribs(nullptr),
- DispatchArgsByteOffset(0)
+ DispatchArgsByteOffset (0)
{}
/// Initializes the structure to perform indirect dispatch command
@@ -183,12 +173,13 @@ struct DispatchComputeAttribs
/// \param [in] pDispatchAttribs - Pointer to the buffer containing dispatch arguments.
/// \param [in] Offset - Offset from the beginning of the buffer to the dispatch command
/// arguments. Default value is 0.
- DispatchComputeAttribs( IBuffer* pDispatchAttribs, Uint32 Offset = 0 ) :
- ThreadGroupCountX( 0 ),
- ThreadGroupCountY( 0 ),
- ThreadGroupCountZ( 0 ),
- pIndirectDispatchAttribs( pDispatchAttribs ),
- DispatchArgsByteOffset( Offset )
+ explicit
+ DispatchComputeAttribs(IBuffer* pDispatchAttribs, Uint32 Offset = 0) :
+ ThreadGroupCountX (0),
+ ThreadGroupCountY (0),
+ ThreadGroupCountZ (0),
+ pIndirectDispatchAttribs (pDispatchAttribs),
+ DispatchArgsByteOffset (Offset)
{}
};
@@ -226,38 +217,39 @@ enum COMMIT_SHADER_RESOURCES_FLAG
struct Viewport
{
/// X coordinate of the left boundary of the viewport.
- Float32 TopLeftX;
+ Float32 TopLeftX = 0.f;
/// Y coordinate of the top boundary of the viewport.
/// When defining a viewport, DirectX convention is used:
/// window coordinate systems originates in the LEFT TOP corner
/// of the screen with Y axis pointing down.
- Float32 TopLeftY;
+ Float32 TopLeftY = 0.f;
/// Viewport width
- Float32 Width;
+ Float32 Width = 0.f;
/// Viewport Height
- Float32 Height;
+ Float32 Height = 0.f;
/// Minimum depth of the viewport. Ranges between 0 and 1.
- Float32 MinDepth;
+ Float32 MinDepth = 0.f;
/// Maximum depth of the viewport. Ranges between 0 and 1.
- Float32 MaxDepth;
+ Float32 MaxDepth = 1.f;
/// Initializes the structure
- Viewport(Float32 _TopLeftX = 0, Float32 _TopLeftY = 0,
- Float32 _Width = 0, Float32 _Height = 0,
- Float32 _MinDepth = 0, Float32 _MaxDepth = 1 )
- :
- TopLeftX( _TopLeftX ),
- TopLeftY( _TopLeftY ),
- Width ( _Width ),
- Height ( _Height ),
- MinDepth( _MinDepth ),
- MaxDepth( _MaxDepth )
+ Viewport(Float32 _TopLeftX, Float32 _TopLeftY,
+ Float32 _Width, Float32 _Height,
+ Float32 _MinDepth = 0, Float32 _MaxDepth = 1)noexcept :
+ TopLeftX (_TopLeftX),
+ TopLeftY (_TopLeftY),
+ Width (_Width ),
+ Height (_Height ),
+ MinDepth (_MinDepth),
+ MaxDepth (_MaxDepth)
{}
+
+ Viewport()noexcept{}
};
/// Describes the rectangle.
@@ -269,18 +261,20 @@ struct Viewport
/// of the screen with Y axis pointing down.
struct Rect
{
- Int32 left; ///< X coordinate of the left boundary of the viewport.
- Int32 top; ///< Y coordinate of the top boundary of the viewport.
- Int32 right; ///< X coordinate of the right boundary of the viewport.
- Int32 bottom;///< Y coordinate of the bottom boundary of the viewport.
+ Int32 left = 0; ///< X coordinate of the left boundary of the viewport.
+ Int32 top = 0; ///< Y coordinate of the top boundary of the viewport.
+ Int32 right = 0; ///< X coordinate of the right boundary of the viewport.
+ Int32 bottom = 0; ///< Y coordinate of the bottom boundary of the viewport.
/// Initializes the structure
- Rect( Int32 _left = 0, Int32 _top = 0, Int32 _right = 0, Int32 _bottom = 0 ) :
+ Rect(Int32 _left, Int32 _top, Int32 _right, Int32 _bottom) :
left ( _left ),
top ( _top ),
right ( _right ),
bottom( _bottom )
{}
+
+ Rect(){}
};
@@ -294,7 +288,7 @@ class IDeviceContext : public IObject
{
public:
/// Queries the specific interface, see IObject::QueryInterface() for details
- virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject** ppInterface ) = 0;
+ virtual void QueryInterface(const Diligent::INTERFACE_ID &IID, IObject** ppInterface) = 0;
/// Sets the pipeline state
@@ -404,7 +398,7 @@ public:
/// following call:
///
/// pContext->SetViewports(1, nullptr, 0, 0);
- virtual void SetViewports( Uint32 NumViewports, const Viewport* pViewports, Uint32 RTWidth, Uint32 RTHeight ) = 0;
+ virtual void SetViewports(Uint32 NumViewports, const Viewport* pViewports, Uint32 RTWidth, Uint32 RTHeight) = 0;
/// Sets active scissor rects
@@ -419,7 +413,7 @@ public:
/// required to convert viewport from DirectX to OpenGL coordinate system if OpenGL device is used.\n\n
/// All scissor rects must be set atomically as one operation. Any rects not
/// defined by the call are disabled.
- virtual void SetScissorRects( Uint32 NumRects, const Rect* pRects, Uint32 RTWidth, Uint32 RTHeight ) = 0;
+ virtual void SetScissorRects(Uint32 NumRects, const Rect* pRects, Uint32 RTWidth, Uint32 RTHeight) = 0;
/// Binds one or more render targets and the depth-stencil buffer to the pipeline. It also
/// sets the viewport to match the first non-null render target or depth-stencil buffer.
@@ -440,7 +434,7 @@ public:
/// following call:
///
/// pContext->SetRenderTargets(0, nullptr, nullptr);
- virtual void SetRenderTargets( Uint32 NumRenderTargets, ITextureView* ppRenderTargets[], ITextureView* pDepthStencil ) = 0;
+ virtual void SetRenderTargets(Uint32 NumRenderTargets, ITextureView* ppRenderTargets[], ITextureView* pDepthStencil) = 0;
/// Executes a draw command
@@ -451,7 +445,7 @@ public:
/// \param [in] DispatchAttrs - Structure describing dispatch command attributes,
/// see DispatchComputeAttribs for details.
- virtual void DispatchCompute( const DispatchComputeAttribs &DispatchAttrs ) = 0;
+ virtual void DispatchCompute(const DispatchComputeAttribs &DispatchAttrs) = 0;
/// Clears a depth-stencil view
@@ -462,7 +456,7 @@ public:
/// \param [in] Stencil - Value to clear stencil part of the view with.
/// \remarks The full extent of the view is always cleared. Viewport and scissor settings are not applied.
/// \note The depth-stencil view must be bound to the pipeline for clear operation to be performed.
- virtual void ClearDepthStencil( ITextureView* pView, Uint32 ClearFlags = CLEAR_DEPTH_FLAG, float fDepth = 1.f, Uint8 Stencil = 0 ) = 0;
+ virtual void ClearDepthStencil(ITextureView* pView, Uint32 ClearFlags = CLEAR_DEPTH_FLAG, float fDepth = 1.f, Uint8 Stencil = 0) = 0;
/// Clears a render target view
@@ -472,18 +466,18 @@ public:
/// If nullptr is provided, the default array {0,0,0,0} will be used.
/// \remarks The full extent of the view is always cleared. Viewport and scissor settings are not applied.
/// \note The render target view must be bound to the pipeline for clear operation to be performed.
- virtual void ClearRenderTarget( ITextureView* pView, const float* RGBA = nullptr ) = 0;
+ virtual void ClearRenderTarget(ITextureView* pView, const float* RGBA = nullptr) = 0;
/// Finishes recording commands and generates a command list
/// \param [out] ppCommandList - Memory location where pointer to the recorded command list will be written.
- virtual void FinishCommandList( ICommandList **ppCommandList) = 0;
+ virtual void FinishCommandList(ICommandList **ppCommandList) = 0;
/// Executes recorded commands in a command list
/// \param [in] pCommandList - Pointer to the command list to executre.
/// \remarks After command list is executed, it is no longer valid and should be released.
- virtual void ExecuteCommandList( ICommandList* pCommandList) = 0;
+ virtual void ExecuteCommandList(ICommandList* pCommandList) = 0;
/// Tells the GPU to set a fence to a specified value after all previous work has completed
@@ -514,7 +508,23 @@ public:
/// However, when the engine is initialized by attaching to existing d3d11/d3d12 device or OpenGL/GLES context, the
/// swap chain needs to be set manually if the device context will be using any of the commands above.\n
/// Device context keeps strong reference to the swap chain.
- virtual void SetSwapChain( ISwapChain* pSwapChain) = 0;
+ virtual void SetSwapChain(ISwapChain* pSwapChain) = 0;
+
+
+ /// Finishes the current frame and releases dynamic resources allocated by the context
+
+ /// For immediate context, this method is called automatically by Present(), but can
+ /// also be called explicitly. For deferred context, the method must be called by the application to
+ /// release dynamic resources. The method has some overhead, so it is better to call it once
+ /// per frame, though it can be called with different frequency. Note that unless the GPU is idled,
+ /// the resources may actually be released several frames after the one they were used in last time.
+ /// \param ForceRelease - force release all stale resources. Use this option only if you are sure
+ /// the resources are not in use by the GPU (the GPU has been idled or a
+ /// fence indicates all commands are finished).
+ /// \note After the call all dynamic resources become invalid and must be written again before the next use.
+ /// For deferred contexts, this method must be called after all command lists referencing dynamic resources
+ /// have been executed through immediate context.
+ virtual void FinishFrame(bool ForceRelease = false) = 0;
};
}
diff --git a/Graphics/GraphicsEngineD3D11/include/DeviceContextD3D11Impl.h b/Graphics/GraphicsEngineD3D11/include/DeviceContextD3D11Impl.h
index 469dd621..002e61ab 100644
--- a/Graphics/GraphicsEngineD3D11/include/DeviceContextD3D11Impl.h
+++ b/Graphics/GraphicsEngineD3D11/include/DeviceContextD3D11Impl.h
@@ -85,6 +85,8 @@ public:
virtual void ClearRenderTarget( ITextureView* pView, const float *RGBA )override final;
virtual void Flush()override final;
+
+ virtual void FinishFrame(bool ForceRelease)override final;
void FinishCommandList(class ICommandList **ppCommandList)override final;
diff --git a/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp b/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp
index da8ac073..856bc56c 100644
--- a/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp
+++ b/Graphics/GraphicsEngineD3D11/src/DeviceContextD3D11Impl.cpp
@@ -858,6 +858,10 @@ namespace Diligent
m_pd3d11DeviceContext->Flush();
}
+ void DeviceContextD3D11Impl::FinishFrame(bool ForceRelease)
+ {
+ }
+
void DeviceContextD3D11Impl::SetVertexBuffers( Uint32 StartSlot, Uint32 NumBuffersSet, IBuffer **ppBuffers, Uint32* pOffsets, Uint32 Flags )
{
TDeviceContextBase::SetVertexBuffers( StartSlot, NumBuffersSet, ppBuffers, pOffsets, Flags );
diff --git a/Graphics/GraphicsEngineD3D12/include/DeviceContextD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/DeviceContextD3D12Impl.h
index 2dfdfacf..4c65e1c2 100644
--- a/Graphics/GraphicsEngineD3D12/include/DeviceContextD3D12Impl.h
+++ b/Graphics/GraphicsEngineD3D12/include/DeviceContextD3D12Impl.h
@@ -85,6 +85,8 @@ public:
virtual void ClearRenderTarget( ITextureView *pView, const float *RGBA )override final;
virtual void Flush()override final;
+
+ virtual void FinishFrame(bool ForceRelease)override final;
virtual void FinishCommandList(class ICommandList **ppCommandList)override final;
diff --git a/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp
index eccdcb1d..b7d41c82 100644
--- a/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp
+++ b/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp
@@ -531,6 +531,10 @@ namespace Diligent
Flush(true);
}
+ void DeviceContextD3D12Impl::FinishFrame(bool ForceRelease)
+ {
+ }
+
void DeviceContextD3D12Impl::SetVertexBuffers( Uint32 StartSlot, Uint32 NumBuffersSet, IBuffer** ppBuffers, Uint32* pOffsets, Uint32 Flags )
{
TDeviceContextBase::SetVertexBuffers( StartSlot, NumBuffersSet, ppBuffers, pOffsets, Flags );
diff --git a/Graphics/GraphicsEngineD3D12/src/DynamicUploadHeap.cpp b/Graphics/GraphicsEngineD3D12/src/DynamicUploadHeap.cpp
index b16b62b8..232c5f8e 100644
--- a/Graphics/GraphicsEngineD3D12/src/DynamicUploadHeap.cpp
+++ b/Graphics/GraphicsEngineD3D12/src/DynamicUploadHeap.cpp
@@ -81,14 +81,14 @@ namespace Diligent
m_pBuffer->Map(0, nullptr, &m_CpuVirtualAddress);
}
- LOG_INFO_MESSAGE("GPU ring buffer created. Size: ", MaxSize, "; GPU virtual address 0x", std::hex, m_GpuVirtualAddress);
+ LOG_INFO_MESSAGE("GPU ring buffer created. Size: ", FormatMemorySize(MaxSize,2), "; 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);
+ LOG_INFO_MESSAGE("Destroying GPU ring buffer. Size: ", FormatMemorySize(m_pBuffer->GetDesc().Width,2), "; GPU virtual address 0x", std::hex, m_GpuVirtualAddress);
}
if (m_CpuVirtualAddress)
diff --git a/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.h b/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.h
index bff4710d..6967f15a 100644
--- a/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.h
+++ b/Graphics/GraphicsEngineOpenGL/include/DeviceContextGLImpl.h
@@ -78,6 +78,8 @@ public:
virtual void Flush()override final;
+ virtual void FinishFrame(bool ForceRelease)override final;
+
virtual void FinishCommandList(class ICommandList **ppCommandList)override final;
virtual void ExecuteCommandList(class ICommandList *pCommandList)override final;
diff --git a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp
index 394542aa..e00af85c 100644
--- a/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp
+++ b/Graphics/GraphicsEngineOpenGL/src/DeviceContextGLImpl.cpp
@@ -976,6 +976,10 @@ namespace Diligent
glFlush();
}
+ void DeviceContextGLImpl::FinishFrame(bool ForceRelease)
+ {
+ }
+
void DeviceContextGLImpl::FinishCommandList(class ICommandList **ppCommandList)
{
LOG_ERROR("Deferred contexts are not supported in OpenGL mode");
diff --git a/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h b/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h
index e94ed304..90148d2b 100644
--- a/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/BufferVkImpl.h
@@ -41,6 +41,7 @@ namespace Diligent
{
class FixedBlockMemoryAllocator;
+class DeviceContextVkImpl;
/// Implementation of the Diligent::IBufferVk interface
class BufferVkImpl : public BufferBase<IBufferVk, RenderDeviceVkImpl, BufferViewVkImpl, FixedBlockMemoryAllocator>
@@ -69,10 +70,10 @@ public:
virtual void Unmap( IDeviceContext* pContext, MAP_TYPE MapType, Uint32 MapFlags )override;
#ifdef DEVELOPMENT
- void DvpVerifyDynamicAllocation(Uint32 ContextId)const;
+ void DvpVerifyDynamicAllocation(DeviceContextVkImpl* pCtx)const;
#endif
- Uint32 GetDynamicOffset(Uint32 CtxId)const
+ Uint32 GetDynamicOffset(Uint32 CtxId, DeviceContextVkImpl* pCtx)const
{
if(m_VulkanBuffer != VK_NULL_HANDLE)
{
@@ -83,7 +84,7 @@ public:
VERIFY(m_Desc.Usage == USAGE_DYNAMIC, "Dynamic buffer is expected");
VERIFY_EXPR(!m_DynamicAllocations.empty());
#ifdef DEVELOPMENT
- DvpVerifyDynamicAllocation(CtxId);
+ DvpVerifyDynamicAllocation(pCtx);
#endif
auto& DynAlloc = m_DynamicAllocations[CtxId];
return static_cast<Uint32>(DynAlloc.Offset);
diff --git a/Graphics/GraphicsEngineVulkan/include/CommandQueueVkImpl.h b/Graphics/GraphicsEngineVulkan/include/CommandQueueVkImpl.h
index 5a3de325..8e2bb941 100644
--- a/Graphics/GraphicsEngineVulkan/include/CommandQueueVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/CommandQueueVkImpl.h
@@ -62,7 +62,7 @@ public:
virtual uint32_t GetQueueFamilyIndex()override final { return m_QueueFamilyIndex; }
- virtual void IdleGPU()override final;
+ virtual Uint64 IdleGPU()override final;
virtual Uint64 GetCompletedFenceValue()override final;
diff --git a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h
index 312bdaca..e7483c22 100644
--- a/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/DeviceContextVkImpl.h
@@ -147,6 +147,7 @@ public:
return m_CommandBuffer;
}
+ virtual void FinishFrame(bool ForceRelease)override final;
void FinishFrame(Uint64 CompletedFenceValue);
DescriptorPoolAllocation AllocateDynamicDescriptorSet(VkDescriptorSetLayout SetLayout)
@@ -159,6 +160,7 @@ public:
VulkanDynamicAllocation AllocateDynamicSpace(Uint32 SizeInBytes);
void ResetRenderTargets();
+ Int64 GetContextFrameNumber()const{return m_ContextFrameNumber;}
private:
void CommitRenderPassAndFramebuffer();
@@ -220,6 +222,8 @@ private:
// Number of the command buffer currently being recorded by the context and that will
// be submitted next
Atomics::AtomicInt64 m_NextCmdBuffNumber;
+ Atomics::AtomicInt64 m_ContextFrameNumber;
+ Uint64 m_LastSubmittedFenceValue = 0;
PipelineLayout::DescriptorSetBindInfo m_DescrSetBindInfo;
VulkanDynamicHeap m_DynamicHeap;
diff --git a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h
index 11257bf0..9fbe7c32 100644
--- a/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h
+++ b/Graphics/GraphicsEngineVulkan/include/RenderDeviceVkImpl.h
@@ -133,8 +133,8 @@ public:
return m_MemoryMgr.Allocate(MemReqs, MemoryProperties);
}
- VulkanRingBuffer& GetDynamicHeapRingBuffer(){return m_DynamicHeapRingBuffer;}
-
+ VulkanDynamicMemoryManager& GetDynamicMemoryManager(){return m_DynamicMemoryManager;}
+
private:
virtual void TestTextureFormat( TEXTURE_FORMAT TexFormat )override final;
void ProcessStaleResources(Uint64 SubmittedCmdBufferNumber, Uint64 SubmittedFenceValue, Uint64 CompletedFenceValue);
@@ -150,7 +150,7 @@ private:
std::unique_ptr<VulkanUtilities::VulkanPhysicalDevice> m_PhysicalDevice;
std::shared_ptr<VulkanUtilities::VulkanLogicalDevice> m_LogicalVkDevice;
- std::mutex m_CmdQueueMutex;
+ std::mutex m_SubmitCmdBufferMutex;
RefCntAutoPtr<ICommandQueueVk> m_pCommandQueue;
EngineVkAttribs m_EngineAttribs;
@@ -198,7 +198,7 @@ private:
VulkanUtilities::VulkanMemoryManager m_MemoryMgr;
ResourceReleaseQueue<DynamicStaleResourceWrapper> m_ReleaseQueue;
- VulkanRingBuffer m_DynamicHeapRingBuffer;
+ VulkanDynamicMemoryManager m_DynamicMemoryManager;
};
}
diff --git a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h
index b4a0e3b6..1531d851 100644
--- a/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h
+++ b/Graphics/GraphicsEngineVulkan/include/ShaderResourceCacheVk.h
@@ -174,7 +174,7 @@ public:
template<bool VerifyOnly>
void TransitionResources(DeviceContextVkImpl *pCtxVkImpl);
- Uint32 GetDynamicBufferOffsets(Uint32 CtxId, std::vector<uint32_t>& Offsets)const;
+ Uint32 GetDynamicBufferOffsets(DeviceContextVkImpl *pCtxVkImpl, std::vector<uint32_t>& Offsets)const;
private:
diff --git a/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h b/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h
index a36cb3ba..3e3d0c7f 100644
--- a/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h
+++ b/Graphics/GraphicsEngineVulkan/include/VulkanDynamicHeap.h
@@ -26,119 +26,246 @@
#include <mutex>
#include "Vulkan.h"
#include "RingBuffer.h"
+#include "VulkanUtilities/VulkanMemoryManager.h"
#include "VulkanUtilities/VulkanLogicalDevice.h"
#include "VulkanUtilities/VulkanObjectWrappers.h"
namespace Diligent
{
-// Vulkand dynamic heap implementation consists of a single ring buffer and a number of dynamic heaps,
-// one per context. Every dynamic heap suballocates chunk of memory from the global ring buffer. Within
-// every chunk, memory is allocated in simple lock-free linear fashion:
-//
-// | <----------------------frame 0---------------------------->|<-----------------frame 1-------------->
-// | Ctx0-f0-Chunk0 | Ctx1-f0-Chunk0 | Ctx0-f0-Chunk1 | ... | Ctx0-f1-Chunk0 | Ctx1-f1-Chunk0 |....
-//
+// Vulkand dynamic heap implementation consists of a number of dynamic heaps, one per context.
+// Every dynamic heap suballocates chunk of memory from the global memory manager. Within
+// every chunk, memory is allocated in a simple lock-free linear fashion. All used allocations are discarded
+// when FinishFrame() is called
class RenderDeviceVkImpl;
class VulkanRingBuffer;
+class VulkanDynamicMemoryManager;
// sizeof(VulkanDynamicAllocation) must be at least 16 to avoid false cache line sharing problems
struct VulkanDynamicAllocation
{
- VulkanDynamicAllocation(){}
+ VulkanDynamicAllocation()noexcept{}
- VulkanDynamicAllocation(VulkanRingBuffer& _ParentHeap, size_t _Offset, size_t _Size) :
- pParentDynamicHeap(&_ParentHeap),
- Offset (_Offset),
- Size (_Size)
+ VulkanDynamicAllocation(VulkanDynamicMemoryManager& _DynamicMemMgr, size_t _Offset, size_t _Size)noexcept :
+ pDynamicMemMgr(&_DynamicMemMgr),
+ Offset (_Offset),
+ Size (_Size)
{}
VulkanDynamicAllocation (const VulkanDynamicAllocation&) = delete;
VulkanDynamicAllocation& operator = (const VulkanDynamicAllocation&) = delete;
VulkanDynamicAllocation (VulkanDynamicAllocation&& rhs)noexcept :
- pParentDynamicHeap(rhs.pParentDynamicHeap),
- Offset (rhs.Offset),
- Size (rhs.Size)
-#ifdef _DEBUG
- , dbgFrameNumber(rhs.dbgFrameNumber)
+ pDynamicMemMgr(rhs.pDynamicMemMgr),
+ Offset (rhs.Offset),
+ Size (rhs.Size)
+#ifdef DEVELOPMENT
+ , dvpFrameNumber(rhs.dvpFrameNumber)
#endif
{
- rhs.pParentDynamicHeap = nullptr;
+ rhs.pDynamicMemMgr = nullptr;
rhs.Offset = 0;
rhs.Size = 0;
-#ifdef _DEBUG
- rhs.dbgFrameNumber = 0;
+#ifdef DEVELOPMENT
+ rhs.dvpFrameNumber = 0;
#endif
}
VulkanDynamicAllocation& operator = (VulkanDynamicAllocation&& rhs)noexcept // Must be noexcept on MSVC, so can't use = default
{
- pParentDynamicHeap = rhs.pParentDynamicHeap;
- Offset = rhs.Offset;
- Size = rhs.Size;
- rhs.pParentDynamicHeap = nullptr;
- rhs.Offset = 0;
- rhs.Size = 0;
-#ifdef _DEBUG
- dbgFrameNumber = rhs.dbgFrameNumber;
- rhs.dbgFrameNumber = 0;
+ pDynamicMemMgr = rhs.pDynamicMemMgr;
+ Offset = rhs.Offset;
+ Size = rhs.Size;
+ rhs.pDynamicMemMgr = nullptr;
+ rhs.Offset = 0;
+ rhs.Size = 0;
+#ifdef DEVELOPMENT
+ dvpFrameNumber = rhs.dvpFrameNumber;
+ rhs.dvpFrameNumber = 0;
#endif
return *this;
}
- VulkanRingBuffer* pParentDynamicHeap = nullptr;
- size_t Offset = 0; // Offset from the start of the buffer resource
- size_t Size = 0; // Reserved size of this allocation
-#ifdef _DEBUG
- Uint64 dbgFrameNumber = 0;
+ VulkanDynamicMemoryManager* pDynamicMemMgr = nullptr;
+ size_t Offset = 0; // Offset from the start of the buffer resource
+ size_t Size = 0; // Reserved size of this allocation
+#ifdef DEVELOPMENT
+ Int64 dvpFrameNumber = 0;
#endif
};
-class VulkanRingBuffer
+
+// Having global ring buffer shared between all contexts is inconvinient because all contexts
+// must share the same frame. Having individual ring bufer per context may result in a lot of unused
+// memory. As a result, ring buffer is not currently used for dynamic memory management.
+// Instead, every dynamic heap allocates pages from the global dynamic memory manager.
+class RingBufferAllocationStrategy
{
public:
- VulkanRingBuffer(IMemoryAllocator& Allocator,
- class RenderDeviceVkImpl& DeviceVk,
- Uint32 Size);
- ~VulkanRingBuffer();
+ using OffsetType = RingBuffer::OffsetType;
+ static constexpr const OffsetType InvalidOffset = RingBuffer::InvalidOffset;
+
+ RingBufferAllocationStrategy(IMemoryAllocator& Allocator,
+ Uint32 Size) :
+ m_RingBuffer(Size, Allocator)
+ {}
- VulkanRingBuffer (const VulkanRingBuffer&) = delete;
- VulkanRingBuffer (VulkanRingBuffer&&) = delete;
- VulkanRingBuffer& operator= (const VulkanRingBuffer&) = delete;
- VulkanRingBuffer& operator= (VulkanRingBuffer&&) = delete;
+ RingBufferAllocationStrategy (const RingBufferAllocationStrategy&) = delete;
+ RingBufferAllocationStrategy (RingBufferAllocationStrategy&&) = delete;
+ RingBufferAllocationStrategy& operator= (const RingBufferAllocationStrategy&) = delete;
+ RingBufferAllocationStrategy& operator= (RingBufferAllocationStrategy&&) = delete;
- void FinishFrame(Uint64 FenceValue, Uint64 LastCompletedFenceValue);
- void Destroy();
+ void DiscardAllocations(const std::vector<std::pair<OffsetType, OffsetType>>& Allocations, Uint64 FenceValue)
+ {
+ std::lock_guard<std::mutex> Lock(m_RingBufferMtx);
+ m_RingBuffer.FinishCurrentFrame(FenceValue);
+ }
+
+ void ReleaseStaleAllocations(Uint64 LastCompletedFenceValue)
+ {
+ std::lock_guard<std::mutex> Lock(m_RingBufferMtx);
+ m_RingBuffer.ReleaseCompletedFrames(LastCompletedFenceValue);
+ }
+
+ OffsetType Allocate(OffsetType SizeInBytes)
+ {
+ std::lock_guard<std::mutex> Lock(m_RingBufferMtx);
+ return m_RingBuffer.Allocate(SizeInBytes);
+ }
+
+ OffsetType GetSize() const{return m_RingBuffer.GetMaxSize();}
+ OffsetType GetUsedSize()const{return m_RingBuffer.GetUsedSize();}
+private:
+ std::mutex m_RingBufferMtx;
+ RingBuffer m_RingBuffer;
+};
+
+
+class ListBasedAllocationStrategy
+{
+public:
+ using OffsetType = VariableSizeAllocationsManager::OffsetType;
+ static constexpr const OffsetType InvalidOffset = VariableSizeAllocationsManager::InvalidOffset;
+
+ ListBasedAllocationStrategy(IMemoryAllocator& Allocator,
+ Uint32 Size) :
+ m_AllocationsMgr(Size, Allocator)
+ {}
+
+ ListBasedAllocationStrategy (const ListBasedAllocationStrategy&) = delete;
+ ListBasedAllocationStrategy (ListBasedAllocationStrategy&&) = delete;
+ ListBasedAllocationStrategy& operator= (const ListBasedAllocationStrategy&) = delete;
+ ListBasedAllocationStrategy& operator= (ListBasedAllocationStrategy&&) = delete;
+
+ void DiscardAllocations(const std::vector<std::pair<OffsetType, OffsetType>>& Allocations, Uint64 FenceValue)
+ {
+ std::lock_guard<std::mutex> Lock(m_ReleaseQueueMtx);
+ for(const auto& Allocation : Allocations)
+ m_ReleaseQueue.emplace_back(Allocation.first, Allocation.second, FenceValue);
+ }
+
+ void ReleaseStaleAllocations(Uint64 LastCompletedFenceValue)
+ {
+ std::lock_guard<std::mutex> MgrLock(m_AllocationsMgrMtx);
+ std::lock_guard<std::mutex> QueueLock(m_ReleaseQueueMtx);
+ while (!m_ReleaseQueue.empty())
+ {
+ auto &FirstAllocation = m_ReleaseQueue.front();
+ if (FirstAllocation.FenceValue <= LastCompletedFenceValue)
+ {
+ m_AllocationsMgr.Free(FirstAllocation.Offset, FirstAllocation.Size);
+ m_ReleaseQueue.pop_front();
+ }
+ else
+ break;
+ }
+ }
+
+ OffsetType Allocate(OffsetType SizeInBytes)
+ {
+ std::lock_guard<std::mutex> Lock(m_AllocationsMgrMtx);
+ return m_AllocationsMgr.Allocate(SizeInBytes);
+ }
+ OffsetType GetSize() const{return m_AllocationsMgr.GetMaxSize();}
+ OffsetType GetUsedSize()const{return m_AllocationsMgr.GetUsedSize();}
+
+private:
+ std::mutex m_AllocationsMgrMtx;
+ VariableSizeAllocationsManager m_AllocationsMgr;
+
+ struct StaleAllocationInfo
+ {
+ const OffsetType Offset;
+ const OffsetType Size;
+ const Uint64 FenceValue;
+ StaleAllocationInfo(OffsetType _Offset,
+ OffsetType _Size,
+ Uint64 _FenceValue) :
+ Offset (_Offset),
+ Size (_Size),
+ FenceValue(_FenceValue)
+ {}
+ };
+ std::deque< StaleAllocationInfo > m_ReleaseQueue;
+ std::mutex m_ReleaseQueueMtx;
+};
+
+// We cannot use global memory manager for dynamic resources because they
+// need to use the same Vulkan buffer
+class VulkanDynamicMemoryManager
+{
+public:
+ using AllocationStategy = ListBasedAllocationStrategy; // or RingBufferAllocationStrategy
+ using OffsetType = AllocationStategy::OffsetType;
+
+ VulkanDynamicMemoryManager(IMemoryAllocator& Allocator,
+ class RenderDeviceVkImpl& DeviceVk,
+ Uint32 Size);
+ ~VulkanDynamicMemoryManager();
+
+ VulkanDynamicMemoryManager (const VulkanDynamicMemoryManager&) = delete;
+ VulkanDynamicMemoryManager (VulkanDynamicMemoryManager&&) = delete;
+ VulkanDynamicMemoryManager& operator= (const VulkanDynamicMemoryManager&) = delete;
+ VulkanDynamicMemoryManager& operator= (VulkanDynamicMemoryManager&&) = delete;
+
+ void ReleaseStaleAllocations(Uint64 LastCompletedFenceValue)
+ {
+ m_AllocationStrategy.ReleaseStaleAllocations(LastCompletedFenceValue);
+ }
+
+ void DiscardAllocations(const std::vector<std::pair<OffsetType, OffsetType>>& Allocations, Uint64 FenceValue)
+ {
+ m_AllocationStrategy.DiscardAllocations(Allocations, FenceValue);
+ }
VkBuffer GetVkBuffer() const{return m_VkBuffer;}
Uint8* GetCPUAddress()const{return m_CPUAddress;}
-private:
- friend class VulkanDynamicHeap;
+ void Destroy();
static constexpr const Uint32 MinAlignment = 1024;
- RingBuffer::OffsetType Allocate(size_t SizeInBytes);
- std::mutex m_RingBuffMtx;
- RingBuffer m_RingBuffer;
- RenderDeviceVkImpl& m_DeviceVk;
+private:
+ friend class VulkanDynamicHeap;
+ OffsetType Allocate(OffsetType SizeInBytes);
+ RenderDeviceVkImpl& m_DeviceVk;
VulkanUtilities::BufferWrapper m_VkBuffer;
VulkanUtilities::DeviceMemoryWrapper m_BufferMemory;
Uint8* m_CPUAddress;
const VkDeviceSize m_DefaultAlignment;
- RingBuffer::OffsetType m_TotalPeakSize = 0;
- RingBuffer::OffsetType m_CurrentFrameSize = 0;
- RingBuffer::OffsetType m_FramePeakSize = 0;
+
+ AllocationStategy m_AllocationStrategy;
+
+ OffsetType m_TotalPeakSize = 0;
};
class VulkanDynamicHeap
{
public:
- VulkanDynamicHeap(VulkanRingBuffer& ParentRingBuffer, std::string HeapName, Uint32 PageSize) :
- m_ParentRingBuffer(ParentRingBuffer),
+ VulkanDynamicHeap(VulkanDynamicMemoryManager& DynamicMemMgr, std::string HeapName, Uint32 PageSize) :
+ m_DynamicMemMgr(DynamicMemMgr),
m_HeapName(std::move(HeapName)),
m_PagSize(PageSize)
{}
@@ -151,21 +278,18 @@ public:
~VulkanDynamicHeap();
VulkanDynamicAllocation Allocate(Uint32 SizeInBytes, Uint32 Alignment);
+ void FinishFrame(Uint64 FenceValue);
- void Reset()
- {
- m_CurrOffset = RingBuffer::InvalidOffset;
- m_AvailableSize = 0;
-
- m_CurrAllocatedSize = 0;
- m_CurrUsedSize = 0;
- }
+ using OffsetType = VulkanDynamicMemoryManager::AllocationStategy::OffsetType;
+ static constexpr OffsetType InvalidOffset = VulkanDynamicMemoryManager::AllocationStategy::InvalidOffset;
private:
- VulkanRingBuffer& m_ParentRingBuffer;
+ VulkanDynamicMemoryManager& m_DynamicMemMgr;
const std::string m_HeapName;
- RingBuffer::OffsetType m_CurrOffset = RingBuffer::InvalidOffset;
+ std::vector<std::pair<OffsetType, OffsetType>> m_Allocations;
+
+ OffsetType m_CurrOffset = InvalidOffset;
const Uint32 m_PagSize;
Uint32 m_AvailableSize = 0;
diff --git a/Graphics/GraphicsEngineVulkan/interface/CommandQueueVk.h b/Graphics/GraphicsEngineVulkan/interface/CommandQueueVk.h
index a7c58096..3a5fb616 100644
--- a/Graphics/GraphicsEngineVulkan/interface/CommandQueueVk.h
+++ b/Graphics/GraphicsEngineVulkan/interface/CommandQueueVk.h
@@ -63,7 +63,9 @@ public:
virtual Uint64 GetCompletedFenceValue() = 0;
/// Blocks execution until all pending GPU commands are complete
- virtual void IdleGPU() = 0;
+
+ /// \return Last completed fence value
+ virtual UINT64 IdleGPU() = 0;
/// Signals the given fence
virtual void SignalFence(VkFence vkFence) = 0;
diff --git a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp
index 93ac343b..104dc021 100644
--- a/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/BufferVkImpl.cpp
@@ -332,7 +332,7 @@ void BufferVkImpl :: Map(IDeviceContext* pContext, MAP_TYPE MapType, Uint32 MapF
#endif
auto& DynAllocation = m_DynamicAllocations[pDeviceContextVk->GetContextId()];
- if ( (MapFlags & MAP_FLAG_DISCARD) != 0 || DynAllocation.pParentDynamicHeap == nullptr )
+ if ( (MapFlags & MAP_FLAG_DISCARD) != 0 || DynAllocation.pDynamicMemMgr == nullptr )
{
DynAllocation = pDeviceContextVk->AllocateDynamicSpace(m_Desc.uiSizeInBytes);
}
@@ -342,10 +342,9 @@ void BufferVkImpl :: Map(IDeviceContext* pContext, MAP_TYPE MapType, Uint32 MapF
// Reuse the same allocation
}
- if (DynAllocation.pParentDynamicHeap != nullptr)
+ if (DynAllocation.pDynamicMemMgr != nullptr)
{
- const auto& DynamicHeap = m_pDevice->GetDynamicHeapRingBuffer();
- auto* CPUAddress = DynamicHeap.GetCPUAddress();
+ auto* CPUAddress = DynAllocation.pDynamicMemMgr->GetCPUAddress();
pMappedData = CPUAddress + DynAllocation.Offset;
}
else
@@ -419,7 +418,7 @@ void BufferVkImpl::Unmap( IDeviceContext* pContext, MAP_TYPE MapType, Uint32 Map
if(m_VulkanBuffer != VK_NULL_HANDLE)
{
auto &DynAlloc = m_DynamicAllocations[CtxId];
- auto vkSrcBuff = DynAlloc.pParentDynamicHeap->GetVkBuffer();
+ auto vkSrcBuff = DynAlloc.pDynamicMemMgr->GetVkBuffer();
pDeviceContextVk->UpdateBufferRegion(this, 0, m_Desc.uiSizeInBytes, vkSrcBuff, DynAlloc.Offset);
}
}
@@ -492,19 +491,18 @@ VkBuffer BufferVkImpl::GetVkBuffer()const
else
{
VERIFY(m_Desc.Usage == USAGE_DYNAMIC, "Dynamic buffer expected");
- return m_pDevice->GetDynamicHeapRingBuffer().GetVkBuffer();
+ return m_pDevice->GetDynamicMemoryManager().GetVkBuffer();
}
}
#ifdef DEVELOPMENT
-void BufferVkImpl::DvpVerifyDynamicAllocation(Uint32 ContextId)const
+void BufferVkImpl::DvpVerifyDynamicAllocation(DeviceContextVkImpl* pCtx)const
{
+ auto ContextId = pCtx->GetContextId();
const auto& DynAlloc = m_DynamicAllocations[ContextId];
- if (DynAlloc.pParentDynamicHeap == nullptr)
- LOG_ERROR_MESSAGE("Dynamic buffer '", m_Desc.Name, "' was not mapped before its first use. Context Id: ", ContextId);
- auto CurrentFrame = m_pDevice->GetCurrentFrameNumber();
- if (DynAlloc.dbgFrameNumber != CurrentFrame)
- LOG_ERROR_MESSAGE("Dynamic allocation is out-of-date. Dynamic buffer '", m_Desc.Name, "' must be mapped in the same frame it is used.");
+ DEV_CHECK_ERR(DynAlloc.pDynamicMemMgr != nullptr, "Dynamic buffer '", m_Desc.Name, "' was not mapped before its first use. Context Id: ", ContextId);
+ auto CurrentFrame = pCtx->GetContextFrameNumber();
+ DEV_CHECK_ERR(DynAlloc.dvpFrameNumber == CurrentFrame, "Dynamic allocation is out-of-date. Dynamic buffer '", m_Desc.Name, "' must be mapped in the same frame it is used.");
}
#endif
diff --git a/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp
index e75d4aaf..6e789ee5 100644
--- a/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/CommandQueueVkImpl.cpp
@@ -90,7 +90,7 @@ Uint64 CommandQueueVkImpl::ExecuteCommandBuffer(VkCommandBuffer cmdBuffer)
return ExecuteCommandBuffer(SubmitInfo);
}
-void CommandQueueVkImpl::IdleGPU()
+Uint64 CommandQueueVkImpl::IdleGPU()
{
std::lock_guard<std::mutex> Lock(m_QueueMutex);
@@ -102,6 +102,7 @@ void CommandQueueVkImpl::IdleGPU()
// For some reason after idling the queue not all fences are signaled
m_pFence->Wait();
m_pFence->Reset(LastCompletedFenceValue);
+ return LastCompletedFenceValue;
}
Uint64 CommandQueueVkImpl::GetCompletedFenceValue()
diff --git a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
index a1e858e5..c8b95867 100644
--- a/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/DeviceContextVkImpl.cpp
@@ -107,9 +107,10 @@ namespace Diligent
Attribs.DynamicDescriptorPoolSize.MaxDescriptorSets,
},
m_NextCmdBuffNumber(0),
+ m_ContextFrameNumber(0),
m_DynamicHeap
{
- pDeviceVkImpl->GetDynamicHeapRingBuffer(),
+ pDeviceVkImpl->GetDynamicMemoryManager(),
GetDynamicHeapName(bIsDeferred, ContextId),
bIsDeferred ? Attribs.DeferredCtxDynamicHeapPageSize : Attribs.ImmediateCtxDynamicHeapPageSize
},
@@ -138,14 +139,14 @@ namespace Diligent
// We need to idle when destroying deferred contexts as well since some resources may still be in use.
pDeviceVkImpl->IdleGPU(true);
- DisposeCurrentCmdBuffer(pDeviceVkImpl->GetNextFenceValue());
+ DisposeCurrentCmdBuffer(m_LastSubmittedFenceValue);
// There must be no resources in the stale resource list. For immediate context, all stale resources must have been
// moved to the release queue by Flush(). For deferred contexts, this should have happened in the last FinishCommandList()
// call.
VERIFY(m_ReleaseQueue.GetStaleResourceCount() == 0, "All stale resources must have been discarded at this point");
VERIFY(m_DynamicDescriptorPool.GetStaleAllocationCount() == 0, "All stale dynamic descriptor set allocations must have been discarded at this point");
- ReleaseStaleContextResources(m_NextCmdBuffNumber, pDeviceVkImpl->GetNextFenceValue(), pDeviceVkImpl->GetCompletedFenceValue());
+ ReleaseStaleContextResources(m_NextCmdBuffNumber, m_LastSubmittedFenceValue, pDeviceVkImpl->GetCompletedFenceValue());
// Since we idled the GPU, all stale resources must have been destroyed now
VERIFY(m_ReleaseQueue.GetPendingReleaseResourceCount() == 0, "All stale resources must have been destroyed at this point");
VERIFY(m_DynamicDescriptorPool.GetPendingReleaseAllocationCount() == 0, "All stale descriptor set allocations must have been destroyed at this point");
@@ -316,7 +317,7 @@ namespace Diligent
{
DynamicBufferPresent = true;
#ifdef DEVELOPMENT
- pBufferVk->DvpVerifyDynamicAllocation(m_ContextId);
+ pBufferVk->DvpVerifyDynamicAllocation(this);
#endif
}
if (!pBufferVk->CheckAccessFlags(VK_ACCESS_VERTEX_ATTRIBUTE_READ_BIT))
@@ -325,7 +326,7 @@ namespace Diligent
// Device context keeps strong references to all vertex buffers.
vkVertexBuffers[slot] = pBufferVk->GetVkBuffer();
- Offsets[slot] = CurrStream.Offset + pBufferVk->GetDynamicOffset(m_ContextId);
+ Offsets[slot] = CurrStream.Offset + pBufferVk->GetDynamicOffset(m_ContextId, this);
}
//GraphCtx.FlushResourceBarriers();
@@ -410,7 +411,7 @@ namespace Diligent
DEV_CHECK_ERR(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 + pBuffVk->GetDynamicOffset(m_ContextId), vkIndexType);
+ m_CommandBuffer.BindIndexBuffer(pBuffVk->GetVkBuffer(), m_IndexDataStartOffset + pBuffVk->GetDynamicOffset(m_ContextId, this), vkIndexType);
}
if (m_State.CommittedVBsUpToDate)
@@ -461,15 +462,15 @@ namespace Diligent
{
#ifdef DEVELOPMENT
if (pBufferVk->GetDesc().Usage == USAGE_DYNAMIC)
- pBufferVk->DvpVerifyDynamicAllocation(m_ContextId);
+ pBufferVk->DvpVerifyDynamicAllocation(this);
#endif
if (!pBufferVk->CheckAccessFlags(VK_ACCESS_INDIRECT_COMMAND_READ_BIT))
BufferMemoryBarrier(*pBufferVk, VK_ACCESS_INDIRECT_COMMAND_READ_BIT);
if ( DrawAttribs.IsIndexed )
- m_CommandBuffer.DrawIndexedIndirect(pBufferVk->GetVkBuffer(), pBufferVk->GetDynamicOffset(m_ContextId) + DrawAttribs.IndirectDrawArgsOffset, 1, 0);
+ m_CommandBuffer.DrawIndexedIndirect(pBufferVk->GetVkBuffer(), pBufferVk->GetDynamicOffset(m_ContextId, this) + DrawAttribs.IndirectDrawArgsOffset, 1, 0);
else
- m_CommandBuffer.DrawIndirect(pBufferVk->GetVkBuffer(), pBufferVk->GetDynamicOffset(m_ContextId) + DrawAttribs.IndirectDrawArgsOffset, 1, 0);
+ m_CommandBuffer.DrawIndirect(pBufferVk->GetVkBuffer(), pBufferVk->GetDynamicOffset(m_ContextId, this) + DrawAttribs.IndirectDrawArgsOffset, 1, 0);
}
}
else
@@ -522,14 +523,14 @@ namespace Diligent
{
#ifdef DEVELOPMENT
if (pBufferVk->GetDesc().Usage == USAGE_DYNAMIC)
- pBufferVk->DvpVerifyDynamicAllocation(m_ContextId);
+ pBufferVk->DvpVerifyDynamicAllocation(this);
#endif
// Buffer memory barries must be executed outside of render pass
if (!pBufferVk->CheckAccessFlags(VK_ACCESS_INDIRECT_COMMAND_READ_BIT))
BufferMemoryBarrier(*pBufferVk, VK_ACCESS_INDIRECT_COMMAND_READ_BIT);
- m_CommandBuffer.DispatchIndirect(pBufferVk->GetVkBuffer(), pBufferVk->GetDynamicOffset(m_ContextId) + DispatchAttrs.DispatchArgsByteOffset);
+ m_CommandBuffer.DispatchIndirect(pBufferVk->GetVkBuffer(), pBufferVk->GetDynamicOffset(m_ContextId, this) + DispatchAttrs.DispatchArgsByteOffset);
}
else
{
@@ -754,12 +755,23 @@ namespace Diligent
++m_State.NumCommands;
}
+ void DeviceContextVkImpl::FinishFrame(bool ForceRelease)
+ {
+ FinishFrame(ForceRelease ? std::numeric_limits<Uint64>::max() : m_pDevice.RawPtr<RenderDeviceVkImpl>()->GetCompletedFenceValue());
+ }
+
void DeviceContextVkImpl::FinishFrame(Uint64 CompletedFenceValue)
{
+ if(GetNumCommandsInCtx() != 0)
+ LOG_ERROR_MESSAGE(m_bIsDeferred ?
+ "There are outstanding commands in the deferred device context when finishing the frame. This is an error and may cause unpredicted behaviour. Close all deferred contexts and execute them before finishing the frame" :
+ "There are outstanding commands in the immediate device context when finishing the frame. This is an error and may cause unpredicted behaviour. Call Flush() to submit all commands for execution before finishing the frame");
+
m_ReleaseQueue.Purge(CompletedFenceValue);
m_UploadHeap.ShrinkMemory();
m_DynamicDescriptorPool.ReleaseStaleAllocations(CompletedFenceValue);
- m_DynamicHeap.Reset();
+ m_DynamicHeap.FinishFrame(m_LastSubmittedFenceValue);
+ Atomics::AtomicIncrement(m_ContextFrameNumber);
}
void DeviceContextVkImpl::ReleaseStaleContextResources(Uint64 SubmittedCmdBufferNumber, Uint64 SubmittedFenceValue, Uint64 CompletedFenceValue)
@@ -814,7 +826,7 @@ namespace Diligent
// Submit command buffer even if there are no commands to release stale resources.
//if (SubmitInfo.commandBufferCount != 0 || SubmitInfo.waitSemaphoreCount !=0 || SubmitInfo.signalSemaphoreCount != 0)
- auto SubmittedFenceValue = pDeviceVkImpl->ExecuteCommandBuffer(SubmitInfo, this, &m_PendingFences);
+ m_LastSubmittedFenceValue = pDeviceVkImpl->ExecuteCommandBuffer(SubmitInfo, this, &m_PendingFences);
m_WaitSemaphores.clear();
m_WaitDstStageMasks.clear();
@@ -823,14 +835,14 @@ namespace Diligent
if (vkCmdBuff != VK_NULL_HANDLE)
{
- DisposeCurrentCmdBuffer(SubmittedFenceValue);
+ DisposeCurrentCmdBuffer(m_LastSubmittedFenceValue);
}
// Release temporary resources that were used by this context while recording the last command buffer
auto SubmittedCmdBuffNumber = m_NextCmdBuffNumber;
Atomics::AtomicIncrement(m_NextCmdBuffNumber);
auto CompletedFenceValue = pDeviceVkImpl->GetCompletedFenceValue();
- ReleaseStaleContextResources(SubmittedCmdBuffNumber, SubmittedFenceValue, CompletedFenceValue);
+ ReleaseStaleContextResources(SubmittedCmdBuffNumber, m_LastSubmittedFenceValue, CompletedFenceValue);
m_State = ContextState{};
m_DescrSetBindInfo.Reset();
@@ -1112,11 +1124,11 @@ namespace Diligent
if (!pDstBuffVk->CheckAccessFlags(VK_ACCESS_TRANSFER_WRITE_BIT))
BufferMemoryBarrier(*pDstBuffVk, VK_ACCESS_TRANSFER_WRITE_BIT);
VkBufferCopy CopyRegion;
- CopyRegion.srcOffset = SrcOffset + pSrcBuffVk->GetDynamicOffset(m_ContextId);
+ CopyRegion.srcOffset = SrcOffset + pSrcBuffVk->GetDynamicOffset(m_ContextId, this);
CopyRegion.dstOffset = DstOffset;
CopyRegion.size = NumBytes;
VERIFY(pDstBuffVk->m_VulkanBuffer != VK_NULL_HANDLE, "Copy destination buffer must not be suballocated");
- VERIFY_EXPR(pDstBuffVk->GetDynamicOffset(m_ContextId) == 0);
+ VERIFY_EXPR(pDstBuffVk->GetDynamicOffset(m_ContextId, this) == 0);
m_CommandBuffer.CopyBuffer(pSrcBuffVk->GetVkBuffer(), pDstBuffVk->GetVkBuffer(), 1, &CopyRegion);
++m_State.NumCommands;
}
@@ -1275,9 +1287,10 @@ namespace Diligent
SubmitInfo.pCommandBuffers = &vkCmdBuff;
auto pDeviceVkImpl = m_pDevice.RawPtr<RenderDeviceVkImpl>();
VERIFY_EXPR(m_PendingFences.empty());
- auto SubmittedFenceValue = pDeviceVkImpl->ExecuteCommandBuffer(SubmitInfo, this, nullptr);
-
auto pDeferredCtxVkImpl = pDeferredCtx.RawPtr<DeviceContextVkImpl>();
+ auto SubmittedFenceValue = pDeviceVkImpl->ExecuteCommandBuffer(SubmitInfo, this, nullptr);
+ pDeferredCtxVkImpl->m_LastSubmittedFenceValue = SubmittedFenceValue;
+
// It is OK to dispose command buffer from another thread. We are not going to
// record any commands and only need to add the buffer to the queue
pDeferredCtxVkImpl->DisposeVkCmdBuffer(vkCmdBuff, SubmittedFenceValue);
@@ -1356,7 +1369,7 @@ namespace Diligent
EnsureVkCmdBuffer();
VERIFY(BufferVk.m_VulkanBuffer != VK_NULL_HANDLE, "Cannot transition suballocated buffer");
- VERIFY_EXPR(BufferVk.GetDynamicOffset(m_ContextId) == 0);
+ VERIFY_EXPR(BufferVk.GetDynamicOffset(m_ContextId, this) == 0);
auto vkBuff = BufferVk.GetVkBuffer();
m_CommandBuffer.BufferMemoryBarrier(vkBuff, BufferVk.m_AccessFlags, NewAccessFlags);
BufferVk.SetAccessFlags(NewAccessFlags);
@@ -1392,11 +1405,9 @@ namespace Diligent
VulkanDynamicAllocation DeviceContextVkImpl::AllocateDynamicSpace(Uint32 SizeInBytes)
{
- auto *pDeviceVkImpl = m_pDevice.RawPtr<RenderDeviceVkImpl>();
-
auto DynAlloc = m_DynamicHeap.Allocate(SizeInBytes, 0);
-#ifdef _DEBUG
- DynAlloc.dbgFrameNumber = pDeviceVkImpl->GetCurrentFrameNumber();
+#ifdef DEVELOPMENT
+ DynAlloc.dvpFrameNumber = m_ContextFrameNumber;
#endif
return DynAlloc;
}
diff --git a/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp b/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp
index 6ee46d16..e34a9305 100644
--- a/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/PipelineLayout.cpp
@@ -506,7 +506,7 @@ void PipelineLayout::BindDescriptorSetsWithDynamicOffsets(DeviceContextVkImpl*
VERIFY_EXPR(BindInfo.DynamicOffsets.size() >= BindInfo.DynamicOffsetCount);
#endif
- auto NumOffsetsWritten = BindInfo.pResourceCache->GetDynamicBufferOffsets(pCtxVkImpl->GetContextId(), BindInfo.DynamicOffsets);
+ auto NumOffsetsWritten = BindInfo.pResourceCache->GetDynamicBufferOffsets(pCtxVkImpl, BindInfo.DynamicOffsets);
VERIFY_EXPR(NumOffsetsWritten == BindInfo.DynamicOffsetCount);
auto& CmdBuffer = pCtxVkImpl->GetCommandBuffer();
diff --git a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp
index 6095b2e8..476766aa 100644
--- a/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/RenderDeviceVkImpl.cpp
@@ -91,7 +91,7 @@ RenderDeviceVkImpl :: RenderDeviceVkImpl(IReferenceCounters*
m_TransientCmdPoolMgr(*m_LogicalVkDevice, pCmdQueue->GetQueueFamilyIndex(), VK_COMMAND_POOL_CREATE_TRANSIENT_BIT),
m_MemoryMgr("Global resource memory manager", *m_LogicalVkDevice, *m_PhysicalDevice, GetRawAllocator(), CreationAttribs.DeviceLocalMemoryPageSize, CreationAttribs.HostVisibleMemoryPageSize, CreationAttribs.DeviceLocalMemoryReserveSize, CreationAttribs.HostVisibleMemoryReserveSize),
m_ReleaseQueue(GetRawAllocator()),
- m_DynamicHeapRingBuffer
+ m_DynamicMemoryManager
{
GetRawAllocator(),
*this,
@@ -110,7 +110,7 @@ RenderDeviceVkImpl :: RenderDeviceVkImpl(IReferenceCounters*
RenderDeviceVkImpl::~RenderDeviceVkImpl()
{
// Explicitly destroy dynamic heap
- m_DynamicHeapRingBuffer.Destroy();
+ m_DynamicMemoryManager.Destroy();
// Finish current frame. This will release resources taken by previous frames, and
// will move all stale resources to the release queues. The resources will not be
// release until the next call to FinishFrame()
@@ -209,7 +209,8 @@ void RenderDeviceVkImpl::SubmitCommandBuffer(const VkSubmitInfo& SubmitInfo,
std::vector<std::pair<Uint64, RefCntAutoPtr<IFence> > >* pFences // List of fences to signal
)
{
- std::lock_guard<std::mutex> LockGuard(m_CmdQueueMutex);
+ // The lock is required to atomically update m_NextCmdBuffNumber
+ std::lock_guard<std::mutex> LockGuard(m_SubmitCmdBufferMutex);
auto NextFenceValue = m_pCommandQueue->GetNextFenceValue();
// Submit the command list to the queue
SubmittedFenceValue = m_pCommandQueue->ExecuteCommandBuffer(SubmitInfo);
@@ -265,11 +266,9 @@ Uint64 RenderDeviceVkImpl::IdleGPU(bool ReleaseStaleObjects)
Uint64 SubmittedCmdBuffNumber = 0;
{
- // Lock the command queue to avoid other threads interfering with the GPU
- std::lock_guard<std::mutex> LockGuard(m_CmdQueueMutex);
- SubmittedFenceValue = m_pCommandQueue->GetNextFenceValue();
- // CommandQueueVkImpl::IdleGPU increments next fence value
- m_pCommandQueue->IdleGPU();
+ // CommandQueueVkImpl::IdleGPU increments next fence value and returns
+ // the previous value
+ SubmittedFenceValue = m_pCommandQueue->IdleGPU();
m_LogicalVkDevice->WaitIdle();
@@ -311,26 +310,6 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources)
{
auto CompletedFenceValue = ReleaseAllResources ? std::numeric_limits<Uint64>::max() : GetCompletedFenceValue();
- {
- if (auto pImmediateCtx = m_wpImmediateContext.Lock())
- {
- auto pImmediateCtxVk = pImmediateCtx.RawPtr<DeviceContextVkImpl>();
- if(pImmediateCtxVk->GetNumCommandsInCtx() != 0)
- LOG_ERROR_MESSAGE("There are outstanding commands in the immediate device context when finishing the frame. This is an error and may cause unpredicted behaviour. Call Flush() to submit all commands for execution before finishing the frame");
- pImmediateCtxVk->FinishFrame(ReleaseAllResources);
- }
-
- for (auto wpDeferredCtx : m_wpDeferredContexts)
- {
- if (auto pDeferredCtx = wpDeferredCtx.Lock())
- {
- auto pDeferredCtxVk = pDeferredCtx.RawPtr<DeviceContextVkImpl>();
- if(pDeferredCtxVk->GetNumCommandsInCtx() != 0)
- LOG_ERROR_MESSAGE("There are outstanding commands in the deferred device context when finishing the frame. This is an error and may cause unpredicted behaviour. Close all deferred contexts and execute them before finishing the frame");
- pDeferredCtxVk->FinishFrame(CompletedFenceValue);
- }
- }
- }
Uint64 SubmittedFenceValue = 0;
Uint64 SubmittedCmdBuffNumber = 0;
@@ -344,7 +323,7 @@ void RenderDeviceVkImpl::FinishFrame(bool ReleaseAllResources)
// until the GPU is finished with the current frame
ProcessStaleResources(SubmittedCmdBuffNumber, SubmittedFenceValue, CompletedFenceValue);
- m_DynamicHeapRingBuffer.FinishFrame(SubmittedFenceValue, CompletedFenceValue);
+ m_DynamicMemoryManager.ReleaseStaleAllocations(CompletedFenceValue);
Atomics::AtomicIncrement(m_FrameNumber);
}
diff --git a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp
index c4d57b54..82039178 100644
--- a/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/ShaderResourceCacheVk.cpp
@@ -330,8 +330,10 @@ VkDescriptorImageInfo ShaderResourceCacheVk::Resource::GetSamplerDescriptorWrite
return DescrImgInfo;
}
-Uint32 ShaderResourceCacheVk::GetDynamicBufferOffsets(Uint32 CtxId, std::vector<uint32_t>& Offsets)const
+Uint32 ShaderResourceCacheVk::GetDynamicBufferOffsets(DeviceContextVkImpl *pCtxVkImpl, std::vector<uint32_t>& Offsets)const
{
+ auto CtxId = pCtxVkImpl->GetContextId();
+
// If any of the sets being bound include dynamic uniform or storage buffers, then
// pDynamicOffsets includes one element for each array element in each dynamic descriptor
// type binding in each set. Values are taken from pDynamicOffsets in an order such that
@@ -353,7 +355,7 @@ Uint32 ShaderResourceCacheVk::GetDynamicBufferOffsets(Uint32 CtxId, std::vector<
break;
const auto* pBufferVk = Res.pObject.RawPtr<const BufferVkImpl>();
- auto Offset = pBufferVk->GetDynamicOffset(CtxId);
+ auto Offset = pBufferVk->GetDynamicOffset(CtxId, pCtxVkImpl);
Offsets[OffsetInd++] = Offset;
++res;
@@ -367,7 +369,7 @@ Uint32 ShaderResourceCacheVk::GetDynamicBufferOffsets(Uint32 CtxId, std::vector<
const auto* pBufferVkView = Res.pObject.RawPtr<const BufferViewVkImpl>();
const auto* pBufferVk = pBufferVkView->GetBufferVk();
- auto Offset = pBufferVk->GetDynamicOffset(CtxId);
+ auto Offset = pBufferVk->GetDynamicOffset(CtxId, pCtxVkImpl);
Offsets[OffsetInd++] = Offset;
++res;
diff --git a/Graphics/GraphicsEngineVulkan/src/SwapChainVkImpl.cpp b/Graphics/GraphicsEngineVulkan/src/SwapChainVkImpl.cpp
index e1d1810e..3f59f2ee 100644
--- a/Graphics/GraphicsEngineVulkan/src/SwapChainVkImpl.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/SwapChainVkImpl.cpp
@@ -460,6 +460,7 @@ void SwapChainVkImpl::Present(Uint32 SyncInterval)
VERIFY(Result == VK_SUCCESS, "Present failed");
}
+ pImmediateCtxVk->FinishFrame(false);
pDeviceVk->FinishFrame();
if (!m_IsMinimized)
diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp
index 4dc335d3..429c174c 100644
--- a/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/VulkanDynamicHeap.cpp
@@ -37,12 +37,12 @@ static VkDeviceSize GetDefaultAlignment(const VulkanUtilities::VulkanPhysicalDev
return std::max(std::max(Limits.minUniformBufferOffsetAlignment, Limits.minTexelBufferOffsetAlignment), Limits.minStorageBufferOffsetAlignment);
}
-VulkanRingBuffer::VulkanRingBuffer(IMemoryAllocator& Allocator,
- RenderDeviceVkImpl& DeviceVk,
- Uint32 Size) :
- m_RingBuffer(Size, Allocator),
+VulkanDynamicMemoryManager::VulkanDynamicMemoryManager(IMemoryAllocator& Allocator,
+ RenderDeviceVkImpl& DeviceVk,
+ Uint32 Size) :
m_DeviceVk(DeviceVk),
- m_DefaultAlignment(GetDefaultAlignment(DeviceVk.GetPhysicalDevice()))
+ m_DefaultAlignment(GetDefaultAlignment(DeviceVk.GetPhysicalDevice())),
+ m_AllocationStrategy(Allocator, Size)
{
VERIFY( (Size & (MinAlignment-1)) == 0, "Heap size is not min aligned");
VkBufferCreateInfo VkBuffCI = {};
@@ -100,7 +100,7 @@ VulkanRingBuffer::VulkanRingBuffer(IMemoryAllocator& Allocator,
LOG_INFO_MESSAGE("GPU dynamic heap created. Total buffer size: ", FormatMemorySize(Size, 2) );
}
-void VulkanRingBuffer::Destroy()
+void VulkanDynamicMemoryManager::Destroy()
{
if (m_VkBuffer)
{
@@ -111,44 +111,40 @@ void VulkanRingBuffer::Destroy()
m_CPUAddress = nullptr;
}
-VulkanRingBuffer::~VulkanRingBuffer()
+VulkanDynamicMemoryManager::~VulkanDynamicMemoryManager()
{
VERIFY(m_BufferMemory == VK_NULL_HANDLE && m_VkBuffer == VK_NULL_HANDLE, "Vulkan resources must be explcitly released with Destroy()");
- LOG_INFO_MESSAGE("Dynamic heap ring buffer usage stats:\n"
- " Total size: ", FormatMemorySize(m_RingBuffer.GetMaxSize(), 2),
- ". Peak allocated size: ", FormatMemorySize(m_TotalPeakSize, 2, m_RingBuffer.GetMaxSize()),
- ". Peak frame size: ", FormatMemorySize(m_FramePeakSize, 2, m_RingBuffer.GetMaxSize()),
- ". Peak utilization: ", std::fixed, std::setprecision(1), static_cast<double>(m_TotalPeakSize) / static_cast<double>(std::max(m_RingBuffer.GetMaxSize(), size_t{1})) * 100.0, '%' );
+ auto Size = m_AllocationStrategy.GetSize();
+ LOG_INFO_MESSAGE("Dynamic memory manager usage stats:\n"
+ " Total size: ", FormatMemorySize(Size, 2),
+ ". Peak allocated size: ", FormatMemorySize(m_TotalPeakSize, 2, Size),
+ ". Peak utilization: ", std::fixed, std::setprecision(1), static_cast<double>(m_TotalPeakSize) / static_cast<double>(std::max(Size, size_t{1})) * 100.0, '%' );
}
-RingBuffer::OffsetType VulkanRingBuffer::Allocate(size_t SizeInBytes)
+VulkanDynamicMemoryManager::OffsetType VulkanDynamicMemoryManager::Allocate(OffsetType SizeInBytes)
{
VERIFY( (SizeInBytes & (MinAlignment-1)) == 0, "Allocation size is not minimally aligned" );
- if (SizeInBytes > m_RingBuffer.GetMaxSize())
+ if (SizeInBytes > m_AllocationStrategy.GetSize())
{
- LOG_ERROR("Requested dynamic allocation size ", SizeInBytes, " exceeds maximum ring buffer size ", m_RingBuffer.GetMaxSize(), ". The app should increase dynamic heap size.");
- return RingBuffer::InvalidOffset;
+ LOG_ERROR("Requested dynamic allocation size ", SizeInBytes, " exceeds maximum dynamic memory size ", m_AllocationStrategy.GetSize(), ". The app should increase dynamic heap size.");
+ return AllocationStategy::InvalidOffset;
}
-
- std::lock_guard<std::mutex> Lock(m_RingBuffMtx);
- RingBuffer::OffsetType Offset = m_RingBuffer.Allocate(SizeInBytes);
- if(Offset == RingBuffer::InvalidOffset)
+ auto Offset = m_AllocationStrategy.Allocate(SizeInBytes);
+ if(Offset == AllocationStategy::InvalidOffset)
{
- // Failed to allocate space in the ring buffer. Try to wait for GPU to finish pening frames
- // to release some space
+ // Allocation failed. Try to wait for GPU to finish pending 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)
+ while(Offset == AllocationStategy::InvalidOffset && IdleDuration < MaxIdleDuration)
{
auto LastCompletedFenceValue = m_DeviceVk.GetCompletedFenceValue();
- m_RingBuffer.ReleaseCompletedFrames(LastCompletedFenceValue);
-
- Offset = m_RingBuffer.Allocate(SizeInBytes);
- if (Offset == RingBuffer::InvalidOffset)
+ m_AllocationStrategy.ReleaseStaleAllocations(LastCompletedFenceValue);
+ Offset = m_AllocationStrategy.Allocate(SizeInBytes);
+ if (Offset == AllocationStategy::InvalidOffset)
{
std::this_thread::sleep_for(SleepPeriod);
++SleepIterations;
@@ -158,48 +154,47 @@ RingBuffer::OffsetType VulkanRingBuffer::Allocate(size_t SizeInBytes)
IdleDuration = std::chrono::duration_cast<std::chrono::duration<double>>(CurrTime - StartIdleTime);
}
- if(Offset == RingBuffer::InvalidOffset)
+ if(Offset == AllocationStategy::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");
+ // Last resort - idle GPU (there seems to be a driver bug: vkQueueWaitIdle() deadlocks and never returns)
+ //m_DeviceVk.IdleGPU(true);
+ //auto LastCompletedFenceValue = m_DeviceVk.GetCompletedFenceValue();
+ //m_AllocationStrategy.ReleaseStaleAllocations(LastCompletedFenceValue);
+ //Offset = m_AllocationStrategy.Allocate(SizeInBytes);
+ if (Offset == AllocationStategy::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 heap 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 forced idling the GPU. Increase the size of the heap 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");
+ LOG_WARNING_MESSAGE("Space in dynamic heap is almost exausted forcing mid-frame shrinkage. Increase the size of the heap 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");
+ LOG_WARNING_MESSAGE("Space in dynamic heap is almost exausted. Allocation forced wait time of ", std::fixed, std::setprecision(1), IdleDuration.count()*1000.0, " ms. Increase the size of the heap by setting EngineVkAttribs::DynamicHeapSize to a greater value or optimize dynamic resource usage");
}
}
}
- if (Offset != RingBuffer::InvalidOffset)
+ if (Offset != AllocationStategy::InvalidOffset)
{
- m_CurrentFrameSize += SizeInBytes;
- m_FramePeakSize = std::max(m_FramePeakSize, m_CurrentFrameSize);
- m_TotalPeakSize = std::max(m_TotalPeakSize, m_RingBuffer.GetUsedSize());
+ m_TotalPeakSize = std::max(m_TotalPeakSize, m_AllocationStrategy.GetUsedSize());
}
return Offset;
}
-void VulkanRingBuffer::FinishFrame(Uint64 FenceValue, Uint64 LastCompletedFenceValue)
-{
- //
- // Deferred contexts must not map dynamic buffers across several frames!
- //
-
- std::lock_guard<std::mutex> Lock(m_RingBuffMtx);
- m_RingBuffer.FinishCurrentFrame(FenceValue);
- m_RingBuffer.ReleaseCompletedFrames(LastCompletedFenceValue);
- m_CurrentFrameSize = 0;
-}
VulkanDynamicAllocation VulkanDynamicHeap::Allocate(Uint32 SizeInBytes, Uint32 Alignment)
{
if (Alignment == 0)
- Alignment = static_cast<Uint32>(m_ParentRingBuffer.m_DefaultAlignment);
+ Alignment = static_cast<Uint32>(m_DynamicMemMgr.m_DefaultAlignment);
const Uint32 AlignmentMask = Alignment - 1;
// Assert that it's a power of two.
@@ -208,23 +203,23 @@ VulkanDynamicAllocation VulkanDynamicHeap::Allocate(Uint32 SizeInBytes, Uint32 A
// Align the allocation
Uint32 AlignedSize = (SizeInBytes + AlignmentMask) & ~AlignmentMask;
- //
- // Deferred contexts must not map dynamic buffers across several frames!
- //
- auto Offset = RingBuffer::InvalidOffset;
- if(AlignedSize > m_PagSize)
+ auto Offset = InvalidOffset;
+ if(AlignedSize > m_PagSize/2)
{
- // Allocate directly from the ring buffer
- Offset = m_ParentRingBuffer.Allocate(AlignedSize);
+ AlignedSize = (SizeInBytes + VulkanDynamicMemoryManager::MinAlignment) & ~(VulkanDynamicMemoryManager::MinAlignment-1);
+ // Allocate directly from the memory manager
+ Offset = m_DynamicMemMgr.Allocate(AlignedSize);
+ m_Allocations.emplace_back(Offset, AlignedSize);
}
else
{
- if(m_CurrOffset == RingBuffer::InvalidOffset || AlignedSize > m_AvailableSize)
+ if(m_CurrOffset == InvalidOffset || AlignedSize > m_AvailableSize)
{
- m_CurrOffset = m_ParentRingBuffer.Allocate(m_PagSize);
+ m_CurrOffset = m_DynamicMemMgr.Allocate(m_PagSize);
+ m_Allocations.emplace_back(m_CurrOffset, m_PagSize);
m_AvailableSize = m_PagSize;
}
- if(m_CurrOffset != RingBuffer::InvalidOffset)
+ if(m_CurrOffset != InvalidOffset)
{
Offset = m_CurrOffset;
m_AvailableSize -= AlignedSize;
@@ -233,23 +228,35 @@ VulkanDynamicAllocation VulkanDynamicHeap::Allocate(Uint32 SizeInBytes, Uint32 A
}
// Every device context uses its own dynamic heap, so there is no need to lock
- if(Offset != RingBuffer::InvalidOffset)
+ if(Offset != InvalidOffset)
{
m_CurrAllocatedSize += AlignedSize;
m_CurrUsedSize += SizeInBytes;
m_PeakAllocatedSize = std::max(m_PeakAllocatedSize, m_CurrAllocatedSize);
m_PeakUsedSize = std::max(m_PeakUsedSize, m_CurrUsedSize);
- return VulkanDynamicAllocation{ m_ParentRingBuffer, Offset, SizeInBytes };
+ return VulkanDynamicAllocation{ m_DynamicMemMgr, Offset, SizeInBytes };
}
else
return VulkanDynamicAllocation{};
}
+void VulkanDynamicHeap::FinishFrame(Uint64 FenceValue)
+{
+ m_DynamicMemMgr.DiscardAllocations(m_Allocations, FenceValue);
+ m_Allocations.clear();
+
+ m_CurrOffset = InvalidOffset;
+ m_AvailableSize = 0;
+
+ m_CurrAllocatedSize = 0;
+ m_CurrUsedSize = 0;
+}
+
VulkanDynamicHeap::~VulkanDynamicHeap()
{
LOG_INFO_MESSAGE(m_HeapName, " usage stats:\n"
- " Peak used/peak allocated size: ", FormatMemorySize(m_PeakUsedSize, 2, m_PeakAllocatedSize), '/', FormatMemorySize(m_PeakAllocatedSize, 2, m_PeakAllocatedSize),
+ " Peak used/peak allocated size: ", FormatMemorySize(m_PeakUsedSize, 2, m_PeakAllocatedSize), '/', FormatMemorySize(m_PeakAllocatedSize, 2, m_PeakAllocatedSize),
". Peak utilization: ", std::fixed, std::setprecision(1), static_cast<double>(m_PeakUsedSize) / static_cast<double>(std::max(m_PeakAllocatedSize, 1U)) * 100.0, '%');
}
diff --git a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp
index 38fd781f..b4660819 100644
--- a/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp
+++ b/Graphics/GraphicsEngineVulkan/src/VulkanUtilities/VulkanMemoryManager.cpp
@@ -202,10 +202,10 @@ void VulkanMemoryManager::OnFreeAllocation(VkDeviceSize Size, bool IsHostVisble)
VulkanMemoryManager::~VulkanMemoryManager()
{
LOG_INFO_MESSAGE("VulkanMemoryManager '", m_MgrName, "' stats:\n"
- " Peak used/peak allocated device-local memory size: ",
+ " Peak used/peak allocated device-local memory size: ",
Diligent::FormatMemorySize(m_PeakUsedSize[0], 2, m_PeakAllocatedSize[0]), "/",
Diligent::FormatMemorySize( m_PeakAllocatedSize[0], 2, m_PeakAllocatedSize[0]),
- "\n Peak used/peak allocated host-visible memory size: ",
+ "\n Peak used/peak allocated host-visible memory size: ",
Diligent::FormatMemorySize(m_PeakUsedSize[1], 2, m_PeakAllocatedSize[1]), "/",
Diligent::FormatMemorySize(m_PeakAllocatedSize[1], 2, m_PeakAllocatedSize[1]));