diff options
| author | Egor Yusov <egor.yusov@gmail.com> | 2017-11-13 02:54:50 +0000 |
|---|---|---|
| committer | Egor Yusov <egor.yusov@gmail.com> | 2017-11-13 02:54:50 +0000 |
| commit | 8e24a0919d30f939140e776bc5e64bd77767e99c (patch) | |
| tree | bfa1c37385cafe7d6c192ed0c8eb936aa828cb5a /Graphics/GraphicsEngineD3D12 | |
| parent | Merge from dev branch (diff) | |
| download | DiligentCore-8e24a0919d30f939140e776bc5e64bd77767e99c.tar.gz DiligentCore-8e24a0919d30f939140e776bc5e64bd77767e99c.zip | |
Updated to Diligent Engine 2.1
Diffstat (limited to 'Graphics/GraphicsEngineD3D12')
53 files changed, 1712 insertions, 728 deletions
diff --git a/Graphics/GraphicsEngineD3D12/build/Windows.Shared/GraphicsEngineD3D12.Shared.vcxitems b/Graphics/GraphicsEngineD3D12/build/Windows.Shared/GraphicsEngineD3D12.Shared.vcxitems index c67117a6..74cd1dbe 100644 --- a/Graphics/GraphicsEngineD3D12/build/Windows.Shared/GraphicsEngineD3D12.Shared.vcxitems +++ b/Graphics/GraphicsEngineD3D12/build/Windows.Shared/GraphicsEngineD3D12.Shared.vcxitems @@ -22,6 +22,7 @@ <ClInclude Include="$(MSBuildThisFileDirectory)..\..\include\CommandContext.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)..\..\include\CommandListD3D12Impl.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)..\..\include\CommandListManager.h" /> + <ClInclude Include="$(MSBuildThisFileDirectory)..\..\include\CommandQueueD3D12Impl.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)..\..\include\D3D12ResourceBase.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)..\..\include\D3D12TypeConversions.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)..\..\include\D3D12TypeDefinitions.h" /> @@ -46,6 +47,8 @@ <ClInclude Include="$(MSBuildThisFileDirectory)..\..\include\TextureViewD3D12Impl.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)..\..\interface\BufferD3D12.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)..\..\interface\BufferViewD3D12.h" /> + <ClInclude Include="$(MSBuildThisFileDirectory)..\..\interface\CommandQueueD3D12.h" /> + <ClInclude Include="$(MSBuildThisFileDirectory)..\..\interface\DeviceContextD3D12.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)..\..\interface\PipelineStateD3D12.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)..\..\interface\RenderDeviceD3D12.h" /> <ClInclude Include="$(MSBuildThisFileDirectory)..\..\interface\RenderDeviceFactoryD3D12.h" /> @@ -123,6 +126,7 @@ </FxCompile> </ItemGroup> <ItemGroup> + <None Include="$(MSBuildThisFileDirectory)..\..\readme.md" /> <None Include="$(MSBuildThisFileDirectory)..\..\shaders\GenerateMips\GenerateMipsCS.hlsli"> <FileType>Document</FileType> </None> @@ -132,6 +136,7 @@ <ClCompile Include="$(MSBuildThisFileDirectory)..\..\src\BufferViewD3D12Impl.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)..\..\src\CommandContext.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)..\..\src\CommandListManager.cpp" /> + <ClCompile Include="$(MSBuildThisFileDirectory)..\..\src\CommandQueueD3D12Impl.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)..\..\src\D3D12ResourceBase.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)..\..\src\D3D12TypeConversions.cpp" /> <ClCompile Include="$(MSBuildThisFileDirectory)..\..\src\D3D12Utils.cpp" /> diff --git a/Graphics/GraphicsEngineD3D12/build/Windows.Shared/GraphicsEngineD3D12.Shared.vcxitems.filters b/Graphics/GraphicsEngineD3D12/build/Windows.Shared/GraphicsEngineD3D12.Shared.vcxitems.filters index 30e44dcd..1bbf5b73 100644 --- a/Graphics/GraphicsEngineD3D12/build/Windows.Shared/GraphicsEngineD3D12.Shared.vcxitems.filters +++ b/Graphics/GraphicsEngineD3D12/build/Windows.Shared/GraphicsEngineD3D12.Shared.vcxitems.filters @@ -132,6 +132,15 @@ <ClInclude Include="$(MSBuildThisFileDirectory)..\..\interface\TextureViewD3D12.h"> <Filter>interface</Filter> </ClInclude> + <ClInclude Include="$(MSBuildThisFileDirectory)..\..\interface\DeviceContextD3D12.h"> + <Filter>interface</Filter> + </ClInclude> + <ClInclude Include="$(MSBuildThisFileDirectory)..\..\interface\CommandQueueD3D12.h"> + <Filter>interface</Filter> + </ClInclude> + <ClInclude Include="$(MSBuildThisFileDirectory)..\..\include\CommandQueueD3D12Impl.h"> + <Filter>include</Filter> + </ClInclude> </ItemGroup> <ItemGroup> <FxCompile Include="$(MSBuildThisFileDirectory)..\..\shaders\GenerateMips\GenerateMipsGammaCS.hlsl"> @@ -163,6 +172,7 @@ <None Include="$(MSBuildThisFileDirectory)..\..\shaders\GenerateMips\GenerateMipsCS.hlsli"> <Filter>Shaders\GenerateMips</Filter> </None> + <None Include="$(MSBuildThisFileDirectory)..\..\readme.md" /> </ItemGroup> <ItemGroup> <ClCompile Include="$(MSBuildThisFileDirectory)..\..\src\BufferD3D12Impl.cpp"> @@ -240,5 +250,8 @@ <ClCompile Include="$(MSBuildThisFileDirectory)..\..\src\TextureViewD3D12Impl.cpp"> <Filter>src</Filter> </ClCompile> + <ClCompile Include="$(MSBuildThisFileDirectory)..\..\src\CommandQueueD3D12Impl.cpp"> + <Filter>src</Filter> + </ClCompile> </ItemGroup> </Project>
\ No newline at end of file diff --git a/Graphics/GraphicsEngineD3D12/include/BufferD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/BufferD3D12Impl.h index 289bf152..6333f112 100644 --- a/Graphics/GraphicsEngineD3D12/include/BufferD3D12Impl.h +++ b/Graphics/GraphicsEngineD3D12/include/BufferD3D12Impl.h @@ -39,15 +39,20 @@ namespace Diligent class FixedBlockMemoryAllocator; /// Implementation of the Diligent::IBufferD3D12 interface -class BufferD3D12Impl : public BufferBase<IBufferD3D12, BufferViewD3D12Impl, FixedBlockMemoryAllocator, FixedBlockMemoryAllocator>, public D3D12ResourceBase +class BufferD3D12Impl : public BufferBase<IBufferD3D12, BufferViewD3D12Impl, FixedBlockMemoryAllocator>, public D3D12ResourceBase { public: - typedef BufferBase<IBufferD3D12, BufferViewD3D12Impl, FixedBlockMemoryAllocator, FixedBlockMemoryAllocator> TBufferBase; - BufferD3D12Impl(FixedBlockMemoryAllocator &BufferObjMemAllocator, + typedef BufferBase<IBufferD3D12, BufferViewD3D12Impl, FixedBlockMemoryAllocator> TBufferBase; + BufferD3D12Impl(IReferenceCounters *pRefCounters, FixedBlockMemoryAllocator &BuffViewObjMemAllocator, class RenderDeviceD3D12Impl *pDeviceD3D12, const BufferDesc& BuffDesc, const BufferData &BuffData = BufferData()); + BufferD3D12Impl(IReferenceCounters *pRefCounters, + FixedBlockMemoryAllocator &BuffViewObjMemAllocator, + class RenderDeviceD3D12Impl *pDeviceD3D12, + const BufferDesc& BuffDesc, + ID3D12Resource *pd3d12Buffer); ~BufferD3D12Impl(); virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface )override; @@ -55,7 +60,7 @@ public: virtual void UpdateData( IDeviceContext *pContext, Uint32 Offset, Uint32 Size, const PVoid pData )override; virtual void CopyData( IDeviceContext *pContext, IBuffer *pSrcBuffer, Uint32 SrcOffset, Uint32 DstOffset, Uint32 Size )override; virtual void Map( IDeviceContext *pContext, MAP_TYPE MapType, Uint32 MapFlags, PVoid &pMappedData )override; - virtual void Unmap( IDeviceContext *pContext, MAP_TYPE MapType )override; + virtual void Unmap( IDeviceContext *pContext, MAP_TYPE MapType, Uint32 MapFlags )override; #ifdef _DEBUG void DbgVerifyDynamicAllocation(Uint32 ContextId); @@ -63,21 +68,36 @@ public: virtual ID3D12Resource *GetD3D12Buffer(size_t &DataStartByteOffset, Uint32 ContextId)override final { - if(m_Desc.Usage == USAGE_DYNAMIC) + auto *pd3d12Resource = GetD3D12Resource(); + if(pd3d12Resource != 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 pd3d12Resource; + } + 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; } - else - { - DataStartByteOffset = 0; - return GetD3D12Resource(); - } } + virtual void* GetNativeHandle()override final + { + VERIFY(GetD3D12Resource() != nullptr, "The buffer is dynamic and has no pointer to D3D12 resource"); + size_t DataStartByteOffset = 0; + auto *pd3d12Buffer = GetD3D12Buffer(DataStartByteOffset, 0); + VERIFY(DataStartByteOffset == 0, "0 offset expected"); + return pd3d12Buffer; + } + + virtual void SetD3D12ResourceState(D3D12_RESOURCE_STATES state)override final{ SetState(state); } + D3D12_GPU_VIRTUAL_ADDRESS GetGPUAddress(Uint32 ContextId) { if(m_Desc.Usage == USAGE_DYNAMIC) @@ -104,7 +124,7 @@ private: DescriptorHeapAllocation m_CBVDescriptorAllocation; #ifdef _DEBUG - std::vector<MAP_TYPE, STDAllocatorRawMem<MAP_TYPE> > m_DbgMapType; + std::vector< std::pair<MAP_TYPE, Uint32>, STDAllocatorRawMem<std::pair<MAP_TYPE, Uint32>> > m_DbgMapType; #endif friend class DeviceContextD3D12Impl; diff --git a/Graphics/GraphicsEngineD3D12/include/BufferViewD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/BufferViewD3D12Impl.h index 0c102f41..45c9007e 100644 --- a/Graphics/GraphicsEngineD3D12/include/BufferViewD3D12Impl.h +++ b/Graphics/GraphicsEngineD3D12/include/BufferViewD3D12Impl.h @@ -36,12 +36,12 @@ namespace Diligent class FixedBlockMemoryAllocator; /// Implementation of the Diligent::IBufferViewD3D12 interface -class BufferViewD3D12Impl : public BufferViewBase<IBufferViewD3D12, FixedBlockMemoryAllocator> +class BufferViewD3D12Impl : public BufferViewBase<IBufferViewD3D12> { public: - typedef BufferViewBase<IBufferViewD3D12, FixedBlockMemoryAllocator> TBufferViewBase; + typedef BufferViewBase<IBufferViewD3D12> TBufferViewBase; - BufferViewD3D12Impl( FixedBlockMemoryAllocator &BuffViewAllocator, + BufferViewD3D12Impl( IReferenceCounters *pRefCounters, IRenderDevice *pDevice, const BufferViewDesc& ViewDesc, class IBuffer *pBuffer, diff --git a/Graphics/GraphicsEngineD3D12/include/CommandContext.h b/Graphics/GraphicsEngineD3D12/include/CommandContext.h index 41be0796..cc184872 100644 --- a/Graphics/GraphicsEngineD3D12/include/CommandContext.h +++ b/Graphics/GraphicsEngineD3D12/include/CommandContext.h @@ -24,7 +24,6 @@ #pragma once -#include "pch.h" #include <vector> #include "D3D12ResourceBase.h" @@ -116,11 +115,19 @@ public: void SetID(const Char* ID) { m_ID = ID; } ID3D12GraphicsCommandList *GetCommandList(){return m_pCommandList;} - void DiscardDynamicDescriptors(Uint64 FrameNumber); + void DiscardDynamicDescriptors(Uint64 FenceValue); DescriptorHeapAllocation AllocateDynamicGPUVisibleDescriptor( D3D12_DESCRIPTOR_HEAP_TYPE Type, UINT Count = 1 ); void InsertUAVBarrier(D3D12ResourceBase& Resource, IDeviceObject &Object, bool FlushImmediate = false); + void SetPipelineState( ID3D12PipelineState* pPSO ) + { + if (pPSO != m_pCurPipelineState) + { + m_pCommandList->SetPipelineState(m_pCurPipelineState = pPSO); + } + } + protected: void TransitionResource(D3D12ResourceBase& Resource, IDeviceObject &Object, D3D12_RESOURCE_STATES NewState, bool FlushImmediate); void InsertAliasBarrier(D3D12ResourceBase& Before, D3D12ResourceBase& After, IDeviceObject &BeforeObj, IDeviceObject &AfterObj, bool FlushImmediate = false); @@ -128,10 +135,9 @@ protected: CComPtr<ID3D12GraphicsCommandList> m_pCommandList; CComPtr<ID3D12CommandAllocator> m_pCurrentAllocator; - ID3D12RootSignature* m_pCurGraphicsRootSignature; - ID3D12PipelineState* m_pCurGraphicsPipelineState; - ID3D12RootSignature* m_pCurComputeRootSignature; - ID3D12PipelineState* m_pCurComputePipelineState; + ID3D12PipelineState* m_pCurPipelineState = nullptr; + ID3D12RootSignature* m_pCurGraphicsRootSignature = nullptr; + ID3D12RootSignature* m_pCurComputeRootSignature = nullptr; static const int MaxPendingBarriers = 16; std::vector<D3D12_RESOURCE_BARRIER, STDAllocatorRawMem<D3D12_RESOURCE_BARRIER> > m_PendingResourceBarriers; @@ -199,14 +205,6 @@ public: } } - void SetPipelineState( ID3D12PipelineState* pPSO ) - { - if (pPSO != m_pCurGraphicsPipelineState) - { - m_pCommandList->SetPipelineState(m_pCurGraphicsPipelineState = pPSO); - } - } - void SetConstants( UINT RootIndex, UINT NumConstants, const void* pConstants ) { m_pCommandList->SetGraphicsRoot32BitConstants( RootIndex, NumConstants, pConstants, 0 ); @@ -285,15 +283,6 @@ public: } } - - void SetPipelineState( ID3D12PipelineState* pPSO ) - { - if (pPSO != m_pCurComputePipelineState) - { - m_pCommandList->SetPipelineState(m_pCurComputePipelineState = pPSO); - } - } - void SetConstants( UINT RootIndex, UINT NumConstants, const void* pConstants ) { m_pCommandList->SetComputeRoot32BitConstants( RootIndex, NumConstants, pConstants, 0 ); diff --git a/Graphics/GraphicsEngineD3D12/include/CommandListD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/CommandListD3D12Impl.h index e73e36d1..4ec16f16 100644 --- a/Graphics/GraphicsEngineD3D12/include/CommandListD3D12Impl.h +++ b/Graphics/GraphicsEngineD3D12/include/CommandListD3D12Impl.h @@ -32,12 +32,12 @@ namespace Diligent { /// Implementation of the Diligent::ICommandList interface -class CommandListD3D12Impl : public CommandListBase<ICommandList, FixedBlockMemoryAllocator> +class CommandListD3D12Impl : public CommandListBase<ICommandList> { public: - typedef CommandListBase<ICommandList, FixedBlockMemoryAllocator> TCommandListBase; - CommandListD3D12Impl(FixedBlockMemoryAllocator &Allocator, IRenderDevice *pDevice, class CommandContext* pCmdContext) : - TCommandListBase(Allocator, pDevice), + typedef CommandListBase<ICommandList> TCommandListBase; + CommandListD3D12Impl(IReferenceCounters *pRefCounters, IRenderDevice *pDevice, class CommandContext* pCmdContext) : + TCommandListBase(pRefCounters, pDevice), m_pCmdContext(pCmdContext) { } diff --git a/Graphics/GraphicsEngineD3D12/include/CommandListManager.h b/Graphics/GraphicsEngineD3D12/include/CommandListManager.h index 21866ad7..608c5075 100644 --- a/Graphics/GraphicsEngineD3D12/include/CommandListManager.h +++ b/Graphics/GraphicsEngineD3D12/include/CommandListManager.h @@ -21,20 +21,6 @@ * of the possibility of such damages. */ -// -// Copyright (c) Microsoft. All rights reserved. -// This code is licensed under the MIT License (MIT). -// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF -// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY -// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR -// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. -// -// Developed by Minigraph -// -// Author: James Stanard -// -// Adapted to Diligent Engine: Egor Yusov - #pragma once #include <vector> @@ -59,19 +45,22 @@ public: void CreateNewCommandList( ID3D12GraphicsCommandList** ppList, ID3D12CommandAllocator** ppAllocator ); // Discards the allocator. - // CmdListNum is the serial number of the command list that was created by this allocator - void DiscardAllocator( Uint64 CmdListNum, ID3D12CommandAllocator* pAllocator ); + // FenceValue is the value that was signaled by the command queue after it + // executed the the command list created by the allocator + void DiscardAllocator( Uint64 FenceValue, ID3D12CommandAllocator* pAllocator ); void RequestAllocator(ID3D12CommandAllocator** ppAllocator); private: - // fist - the SERIAL NUMBER of command list that was created by the allocator + // fist - the fence value associated with the command list that was created by the allocator // second - the allocator to be discarded typedef std::pair<Uint64, CComPtr<ID3D12CommandAllocator> > DiscardedAllocatorQueueElemType; std::deque< DiscardedAllocatorQueueElemType, STDAllocatorRawMem<DiscardedAllocatorQueueElemType> > m_DiscardedAllocators; std::mutex m_AllocatorMutex; RenderDeviceD3D12Impl *m_pDeviceD3D12; + + Atomics::AtomicLong m_NumAllocators = 0; // For debug purposes only }; }
\ No newline at end of file diff --git a/Graphics/GraphicsEngineD3D12/include/CommandQueueD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/CommandQueueD3D12Impl.h new file mode 100644 index 00000000..7b3a465d --- /dev/null +++ b/Graphics/GraphicsEngineD3D12/include/CommandQueueD3D12Impl.h @@ -0,0 +1,76 @@ +/* Copyright 2015-2017 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 + +/// \file +/// Declaration of Diligent::CommandQueueD3D12Impl class + +#include "CommandQueueD3D12.h" +#include "ObjectBase.h" + +namespace Diligent +{ + +/// Implementation of the Diligent::ICommandQueueD3D12 interface +class CommandQueueD3D12Impl : public ObjectBase<ICommandQueueD3D12> +{ +public: + typedef ObjectBase<ICommandQueueD3D12> TBase; + + CommandQueueD3D12Impl(IReferenceCounters *pRefCounters, ID3D12CommandQueue *pd3d12NativeCmdQueue, ID3D12Fence *pd3d12Fence); + ~CommandQueueD3D12Impl(); + + virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface )override; + + // Returns the fence value that will be signaled next time + virtual UINT64 GetNextFenceValue()override final { return m_NextFenceValue; } + + // Executes a given command list + virtual UINT64 ExecuteCommandList(ID3D12GraphicsCommandList* commandList)override final; + + virtual ID3D12CommandQueue* GetD3D12CommandQueue()override final { return m_pd3d12CmdQueue; } + + virtual void IdleGPU()override final; + + virtual Uint64 GetCompletedFenceValue()override final; + +private: + // A value that will be signaled by the command queue next + Atomics::AtomicInt64 m_NextFenceValue; + + // Last fence value completed by the GPU + volatile Uint64 m_LastCompletedFenceValue = 0; + + CComPtr<ID3D12CommandQueue> m_pd3d12CmdQueue; + + // The fence is signaled right after the command list has been + // submitted to the command queue for execution. + // All command lists with fence value less or equal to the signaled value + // are guaranteed to be finished by the GPU + CComPtr<ID3D12Fence> m_d3d12Fence; + + HANDLE m_WaitForGPUEventHandle = {}; +}; + +} diff --git a/Graphics/GraphicsEngineD3D12/include/DescriptorHeap.h b/Graphics/GraphicsEngineD3D12/include/DescriptorHeap.h index a9d895e3..9b7c404e 100644 --- a/Graphics/GraphicsEngineD3D12/include/DescriptorHeap.h +++ b/Graphics/GraphicsEngineD3D12/include/DescriptorHeap.h @@ -281,10 +281,9 @@ public: void Free(DescriptorHeapAllocation&& Allocation); // Releases all stale allocation used by completed command lists - // The method takes the NUMBER of completed command lists (N) - // and releases all allocations whose number n < N - // (cmd list n == N is NOT released) - void ReleaseStaleAllocations(Uint64 NumCompletedCmdLists); + // The method takes the last known completed fence value N + // and releases all allocations whose associated fence value n <= N + void ReleaseStaleAllocations(Uint64 LastCompletedFenceValue); size_t GetNumAvailableDescriptors()const{return m_FreeBlockManager.GetFreeSize();} size_t GetNumStaleDescriptors()const { return m_FreeBlockManager.GetStaleAllocationsSize(); } @@ -360,10 +359,9 @@ public: virtual Uint32 GetDescriptorSize()const override{return m_DescriptorSize;} // Releases all stale allocation used by completed command lists - // The method takes the NUMBER of completed command lists (N) - // and releases all allocations whose number n < N - // (cmd list n == N is NOT released) - void ReleaseStaleAllocations(Uint64 NumCompletedCmdLists); + // The method takes the last known completed fence value N + // and releases all allocations whose associated fence value n <= N + void ReleaseStaleAllocations(Uint64 LastCompletedFenceValue); protected: @@ -448,10 +446,9 @@ public: DescriptorHeapAllocation AllocateDynamic( uint32_t Count ); // Releases all stale allocation used by completed command lists - // The method takes the NUMBER of completed command lists (N) - // and releases all allocations whose number n < N - // (cmd list n == N is NOT released) - void ReleaseStaleAllocations(Uint64 NumCompletedCmdLists); + // The method takes the last known completed fence value N + // and releases all allocations whose associated fence value n <= N + void ReleaseStaleAllocations(Uint64 LastCompletedFenceValue); const D3D12_DESCRIPTOR_HEAP_DESC &GetHeapDesc()const{return m_HeapDesc;} Uint32 GetMaxStaticDescriptors()const { return m_HeapAllocationManager.GetMaxDescriptors(); } @@ -503,7 +500,7 @@ public: DynamicSuballocationsManager& operator = (const DynamicSuballocationsManager&) = delete; DynamicSuballocationsManager& operator = (DynamicSuballocationsManager&&) = delete; - void DiscardAllocations(Uint64 FrameNumber); + void DiscardAllocations(Uint64 /*FenceValue*/); virtual DescriptorHeapAllocation Allocate( Uint32 Count )override; virtual void Free(DescriptorHeapAllocation&& Allocation)override; diff --git a/Graphics/GraphicsEngineD3D12/include/DeviceContextD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/DeviceContextD3D12Impl.h index 14555715..dcc2ecf5 100644 --- a/Graphics/GraphicsEngineD3D12/include/DeviceContextD3D12Impl.h +++ b/Graphics/GraphicsEngineD3D12/include/DeviceContextD3D12Impl.h @@ -26,7 +26,7 @@ /// \file /// Declaration of Diligent::DeviceContextD3D12Impl class -#include "DeviceContext.h" +#include "DeviceContextD3D12.h" #include "DeviceContextBase.h" #include "GenerateMips.h" @@ -38,14 +38,15 @@ namespace Diligent { /// Implementation of the Diligent::IDeviceContext interface -class DeviceContextD3D12Impl : public DeviceContextBase<IDeviceContext> +class DeviceContextD3D12Impl : public DeviceContextBase<IDeviceContextD3D12> { public: - typedef DeviceContextBase<IDeviceContext> TDeviceContextBase; + typedef DeviceContextBase<IDeviceContextD3D12> TDeviceContextBase; - DeviceContextD3D12Impl(IMemoryAllocator &RawMemAllocator, class RenderDeviceD3D12Impl *pDevice, bool bIsDeferred, const EngineD3D12Attribs &Attribs, Uint32 ContextId); + DeviceContextD3D12Impl(IReferenceCounters *pRefCounters, class RenderDeviceD3D12Impl *pDevice, bool bIsDeferred, const EngineD3D12Attribs &Attribs, Uint32 ContextId); ~DeviceContextD3D12Impl(); - //virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface )override final; + + virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface )override final; virtual void SetPipelineState(IPipelineState *pPipelineState)override final; @@ -58,7 +59,8 @@ public: virtual void SetBlendFactors(const float* pBlendFactors = nullptr)override final; virtual void SetVertexBuffers( Uint32 StartSlot, Uint32 NumBuffersSet, IBuffer **ppBuffers, Uint32 *pStrides, Uint32 *pOffsets, Uint32 Flags )override final; - virtual void ClearState()override final; + + virtual void InvalidateState()override final; virtual void SetIndexBuffer( IBuffer *pIndexBuffer, Uint32 ByteOffset )override final; @@ -82,6 +84,10 @@ public: virtual void ExecuteCommandList(class ICommandList *pCommandList)override final; + virtual void TransitionTextureState(ITexture *pTexture, D3D12_RESOURCE_STATES State)override final; + + virtual void TransitionBufferState(IBuffer *pBuffer, D3D12_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 @@ -90,10 +96,12 @@ public: ///// Number of different shader types (Vertex, Pixel, Geometry, Domain, Hull, Compute) //static const int NumShaderTypes = 6; + void UpdateBufferRegion(class BufferD3D12Impl *pBuffD3D12, struct DynamicAllocation& Allocation, Uint64 DstOffset, Uint64 NumBytes); void UpdateBufferRegion(class BufferD3D12Impl *pBuffD3D12, const void *pData, Uint64 DstOffset, Uint64 NumBytes); void CopyBufferRegion(class BufferD3D12Impl *pSrcBuffD3D12, class BufferD3D12Impl *pDstBuffD3D12, Uint64 SrcOffset, Uint64 DstOffset, Uint64 NumBytes); void CopyTextureRegion(class TextureD3D12Impl *pSrcTexture, Uint32 SrcSubResIndex, const D3D12_BOX *pD3D12SrcBox, class TextureD3D12Impl *pDstTexture, Uint32 DstSubResIndex, Uint32 DstX, Uint32 DstY, Uint32 DstZ); + void CopyTextureRegion(IBuffer *pSrcBuffer, Uint32 SrcStride, Uint32 SrcDepthStride, class TextureD3D12Impl *pTextureD3D12, Uint32 DstSubResIndex, const Box &DstBox); void GenerateMips(class TextureViewD3D12Impl *pTexView); @@ -101,17 +109,25 @@ public: Uint32 GetContextId()const{return m_ContextId;} + size_t GetNumCommandsInCtx()const { return m_NumCommandsInCurCtx; } + private: void CommitD3D12IndexBuffer(VALUE_TYPE IndexType); void CommitD3D12VertexBuffers(class GraphicsContext &GraphCtx); void TransitionD3D12VertexBuffers(class GraphicsContext &GraphCtx); - void RebindRenderTargets(); + void CommitRenderTargets(); void CommitViewports(); void CommitScissorRects(); void Flush(bool RequestNewCmdCtx); friend class SwapChainD3D12Impl; - inline class CommandContext* RequestCmdContext(){return m_pCurrCmdCtx;} + inline class CommandContext* RequestCmdContext() + { + // Make sure that the number of commands in the context is at least one, + // so that the context cannot be disposed by Flush() + m_NumCommandsInCurCtx = m_NumCommandsInCurCtx != 0 ? m_NumCommandsInCurCtx : 1; + return m_pCurrCmdCtx; + } size_t m_NumCommandsInCurCtx = 0; const Uint32 m_NumCommandsToFlush = 192; CommandContext* m_pCurrCmdCtx = nullptr; diff --git a/Graphics/GraphicsEngineD3D12/include/DynamicUploadHeap.h b/Graphics/GraphicsEngineD3D12/include/DynamicUploadHeap.h index 00140f8d..8b62238d 100644 --- a/Graphics/GraphicsEngineD3D12/include/DynamicUploadHeap.h +++ b/Graphics/GraphicsEngineD3D12/include/DynamicUploadHeap.h @@ -121,7 +121,7 @@ public: DynamicAllocation Allocate( size_t SizeInBytes, size_t Alignment = DEFAULT_ALIGN ); - void FinishFrame(Uint64 FrameNum, Uint64 NumCompletedFrames); + void FinishFrame(Uint64 FenceValue, Uint64 LastCompletedFenceValue); private: const bool m_bIsCPUAccessible; diff --git a/Graphics/GraphicsEngineD3D12/include/PipelineStateD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/PipelineStateD3D12Impl.h index 84e2fb09..872bd87b 100644 --- a/Graphics/GraphicsEngineD3D12/include/PipelineStateD3D12Impl.h +++ b/Graphics/GraphicsEngineD3D12/include/PipelineStateD3D12Impl.h @@ -39,12 +39,12 @@ namespace Diligent class FixedBlockMemoryAllocator; /// Implementation of the Diligent::IRenderDeviceD3D12 interface -class PipelineStateD3D12Impl : public PipelineStateBase<IPipelineStateD3D12, IRenderDeviceD3D12, FixedBlockMemoryAllocator> +class PipelineStateD3D12Impl : public PipelineStateBase<IPipelineStateD3D12, IRenderDeviceD3D12> { public: - typedef PipelineStateBase<IPipelineStateD3D12, IRenderDeviceD3D12, FixedBlockMemoryAllocator> TPipelineStateBase; + typedef PipelineStateBase<IPipelineStateD3D12, IRenderDeviceD3D12> TPipelineStateBase; - PipelineStateD3D12Impl( FixedBlockMemoryAllocator &PSOAllocator, class RenderDeviceD3D12Impl *pDeviceD3D12, const PipelineStateDesc &PipelineDesc ); + PipelineStateD3D12Impl( IReferenceCounters *pRefCounters, class RenderDeviceD3D12Impl *pDeviceD3D12, const PipelineStateDesc &PipelineDesc ); ~PipelineStateD3D12Impl(); virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface ); @@ -69,7 +69,12 @@ public: bool dbgContainsShaderResources()const; IMemoryAllocator &GetResourceCacheDataAllocator(){return m_ResourceCacheDataAllocator;} - IMemoryAllocator &GetShaderResourceLayoutDataAllocator(Uint32 ShaderInd){return m_ShaderResLayoutDataAllocators[ShaderInd];} + IMemoryAllocator &GetShaderResourceLayoutDataAllocator(Uint32 ActiveShaderInd) + { + VERIFY_EXPR(ActiveShaderInd < m_NumShaders); + auto *pAllocator = m_ResLayoutDataAllocators.GetAllocator(ActiveShaderInd); + return pAllocator != nullptr ? *pAllocator : GetRawAllocator(); + } IShaderVariable *GetDummyShaderVar(){return &m_DummyVar;} @@ -82,11 +87,37 @@ private: RootSignature m_RootSig; DummyShaderVariable m_DummyVar; - AdaptiveFixedBlockAllocator m_ShaderResLayoutDataAllocators[6]; + // Looks like there may be a bug in msvc: when allocators are declared as + // an array and if an exception is thrown from constructor, the app crashes + class ResLayoutDataAllocators + { + public: + ~ResLayoutDataAllocators() + { + for(size_t i=0; i < _countof(m_pAllocators); ++i) + if(m_pAllocators[i] != nullptr) + DESTROY_POOL_OBJECT(m_pAllocators[i]); + } + void Init(Uint32 NumActiveShaders, Uint32 SRBAllocationGranularity) + { + VERIFY_EXPR(NumActiveShaders <= _countof(m_pAllocators) ); + for(Uint32 i=0; i < NumActiveShaders; ++i) + m_pAllocators[i] = NEW_POOL_OBJECT(AdaptiveFixedBlockAllocator, "Shader resource layout data allocator", GetRawAllocator(), SRBAllocationGranularity); + } + AdaptiveFixedBlockAllocator *GetAllocator(Uint32 ActiveShaderInd) + { + VERIFY_EXPR(ActiveShaderInd < _countof(m_pAllocators) ); + return m_pAllocators[ActiveShaderInd]; + } + private: + AdaptiveFixedBlockAllocator *m_pAllocators[5] = {}; + }m_ResLayoutDataAllocators; // Allocators must be defined before default SRB + ShaderResourceLayoutD3D12* m_pShaderResourceLayouts[6] = {}; AdaptiveFixedBlockAllocator m_ResourceCacheDataAllocator; // Use separate allocator for every shader stage // Do not use strong reference to avoid cyclic references + // Default SRB must be defined after allocators std::unique_ptr<class ShaderResourceBindingD3D12Impl, STDDeleter<ShaderResourceBindingD3D12Impl, FixedBlockMemoryAllocator> > m_pDefaultShaderResBinding; }; diff --git a/Graphics/GraphicsEngineD3D12/include/RenderDeviceD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/RenderDeviceD3D12Impl.h index c353f64e..a2309db5 100644 --- a/Graphics/GraphicsEngineD3D12/include/RenderDeviceD3D12Impl.h +++ b/Graphics/GraphicsEngineD3D12/include/RenderDeviceD3D12Impl.h @@ -33,6 +33,7 @@ #include "CommandContext.h" #include "DynamicUploadHeap.h" #include "Atomics.h" +#include "CommandQueueD3D12.h" /// Namespace for the Direct3D11 implementation of the graphics engine namespace Diligent @@ -44,52 +45,63 @@ class RenderDeviceD3D12Impl : public RenderDeviceD3DBase<IRenderDeviceD3D12> public: typedef RenderDeviceD3DBase<IRenderDeviceD3D12> TRenderDeviceBase; - RenderDeviceD3D12Impl( IMemoryAllocator &RawMemAllocator, const EngineD3D12Attribs &CreationAttribs, ID3D12Device *pD3D12Device, ID3D12CommandQueue *pd3d12CmdQueue, Uint32 NumDeferredContexts ); + RenderDeviceD3D12Impl( IReferenceCounters *pRefCounters, IMemoryAllocator &RawMemAllocator, const EngineD3D12Attribs &CreationAttribs, ID3D12Device *pD3D12Device, ICommandQueueD3D12 *pCmdQueue, Uint32 NumDeferredContexts ); ~RenderDeviceD3D12Impl(); - virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface )override; + virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface )override final; - virtual void CreatePipelineState( const PipelineStateDesc &PipelineDesc, IPipelineState **ppPipelineState )override; + virtual void CreatePipelineState( const PipelineStateDesc &PipelineDesc, IPipelineState **ppPipelineState )override final; - virtual void CreateBuffer(const BufferDesc& BuffDesc, const BufferData &BuffData, IBuffer **ppBuffer); + virtual void CreateBuffer(const BufferDesc& BuffDesc, const BufferData &BuffData, IBuffer **ppBuffer)override final; - virtual void CreateShader(const ShaderCreationAttribs &ShaderCreationAttribs, IShader **ppShader); + virtual void CreateShader(const ShaderCreationAttribs &ShaderCreationAttribs, IShader **ppShader)override final; - virtual void CreateTexture(const TextureDesc& TexDesc, const TextureData &Data, ITexture **ppTexture); + virtual void CreateTexture(const TextureDesc& TexDesc, const TextureData &Data, ITexture **ppTexture)override final; - void CreateTexture(TextureDesc& TexDesc, ID3D12Resource *pd3d12Texture, class TextureD3D12Impl **ppTexture); + void CreateTexture(const TextureDesc& TexDesc, ID3D12Resource *pd3d12Texture, class TextureD3D12Impl **ppTexture); - virtual void CreateSampler(const SamplerDesc& SamplerDesc, ISampler **ppSampler); + virtual void CreateSampler(const SamplerDesc& SamplerDesc, ISampler **ppSampler)override final; - virtual ID3D12Device* GetD3D12Device()override{return m_pd3d12Device;} + virtual ID3D12Device* GetD3D12Device()override final{return m_pd3d12Device;} + virtual void CreateTextureFromD3DResource(ID3D12Resource *pd3d12Texture, ITexture **ppTexture)override final; + + virtual void CreateBufferFromD3DResource(ID3D12Resource *pd3d12Buffer, const BufferDesc& BuffDesc, IBuffer **ppBuffer)override final; + DescriptorHeapAllocation AllocateDescriptor( D3D12_DESCRIPTOR_HEAP_TYPE Type, UINT Count = 1 ); DescriptorHeapAllocation AllocateGPUDescriptors( D3D12_DESCRIPTOR_HEAP_TYPE Type, UINT Count = 1 ); - Uint64 GetNumCompletedCmdLists(); - Uint64 GetNextCmdListNumber() const {return static_cast<Uint64>(m_NextCmdList);} - Uint64 GetCurrentFrameNumber()const {return static_cast<Uint64>(m_CurrentFrameNumber);} + Uint64 GetCompletedFenceValue(); + virtual Uint64 GetNextFenceValue() override final + { + return m_pCommandQueue->GetNextFenceValue(); + } - ID3D12CommandQueue *GetCmdQueue(){return m_pd3d12CmdQueue;} + Uint64 GetCurrentFrameNumber()const {return static_cast<Uint64>(m_FrameNumber);} + virtual Bool IsFenceSignaled(Uint64 FenceValue) override final; - void IdleGPU(bool ReleasePendingObjects = false); + ICommandQueueD3D12 *GetCmdQueue(){return m_pCommandQueue;} + + void IdleGPU(bool ReleaseStaleObjects); CommandContext* AllocateCommandContext(const Char *ID = ""); - void CloseAndExecuteCommandContext(CommandContext *pCtx); + void CloseAndExecuteCommandContext(CommandContext *pCtx, bool DiscardStaleObjects); void DisposeCommandContext(CommandContext*); void SafeReleaseD3D12Object(ID3D12Object* pObj); - void FinishFrame(); - + void FinishFrame(bool ReleaseAllResources); + virtual void FinishFrame()override final { FinishFrame(false); } + DynamicUploadHeap* RequestUploadHeap(); void ReleaseUploadHeap(DynamicUploadHeap* pUploadHeap); private: virtual void TestTextureFormat( TEXTURE_FORMAT TexFormat ); - void ProcessReleaseQueue(bool ForceRelease = false); + void ProcessReleaseQueue(Uint64 CompletedFenceValue); + void DiscardStaleD3D12Objects(Uint64 CmdListNumber, Uint64 FenceValue); /// D3D12 device CComPtr<ID3D12Device> m_pd3d12Device; - CComPtr<ID3D12CommandQueue> m_pd3d12CmdQueue; + RefCntAutoPtr<ICommandQueueD3D12> m_pCommandQueue; EngineD3D12Attribs m_EngineAttribs; @@ -101,18 +113,8 @@ private: std::mutex m_CmdQueueMutex; - Atomics::AtomicInt64 m_CurrentFrameNumber; - - // 0-based ordinal number of the command list that will be submitted to the GPU next time - Atomics::AtomicInt64 m_NextCmdList; - - // Number of command lists that retired the GPU pipeline - // - // To check if frame X is completed, - // strong inequality must be used: - // X < m_NumCompletedCmdLists - // - volatile Uint64 m_NumCompletedCmdLists; + Atomics::AtomicInt64 m_FrameNumber; + Atomics::AtomicInt64 m_NextCmdListNumber; // The following basic requirement guarantees correctness of resource deallocation: // @@ -128,10 +130,10 @@ private: // | Submit Cmd Submit Cmd Submit Cmd // | List N List N+1 List N+2 // V | | | - // m_NextCmdList | * N | N+1 | N+2 | + // NextFenceValue | * N | N+1 | N+2 | // // - // m_NumCompletedCmdLists | N-2 | N-1 | N | N+1 | + // CompletedFenceValue | N-3 | N-2 | N-1 | N | // . . . . . // -----------------------------.--------------.---------------.-------------------.----------------.------------- // . . . . . @@ -141,19 +143,7 @@ private: // | // Resource X can // be released - - // The fence is signaled right after the command list has been - // submitted to the command queue for execution. - // Note that the meaning of the fence value is the TOTAL NUMBER OF COMPLETED COMMAND LISTS, - // NOT the ordinal number of the LAST completed cmd list - // If ordinal number of the current cmd list is N, then N+1 is signaled, so the - // cmd list X is complete when X < m_NumCompletedCmdLists - // This is convenient as the default fence value is 0, and hence - // by default there are no completed cmd lists - CComPtr<ID3D12Fence> m_pCompletedCmdListFence; - - HANDLE m_WaitForGPUEventHandle; - + CommandListManager m_CmdListManager; typedef std::unique_ptr<CommandContext, STDDeleterRawMem<CommandContext> > ContextPoolElemType; @@ -162,12 +152,13 @@ private: std::deque<CommandContext*, STDAllocatorRawMem<CommandContext*> > m_AvailableContexts; std::mutex m_ContextAllocationMutex; - // Object that must be kept alive - std::mutex m_ReleasedObjectsMutex; - // Release queue + std::mutex m_ReleaseQueueMutex; typedef std::pair<Uint64, CComPtr<ID3D12Object> > ReleaseQueueElemType; std::deque< ReleaseQueueElemType, STDAllocatorRawMem<ReleaseQueueElemType> > m_D3D12ObjReleaseQueue; + std::mutex m_StaleObjectsMutex; + std::deque< ReleaseQueueElemType, STDAllocatorRawMem<ReleaseQueueElemType> > m_StaleD3D12Objects; + std::mutex m_UploadHeapMutex; typedef std::unique_ptr<DynamicUploadHeap, STDDeleterRawMem<DynamicUploadHeap> > UploadHeapPoolElemType; std::vector< UploadHeapPoolElemType, STDAllocatorRawMem<UploadHeapPoolElemType> > m_UploadHeaps; diff --git a/Graphics/GraphicsEngineD3D12/include/SamplerD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/SamplerD3D12Impl.h index fc920441..eb508f01 100644 --- a/Graphics/GraphicsEngineD3D12/include/SamplerD3D12Impl.h +++ b/Graphics/GraphicsEngineD3D12/include/SamplerD3D12Impl.h @@ -36,12 +36,12 @@ namespace Diligent class FixedBlockMemoryAllocator; /// Implementation of the Diligent::ISamplerD3D12 interface -class SamplerD3D12Impl : public SamplerBase<ISamplerD3D12, IRenderDeviceD3D12, FixedBlockMemoryAllocator> +class SamplerD3D12Impl : public SamplerBase<ISamplerD3D12, IRenderDeviceD3D12> { public: - typedef SamplerBase<ISamplerD3D12, IRenderDeviceD3D12, FixedBlockMemoryAllocator> TSamplerBase; + typedef SamplerBase<ISamplerD3D12, IRenderDeviceD3D12> TSamplerBase; - SamplerD3D12Impl(FixedBlockMemoryAllocator &SamplerObjAllocator, class RenderDeviceD3D12Impl *pRenderDeviceD3D12, const SamplerDesc& SamplerDesc); + SamplerD3D12Impl(IReferenceCounters *pRefCounters, class RenderDeviceD3D12Impl *pRenderDeviceD3D12, const SamplerDesc& SamplerDesc); ~SamplerD3D12Impl(); virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface )override; diff --git a/Graphics/GraphicsEngineD3D12/include/ShaderD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/ShaderD3D12Impl.h index 79ef7fec..ec2cea1f 100644 --- a/Graphics/GraphicsEngineD3D12/include/ShaderD3D12Impl.h +++ b/Graphics/GraphicsEngineD3D12/include/ShaderD3D12Impl.h @@ -43,12 +43,12 @@ class ResourceMapping; class FixedBlockMemoryAllocator; /// Implementation of the Diligent::IShaderD3D12 interface -class ShaderD3D12Impl : public ShaderBase<IShaderD3D12, IRenderDeviceD3D12, FixedBlockMemoryAllocator>, public ShaderD3DBase +class ShaderD3D12Impl : public ShaderBase<IShaderD3D12, IRenderDeviceD3D12>, public ShaderD3DBase { public: - typedef ShaderBase<IShaderD3D12, IRenderDeviceD3D12, FixedBlockMemoryAllocator> TShaderBase; + typedef ShaderBase<IShaderD3D12, IRenderDeviceD3D12> TShaderBase; - ShaderD3D12Impl(FixedBlockMemoryAllocator& ShaderObjAllocator, class RenderDeviceD3D12Impl *pRenderDeviceD3D12, const ShaderCreationAttribs &ShaderCreationAttribs); + ShaderD3D12Impl(IReferenceCounters *pRefCounters, class RenderDeviceD3D12Impl *pRenderDeviceD3D12, const ShaderCreationAttribs &ShaderCreationAttribs); ~ShaderD3D12Impl(); //virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface )override; diff --git a/Graphics/GraphicsEngineD3D12/include/ShaderResourceBindingD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/ShaderResourceBindingD3D12Impl.h index b5824b17..67155e01 100644 --- a/Graphics/GraphicsEngineD3D12/include/ShaderResourceBindingD3D12Impl.h +++ b/Graphics/GraphicsEngineD3D12/include/ShaderResourceBindingD3D12Impl.h @@ -38,11 +38,11 @@ namespace Diligent class FixedBlockMemoryAllocator; /// Implementation of the Diligent::IShaderResourceBindingD3D12 interface -class ShaderResourceBindingD3D12Impl : public ShaderResourceBindingBase<IShaderResourceBindingD3D12, FixedBlockMemoryAllocator> +class ShaderResourceBindingD3D12Impl : public ShaderResourceBindingBase<IShaderResourceBindingD3D12> { public: - typedef ShaderResourceBindingBase<IShaderResourceBindingD3D12, FixedBlockMemoryAllocator> TBase; - ShaderResourceBindingD3D12Impl(FixedBlockMemoryAllocator &SRBAllocator, class PipelineStateD3D12Impl *pPSO, bool IsPSOInternal); + typedef ShaderResourceBindingBase<IShaderResourceBindingD3D12> TBase; + ShaderResourceBindingD3D12Impl(IReferenceCounters *pRefCounters, class PipelineStateD3D12Impl *pPSO, bool IsPSOInternal); ~ShaderResourceBindingD3D12Impl(); virtual void QueryInterface( const Diligent::INTERFACE_ID &IID, IObject **ppInterface )override; diff --git a/Graphics/GraphicsEngineD3D12/include/SwapChainD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/SwapChainD3D12Impl.h index 197ccd68..4027daf1 100644 --- a/Graphics/GraphicsEngineD3D12/include/SwapChainD3D12Impl.h +++ b/Graphics/GraphicsEngineD3D12/include/SwapChainD3D12Impl.h @@ -26,9 +26,9 @@ /// \file /// Declaration of Diligent::SwapChainD3D12Impl class +#include <dxgi1_4.h> #include "SwapChainD3D12.h" #include "SwapChainBase.h" -#include <dxgi1_4.h> namespace Diligent { @@ -36,11 +36,11 @@ namespace Diligent class ITextureViewD3D12; class IMemoryAllocator; /// Implementation of the Diligent::ISwapChainD3D12 interface -class SwapChainD3D12Impl : public SwapChainBase<ISwapChainD3D12, IMemoryAllocator> +class SwapChainD3D12Impl : public SwapChainBase<ISwapChainD3D12> { public: - typedef SwapChainBase<ISwapChainD3D12, IMemoryAllocator> TSwapChainBase; - SwapChainD3D12Impl(IMemoryAllocator &Allocator, + typedef SwapChainBase<ISwapChainD3D12> TSwapChainBase; + SwapChainD3D12Impl(IReferenceCounters *pRefCounters, const SwapChainDesc& SwapChainDesc, class RenderDeviceD3D12Impl* pRenderDeviceD3D12, class DeviceContextD3D12Impl* pDeviceContextD3D12, @@ -52,9 +52,9 @@ public: virtual void Present(); virtual void Resize( Uint32 NewWidth, Uint32 NewHeight ); - virtual IDXGISwapChain *GetDXGISwapChain(){ return m_pSwapChain; } - ITextureView *GetCurrentBackBufferRTV(); - ITextureView *GetDepthBufferDSV(){return m_pDepthBufferDSV;} + virtual IDXGISwapChain* GetDXGISwapChain()override final{ return m_pSwapChain; } + virtual ITextureViewD3D12* GetCurrentBackBufferRTV()override final; + virtual ITextureViewD3D12* GetDepthBufferDSV()override final{return m_pDepthBufferDSV;} private: void InitBuffersAndViews(); @@ -62,8 +62,8 @@ private: /// DXGI swap chain CComPtr<IDXGISwapChain3> m_pSwapChain; - std::vector< RefCntAutoPtr<ITextureView>, STDAllocatorRawMem<RefCntAutoPtr<ITextureView>> > m_pBackBufferRTV; - RefCntAutoPtr<ITextureView> m_pDepthBufferDSV; + std::vector< RefCntAutoPtr<ITextureViewD3D12>, STDAllocatorRawMem<RefCntAutoPtr<ITextureViewD3D12>> > m_pBackBufferRTV; + RefCntAutoPtr<ITextureViewD3D12> m_pDepthBufferDSV; }; } diff --git a/Graphics/GraphicsEngineD3D12/include/TextureD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/TextureD3D12Impl.h index 29c0d117..89c63702 100644 --- a/Graphics/GraphicsEngineD3D12/include/TextureD3D12Impl.h +++ b/Graphics/GraphicsEngineD3D12/include/TextureD3D12Impl.h @@ -39,19 +39,19 @@ namespace Diligent class FixedBlockMemoryAllocator; /// Base implementation of the Diligent::ITextureD3D12 interface -class TextureD3D12Impl : public TextureBase<ITextureD3D12, TextureViewD3D12Impl, FixedBlockMemoryAllocator, FixedBlockMemoryAllocator>, public D3D12ResourceBase +class TextureD3D12Impl : public TextureBase<ITextureD3D12, TextureViewD3D12Impl, FixedBlockMemoryAllocator>, public D3D12ResourceBase { public: - typedef TextureBase<ITextureD3D12, TextureViewD3D12Impl, FixedBlockMemoryAllocator, FixedBlockMemoryAllocator> TTextureBase; + typedef TextureBase<ITextureD3D12, TextureViewD3D12Impl, FixedBlockMemoryAllocator> TTextureBase; // Creates a new D3D12 resource - TextureD3D12Impl(FixedBlockMemoryAllocator &TexObjAllocator, + TextureD3D12Impl(IReferenceCounters *pRefCounters, FixedBlockMemoryAllocator &TexViewObjAllocator, class RenderDeviceD3D12Impl *pDeviceD3D12, const TextureDesc& TexDesc, const TextureData &InitData = TextureData()); // Attaches to an existing D3D12 resource - TextureD3D12Impl(FixedBlockMemoryAllocator &TexObjAllocator, + TextureD3D12Impl(IReferenceCounters *pRefCounters, FixedBlockMemoryAllocator &TexViewObjAllocator, class RenderDeviceD3D12Impl *pDeviceD3D12, const TextureDesc& TexDesc, @@ -63,11 +63,15 @@ public: virtual void UpdateData( IDeviceContext *pContext, Uint32 MipLevel, Uint32 Slice, const Box &DstBox, const TextureSubResData &SubresData )override; //virtual void CopyData(CTexture *pSrcTexture, Uint32 SrcOffset, Uint32 DstOffset, Uint32 Size); - virtual void Map( IDeviceContext *pContext, MAP_TYPE MapType, Uint32 MapFlags, PVoid &pMappedData )override; - virtual void Unmap( IDeviceContext *pContext, MAP_TYPE MapType )override; + virtual void Map( IDeviceContext *pContext, Uint32 Subresource, MAP_TYPE MapType, Uint32 MapFlags, MappedTextureSubresource &MappedData )override; + virtual void Unmap( IDeviceContext *pContext, Uint32 Subresource, MAP_TYPE MapType, Uint32 MapFlags )override; virtual ID3D12Resource* GetD3D12Texture(){ return GetD3D12Resource(); } + virtual void* GetNativeHandle()override final { return GetD3D12Texture(); } + + virtual void SetD3D12ResourceState(D3D12_RESOURCE_STATES state)override final{ SetState(state); } + void CopyData(IDeviceContext *pContext, ITexture *pSrcTexture, Uint32 SrcMipLevel, @@ -79,7 +83,15 @@ public: Uint32 DstY, Uint32 DstZ); - D3D12_CPU_DESCRIPTOR_HANDLE GetUAVDescriptorHandle(Uint32 Mip, Uint32 Slice); + D3D12_CPU_DESCRIPTOR_HANDLE GetMipLevelUAV(Uint32 Mip) + { + return m_MipUAVs.GetCpuHandle(Mip); + } + + D3D12_CPU_DESCRIPTOR_HANDLE GetTexArraySRV() + { + return m_TexArraySRV.GetCpuHandle(); + } protected: void CreateViewInternal( const struct TextureViewDesc &ViewDesc, ITextureView **ppView, bool bIsDefaultView )override; @@ -90,8 +102,10 @@ protected: void CreateDSV( TextureViewDesc &DSVDesc, D3D12_CPU_DESCRIPTOR_HANDLE DSVHandle ); void CreateUAV( TextureViewDesc &UAVDesc, D3D12_CPU_DESCRIPTOR_HANDLE UAVHandle ); - // UAVs for every subresource to facilitate mipmap generation - DescriptorHeapAllocation m_SubresUAVs; + // UAVs for every mip level to facilitate mipmap generation + DescriptorHeapAllocation m_MipUAVs; + // SRV as texture array (even for a non-array texture) required for mipmap generation + DescriptorHeapAllocation m_TexArraySRV; friend class RenderDeviceD3D12Impl; }; diff --git a/Graphics/GraphicsEngineD3D12/include/TextureViewD3D12Impl.h b/Graphics/GraphicsEngineD3D12/include/TextureViewD3D12Impl.h index 29bcbd9f..17b33922 100644 --- a/Graphics/GraphicsEngineD3D12/include/TextureViewD3D12Impl.h +++ b/Graphics/GraphicsEngineD3D12/include/TextureViewD3D12Impl.h @@ -36,12 +36,12 @@ namespace Diligent class FixedBlockMemoryAllocator; /// Implementation of the Diligent::ITextureViewD3D12 interface -class TextureViewD3D12Impl : public TextureViewBase<ITextureViewD3D12, FixedBlockMemoryAllocator> +class TextureViewD3D12Impl : public TextureViewBase<ITextureViewD3D12> { public: - typedef TextureViewBase<ITextureViewD3D12, FixedBlockMemoryAllocator> TTextureViewBase; + typedef TextureViewBase<ITextureViewD3D12> TTextureViewBase; - TextureViewD3D12Impl( FixedBlockMemoryAllocator &TexViewObjAllocator, + TextureViewD3D12Impl( IReferenceCounters *pRefCounters, IRenderDevice *pDevice, const TextureViewDesc& ViewDesc, class ITexture *pTexture, diff --git a/Graphics/GraphicsEngineD3D12/interface/BufferD3D12.h b/Graphics/GraphicsEngineD3D12/interface/BufferD3D12.h index 823965a9..1065446a 100644 --- a/Graphics/GraphicsEngineD3D12/interface/BufferD3D12.h +++ b/Graphics/GraphicsEngineD3D12/interface/BufferD3D12.h @@ -49,8 +49,12 @@ public: /// is required for dynamic buffers, which are /// suballocated in a dynamic upload heap /// \param [in] ContextId - Id of the context within which address of the buffer is requested. - /// - virtual ID3D12Resource *GetD3D12Buffer(size_t &DataStartByteOffset, Uint32 ContextId) = 0; + virtual ID3D12Resource* GetD3D12Buffer(size_t &DataStartByteOffset, Uint32 ContextId) = 0; + + /// Sets the buffer usage state + + /// \param [in] state - D3D12 resource state to be set for this buffer + virtual void SetD3D12ResourceState(D3D12_RESOURCE_STATES state) = 0; }; } diff --git a/Graphics/GraphicsEngineD3D12/interface/BufferViewD3D12.h b/Graphics/GraphicsEngineD3D12/interface/BufferViewD3D12.h index a388e4c6..f46c7cae 100644 --- a/Graphics/GraphicsEngineD3D12/interface/BufferViewD3D12.h +++ b/Graphics/GraphicsEngineD3D12/interface/BufferViewD3D12.h @@ -40,7 +40,7 @@ class IBufferViewD3D12 : public IBufferView { public: - /// Returns CPU descriptor handle of the internal Direct3D12 object. + /// Returns CPU descriptor handle of the buffer view. virtual D3D12_CPU_DESCRIPTOR_HANDLE GetCPUDescriptorHandle() = 0; }; diff --git a/Graphics/GraphicsEngineD3D12/interface/CommandQueueD3D12.h b/Graphics/GraphicsEngineD3D12/interface/CommandQueueD3D12.h new file mode 100644 index 00000000..f78ce562 --- /dev/null +++ b/Graphics/GraphicsEngineD3D12/interface/CommandQueueD3D12.h @@ -0,0 +1,58 @@ +/* Copyright 2015-2017 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 + +/// \file +/// Definition of the Diligent::ICommandQueueD3D12 interface + +namespace Diligent +{ + +// {D89693CE-F3F4-44B5-B7EF-24115AAD085E} +static const Diligent::INTERFACE_ID IID_CommandQueueD3D12 = +{ 0xd89693ce, 0xf3f4, 0x44b5, { 0xb7, 0xef, 0x24, 0x11, 0x5a, 0xad, 0x8, 0x5e } }; + +/// Command queue interface +class ICommandQueueD3D12 : public Diligent::IObject +{ +public: + /// Returns the fence value that will be signaled next time + virtual UINT64 GetNextFenceValue() = 0; + + /// Executes a given command list + + /// \return Fence value associated with the executed command list + virtual UINT64 ExecuteCommandList(ID3D12GraphicsCommandList* commandList) = 0; + + /// Returns D3D12 command queue. May return null if queue is anavailable + virtual ID3D12CommandQueue* GetD3D12CommandQueue() = 0; + + /// Returns value of the last completed fence + virtual Uint64 GetCompletedFenceValue() = 0; + + /// Blocks execution until all pending GPU commands are complete + virtual void IdleGPU() = 0; +}; + +} diff --git a/Graphics/GraphicsEngineD3D12/interface/DeviceContextD3D12.h b/Graphics/GraphicsEngineD3D12/interface/DeviceContextD3D12.h new file mode 100644 index 00000000..372329c7 --- /dev/null +++ b/Graphics/GraphicsEngineD3D12/interface/DeviceContextD3D12.h @@ -0,0 +1,56 @@ +/* Copyright 2015-2017 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 + +/// \file +/// Definition of the Diligent::IDeviceContextD3D11 interface + +#include "DeviceContext.h" + +namespace Diligent +{ + +// {DDE9E3AB-5109-4026-92B7-F5E7EC83E21E} +static const Diligent::INTERFACE_ID IID_DeviceContextD3D12 = +{ 0xdde9e3ab, 0x5109, 0x4026, { 0x92, 0xb7, 0xf5, 0xe7, 0xec, 0x83, 0xe2, 0x1e } }; + +/// Interface to the device context object implemented in D3D12 +class IDeviceContextD3D12 : public Diligent::IDeviceContext +{ +public: + + /// Transitions internal D3D12 texture object to a specified state + + /// \param [in] pTexture - texture to transition + /// \param [in] State - D3D12 resource state this texture to transition to + virtual void TransitionTextureState(ITexture *pTexture, D3D12_RESOURCE_STATES State) = 0; + + /// Transitions internal D3D12 buffer object to a specified state + + /// \param [in] pBuffer - Buffer to transition + /// \param [in] State - D3D12 resource state this buffer to transition to + virtual void TransitionBufferState(IBuffer *pBuffer, D3D12_RESOURCE_STATES State) = 0; +}; + +} diff --git a/Graphics/GraphicsEngineD3D12/interface/PipelineStateD3D12.h b/Graphics/GraphicsEngineD3D12/interface/PipelineStateD3D12.h index e978c014..ae94cce0 100644 --- a/Graphics/GraphicsEngineD3D12/interface/PipelineStateD3D12.h +++ b/Graphics/GraphicsEngineD3D12/interface/PipelineStateD3D12.h @@ -41,17 +41,17 @@ class IPipelineStateD3D12 : public IPipelineState { public: - /// Returns a pointer to the ID3D12PipelineState interface of the internal Direct3D12 object. + /// Returns ID3D12PipelineState interface of the internal D3D12 pipeline state object object. /// The method does *NOT* call AddRef() on the returned interface, /// so Release() must not be called. - virtual ID3D12PipelineState *GetD3D12PipelineState()const = 0; + virtual ID3D12PipelineState* GetD3D12PipelineState()const = 0; /// Returns a pointer to the root signature object associated with this pipeline state. /// The method does *NOT* call AddRef() on the returned interface, /// so Release() must not be called. - virtual ID3D12RootSignature *GetD3D12RootSignature()const = 0; + virtual ID3D12RootSignature* GetD3D12RootSignature()const = 0; }; } diff --git a/Graphics/GraphicsEngineD3D12/interface/RenderDeviceD3D12.h b/Graphics/GraphicsEngineD3D12/interface/RenderDeviceD3D12.h index 3d37c0a8..d5a72c8c 100644 --- a/Graphics/GraphicsEngineD3D12/interface/RenderDeviceD3D12.h +++ b/Graphics/GraphicsEngineD3D12/interface/RenderDeviceD3D12.h @@ -39,11 +39,43 @@ class IRenderDeviceD3D12 : public Diligent::IRenderDevice { public: - /// Returns a pointer to the ID3D12Device interface of the internal Direct3D12 object. + /// Returns ID3D12Device interface of the internal Direct3D12 device object. /// The method does *NOT* call AddRef() on the returned interface, /// so Release() must not be called. virtual ID3D12Device* GetD3D12Device() = 0; + + /// Returns the fence value that will be signaled by the GPU command queue next + virtual Uint64 GetNextFenceValue() = 0; + + /// Checks if the fence value has been signaled by the GPU. True means + /// that all associated work has been finished + virtual Bool IsFenceSignaled(Uint64 FenceValue) = 0; + + /// Should be called at the end of the frame when attached to existing D3D12 device + /// Otherwise the method is automatically called before present + virtual void FinishFrame() = 0; + + /// Creates a texture object from native d3d12 resource + + /// \param [in] pd3d12Texture - pointer to the native D3D12 texture + /// \param [out] ppTexture - Address of the memory location where the pointer to the + /// texture interface will be stored. + /// The function calls AddRef(), so that the new object will contain + /// one refernce. + virtual void CreateTextureFromD3DResource(ID3D12Resource *pd3d12Texture, ITexture **ppTexture) = 0; + + /// Creates a buffer object from native d3d12 resoruce + + /// \param [in] pd3d12Buffer - Pointer to the native d3d12 buffer resource + /// \param [in] BuffDesc - Buffer description. The system can recover buffer size, but + /// the rest of the fields need to be populated by the client + /// as they cannot be recovered from d3d12 resource description + /// \param [out] ppBuffer - Address of the memory location where the pointer to the + /// buffer interface will be stored. + /// The function calls AddRef(), so that the new object will contain + /// one reference. + virtual void CreateBufferFromD3DResource(ID3D12Resource *pd3d12Buffer, const BufferDesc& BuffDesc, IBuffer **ppBuffer) = 0; }; } diff --git a/Graphics/GraphicsEngineD3D12/interface/RenderDeviceFactoryD3D12.h b/Graphics/GraphicsEngineD3D12/interface/RenderDeviceFactoryD3D12.h index 2a81cf76..3bdaca16 100644 --- a/Graphics/GraphicsEngineD3D12/interface/RenderDeviceFactoryD3D12.h +++ b/Graphics/GraphicsEngineD3D12/interface/RenderDeviceFactoryD3D12.h @@ -27,29 +27,47 @@ /// Declaration of functions that initialize Direct3D12-based engine implementation #include "Errors.h" +#include "RenderDevice.h" +#include "DeviceContext.h" +#include "SwapChain.h" -extern "C" +namespace Diligent { -#ifdef ENGINE_DLL - typedef void (*CreateDeviceAndContextsD3D12Type)( - const Diligent::EngineD3D12Attribs& CreationAttribs, - Diligent::IRenderDevice **ppDevice, - Diligent::IDeviceContext **ppContexts, - Diligent::Uint32 NumDeferredContexts ); +class IEngineFactoryD3D12 +{ +public: + virtual void CreateDeviceAndContextsD3D12( const EngineD3D12Attribs& CreationAttribs, + IRenderDevice **ppDevice, + IDeviceContext **ppContexts, + Uint32 NumDeferredContexts) = 0; + + virtual void AttachToD3D12Device(void *pd3d12NativeDevice, + class ICommandQueueD3D12 *pCommandQueue, + const EngineD3D12Attribs& EngineAttribs, + IRenderDevice **ppDevice, + IDeviceContext **ppContexts, + Uint32 NumDeferredContexts) = 0; + virtual void CreateSwapChainD3D12( IRenderDevice *pDevice, + IDeviceContext *pImmediateContext, + const SwapChainDesc& SwapChainDesc, + void* pNativeWndHandle, + ISwapChain **ppSwapChain ) = 0; - typedef void (*CreateSwapChainD3D12Type)( - Diligent::IRenderDevice *pDevice, - Diligent::IDeviceContext *pImmediateContext, - const Diligent::SwapChainDesc& SwapChainDesc, - void* pNativeWndHandle, - Diligent::ISwapChain **ppSwapChain ); +}; - static void LoadGraphicsEngineD3D12(CreateDeviceAndContextsD3D12Type &CreateDeviceFunc, CreateSwapChainD3D12Type &CreateSwapChainFunc) +} + +extern "C" +{ +#ifdef ENGINE_DLL + + typedef Diligent::IEngineFactoryD3D12* (*GetEngineFactoryD3D12Type)(); + + static bool LoadGraphicsEngineD3D12(GetEngineFactoryD3D12Type &GetFactoryFunc) { - CreateDeviceFunc = nullptr; - CreateSwapChainFunc = nullptr; + GetFactoryFunc = nullptr; std::string LibName = "GraphicsEngineD3D12_"; #if _WIN64 @@ -69,36 +87,22 @@ extern "C" if( hModule == NULL ) { LOG_ERROR_MESSAGE( "Failed to load ", LibName, " library." ); - return; - } - - CreateDeviceFunc = reinterpret_cast<CreateDeviceAndContextsD3D12Type>( GetProcAddress(hModule, "CreateDeviceAndContextsD3D12") ); - if( CreateDeviceFunc == NULL ) - { - LOG_ERROR_MESSAGE( "Failed to load CreateDeviceAndContextsD3D12() from ", LibName, " library." ); - FreeLibrary( hModule ); - return; + return false; } - CreateSwapChainFunc = reinterpret_cast<CreateSwapChainD3D12Type>( GetProcAddress(hModule, "CreateSwapChainD3D12") ); - if( CreateSwapChainFunc == NULL ) + GetFactoryFunc = reinterpret_cast<GetEngineFactoryD3D12Type>( GetProcAddress(hModule, "GetEngineFactoryD3D12") ); + if( GetFactoryFunc == NULL ) { - LOG_ERROR_MESSAGE( "Failed to load CreateSwapChainD3D12() from ", LibName, " library." ); + LOG_ERROR_MESSAGE( "Failed to load GetEngineFactoryD3D12() from ", LibName, " library." ); FreeLibrary( hModule ); - return; + return false; } + + return true; } #else - void CreateDeviceAndContextsD3D12( const Diligent::EngineD3D12Attribs& CreationAttribs, - Diligent::IRenderDevice **ppDevice, - Diligent::IDeviceContext **ppContexts, - Diligent::Uint32 NumDeferredContexts); + Diligent::IEngineFactoryD3D12* GetEngineFactoryD3D12(); - void CreateSwapChainD3D12( Diligent::IRenderDevice *pDevice, - Diligent::IDeviceContext *pImmediateContext, - const Diligent::SwapChainDesc& SwapChainDesc, - void* pNativeWndHandle, - Diligent::ISwapChain **ppSwapChain ); #endif } diff --git a/Graphics/GraphicsEngineD3D12/interface/SamplerD3D12.h b/Graphics/GraphicsEngineD3D12/interface/SamplerD3D12.h index f8d98e69..2dec2654 100644 --- a/Graphics/GraphicsEngineD3D12/interface/SamplerD3D12.h +++ b/Graphics/GraphicsEngineD3D12/interface/SamplerD3D12.h @@ -40,7 +40,7 @@ class ISamplerD3D12 : public Diligent::ISampler { public: - /// Returns a pointer to the ID3D12SamplerState interface of the internal Direct3D12 object. + /// Returns a CPU descriptor handle of the D3D12 sampler object /// The method does *NOT* call AddRef() on the returned interface, /// so Release() must not be called. diff --git a/Graphics/GraphicsEngineD3D12/interface/SwapChainD3D12.h b/Graphics/GraphicsEngineD3D12/interface/SwapChainD3D12.h index ab936019..ae5fcf52 100644 --- a/Graphics/GraphicsEngineD3D12/interface/SwapChainD3D12.h +++ b/Graphics/GraphicsEngineD3D12/interface/SwapChainD3D12.h @@ -27,6 +27,7 @@ /// Definition of the Diligent::ISwapChainD3D12 interface #include "SwapChain.h" +#include "TextureViewD3D12.h" namespace Diligent { @@ -45,6 +46,18 @@ public: /// The method does *NOT* call AddRef() on the returned interface, /// so Release() must not be called. virtual IDXGISwapChain *GetDXGISwapChain() = 0; + + /// Returns a pointer to the render target view of the current back buffer in the swap chain + + /// The method does *NOT* call AddRef() on the returned interface, + /// so Release() must not be called. + virtual ITextureViewD3D12* GetCurrentBackBufferRTV() = 0; + + /// Returns a pointer to the depth-stencil view of the depth buffer + + /// The method does *NOT* call AddRef() on the returned interface, + /// so Release() must not be called. + virtual ITextureViewD3D12* GetDepthBufferDSV() = 0; }; } diff --git a/Graphics/GraphicsEngineD3D12/interface/TextureD3D12.h b/Graphics/GraphicsEngineD3D12/interface/TextureD3D12.h index 449f2692..1c7d3122 100644 --- a/Graphics/GraphicsEngineD3D12/interface/TextureD3D12.h +++ b/Graphics/GraphicsEngineD3D12/interface/TextureD3D12.h @@ -45,6 +45,11 @@ public: /// The method does *NOT* call AddRef() on the returned interface, /// so Release() must not be called. virtual ID3D12Resource* GetD3D12Texture() = 0; + + /// Sets the texture usage state + + /// \param [in] state - D3D12 resource state to be set for this texture + virtual void SetD3D12ResourceState(D3D12_RESOURCE_STATES state) = 0; }; } diff --git a/Graphics/GraphicsEngineD3D12/interface/TextureViewD3D12.h b/Graphics/GraphicsEngineD3D12/interface/TextureViewD3D12.h index f9c83a61..8c65142f 100644 --- a/Graphics/GraphicsEngineD3D12/interface/TextureViewD3D12.h +++ b/Graphics/GraphicsEngineD3D12/interface/TextureViewD3D12.h @@ -40,7 +40,7 @@ class ITextureViewD3D12 : public Diligent::ITextureView { public: - /// Returns CPU descriptor handle of the internal Direct3D12 object. + /// Returns CPU descriptor handle of the texture view. virtual D3D12_CPU_DESCRIPTOR_HANDLE GetCPUDescriptorHandle() = 0; }; diff --git a/Graphics/GraphicsEngineD3D12/readme.md b/Graphics/GraphicsEngineD3D12/readme.md new file mode 100644 index 00000000..af51251e --- /dev/null +++ b/Graphics/GraphicsEngineD3D12/readme.md @@ -0,0 +1,154 @@ + +# GraphicsEngineD3D12 + +Implementation of Diligent Engine API using Direct3D12 + +# Initialization + +The following code snippet shows how to initialize diligent engine in D3D12 mode. + +```cpp +#include "RenderDeviceFactoryD3D12.h" +using namespace Diligent; + +// ... +#ifdef ENGINE_DLL + GetEngineFactoryD3D12Type GetEngineFactoryD3D12 = nullptr; + // Load the dll and import GetEngineFactoryD3D12() function + LoadGraphicsEngineD3D12(GetEngineFactoryD3D12); +#endif +auto *pFactoryD3D11 = GetEngineFactoryD3D12(); +EngineD3D12Attribs EngD3D12Attribs; +EngD3D12Attribs.CPUDescriptorHeapAllocationSize[0] = 1024; +EngD3D12Attribs.CPUDescriptorHeapAllocationSize[1] = 32; +EngD3D12Attribs.CPUDescriptorHeapAllocationSize[2] = 16; +EngD3D12Attribs.CPUDescriptorHeapAllocationSize[3] = 16; +EngD3D12Attribs.NumCommandsToFlushCmdList = 64; +RefCntAutoPtr<IRenderDevice> pRenderDevice; +RefCntAutoPtr<IDeviceContext> pImmediateContext; +SwapChainDesc SwapChainDesc; +RefCntAutoPtr<ISwapChain> pSwapChain; +pFactoryD3D11->CreateDeviceAndContextsD3D12( EngD3D12Attribs, &pRenderDevice, &pImmediateContext, 0 ); +pFactoryD3D11->CreateSwapChainD3D12( pRenderDevice, pImmediateContext, SwapChainDesc, hWnd, &pSwapChain ); +``` + +Alternatively, the engine can be initialized by attaching to existing D3D12 device (see below). + +# Interoperability with Direct3D12 + +Diligent Engine exposes methods to access internal D3D12 objects, is able to create diligent engine buffers +and textures from existing Direct3D12 resources, and can be initialized by attaching to existing D3D12 +device and provide synchronization tools. + +## Accessing Native D3D12 Resources + +Below are some of the methods that provide access to internal D3D12 objects: + +| Function | Description | +|-----------------------------------------------------------------------------|---------------------------------------------------------------------------------------------------------------| +| `ID3D12Resource* IBufferD3D12::GetD3D12Buffer(size_t &DataStartByteOffset, Uint32 ContextId)` | returns a pointer to the ID3D12Resource interface of the internal Direct3D12 buffer object. Note that dynamic buffers are suballocated from dynamic heap, and every context has its own dynamic heap. Offset from the beginning of the dynamic heap for a context identified by ContextId is returned in DataStartByteOffset parameter | +| `void IBufferD3D12::SetD3D12ResourceState(D3D12_RESOURCE_STATES state) | sets the buffer usage state. This method should be used when an application transitions the buffer to inform diligent engine about the current usage state | +| `D3D12_CPU_DESCRIPTOR_HANDLE IBufferViewD3D12::GetCPUDescriptorHandle()` | returns CPU descriptor handle of the buffer view | +| `ID3D12Resource* ITextureD3D12::GetD3D12Texture()` | returns a pointer to the ID3D12Resource interface of the internal Direct3D12 texture object | +| `void ITextureD3D12::SetD3D12ResourceState(D3D12_RESOURCE_STATES state)` | sets the texture usage state. This method should be used when an application transitions the texture to inform diligent engine about the current usage state | +| `D3D12_CPU_DESCRIPTOR_HANDLE ITextureViewD3D12::GetCPUDescriptorHandle()` | returns CPU descriptor handle of the texture view | +| `void IDeviceContextD3D12::TransitionTextureState(ITexture *pTexture, D3D12_RESOURCE_STATES State)` | transitions internal D3D12 texture object to a specified state | +| `void IDeviceContextD3D12::TransitionBufferState(IBuffer *pBuffer, D3D12_RESOURCE_STATES State)` | transitions internal D3D12 buffer object to a specified state | +| `ID3D12PipelineState* IPipelineStateD3D12::GetD3D12PipelineState()` | returns ID3D12PipelineState interface of the internal D3D12 pipeline state object object | +| `ID3D12RootSignature* IPipelineStateD3D12::GetD3D12RootSignature()` | returns a pointer to the root signature object associated with this pipeline state | +| `D3D12_CPU_DESCRIPTOR_HANDLE ISamplerD3D12::GetCPUDescriptorHandle()` | returns a CPU descriptor handle of the D3D12 sampler object | +| `ID3D12Device* IRenderDeviceD3D12::GetD3D12Device()` | returns ID3D12Device interface of the internal Direct3D12 device object | + +## Synchronization Tools + +| `Uint64 IRenderDeviceD3D12::GetNextFenceValue()` | returns the fence value that will be signaled by the GPU command queue when the next command list is submitted for execution | +| `Bool IRenderDeviceD3D12::IsFenceSignaled(Uint64 FenceValue)` | checks if the fence value has been signaled by the GPU. True means that all associated work has been finished | +| `void IRenderDeviceD3D12::FinishFrame()` | this method should be called at the end of the frame when attached to existing D3D12 device. Otherwise the method is automatically called before present | + +## Creating Diligent Engine Objects from D3D12 Resources + +* `void IRenderDeviceD3D12::CreateTextureFromD3DResource(ID3D12Resource *pd3d12Texture, ITexture **ppTexture)` - + creates a diligent engine texture object from native D3D12 resource. +* `void IRenderDeviceD3D12::CreateBufferFromD3DResource(ID3D12Resource *pd3d12Buffer, const BufferDesc& BuffDesc, IBuffer **ppBuffer)` - + creates a diligent engine buffer object from native D3D12 resource. + The method takes a pointer to the native d3d12 resiyrce pd3d12Buffer, buffer description BuffDesc and writes a pointer to the IBuffer + interface at the memory location pointed to by ppBuffer. The system can recover buffer size, but the rest of the fields of + BuffDesc structure need to be populated by the client as they cannot be recovered from d3d12 resource description. + + +## Initializing the Engine by Attaching to Existing D3D12 Device + +To attach diligent engine to existing D3D12 device, use the following factory function: + +```cpp +void IEngineFactoryD3D12::AttachToD3D12Device(void *pd3d12NativeDevice, + class ICommandQueueD3D12 *pCommandQueue, + const EngineD3D12Attribs& EngineAttribs, + IRenderDevice **ppDevice, + IDeviceContext **ppContexts, + Uint32 NumDeferredContexts); +``` + +The method takes a pointer to the native D3D12 device `pd3d12NativeDevice`, initialization parameters `EngineAttribs`, +and returns diligent engine device interface in `ppDevice`, and diligent engine contexts in `ppContexts`. Pointer to the +immediate goes at position 0. If `NumDeferredContexts` > 0, pointers to deferred contexts go afterwards. +The function also takes a pointer to the command queue object `pCommandQueue`, which needs to implement +`ICommandQueueD3D12` interface. + +For more information about interoperability with D3D12, please visit [Diligent Engine web site](http://diligentgraphics.com/diligent-engine/native-api-interoperability/direct3d12-interoperability/) + +# References + +[Diligent Engine](http://diligentgraphics.com/diligent-engine) + +[Interoperability with Direct3D12](http://diligentgraphics.com/diligent-engine/native-api-interoperability/direct3d12-interoperability/) + +[Architecture of D3D12-based implementation](http://diligentgraphics.com/diligent-engine/architecture/D3D12) + +# Release Notes + +## 2.1 + +### New fatures + +* Interoperability with Direct3D12 +** Accessing internal D3D12 objects and handles +** Createing diligent engine buffers/textures from D3D12 resources +** Attaching to existing D3D12 device +** Resource state and command queue synchronization +* Integraion with Unity +* Geometry shader support +* Tessellation support +* Support for dynamic buffers with SRV/UAV bind flags +* Support for structured buffers in HSLS shaders +* Performance optimizations + +### API Changes + +* Updated map interface: removed MAP_WRITE_DISCARD and MAP_WRITE_NO_OVERWRITE map types and added MAP_FLAG_DISCARD and MAP_FLAG_DO_NOT_SYNCHRONIZE flags instead + +### Bug fixes + +* Issues with deferred contexts + - Never flush deferred D3D12 context + - Invalidate context state from FinishCommandList() and ExecuteCommandList() + - Not binding default RTV & DSV for deferred contexts +* Issues with idling GPU +* Object relase bug: when releasing last reference to a device object, render device was destroyed before the object was fully released +* Fixed mipmap generation for SRGB textures +* Fixed mipmap generation for texture arrays + + +## 2.0 + +Reworked the API to follow D3D12 style + +## 1.0 + +Initial release + + + +**Copyright 2015-2017 Egor Yusov** + +[Diligent Graphics](http://diligentgraphics.com) diff --git a/Graphics/GraphicsEngineD3D12/shaders/GenerateMips/GenerateMipsCS.hlsli b/Graphics/GraphicsEngineD3D12/shaders/GenerateMips/GenerateMipsCS.hlsli index 3c25ff54..56ac04be 100644 --- a/Graphics/GraphicsEngineD3D12/shaders/GenerateMips/GenerateMipsCS.hlsli +++ b/Graphics/GraphicsEngineD3D12/shaders/GenerateMips/GenerateMipsCS.hlsli @@ -13,7 +13,7 @@ #define RootSig \ "RootFlags(0), " \ - "RootConstants(b0, num32BitConstants = 4), " \ + "RootConstants(b0, num32BitConstants = 6), " \ "DescriptorTable(SRV(t0, numDescriptors = 1))," \ "DescriptorTable(UAV(u0, numDescriptors = 4))," \ "StaticSampler(s0," \ @@ -26,17 +26,19 @@ #define NON_POWER_OF_TWO 0 #endif -RWTexture2D<float4> OutMip1 : register(u0); -RWTexture2D<float4> OutMip2 : register(u1); -RWTexture2D<float4> OutMip3 : register(u2); -RWTexture2D<float4> OutMip4 : register(u3); -Texture2D<float4> SrcTex : register(t0); +RWTexture2DArray<float4> OutMip1 : register(u0); +RWTexture2DArray<float4> OutMip2 : register(u1); +RWTexture2DArray<float4> OutMip3 : register(u2); +RWTexture2DArray<float4> OutMip4 : register(u3); +Texture2DArray<float4> SrcTex : register(t0); SamplerState BilinearClamp : register(s0); cbuffer CB : register(b0) { uint SrcMipLevel; // Texture level of source mip uint NumMipLevels; // Number of OutMips to write: [1, 4] + uint ArraySlice; // Texture array slice + uint Dummy; float2 TexelSize; // 1.0 / OutMip1.Dimensions } @@ -84,7 +86,8 @@ float4 PackColor(float4 Linear) void main( uint GI : SV_GroupIndex, uint3 DTid : SV_DispatchThreadID ) { uint2 DstMipSize; - SrcTex.GetDimensions(DstMipSize.x, DstMipSize.y); + uint Elements; + SrcTex.GetDimensions(DstMipSize.x, DstMipSize.y, Elements); DstMipSize >>= SrcMipLevel; bool IsValidThread = all(DTid.xy < DstMipSize); @@ -99,37 +102,37 @@ void main( uint GI : SV_GroupIndex, uint3 DTid : SV_DispatchThreadID ) // have to take more source texture samples. #if NON_POWER_OF_TWO == 0 float2 UV = TexelSize * (DTid.xy + 0.5); - Src1 = SrcTex.SampleLevel(BilinearClamp, UV, SrcMipLevel); + Src1 = SrcTex.SampleLevel(BilinearClamp, float3(UV, ArraySlice), SrcMipLevel); #elif NON_POWER_OF_TWO == 1 // > 2:1 in X dimension // Use 2 bilinear samples to guarantee we don't undersample when downsizing by more than 2x // horizontally. float2 UV1 = TexelSize * (DTid.xy + float2(0.25, 0.5)); float2 Off = TexelSize * float2(0.5, 0.0); - Src1 = 0.5 * (SrcTex.SampleLevel(BilinearClamp, UV1, SrcMipLevel) + - SrcTex.SampleLevel(BilinearClamp, UV1 + Off, SrcMipLevel)); + Src1 = 0.5 * (SrcTex.SampleLevel(BilinearClamp, float3(UV1, ArraySlice), SrcMipLevel) + + SrcTex.SampleLevel(BilinearClamp, float3(UV1 + Off, ArraySlice), SrcMipLevel)); #elif NON_POWER_OF_TWO == 2 // > 2:1 in Y dimension // Use 2 bilinear samples to guarantee we don't undersample when downsizing by more than 2x // vertically. float2 UV1 = TexelSize * (DTid.xy + float2(0.5, 0.25)); float2 Off = TexelSize * float2(0.0, 0.5); - Src1 = 0.5 * (SrcTex.SampleLevel(BilinearClamp, UV1, SrcMipLevel) + - SrcTex.SampleLevel(BilinearClamp, UV1 + Off, SrcMipLevel)); + Src1 = 0.5 * (SrcTex.SampleLevel(BilinearClamp, float3(UV1, ArraySlice), SrcMipLevel) + + SrcTex.SampleLevel(BilinearClamp, float3(UV1 + Off, ArraySlice), SrcMipLevel)); #elif NON_POWER_OF_TWO == 3 // > 2:1 in in both dimensions // Use 4 bilinear samples to guarantee we don't undersample when downsizing by more than 2x // in both directions. float2 UV1 = TexelSize * (DTid.xy + float2(0.25, 0.25)); float2 Off = TexelSize * 0.5; - Src1 += SrcTex.SampleLevel(BilinearClamp, UV1, SrcMipLevel); - Src1 += SrcTex.SampleLevel(BilinearClamp, UV1 + float2(Off.x, 0.0), SrcMipLevel); - Src1 += SrcTex.SampleLevel(BilinearClamp, UV1 + float2(0.0, Off.y), SrcMipLevel); - Src1 += SrcTex.SampleLevel(BilinearClamp, UV1 + float2(Off.x, Off.y), SrcMipLevel); + Src1 += SrcTex.SampleLevel(BilinearClamp, float3(UV1, ArraySlice), SrcMipLevel); + Src1 += SrcTex.SampleLevel(BilinearClamp, float3(UV1 + float2(Off.x, 0.0), ArraySlice), SrcMipLevel); + Src1 += SrcTex.SampleLevel(BilinearClamp, float3(UV1 + float2(0.0, Off.y), ArraySlice), SrcMipLevel); + Src1 += SrcTex.SampleLevel(BilinearClamp, float3(UV1 + float2(Off.x, Off.y), ArraySlice), SrcMipLevel); Src1 *= 0.25; #endif - OutMip1[DTid.xy] = PackColor(Src1); + OutMip1[ uint3(DTid.xy, ArraySlice) ] = PackColor(Src1); } // A scalar (constant) branch can exit all threads coherently. @@ -159,7 +162,7 @@ void main( uint GI : SV_GroupIndex, uint3 DTid : SV_DispatchThreadID ) float4 Src4 = LoadColor(GI + 0x09); Src1 = 0.25 * (Src1 + Src2 + Src3 + Src4); - OutMip2[DTid.xy / 2] = PackColor(Src1); + OutMip2[ uint3(DTid.xy / 2, ArraySlice) ] = PackColor(Src1); StoreColor(GI, Src1); } } @@ -179,7 +182,7 @@ void main( uint GI : SV_GroupIndex, uint3 DTid : SV_DispatchThreadID ) float4 Src4 = LoadColor(GI + 0x12); Src1 = 0.25 * (Src1 + Src2 + Src3 + Src4); - OutMip3[DTid.xy / 4] = PackColor(Src1); + OutMip3[ uint3(DTid.xy / 4, ArraySlice) ] = PackColor(Src1); StoreColor(GI, Src1); } } @@ -200,7 +203,7 @@ void main( uint GI : SV_GroupIndex, uint3 DTid : SV_DispatchThreadID ) float4 Src4 = LoadColor(GI + 0x24); Src1 = 0.25 * (Src1 + Src2 + Src3 + Src4); - OutMip4[DTid.xy / 8] = PackColor(Src1); + OutMip4[ uint3(DTid.xy / 8, ArraySlice) ] = PackColor(Src1); } } } diff --git a/Graphics/GraphicsEngineD3D12/src/BufferD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/BufferD3D12Impl.cpp index 9f75d08e..1e1ba5a7 100644 --- a/Graphics/GraphicsEngineD3D12/src/BufferD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/BufferD3D12Impl.cpp @@ -35,14 +35,14 @@ namespace Diligent { -BufferD3D12Impl :: BufferD3D12Impl(FixedBlockMemoryAllocator &BufferObjMemAllocator, +BufferD3D12Impl :: BufferD3D12Impl(IReferenceCounters *pRefCounters, FixedBlockMemoryAllocator &BuffViewObjMemAllocator, RenderDeviceD3D12Impl *pRenderDeviceD3D12, const BufferDesc& BuffDesc, const BufferData &BuffData /*= BufferData()*/) : - TBufferBase(BufferObjMemAllocator, BuffViewObjMemAllocator, pRenderDeviceD3D12, BuffDesc), + TBufferBase(pRefCounters, BuffViewObjMemAllocator, pRenderDeviceD3D12, BuffDesc, false), #ifdef _DEBUG - m_DbgMapType(1 + pRenderDeviceD3D12->GetNumDeferredContexts(), static_cast<MAP_TYPE>(-1), STD_ALLOCATOR_RAW_MEM(DynamicAllocation, GetRawAllocator(), "Allocator for vector<MAP_TYPE>")), + m_DbgMapType(1 + pRenderDeviceD3D12->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>>")), #endif m_DynamicData(BuffDesc.Usage == USAGE_DYNAMIC ? (1 + pRenderDeviceD3D12->GetNumDeferredContexts()) : 0, DynamicAllocation(), STD_ALLOCATOR_RAW_MEM(DynamicAllocation, GetRawAllocator(), "Allocator for vector<DynamicAllocation>")) { @@ -54,15 +54,31 @@ BufferD3D12Impl :: BufferD3D12Impl(FixedBlockMemoryAllocator &BufferObjMemAlloca if( m_Desc.Usage == USAGE_DYNAMIC && BuffData.pData != nullptr ) LOG_BUFFER_ERROR_AND_THROW("Dynamic buffer must be initialized via Map()") + Uint32 AlignmentMask = 1; if (m_Desc.BindFlags & BIND_UNIFORM_BUFFER) + AlignmentMask = 255; + + if (m_Desc.Usage == USAGE_CPU_ACCESSIBLE) { - Uint32 AlignmentMask = 255; - m_Desc.uiSizeInBytes = (m_Desc.uiSizeInBytes + AlignmentMask) & (~AlignmentMask); + if (m_Desc.CPUAccessFlags != CPU_ACCESS_WRITE && m_Desc.CPUAccessFlags != CPU_ACCESS_READ) + LOG_BUFFER_ERROR_AND_THROW("Exactly one of the CPU_ACCESS_WRITE or CPU_ACCESS_READ flags must be specified for a cpu-accessible buffer") + + if (m_Desc.CPUAccessFlags == CPU_ACCESS_WRITE) + { + if(BuffData.pData != nullptr ) + LOG_BUFFER_ERROR_AND_THROW("CPU-writable staging buffers must be updated via map") + + AlignmentMask = D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1; + } } + + if(AlignmentMask != 1) + m_Desc.uiSizeInBytes = (m_Desc.uiSizeInBytes + AlignmentMask) & (~AlignmentMask); - if(m_Desc.Usage == USAGE_DYNAMIC) + if(m_Desc.Usage == USAGE_DYNAMIC && (m_Desc.BindFlags & (BIND_SHADER_RESOURCE|BIND_UNORDERED_ACCESS)) == 0) { - // Dynamic buffers are suballocated in the upload heap when Map() is called. + // 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 D3D12_RESOURCE_STATE_GENERIC_READ state m_UsageState = D3D12_RESOURCE_STATE_GENERIC_READ; @@ -86,12 +102,17 @@ BufferD3D12Impl :: BufferD3D12Impl(FixedBlockMemoryAllocator &BufferObjMemAlloca D3D12BuffDesc.Flags = D3D12_RESOURCE_FLAG_NONE; if( m_Desc.BindFlags & BIND_UNORDERED_ACCESS ) D3D12BuffDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; - + if( !(m_Desc.BindFlags & BIND_SHADER_RESOURCE) ) + D3D12BuffDesc.Flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; + auto *pd3d12Device = pRenderDeviceD3D12->GetD3D12Device(); + D3D12_HEAP_PROPERTIES HeapProps; + if (m_Desc.Usage == USAGE_CPU_ACCESSIBLE) + HeapProps.Type = m_Desc.CPUAccessFlags == CPU_ACCESS_READ ? D3D12_HEAP_TYPE_READBACK : D3D12_HEAP_TYPE_UPLOAD; + else + HeapProps.Type = D3D12_HEAP_TYPE_DEFAULT; - D3D12_HEAP_PROPERTIES HeapProps; - HeapProps.Type = m_Desc.Usage == USAGE_CPU_ACCESSIBLE ? D3D12_HEAP_TYPE_READBACK : D3D12_HEAP_TYPE_DEFAULT; if(HeapProps.Type == D3D12_HEAP_TYPE_READBACK) m_UsageState = D3D12_RESOURCE_STATE_COPY_DEST; else if(HeapProps.Type == D3D12_HEAP_TYPE_UPLOAD) @@ -145,7 +166,25 @@ BufferD3D12Impl :: BufferD3D12Impl(FixedBlockMemoryAllocator &BufferObjMemAlloca // the object is not constructed yet pInitContext->CopyResource(m_pd3d12Resource, UploadBuffer); - pRenderDeviceD3D12->CloseAndExecuteCommandContext(pInitContext); + // 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/d3d12/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 | | + pRenderDeviceD3D12->CloseAndExecuteCommandContext(pInitContext, false); // Add reference to the object to the release queue to keep it alive // until copy operation is complete. This must be done after @@ -159,51 +198,68 @@ BufferD3D12Impl :: BufferD3D12Impl(FixedBlockMemoryAllocator &BufferObjMemAlloca CreateCBV(m_CBVDescriptorAllocation.GetCpuHandle()); } } -#if 0 - D3D12BuffDesc.BindFlags = BindFlagsToD3D12BindFlags(BuffDesc.BindFlags); - D3D12BuffDesc.ByteWidth = - D3D12BuffDesc.MiscFlags = 0; - if( BuffDesc.BindFlags & BIND_INDIRECT_DRAW_ARGS ) +} + +static BufferDesc BufferDescFromD3D12Resource(BufferDesc BuffDesc, ID3D12Resource *pd3d12Buffer) +{ + VERIFY(BuffDesc.Usage != USAGE_DYNAMIC, "Dynamic buffers cannot be attached to native d3d12 resource") + + auto D3D12BuffDesc = pd3d12Buffer->GetDesc(); + VERIFY(D3D12BuffDesc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER, "D3D12 resource is not a buffer") + + VERIFY(BuffDesc.uiSizeInBytes == 0 || BuffDesc.uiSizeInBytes == D3D12BuffDesc.Width, "Buffer size specified by the BufferDesc (", BuffDesc.uiSizeInBytes,") does not match d3d12 resource size (", D3D12BuffDesc.Width, ")" ) + BuffDesc.uiSizeInBytes = static_cast<Uint32>( D3D12BuffDesc.Width ); + + if (D3D12BuffDesc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) + { + VERIFY(BuffDesc.BindFlags == 0 || (BuffDesc.BindFlags & BIND_UNORDERED_ACCESS), "BIND_UNORDERED_ACCESS flag is not specified by the BufferDesc, while d3d12 resource was created with D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS flag") + BuffDesc.BindFlags |= BIND_UNORDERED_ACCESS; + } + if (D3D12BuffDesc.Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE) { - D3D12BuffDesc.MiscFlags |= D3D12_RESOURCE_MISC_DRAWINDIRECT_ARGS; + VERIFY( !(BuffDesc.BindFlags & BIND_SHADER_RESOURCE), "BIND_SHADER_RESOURCE flag is specified by the BufferDesc, while d3d12 resource was created with D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE flag") + BuffDesc.BindFlags &= ~BIND_SHADER_RESOURCE; } - D3D12BuffDesc.Usage = UsageToD3D12Usage(BuffDesc.Usage); - - D3D12BuffDesc.StructureByteStride = 0; - if( (BuffDesc.BindFlags & BIND_UNORDERED_ACCESS) ) + + if( (BuffDesc.BindFlags & BIND_UNORDERED_ACCESS) || (BuffDesc.BindFlags & BIND_SHADER_RESOURCE) ) { - if( BuffDesc.Mode == BUFFER_MODE_STRUCTURED ) + if(BuffDesc.Mode == BUFFER_MODE_STRUCTURED) { - D3D12BuffDesc.MiscFlags |= D3D12_RESOURCE_MISC_BUFFER_STRUCTURED; - D3D12BuffDesc.StructureByteStride = BuffDesc.ElementByteStride; + VERIFY(BuffDesc.ElementByteStride != 0, "Element byte stride cannot be 0 for a structured buffer") } - else if( BuffDesc.Mode == BUFFER_MODE_FORMATED ) + else if(BuffDesc.Mode == BUFFER_MODE_FORMATTED) { - auto ElementStride = GetValueSize( BuffDesc.Format.ValueType ) * BuffDesc.Format.NumComponents; - VERIFY( m_Desc.ElementByteStride == 0 || m_Desc.ElementByteStride == ElementStride, "Element byte stride does not match buffer format" ); - m_Desc.ElementByteStride = ElementStride; - if( BuffDesc.Format.ValueType == VT_FLOAT32 || BuffDesc.Format.ValueType == VT_FLOAT16 ) - m_Desc.Format.IsNormalized = false; + VERIFY( BuffDesc.Format.ValueType != VT_UNDEFINED, "Value type is not specified for a formatted buffer" ); + VERIFY( BuffDesc.Format.NumComponents != 0, "Num components cannot be zero in a formated buffer" ); } else { - UNEXPECTED( "Buffer UAV type is not correct" ); + UNEXPECTED("Buffer mode must be structured or formatted") } } - D3D12BuffDesc.CPUAccessFlags = CPUAccessFlagsToD3D12CPUAccessFlags( BuffDesc.CPUAccessFlags ); - - D3D12_SUBRESOURCE_DATA InitData; - InitData.pSysMem = BuffData.pData; - InitData.SysMemPitch = BuffData.DataSize; - InitData.SysMemSlicePitch = 0; + return BuffDesc; +} - auto *pDeviceD3D12 = pRenderDeviceD3D12->GetD3D12Device(); - CHECK_D3D_RESULT_THROW( pDeviceD3D12->CreateBuffer(&D3D12BuffDesc, InitData.pSysMem ? &InitData : nullptr, &m_pD3D12Buffer), - "Failed to create the Direct3D11 buffer" ); +BufferD3D12Impl :: BufferD3D12Impl(IReferenceCounters *pRefCounters, + FixedBlockMemoryAllocator &BuffViewObjMemAllocator, + RenderDeviceD3D12Impl *pRenderDeviceD3D12, + const BufferDesc& BuffDesc, + ID3D12Resource *pd3d12Buffer) : + TBufferBase(pRefCounters, BuffViewObjMemAllocator, pRenderDeviceD3D12, BufferDescFromD3D12Resource(BuffDesc, pd3d12Buffer), false), +#ifdef _DEBUG + m_DbgMapType(1 + pRenderDeviceD3D12->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>>")), #endif -} + m_DynamicData(BuffDesc.Usage == USAGE_DYNAMIC ? (1 + pRenderDeviceD3D12->GetNumDeferredContexts()) : 0, DynamicAllocation(), STD_ALLOCATOR_RAW_MEM(DynamicAllocation, GetRawAllocator(), "Allocator for vector<DynamicAllocation>")) +{ + m_pd3d12Resource = pd3d12Buffer; + if (m_Desc.BindFlags & BIND_UNIFORM_BUFFER) + { + m_CBVDescriptorAllocation = pRenderDeviceD3D12->AllocateDescriptor(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + CreateCBV(m_CBVDescriptorAllocation.GetCpuHandle()); + } +} BufferD3D12Impl :: ~BufferD3D12Impl() { // D3D12 object can only be destroyed when it is no longer used by the GPU @@ -235,14 +291,15 @@ void BufferD3D12Impl :: Map(IDeviceContext *pContext, MAP_TYPE MapType, Uint32 M TBufferBase::Map( pContext, MapType, MapFlags, pMappedData ); auto *pDeviceContextD3D12 = ValidatedCast<DeviceContextD3D12Impl>(pContext); #ifdef _DEBUG - m_DbgMapType[pDeviceContextD3D12->GetContextId()] = MapType; + if(pDeviceContextD3D12 != nullptr) + m_DbgMapType[pDeviceContextD3D12->GetContextId()] = std::make_pair(MapType, MapFlags); #endif if (MapType == MAP_READ ) { LOG_WARNING_MESSAGE_ONCE("Mapping CPU buffer for reading on D3D12 currently requires flushing context and idling GPU"); pDeviceContextD3D12->Flush(); auto *pDeviceD3D12 = ValidatedCast<RenderDeviceD3D12Impl>(GetDevice()); - pDeviceD3D12->IdleGPU(); + pDeviceD3D12->IdleGPU(false); VERIFY(m_Desc.Usage == USAGE_CPU_ACCESSIBLE, "Buffer must be created as USAGE_CPU_ACCESSIBLE to be mapped for reading") D3D12_RANGE MapRange; @@ -250,16 +307,33 @@ void BufferD3D12Impl :: Map(IDeviceContext *pContext, MAP_TYPE MapType, Uint32 M MapRange.End = m_Desc.uiSizeInBytes; m_pd3d12Resource->Map(0, &MapRange, &pMappedData); } - else if(MapType == MAP_WRITE_DISCARD) + else if(MapType == MAP_WRITE) { - auto *pCtxD3D12 = ValidatedCast<DeviceContextD3D12Impl>(pContext); - auto ContextId = pDeviceContextD3D12->GetContextId(); - m_DynamicData[ContextId] = pCtxD3D12->AllocateDynamicSpace(m_Desc.uiSizeInBytes); - pMappedData = m_DynamicData[ContextId].CPUAddress; + if (m_Desc.Usage == USAGE_CPU_ACCESSIBLE) + { + VERIFY(m_pd3d12Resource != nullptr, "USAGE_CPU_ACCESSIBLE buffer mapped for writing must intialize D3D12 resource") + if (MapFlags & MAP_FLAG_DISCARD) + { + + } + m_pd3d12Resource->Map(0, nullptr, &pMappedData); + } + else if (m_Desc.Usage == USAGE_DYNAMIC) + { + VERIFY(MapFlags & MAP_FLAG_DISCARD, "D3D12 buffer must be mapped for writing with MAP_FLAG_DISCARD flag") + auto *pCtxD3D12 = ValidatedCast<DeviceContextD3D12Impl>(pContext); + auto ContextId = pDeviceContextD3D12->GetContextId(); + m_DynamicData[ContextId] = pCtxD3D12->AllocateDynamicSpace(m_Desc.uiSizeInBytes); + pMappedData = m_DynamicData[ContextId].CPUAddress; + } + else + { + LOG_ERROR("Only USAGE_DYNAMIC and USAGE_CPU_ACCESSIBLE D3D12 buffers can be mapped for writing") + } } - else if(MapType == MAP_READ_WRITE || MapType == MAP_WRITE) + else if(MapType == MAP_READ_WRITE) { - LOG_ERROR("D3D12 allows writing to CPU-readable buffer, but it is not accessable by GPU") + LOG_ERROR("MAP_READ_WRITE is not supported on D3D12") } else { @@ -267,13 +341,17 @@ void BufferD3D12Impl :: Map(IDeviceContext *pContext, MAP_TYPE MapType, Uint32 M } } -void BufferD3D12Impl::Unmap( IDeviceContext *pContext, MAP_TYPE MapType ) +void BufferD3D12Impl::Unmap( IDeviceContext *pContext, MAP_TYPE MapType, Uint32 MapFlags ) { - TBufferBase::Unmap( pContext, MapType ); + TBufferBase::Unmap( pContext, MapType, MapFlags ); auto *pDeviceContextD3D12 = ValidatedCast<DeviceContextD3D12Impl>(pContext); - auto CtxId = pDeviceContextD3D12->GetContextId(); + Uint32 CtxId = pDeviceContextD3D12 != nullptr ? pDeviceContextD3D12->GetContextId() : static_cast<Uint32>(-1); #ifdef _DEBUG - VERIFY(m_DbgMapType[CtxId] == MapType, "Map type provided does not match the type provided to Map()" ); + if (pDeviceContextD3D12 != nullptr) + { + VERIFY(m_DbgMapType[CtxId].first == MapType, "Map type does not match the type provided to Map()"); + VERIFY(m_DbgMapType[CtxId].second == MapFlags, "Map flags do not match the flags provided to Map()"); + } #endif if (MapType == MAP_READ ) @@ -284,12 +362,27 @@ void BufferD3D12Impl::Unmap( IDeviceContext *pContext, MAP_TYPE MapType ) MapRange.End = 0; m_pd3d12Resource->Unmap(0, &MapRange); } - else if(MapType == MAP_WRITE_DISCARD) + else if(MapType == MAP_WRITE) { - // Nothing needs to be done. + if (m_Desc.Usage == USAGE_CPU_ACCESSIBLE) + { + VERIFY(m_pd3d12Resource != nullptr, "USAGE_CPU_ACCESSIBLE buffer mapped for writing must intialize D3D12 resource") + m_pd3d12Resource->Unmap(0, nullptr); + } + else if (m_Desc.Usage == USAGE_DYNAMIC) + { + VERIFY(MapFlags & MAP_FLAG_DISCARD, "D3D12 buffer must be mapped for writing with MAP_FLAG_DISCARD flag") + // Copy data into the resource + if (m_pd3d12Resource) + { + pDeviceContextD3D12->UpdateBufferRegion(this, m_DynamicData[CtxId], 0, m_Desc.uiSizeInBytes); + } + } } + #ifdef _DEBUG - m_DbgMapType[CtxId] = static_cast<MAP_TYPE>(-1); + if(pDeviceContextD3D12 != nullptr) + m_DbgMapType[CtxId] = std::make_pair(static_cast<MAP_TYPE>(-1), static_cast<Uint32>(-1)); #endif } @@ -312,13 +405,15 @@ void BufferD3D12Impl::CreateViewInternal( const BufferViewDesc &OrigViewDesc, IB { auto UAVHandleAlloc = pDeviceD3D12Impl->AllocateDescriptor(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); CreateUAV( ViewDesc, UAVHandleAlloc.GetCpuHandle() ); - *ppView = NEW(BuffViewAllocator, "BufferViewD3D12Impl instance", BufferViewD3D12Impl, GetDevice(), ViewDesc, this, std::move(UAVHandleAlloc), bIsDefaultView ); + *ppView = NEW_RC_OBJ(BuffViewAllocator, "BufferViewD3D12Impl instance", BufferViewD3D12Impl, bIsDefaultView ? this : nullptr) + (GetDevice(), ViewDesc, this, std::move(UAVHandleAlloc), bIsDefaultView ); } else if( ViewDesc.ViewType == BUFFER_VIEW_SHADER_RESOURCE ) { auto SRVHandleAlloc = pDeviceD3D12Impl->AllocateDescriptor(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); CreateSRV( ViewDesc, SRVHandleAlloc.GetCpuHandle() ); - *ppView = NEW(BuffViewAllocator, "BufferViewD3D12Impl instance", BufferViewD3D12Impl, GetDevice(), ViewDesc, this, std::move(SRVHandleAlloc), bIsDefaultView ); + *ppView = NEW_RC_OBJ(BuffViewAllocator, "BufferViewD3D12Impl instance", BufferViewD3D12Impl, bIsDefaultView ? this : nullptr) + (GetDevice(), ViewDesc, this, std::move(SRVHandleAlloc), bIsDefaultView ); } if( !bIsDefaultView && *ppView ) @@ -368,7 +463,7 @@ void BufferD3D12Impl::DbgVerifyDynamicAllocation(Uint32 ContextId) { VERIFY(m_DynamicData[ContextId].GPUAddress != 0, "Dynamic buffer must be mapped before the first use"); auto CurrentFrame = ValidatedCast<RenderDeviceD3D12Impl>(GetDevice())->GetCurrentFrameNumber(); - VERIFY(m_DynamicData[ContextId].FrameNum == CurrentFrame, "Dynamic allocation is out-of-date. Dynamic buffer must be mapped in the same frame it is used."); + 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() == D3D12_RESOURCE_STATE_GENERIC_READ, "Dynamic buffers are expected to always be in D3D12_RESOURCE_STATE_GENERIC_READ state") } #endif diff --git a/Graphics/GraphicsEngineD3D12/src/BufferViewD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/BufferViewD3D12Impl.cpp index f0f7b2d7..1dfe7cdc 100644 --- a/Graphics/GraphicsEngineD3D12/src/BufferViewD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/BufferViewD3D12Impl.cpp @@ -27,13 +27,13 @@ namespace Diligent { -BufferViewD3D12Impl::BufferViewD3D12Impl( FixedBlockMemoryAllocator &BuffViewAllocator, +BufferViewD3D12Impl::BufferViewD3D12Impl( IReferenceCounters *pRefCounters, IRenderDevice *pDevice, const BufferViewDesc& ViewDesc, IBuffer *pBuffer, DescriptorHeapAllocation &&HandleAlloc, bool bIsDefaultView ) : - TBufferViewBase( BuffViewAllocator, pDevice, ViewDesc, pBuffer, bIsDefaultView ), + TBufferViewBase( pRefCounters, pDevice, ViewDesc, pBuffer, bIsDefaultView ), m_DescriptorHandle( std::move(HandleAlloc) ) { } diff --git a/Graphics/GraphicsEngineD3D12/src/CommandContext.cpp b/Graphics/GraphicsEngineD3D12/src/CommandContext.cpp index 9f689040..1219abaf 100644 --- a/Graphics/GraphicsEngineD3D12/src/CommandContext.cpp +++ b/Graphics/GraphicsEngineD3D12/src/CommandContext.cpp @@ -35,9 +35,8 @@ CommandContext::CommandContext( IMemoryAllocator &MemAllocator, GPUDescriptorHeap GPUDescriptorHeaps[], const Uint32 DynamicDescriptorAllocationChunkSize[]) : m_pCurGraphicsRootSignature( nullptr), - m_pCurGraphicsPipelineState( nullptr), + m_pCurPipelineState( nullptr), m_pCurComputeRootSignature( nullptr), - m_pCurComputePipelineState( nullptr), m_DynamicGPUDescriptorAllocator { {MemAllocator, GPUDescriptorHeaps[0], DynamicDescriptorAllocationChunkSize[0]}, @@ -60,17 +59,19 @@ CommandContext::~CommandContext( void ) void CommandContext::Reset( CommandListManager& CmdListManager ) { - // We only call Reset() on previously freed contexts. The command list persists, but we must - // request a new allocator. - VERIFY_EXPR(m_pCommandList != nullptr && m_pCurrentAllocator == nullptr); - m_pCurrentAllocator.Release(); - CmdListManager.RequestAllocator(&m_pCurrentAllocator); - m_pCommandList->Reset(m_pCurrentAllocator, nullptr); + // We only call Reset() on previously freed contexts. The command list persists, but we need to + // request a new allocator if there is none + // The allocator may not be null if the command context was previously disposed without being executed + VERIFY_EXPR(m_pCommandList != nullptr) + if( !m_pCurrentAllocator ) + { + CmdListManager.RequestAllocator(&m_pCurrentAllocator); + m_pCommandList->Reset(m_pCurrentAllocator, nullptr); + } + m_pCurPipelineState = nullptr; m_pCurGraphicsRootSignature = nullptr; - m_pCurGraphicsPipelineState = nullptr; m_pCurComputeRootSignature = nullptr; - m_pCurComputePipelineState = nullptr; m_PendingResourceBarriers.clear(); m_PendingBarrierObjects.clear(); m_BoundDescriptorHeaps = ShaderDescriptorHeaps(); @@ -92,7 +93,8 @@ ID3D12GraphicsCommandList* CommandContext::Close(ID3D12CommandAllocator **ppAllo auto hr = m_pCommandList->Close(); VERIFY(SUCCEEDED(hr), "Failed to close the command list"); - *ppAllocator = m_pCurrentAllocator.Detach(); + if( ppAllocator != nullptr ) + *ppAllocator = m_pCurrentAllocator.Detach(); return m_pCommandList; } @@ -110,6 +112,7 @@ void GraphicsContext::SetRenderTargets( UINT NumRTVs, ITextureViewD3D12** ppRTVs auto *pTexture = ValidatedCast<TextureD3D12Impl>( pRTV->GetTexture() ); TransitionResource(pTexture, D3D12_RESOURCE_STATE_RENDER_TARGET); RTVHandles[i] = pRTV->GetCPUDescriptorHandle(); + VERIFY_EXPR(RTVHandles[i].ptr != 0); } } @@ -125,10 +128,11 @@ void GraphicsContext::SetRenderTargets( UINT NumRTVs, ITextureViewD3D12** ppRTVs { TransitionResource(pTexture, D3D12_RESOURCE_STATE_DEPTH_WRITE); auto DSVHandle = pDSV->GetCPUDescriptorHandle(); + VERIFY_EXPR(DSVHandle.ptr != 0); m_pCommandList->OMSetRenderTargets( NumRTVs, RTVHandles, FALSE, &DSVHandle ); } } - else + else if(NumRTVs > 0) { m_pCommandList->OMSetRenderTargets( NumRTVs, RTVHandles, FALSE, nullptr ); } @@ -190,11 +194,12 @@ void CommandContext::TransitionResource(IBufferD3D12 *pBuffer, D3D12_RESOURCE_ST auto *pBuffD3D12 = ValidatedCast<BufferD3D12Impl>(pBuffer); #ifdef _DEBUG - // Dynamic buffers are suballocated in the upload heap when Map() is called - // and must always be in D3D12_RESOURCE_STATE_GENERIC_READ state - if(pBuffD3D12->GetDesc().Usage == USAGE_DYNAMIC) + // Dynamic buffers wtih no SRV/UAV bind flags are suballocated in + // the upload heap when Map() is called and must always be in + // D3D12_RESOURCE_STATE_GENERIC_READ state + if(pBuffD3D12->GetDesc().Usage == USAGE_DYNAMIC && (pBuffD3D12->GetDesc().BindFlags & (BIND_SHADER_RESOURCE|BIND_UNORDERED_ACCESS)) == 0) { - VERIFY(pBuffD3D12->GetState() == D3D12_RESOURCE_STATE_GENERIC_READ, "Dynamic buffers are expected to always be in D3D12_RESOURCE_STATE_GENERIC_READ state") + VERIFY(pBuffD3D12->GetState() == D3D12_RESOURCE_STATE_GENERIC_READ, "Dynamic buffers that cannot be bound as SRV or UAV are expected to always be in D3D12_RESOURCE_STATE_GENERIC_READ state") VERIFY( (NewState & D3D12_RESOURCE_STATE_GENERIC_READ) == NewState, "Dynamic buffers can only transition to one of D3D12_RESOURCE_STATE_GENERIC_READ states") } #endif @@ -202,8 +207,8 @@ void CommandContext::TransitionResource(IBufferD3D12 *pBuffer, D3D12_RESOURCE_ST TransitionResource(*pBuffD3D12, *pBuffD3D12, NewState, FlushImmediate); #ifdef _DEBUG - if(pBuffD3D12->GetDesc().Usage == USAGE_DYNAMIC) - VERIFY(pBuffD3D12->GetState() == D3D12_RESOURCE_STATE_GENERIC_READ, "Dynamic buffers are expected to never transition from D3D12_RESOURCE_STATE_GENERIC_READ state") + if(pBuffD3D12->GetDesc().Usage == USAGE_DYNAMIC && (pBuffD3D12->GetDesc().BindFlags & (BIND_SHADER_RESOURCE|BIND_UNORDERED_ACCESS)) == 0) + VERIFY(pBuffD3D12->GetState() == D3D12_RESOURCE_STATE_GENERIC_READ, "Dynamic buffers without SRV/UAV bind flag are expected to never transition from D3D12_RESOURCE_STATE_GENERIC_READ state") #endif } @@ -293,10 +298,10 @@ void CommandContext::InsertAliasBarrier(D3D12ResourceBase& Before, D3D12Resource FlushResourceBarriers(); } -void CommandContext::DiscardDynamicDescriptors(Uint64 FrameNumber) +void CommandContext::DiscardDynamicDescriptors(Uint64 FenceValue) { for(size_t HeapType = 0; HeapType < _countof(m_DynamicGPUDescriptorAllocator); ++HeapType) - m_DynamicGPUDescriptorAllocator[HeapType].DiscardAllocations(FrameNumber); + m_DynamicGPUDescriptorAllocator[HeapType].DiscardAllocations(FenceValue); } DescriptorHeapAllocation CommandContext::AllocateDynamicGPUVisibleDescriptor( D3D12_DESCRIPTOR_HEAP_TYPE Type, UINT Count ) diff --git a/Graphics/GraphicsEngineD3D12/src/CommandListManager.cpp b/Graphics/GraphicsEngineD3D12/src/CommandListManager.cpp index b9b10954..2657200a 100644 --- a/Graphics/GraphicsEngineD3D12/src/CommandListManager.cpp +++ b/Graphics/GraphicsEngineD3D12/src/CommandListManager.cpp @@ -21,20 +21,6 @@ * of the possibility of such damages. */ -// -// Copyright (c) Microsoft. All rights reserved. -// This code is licensed under the MIT License (MIT). -// THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF -// ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY -// IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR -// PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. -// -// Developed by Minigraph -// -// Author: James Stanard -// -// Adapted to Diligent Engine: Egor Yusov - #include "pch.h" #include "CommandListManager.h" #include "RenderDeviceD3D12Impl.h" @@ -73,22 +59,22 @@ void CommandListManager::RequestAllocator(ID3D12CommandAllocator** ppAllocator) if (!m_DiscardedAllocators.empty()) { // Pick the oldest allocator at the front of the deque - // If this allocator is not yet available, there is no sense in - // looking at the rest of allocators + // If this allocator is not yet available, there is no point in + // looking at other allocators since they were released even + // later auto& AllocatorPair = m_DiscardedAllocators.front(); - // Get the NUMBER OF COMPLETED cmd lists - auto NumCompeltedCmdLists = m_pDeviceD3D12->GetNumCompletedCmdLists(); - // Note that NumCompeltedCmdLists only grows. So if after we queried + // Get the last completed fence value + auto CompletedFenceValue = m_pDeviceD3D12->GetCompletedFenceValue(); + // Note that CompletedFenceValue only grows. So if after we queried // the value, the actual value is increased in other thread, this will not - // be an issue as the only consequence is that the allocator that is - // potentially available may not be used. + // be an issue as the only consequence is that potentially available + // allocator may not be used. - // AllocatorPair.first is the NUMBER of the command list. - // If command list number is N, it can be reused when - // NumCompeltedCmdLists is at least N+1 - // (To reuse allocator used to create CmdList 0, NumCompeltedCmdLists must be at least 1) - if ( AllocatorPair.first < NumCompeltedCmdLists ) + // AllocatorPair.first is the fence value that was signaled AFTER the + // command list has been submitted. If CompletedFenceValue is at least + // this value, the allocator can be safely reused + if ( CompletedFenceValue >= AllocatorPair.first ) { *ppAllocator = AllocatorPair.second.Detach(); auto hr = (*ppAllocator)->Reset(); @@ -103,19 +89,19 @@ void CommandListManager::RequestAllocator(ID3D12CommandAllocator** ppAllocator) auto *pd3d12Device = m_pDeviceD3D12->GetD3D12Device(); auto hr = pd3d12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, __uuidof(*ppAllocator), reinterpret_cast<void**>(ppAllocator)); VERIFY(SUCCEEDED(hr), "Failed to create command allocator") - //wchar_t AllocatorName[32]; - //swprintf(AllocatorName, 32, L"CommandAllocator %zu", m_AllocatorPool.size()); - //(*ppAllocator)->SetName(AllocatorName); + wchar_t AllocatorName[32]; + swprintf(AllocatorName, _countof(AllocatorName), L"Cmd list allocator %ld", Atomics::AtomicIncrement(m_NumAllocators)-1); + (*ppAllocator)->SetName(AllocatorName); } } -void CommandListManager::DiscardAllocator( Uint64 CmdListNum, ID3D12CommandAllocator* pAllocator ) +void CommandListManager::DiscardAllocator( Uint64 FenceValue, ID3D12CommandAllocator* pAllocator ) { std::lock_guard<std::mutex> LockGuard(m_AllocatorMutex); - // CmdListNum is the number of the command list that was created by this allocator. - // This value is used to understand when it is safe to reuse the allocator - m_DiscardedAllocators.emplace_back( CmdListNum, CComPtr<ID3D12CommandAllocator>(pAllocator) ); + // FenceValue is the value that was signaled by the command queue after it + // executed the command list created by the allocator + m_DiscardedAllocators.emplace_back( FenceValue, CComPtr<ID3D12CommandAllocator>(pAllocator) ); } } diff --git a/Graphics/GraphicsEngineD3D12/src/CommandQueueD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/CommandQueueD3D12Impl.cpp new file mode 100644 index 00000000..e57e5a1a --- /dev/null +++ b/Graphics/GraphicsEngineD3D12/src/CommandQueueD3D12Impl.cpp @@ -0,0 +1,81 @@ +/* Copyright 2015-2017 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 "CommandQueueD3D12Impl.h" + +namespace Diligent +{ + +CommandQueueD3D12Impl::CommandQueueD3D12Impl(IReferenceCounters *pRefCounters, ID3D12CommandQueue *pd3d12NativeCmdQueue, ID3D12Fence *pd3d12Fence) : + TBase(pRefCounters), + m_pd3d12CmdQueue(pd3d12NativeCmdQueue), + m_d3d12Fence(pd3d12Fence), + m_NextFenceValue(1), + m_WaitForGPUEventHandle( CreateEvent(nullptr, false, false, nullptr) ) +{ + VERIFY_EXPR(m_WaitForGPUEventHandle != INVALID_HANDLE_VALUE); + m_d3d12Fence->Signal(0); +} + +CommandQueueD3D12Impl::~CommandQueueD3D12Impl() +{ + CloseHandle(m_WaitForGPUEventHandle); +} + +IMPLEMENT_QUERY_INTERFACE( CommandQueueD3D12Impl, IID_CommandQueueD3D12, TBase ) + +UINT64 CommandQueueD3D12Impl::ExecuteCommandList(ID3D12GraphicsCommandList* commandList) +{ + ID3D12CommandList *const ppCmdLists[] = {commandList}; + m_pd3d12CmdQueue->ExecuteCommandLists(1, ppCmdLists); + auto FenceValue = m_NextFenceValue; + // Signal the fence + m_pd3d12CmdQueue->Signal(m_d3d12Fence, FenceValue); + // Increment the value + Atomics::AtomicIncrement(m_NextFenceValue); + return FenceValue; +} + +void CommandQueueD3D12Impl::IdleGPU() +{ + Uint64 LastSignaledFenceValue = m_NextFenceValue; + m_pd3d12CmdQueue->Signal(m_d3d12Fence, LastSignaledFenceValue); + Atomics::AtomicIncrement(m_NextFenceValue); + if (GetCompletedFenceValue() < LastSignaledFenceValue) + { + m_d3d12Fence->SetEventOnCompletion(LastSignaledFenceValue, m_WaitForGPUEventHandle); + WaitForSingleObject(m_WaitForGPUEventHandle, INFINITE); + VERIFY(GetCompletedFenceValue() == LastSignaledFenceValue, "Unexpected signaled fence value"); + } +} + +Uint64 CommandQueueD3D12Impl::GetCompletedFenceValue() +{ + auto CompletedFenceValue = m_d3d12Fence->GetCompletedValue(); + if(CompletedFenceValue > m_LastCompletedFenceValue) + m_LastCompletedFenceValue = CompletedFenceValue; + return m_LastCompletedFenceValue; +} + +} diff --git a/Graphics/GraphicsEngineD3D12/src/D3D12TypeConversions.cpp b/Graphics/GraphicsEngineD3D12/src/D3D12TypeConversions.cpp index 68082795..f50107f9 100644 --- a/Graphics/GraphicsEngineD3D12/src/D3D12TypeConversions.cpp +++ b/Graphics/GraphicsEngineD3D12/src/D3D12TypeConversions.cpp @@ -268,12 +268,18 @@ void BufferViewDesc_to_D3D12_SRV_DESC(const BufferDesc &BuffDesc, const BufferVi BufferViewDesc_to_D3D_SRV_DESC(BuffDesc, SRVDesc, D3D12SRVDesc); D3D12SRVDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; D3D12SRVDesc.Buffer.Flags = D3D12_BUFFER_SRV_FLAG_NONE; + VERIFY_EXPR(BuffDesc.BindFlags & BIND_SHADER_RESOURCE) + if (BuffDesc.Mode == BUFFER_MODE_STRUCTURED) + D3D12SRVDesc.Buffer.StructureByteStride = BuffDesc.ElementByteStride; } void BufferViewDesc_to_D3D12_UAV_DESC(const BufferDesc &BuffDesc, const BufferViewDesc& UAVDesc, D3D12_UNORDERED_ACCESS_VIEW_DESC &D3D12UAVDesc) { BufferViewDesc_to_D3D_UAV_DESC(BuffDesc, UAVDesc, D3D12UAVDesc); D3D12UAVDesc.Buffer.Flags = D3D12_BUFFER_UAV_FLAG_NONE; + VERIFY_EXPR(BuffDesc.BindFlags & BIND_UNORDERED_ACCESS) + if (BuffDesc.Mode == BUFFER_MODE_STRUCTURED) + D3D12UAVDesc.Buffer.StructureByteStride = BuffDesc.ElementByteStride; } D3D12_STATIC_BORDER_COLOR BorderColorToD3D12StaticBorderColor(const Float32 BorderColor[]) diff --git a/Graphics/GraphicsEngineD3D12/src/DescriptorHeap.cpp b/Graphics/GraphicsEngineD3D12/src/DescriptorHeap.cpp index d226a3b8..bef31f68 100644 --- a/Graphics/GraphicsEngineD3D12/src/DescriptorHeap.cpp +++ b/Graphics/GraphicsEngineD3D12/src/DescriptorHeap.cpp @@ -131,20 +131,20 @@ void DescriptorHeapAllocationManager::Free(DescriptorHeapAllocation&& Allocation // // See http://diligentgraphics.com/diligent-engine/architecture/d3d12/managing-resource-lifetimes/ // - // If basic requirement is met, GetNextCmdListNumber() will never return a number that is less than the last - // command list number that references descriptors from the allocation - m_FreeBlockManager.Free(DescriptorOffset, Allocation.GetNumHandles(), m_pDeviceD3D12Impl->GetNextCmdListNumber()); + // If basic requirement is met, GetNextFenceValue() will never return a number that is less than the fence value + // associated with the last command list that references descriptors from the allocation + m_FreeBlockManager.Free(DescriptorOffset, Allocation.GetNumHandles(), m_pDeviceD3D12Impl->GetNextFenceValue()); // Clear the allocation Allocation = DescriptorHeapAllocation(); } -void DescriptorHeapAllocationManager::ReleaseStaleAllocations(Uint64 NumCompletedCmdLists) +void DescriptorHeapAllocationManager::ReleaseStaleAllocations(Uint64 LastCompletedFenceValue) { std::lock_guard<std::mutex> LockGuard(m_AllocationMutex); // Methods of VariableSizeGPUAllocationsManager class are not thread safe! - m_FreeBlockManager.ReleaseStaleAllocations(NumCompletedCmdLists); + m_FreeBlockManager.ReleaseStaleAllocations(LastCompletedFenceValue); } @@ -175,9 +175,10 @@ CPUDescriptorHeap::~CPUDescriptorHeap() Uint32 TotalDescriptors = 0; for (auto HeapPoolIt = m_HeapPool.begin(); HeapPoolIt != m_HeapPool.end(); ++HeapPoolIt) { - VERIFY(HeapPoolIt->GetNumAvailableDescriptors() == m_HeapDesc.NumDescriptors, "Not all descriptors in the descriptor pool are released"); + VERIFY(HeapPoolIt->GetNumAvailableDescriptors() == HeapPoolIt->GetMaxDescriptors(), "Not all descriptors in the descriptor pool are released"); TotalDescriptors += HeapPoolIt->GetMaxDescriptors(); } + TotalDescriptors = std::max(TotalDescriptors, 1u); LOG_INFO_MESSAGE(GetD3D12DescriptorHeapTypeLiteralName(m_HeapDesc.Type), " CPU heap max size: ", m_MaxHeapSize, " (", m_MaxHeapSize*100/ TotalDescriptors, "%) " ". Max stale size: ", m_MaxStaleSize, " (", m_MaxStaleSize * 100 / TotalDescriptors, "%)"); @@ -191,18 +192,22 @@ DescriptorHeapAllocation CPUDescriptorHeap::Allocate( uint32_t Count ) DescriptorHeapAllocation Allocation; // Go through all descriptor heap managers that have free descriptors - for (auto AvailableHeapIt = m_AvailableHeaps.begin(); AvailableHeapIt != m_AvailableHeaps.end(); ++AvailableHeapIt) + auto AvailableHeapIt = m_AvailableHeaps.begin(); + while( AvailableHeapIt != m_AvailableHeaps.end() ) { + auto NextIt = AvailableHeapIt; + ++NextIt; // Try to allocate descriptor using the current descriptor heap manager Allocation = m_HeapPool[*AvailableHeapIt].Allocate(Count); // Remove the manager from the pool if it has no more available descriptors - if(m_HeapPool[*AvailableHeapIt].GetNumAvailableDescriptors() == 0) + if (m_HeapPool[*AvailableHeapIt].GetNumAvailableDescriptors() == 0) m_AvailableHeaps.erase(*AvailableHeapIt); // Terminate the loop if descriptor was successfully allocated, otherwise // go to the next manager if(Allocation.GetCpuHandle().ptr != 0) break; + AvailableHeapIt = NextIt; } // If there were no available descriptor heap managers or no manager was able @@ -212,7 +217,7 @@ DescriptorHeapAllocation CPUDescriptorHeap::Allocate( uint32_t Count ) // Make sure the heap is large enough to accomodate the requested number of descriptors if(Count > m_HeapDesc.NumDescriptors) { - LOG_WARNING_MESSAGE("Number of requested descriptors exceeds the descriptor heap size. Increasing the number of descriptors in the heap") + LOG_WARNING_MESSAGE("Number of requested CPU descriptors handles (", Count, ") exceeds the descriptor heap size (", m_HeapDesc.NumDescriptors,"). Increasing the number of descriptors in the heap") } m_HeapDesc.NumDescriptors = std::max(m_HeapDesc.NumDescriptors, static_cast<UINT>(Count)); // Create a new descriptor heap manager. Note that this constructor creates a new D3D12 descriptor @@ -239,7 +244,7 @@ void CPUDescriptorHeap::Free(DescriptorHeapAllocation&& Allocation) m_HeapPool[ManagerId].Free(std::move(Allocation)); } -void CPUDescriptorHeap::ReleaseStaleAllocations(Uint64 NumCompletedCmdLists) +void CPUDescriptorHeap::ReleaseStaleAllocations(Uint64 LastCompletedFenceValue) { std::lock_guard<std::mutex> LockGuard(m_AllocationMutex); size_t StaleSize = 0; @@ -248,7 +253,7 @@ void CPUDescriptorHeap::ReleaseStaleAllocations(Uint64 NumCompletedCmdLists) // Update size before releasing stale allocations StaleSize += m_HeapPool[HeapManagerInd].GetNumStaleDescriptors(); - m_HeapPool[HeapManagerInd].ReleaseStaleAllocations(NumCompletedCmdLists); + m_HeapPool[HeapManagerInd].ReleaseStaleAllocations(LastCompletedFenceValue); // Return the manager to the pool of available managers if it has available descriptors if(m_HeapPool[HeapManagerInd].GetNumAvailableDescriptors() > 0) m_AvailableHeaps.insert(HeapManagerInd); @@ -297,9 +302,11 @@ GPUDescriptorHeap::~GPUDescriptorHeap() DescriptorHeapAllocation GPUDescriptorHeap::Allocate(uint32_t Count) { + VERIFY_EXPR(Count > 0); // Note: this mutex may be redundant as DescriptorHeapAllocationManager::Allocate() is itself thread-safe std::lock_guard<std::mutex> LockGuard(m_AllocMutex); DescriptorHeapAllocation Allocation = m_HeapAllocationManager.Allocate(Count); + VERIFY(!Allocation.IsNull(), "Failed to allocate ", Count, " GPU descriptors"); m_CurrentSize += (Allocation.GetCpuHandle().ptr != 0) ? Count : 0; m_MaxHeapSize = std::max(m_MaxHeapSize, m_CurrentSize); @@ -309,9 +316,11 @@ DescriptorHeapAllocation GPUDescriptorHeap::Allocate(uint32_t Count) DescriptorHeapAllocation GPUDescriptorHeap::AllocateDynamic(uint32_t Count) { + VERIFY_EXPR(Count > 0); // Note: this mutex may be redundant as DescriptorHeapAllocationManager::Allocate() is itself thread-safe std::lock_guard<std::mutex> LockGuard(m_DynAllocMutex); DescriptorHeapAllocation Allocation = m_DynamicAllocationsManager.Allocate(Count); + VERIFY(!Allocation.IsNull(), "Failed to allocate ", Count, " dynamic GPU descriptors"); m_CurrentDynamicSize += (Allocation.GetCpuHandle().ptr != 0) ? Count : 0; m_MaxDynamicSize = std::max(m_MaxDynamicSize, m_CurrentDynamicSize); @@ -339,18 +348,18 @@ void GPUDescriptorHeap::Free(DescriptorHeapAllocation&& Allocation) } } -void GPUDescriptorHeap::ReleaseStaleAllocations(Uint64 NumCompletedCmdLists) +void GPUDescriptorHeap::ReleaseStaleAllocations(Uint64 LastCompletedFenceValue) { { std::lock_guard<std::mutex> LockGuard(m_AllocMutex); m_MaxStaleSize = std::max(m_MaxStaleSize, static_cast<Uint32>(m_HeapAllocationManager.GetNumStaleDescriptors())); - m_HeapAllocationManager.ReleaseStaleAllocations(NumCompletedCmdLists); + m_HeapAllocationManager.ReleaseStaleAllocations(LastCompletedFenceValue); } { std::lock_guard<std::mutex> LockGuard(m_DynAllocMutex); m_MaxDynamicStaleSize = std::max(m_MaxDynamicStaleSize, static_cast<Uint32>(m_DynamicAllocationsManager.GetNumStaleDescriptors())); - m_DynamicAllocationsManager.ReleaseStaleAllocations(NumCompletedCmdLists); + m_DynamicAllocationsManager.ReleaseStaleAllocations(LastCompletedFenceValue); } } @@ -365,7 +374,7 @@ DynamicSuballocationsManager::DynamicSuballocationsManager(IMemoryAllocator &All { } -void DynamicSuballocationsManager::DiscardAllocations(Uint64 FrameNumber) +void DynamicSuballocationsManager::DiscardAllocations(Uint64 /*FenceValue*/) { // Clear the list and dispose all allocated chunks of GPU descriptor heap. // The chunks will be added to the release queue in the allocations manager diff --git a/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp index 003a464b..96437f51 100644 --- a/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/DeviceContextD3D12Impl.cpp @@ -24,7 +24,7 @@ #include "pch.h" #include "RenderDeviceD3D12Impl.h" #include "DeviceContextD3D12Impl.h" -#include "SwapChainD3D12Impl.h" +#include "SwapChainD3D12.h" #include "PipelineStateD3D12Impl.h" #include "CommandContext.h" #include "TextureD3D12Impl.h" @@ -33,12 +33,13 @@ #include "d3dx12_win.h" #include "DynamicUploadHeap.h" #include "CommandListD3D12Impl.h" +#include "DXGITypeConversions.h" namespace Diligent { - DeviceContextD3D12Impl::DeviceContextD3D12Impl( IMemoryAllocator &RawMemAllocator, RenderDeviceD3D12Impl *pDeviceD3D12Impl, bool bIsDeferred, const EngineD3D12Attribs &Attribs, Uint32 ContextId) : - TDeviceContextBase(RawMemAllocator, pDeviceD3D12Impl, bIsDeferred), + DeviceContextD3D12Impl::DeviceContextD3D12Impl( IReferenceCounters *pRefCounters, RenderDeviceD3D12Impl *pDeviceD3D12Impl, bool bIsDeferred, const EngineD3D12Attribs &Attribs, Uint32 ContextId) : + TDeviceContextBase(pRefCounters, pDeviceD3D12Impl, bIsDeferred), m_pUploadHeap(pDeviceD3D12Impl->RequestUploadHeap() ), m_NumCommandsInCurCtx(0), m_NumCommandsToFlush(bIsDeferred ? std::numeric_limits<decltype(m_NumCommandsToFlush)>::max() : Attribs.NumCommandsToFlushCmdList), @@ -49,8 +50,6 @@ namespace Diligent m_CmdListAllocator(GetRawAllocator(), sizeof(CommandListD3D12Impl), 64 ), m_ContextId(ContextId) { - m_NumBoundRenderTargets = 0; - auto *pd3d12Device = pDeviceD3D12Impl->GetD3D12Device(); D3D12_COMMAND_SIGNATURE_DESC CmdSignatureDesc = {}; @@ -80,10 +79,15 @@ namespace Diligent if(m_bIsDeferred) ValidatedCast<RenderDeviceD3D12Impl>(m_pDevice.RawPtr())->DisposeCommandContext(m_pCurrCmdCtx); else + { + if (m_NumCommandsInCurCtx != 0) + LOG_WARNING_MESSAGE("Flusing outstanding commands from the device context being destroyed. This may result in D3D12 synchronization errors") + Flush(false); + } } - //IMPLEMENT_QUERY_INTERFACE( DeviceContextD3D12Impl, IID_DeviceContextD3D12, TDeviceContextBase ) + IMPLEMENT_QUERY_INTERFACE( DeviceContextD3D12Impl, IID_DeviceContextD3D12, TDeviceContextBase ) const LONG MaxD3D12TexDim = D3D12_REQ_TEXTURE2D_U_OR_V_DIMENSION; const Uint32 MaxD3D12ScissorRects = D3D12_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; @@ -112,7 +116,8 @@ namespace Diligent void DeviceContextD3D12Impl::SetPipelineState(IPipelineState *pPipelineState) { - if (m_NumCommandsInCurCtx >= m_NumCommandsToFlush) + // Never flush deferred context! + if (!m_bIsDeferred && m_NumCommandsInCurCtx >= m_NumCommandsToFlush) { Flush(true); } @@ -159,7 +164,7 @@ namespace Diligent m_CommittedIBFormat = VT_UNDEFINED; m_bCommittedD3D12VBsUpToDate = false; m_bCommittedD3D12IBUpToDate = false; - RebindRenderTargets(); + CommitRenderTargets(); CommitViewports(); } m_pCommittedResourceCache = nullptr; @@ -464,8 +469,15 @@ namespace Diligent } else { - auto *pDSV = ValidatedCast<SwapChainD3D12Impl>(m_pSwapChain.RawPtr())->GetDepthBufferDSV(); - pDSVD3D12 = ValidatedCast<ITextureViewD3D12>(pDSV); + if (m_pSwapChain) + { + pDSVD3D12 = ValidatedCast<ISwapChainD3D12>(m_pSwapChain.RawPtr())->GetDepthBufferDSV(); + } + else + { + LOG_ERROR("Failed to clear default depth stencil buffer: swap chain is not initialized in the device context"); + return; + } } D3D12_CLEAR_FLAGS d3d12ClearFlags = (D3D12_CLEAR_FLAGS)0; if( ClearFlags & CLEAR_DEPTH_FLAG ) d3d12ClearFlags |= D3D12_CLEAR_FLAG_DEPTH; @@ -489,8 +501,15 @@ namespace Diligent } else { - auto *pBackBufferRTV = ValidatedCast<SwapChainD3D12Impl>(m_pSwapChain.RawPtr())->GetCurrentBackBufferRTV(); - pd3d12RTV = ValidatedCast<ITextureViewD3D12>(pBackBufferRTV); + if (m_pSwapChain) + { + pd3d12RTV = ValidatedCast<ISwapChainD3D12>(m_pSwapChain.RawPtr())->GetCurrentBackBufferRTV(); + } + else + { + LOG_ERROR("Failed to clear default render target: swap chain is not initialized in the device context"); + return; + } } static const float Zero[4] = { 0.f, 0.f, 0.f, 0.f }; @@ -505,13 +524,20 @@ namespace Diligent void DeviceContextD3D12Impl::Flush(bool RequestNewCmdCtx) { + auto pDeviceD3D12Impl = ValidatedCast<RenderDeviceD3D12Impl>(m_pDevice.RawPtr()); if( m_pCurrCmdCtx ) { VERIFY(!m_bIsDeferred, "Deferred contexts cannot execute command lists directly"); - ValidatedCast<RenderDeviceD3D12Impl>(m_pDevice.RawPtr())->CloseAndExecuteCommandContext(m_pCurrCmdCtx); + if (m_NumCommandsInCurCtx != 0) + { + m_pCurrCmdCtx->FlushResourceBarriers(); + pDeviceD3D12Impl->CloseAndExecuteCommandContext(m_pCurrCmdCtx, true); + } + else + pDeviceD3D12Impl->DisposeCommandContext(m_pCurrCmdCtx); } - m_pCurrCmdCtx = RequestNewCmdCtx ? ValidatedCast<RenderDeviceD3D12Impl>(m_pDevice.RawPtr())->AllocateCommandContext() : nullptr; + m_pCurrCmdCtx = RequestNewCmdCtx ? pDeviceD3D12Impl->AllocateCommandContext() : nullptr; m_NumCommandsInCurCtx = 0; m_pPipelineState.Release(); @@ -529,10 +555,17 @@ namespace Diligent m_bCommittedD3D12VBsUpToDate = false; } - void DeviceContextD3D12Impl::ClearState() + void DeviceContextD3D12Impl::InvalidateState() { - TDeviceContextBase::ClearState(); - LOG_ERROR_ONCE("DeviceContextD3D12Impl::ClearState() is not implemented"); + if (m_NumCommandsInCurCtx != 0) + LOG_WARNING_MESSAGE("Invalidating context that has outstanding commands in it. Call Flush() to submit commands for execution") + + TDeviceContextBase::InvalidateState(); + m_CommittedD3D12IndexBuffer = nullptr; + m_CommittedD3D12IndexDataStartOffset = 0; + m_CommittedIBFormat = VT_UNDEFINED; + m_bCommittedD3D12VBsUpToDate = false; + m_bCommittedD3D12IBUpToDate = false; } void DeviceContextD3D12Impl::SetIndexBuffer( IBuffer *pIndexBuffer, Uint32 ByteOffset ) @@ -597,7 +630,7 @@ namespace Diligent } - void DeviceContextD3D12Impl::RebindRenderTargets() + void DeviceContextD3D12Impl::CommitRenderTargets() { const Uint32 MaxD3D12RTs = D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT; Uint32 NumRenderTargets = m_NumBoundRenderTargets; @@ -606,12 +639,20 @@ namespace Diligent ITextureViewD3D12 *ppRTVs[MaxD3D12RTs]; // Do not initialize with zeroes! ITextureViewD3D12 *pDSV = nullptr; - if( NumRenderTargets == 0 && m_pBoundDepthStencil == nullptr ) + if( m_IsDefaultFramebufferBound ) { - NumRenderTargets = 1; - auto *pSwapChainD3D12 = ValidatedCast<SwapChainD3D12Impl>( m_pSwapChain.RawPtr() ); - ppRTVs[0] = ValidatedCast<ITextureViewD3D12>(pSwapChainD3D12->GetCurrentBackBufferRTV()); - pDSV = ValidatedCast<ITextureViewD3D12>(pSwapChainD3D12->GetDepthBufferDSV()); + if (m_pSwapChain) + { + NumRenderTargets = 1; + auto *pSwapChainD3D12 = ValidatedCast<ISwapChainD3D12>(m_pSwapChain.RawPtr()); + ppRTVs[0] = pSwapChainD3D12->GetCurrentBackBufferRTV(); + pDSV = pSwapChainD3D12->GetDepthBufferDSV(); + } + else + { + LOG_WARNING_MESSAGE("Failed to bind default render targets and depth-stencil buffer: swap chain is not initialized in the device context"); + return; + } } else { @@ -626,7 +667,7 @@ namespace Diligent { if( TDeviceContextBase::SetRenderTargets( NumRenderTargets, ppRenderTargets, pDepthStencil ) ) { - RebindRenderTargets(); + CommitRenderTargets(); // Set the viewport to match the render target size SetViewports(1, nullptr, 0, 0); @@ -638,24 +679,27 @@ namespace Diligent return m_pUploadHeap->Allocate(NumBytes); } - void DeviceContextD3D12Impl::UpdateBufferRegion(BufferD3D12Impl *pBuffD3D12, const void *pData, Uint64 DstOffset, Uint64 NumBytes) + void DeviceContextD3D12Impl::UpdateBufferRegion(class BufferD3D12Impl *pBuffD3D12, DynamicAllocation& Allocation, Uint64 DstOffset, Uint64 NumBytes) { - VERIFY(pBuffD3D12->GetDesc().Usage != USAGE_DYNAMIC, "Dynamic buffers must be updated via Map()") - auto pCmdCtx = RequestCmdContext(); -//#if USE_RING_BUFFER - //auto TmpSpace = pCmdCtx->AllocateUploadBuffer(NumBytes); - auto TmpSpace = m_pUploadHeap->Allocate(NumBytes); - memcpy(TmpSpace.CPUAddress, pData, NumBytes); + VERIFY_EXPR( static_cast<size_t>(NumBytes) == NumBytes ); pCmdCtx->TransitionResource(pBuffD3D12, D3D12_RESOURCE_STATE_COPY_DEST, true); - // Source buffer is already in right state, which cannot be changed size_t DstBuffDataStartByteOffset; auto *pd3d12Buff = pBuffD3D12->GetD3D12Buffer(DstBuffDataStartByteOffset, m_ContextId); VERIFY(DstBuffDataStartByteOffset == 0, "Dst buffer must not be suballocated"); - pCmdCtx->GetCommandList()->CopyBufferRegion( pd3d12Buff, DstOffset + DstBuffDataStartByteOffset, TmpSpace.pBuffer, TmpSpace.Offset, NumBytes); + pCmdCtx->GetCommandList()->CopyBufferRegion( pd3d12Buff, DstOffset + DstBuffDataStartByteOffset, Allocation.pBuffer, Allocation.Offset, NumBytes); ++m_NumCommandsInCurCtx; } + void DeviceContextD3D12Impl::UpdateBufferRegion(BufferD3D12Impl *pBuffD3D12, const void *pData, Uint64 DstOffset, Uint64 NumBytes) + { + VERIFY(pBuffD3D12->GetDesc().Usage != USAGE_DYNAMIC, "Dynamic buffers must be updated via Map()") + VERIFY_EXPR( static_cast<size_t>(NumBytes) == NumBytes ); + auto TmpSpace = m_pUploadHeap->Allocate(static_cast<size_t>(NumBytes)); + memcpy(TmpSpace.CPUAddress, pData, static_cast<size_t>(NumBytes)); + UpdateBufferRegion(pBuffD3D12, TmpSpace, DstOffset, NumBytes); + } + void DeviceContextD3D12Impl::CopyBufferRegion(BufferD3D12Impl *pSrcBuffD3D12, BufferD3D12Impl *pDstBuffD3D12, Uint64 SrcOffset, Uint64 DstOffset, Uint64 NumBytes) { VERIFY(pDstBuffD3D12->GetDesc().Usage != USAGE_DYNAMIC, "Dynamic buffers cannot be copy destinations") @@ -684,28 +728,93 @@ namespace Diligent DstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; DstLocation.pResource = pDstTexture->GetD3D12Resource(); - DstLocation.SubresourceIndex = SrcSubResIndex; + DstLocation.SubresourceIndex = DstSubResIndex; SrcLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; SrcLocation.pResource = pSrcTexture->GetD3D12Resource(); - SrcLocation.SubresourceIndex = DstSubResIndex; + SrcLocation.SubresourceIndex = SrcSubResIndex; pCmdCtx->GetCommandList()->CopyTextureRegion( &DstLocation, DstX, DstY, DstZ, &SrcLocation, pD3D12SrcBox); ++m_NumCommandsInCurCtx; } + void DeviceContextD3D12Impl::CopyTextureRegion(IBuffer *pSrcBuffer, Uint32 SrcStride, Uint32 SrcDepthStride, class TextureD3D12Impl *pTextureD3D12, Uint32 DstSubResIndex, const Box &DstBox) + { + auto *pBufferD3D12 = ValidatedCast<BufferD3D12Impl>(pSrcBuffer); + const auto& TexDesc = pTextureD3D12->GetDesc(); + VERIFY(pBufferD3D12->GetState() == D3D12_RESOURCE_STATE_GENERIC_READ, "Staging buffer is expected to always be in D3D12_RESOURCE_STATE_GENERIC_READ state") + + auto *pCmdCtx = RequestCmdContext(); + auto *pCmdList = pCmdCtx->GetCommandList(); + auto TextureState = pTextureD3D12->GetState(); + D3D12_RESOURCE_BARRIER BarrierDesc; + BarrierDesc.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + BarrierDesc.Transition.pResource = pTextureD3D12->GetD3D12Resource(); + BarrierDesc.Transition.Subresource = DstSubResIndex; + BarrierDesc.Transition.StateBefore = TextureState; + BarrierDesc.Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST; + BarrierDesc.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + bool StateTransitionRequired = (TextureState & D3D12_RESOURCE_STATE_COPY_DEST) != D3D12_RESOURCE_STATE_COPY_DEST; + if (StateTransitionRequired) + pCmdList->ResourceBarrier(1, &BarrierDesc); + + D3D12_TEXTURE_COPY_LOCATION DstLocation; + DstLocation.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + DstLocation.pResource = pTextureD3D12->GetD3D12Resource(); + DstLocation.SubresourceIndex = static_cast<UINT>(DstSubResIndex); + + D3D12_TEXTURE_COPY_LOCATION SrcLocation; + SrcLocation.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + SrcLocation.pResource = pBufferD3D12->GetD3D12Resource(); + D3D12_PLACED_SUBRESOURCE_FOOTPRINT &Footpring = SrcLocation.PlacedFootprint; + Footpring.Offset = 0; + Footpring.Footprint.Width = static_cast<UINT>(DstBox.MaxX - DstBox.MinX); + Footpring.Footprint.Height = static_cast<UINT>(DstBox.MaxY - DstBox.MinY); + Footpring.Footprint.Depth = static_cast<UINT>(DstBox.MaxZ - DstBox.MinZ); // Depth cannot be 0 + Footpring.Footprint.Format = TexFormatToDXGI_Format(TexDesc.Format); + + Footpring.Footprint.RowPitch = static_cast<UINT>(SrcStride); + VERIFY(Footpring.Footprint.RowPitch * Footpring.Footprint.Height * Footpring.Footprint.Depth <= pBufferD3D12->GetDesc().uiSizeInBytes, "Buffer is not large enough"); + VERIFY(SrcDepthStride == 0 || static_cast<UINT>(SrcDepthStride) == Footpring.Footprint.RowPitch * Footpring.Footprint.Height, "Depth stride must be equal to the size 2D level"); + + D3D12_BOX D3D12SrcBox; + D3D12SrcBox.left = 0; + D3D12SrcBox.right = Footpring.Footprint.Width; + D3D12SrcBox.top = 0; + D3D12SrcBox.bottom = Footpring.Footprint.Height; + D3D12SrcBox.front = 0; + D3D12SrcBox.back = Footpring.Footprint.Depth; + pCmdCtx->GetCommandList()->CopyTextureRegion( &DstLocation, + static_cast<UINT>( DstBox.MinX ), + static_cast<UINT>( DstBox.MinY ), + static_cast<UINT>( DstBox.MinZ ), + &SrcLocation, &D3D12SrcBox); + + ++m_NumCommandsInCurCtx; + + if (StateTransitionRequired) + { + std::swap(BarrierDesc.Transition.StateBefore, BarrierDesc.Transition.StateAfter); + pCmdList->ResourceBarrier(1, &BarrierDesc); + } + } + void DeviceContextD3D12Impl::GenerateMips(TextureViewD3D12Impl *pTexView) { auto *pCtx = RequestCmdContext(); m_MipsGenerator.GenerateMips(ValidatedCast<RenderDeviceD3D12Impl>(m_pDevice.RawPtr()), pTexView, *pCtx); + ++m_NumCommandsInCurCtx; } void DeviceContextD3D12Impl::FinishCommandList(class ICommandList **ppCommandList) { - CommandListD3D12Impl *pCmdListD3D12( NEW(m_CmdListAllocator, "CommandListD3D12Impl instance", CommandListD3D12Impl, m_pDevice, m_pCurrCmdCtx) ); + CommandListD3D12Impl *pCmdListD3D12( NEW_RC_OBJ(m_CmdListAllocator, "CommandListD3D12Impl instance", CommandListD3D12Impl) + (m_pDevice, m_pCurrCmdCtx) ); pCmdListD3D12->QueryInterface( IID_CommandList, reinterpret_cast<IObject**>(ppCommandList) ); m_pCurrCmdCtx = nullptr; Flush(true); + + InvalidateState(); } void DeviceContextD3D12Impl::ExecuteCommandList(class ICommandList *pCommandList) @@ -718,7 +827,23 @@ namespace Diligent // First execute commands in this context Flush(true); + InvalidateState(); + CommandListD3D12Impl* pCmdListD3D12 = ValidatedCast<CommandListD3D12Impl>(pCommandList); - ValidatedCast<RenderDeviceD3D12Impl>(m_pDevice.RawPtr())->CloseAndExecuteCommandContext(pCmdListD3D12->Close()); + ValidatedCast<RenderDeviceD3D12Impl>(m_pDevice.RawPtr())->CloseAndExecuteCommandContext(pCmdListD3D12->Close(), true); + } + + void DeviceContextD3D12Impl::TransitionTextureState(ITexture *pTexture, D3D12_RESOURCE_STATES State) + { + VERIFY_EXPR(pTexture != nullptr); + auto *pCmdCtx = RequestCmdContext(); + pCmdCtx->TransitionResource(ValidatedCast<ITextureD3D12>(pTexture), State); + } + + void DeviceContextD3D12Impl::TransitionBufferState(IBuffer *pBuffer, D3D12_RESOURCE_STATES State) + { + VERIFY_EXPR(pBuffer != nullptr); + auto *pCmdCtx = RequestCmdContext(); + pCmdCtx->TransitionResource(ValidatedCast<IBufferD3D12>(pBuffer), State); } } diff --git a/Graphics/GraphicsEngineD3D12/src/DynamicUploadHeap.cpp b/Graphics/GraphicsEngineD3D12/src/DynamicUploadHeap.cpp index 12622465..a28d3fc0 100644 --- a/Graphics/GraphicsEngineD3D12/src/DynamicUploadHeap.cpp +++ b/Graphics/GraphicsEngineD3D12/src/DynamicUploadHeap.cpp @@ -131,7 +131,7 @@ namespace Diligent if (!DynAlloc.pBuffer) { auto NewMaxSize = m_RingBuffers.back().GetMaxSize() * 2; - while(NewMaxSize < SizeInBytes)NewMaxSize*=2; + while(NewMaxSize < AlignedSize)NewMaxSize*=2; m_RingBuffers.emplace_back(NewMaxSize, m_Allocator, m_pDeviceD3D12->GetD3D12Device(), m_bIsCPUAccessible); DynAlloc = m_RingBuffers.back().Allocate(AlignedSize); } @@ -141,7 +141,7 @@ namespace Diligent return DynAlloc; } - void DynamicUploadHeap::FinishFrame(Uint64 FrameNum, Uint64 NumCompletedFrames) + 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); @@ -155,8 +155,8 @@ namespace Diligent for(size_t Ind = 0; Ind < m_RingBuffers.size(); ++Ind) { auto &RingBuff = m_RingBuffers[Ind]; - RingBuff.FinishCurrentFrame(FrameNum); - RingBuff.ReleaseCompletedFrames(NumCompletedFrames); + RingBuff.FinishCurrentFrame(FenceValue); + RingBuff.ReleaseCompletedFrames(LastCompletedFenceValue); if ( NumBuffsToDelete == Ind && Ind < m_RingBuffers.size()-1 && RingBuff.IsEmpty()) { ++NumBuffsToDelete; diff --git a/Graphics/GraphicsEngineD3D12/src/GenerateMips.cpp b/Graphics/GraphicsEngineD3D12/src/GenerateMips.cpp index 5ef672d4..26f949fe 100644 --- a/Graphics/GraphicsEngineD3D12/src/GenerateMips.cpp +++ b/Graphics/GraphicsEngineD3D12/src/GenerateMips.cpp @@ -61,7 +61,7 @@ namespace Diligent GenerateMipsHelper::GenerateMipsHelper(ID3D12Device *pd3d12Device) { CD3DX12_ROOT_PARAMETER Params[3]; - Params[0].InitAsConstants(4, 0); + Params[0].InitAsConstants(6, 0); CD3DX12_DESCRIPTOR_RANGE SRVRange(D3D12_DESCRIPTOR_RANGE_TYPE_SRV, 1, 0); Params[1].InitAsDescriptorTable(1, &SRVRange); CD3DX12_DESCRIPTOR_RANGE UAVRange(D3D12_DESCRIPTOR_RANGE_TYPE_UAV, 4, 0); @@ -85,12 +85,13 @@ namespace Diligent PSODesc.pRootSignature = m_pGenerateMipsRS; PSODesc.NodeMask = 0; PSODesc.Flags = D3D12_PIPELINE_STATE_FLAG_NONE; - + #define CreatePSO(PSO, ShaderByteCode) \ PSODesc.CS.pShaderBytecode = ShaderByteCode;\ PSODesc.CS.BytecodeLength = sizeof(ShaderByteCode);\ hr = pd3d12Device->CreateComputePipelineState(&PSODesc, __uuidof(PSO), reinterpret_cast<void**>( static_cast<ID3D12PipelineState**>(&PSO))); \ - CHECK_D3D_RESULT_THROW(hr, "Failed to create Pipeline state for mipmap generation") + CHECK_D3D_RESULT_THROW(hr, "Failed to create Pipeline state for mipmap generation") \ + PSO->SetName(L"Generate mips PSO"); CreatePSO(m_pGenerateMipsLinearPSO[0], g_pGenerateMipsLinearCS); CreatePSO(m_pGenerateMipsLinearPSO[1], g_pGenerateMipsLinearOddXCS); @@ -109,82 +110,86 @@ namespace Diligent auto *pTexture = pTexView->GetTexture(); auto *pTexD3D12 = ValidatedCast<TextureD3D12Impl>( pTexture ); auto &TexDesc = pTexture->GetDesc(); - auto *pSRV = pTexture->GetDefaultView(TEXTURE_VIEW_SHADER_RESOURCE); - auto *pSRVD3D12Impl = ValidatedCast<TextureViewD3D12Impl>(pSRV); - auto SRVDescriptorHandle = pSRVD3D12Impl->GetCPUDescriptorHandle(); + auto SRVDescriptorHandle = pTexD3D12->GetTexArraySRV(); Ctx.TransitionResource(pTexD3D12, D3D12_RESOURCE_STATE_UNORDERED_ACCESS); auto *pd3d12Device = pRenderDeviceD3D12->GetD3D12Device(); - for (uint32_t TopMip = 0; TopMip < TexDesc.MipLevels-1; ) - { - uint32_t SrcWidth = TexDesc.Width >> TopMip; - uint32_t SrcHeight = TexDesc.Height >> TopMip; - uint32_t DstWidth = SrcWidth >> 1; - uint32_t DstHeight = SrcHeight >> 1; - - // Determine if the first downsample is more than 2:1. This happens whenever - // the source width or height is odd. - uint32_t NonPowerOfTwo = (SrcWidth & 1) | (SrcHeight & 1) << 1; - if (TexDesc.Format == TEX_FORMAT_RGBA8_UNORM_SRGB) - ComputeCtx.SetPipelineState(m_pGenerateMipsGammaPSO[NonPowerOfTwo]); - else - ComputeCtx.SetPipelineState(m_pGenerateMipsLinearPSO[NonPowerOfTwo]); - - // We can downsample up to four times, but if the ratio between levels is not - // exactly 2:1, we have to shift our blend weights, which gets complicated or - // expensive. Maybe we can update the code later to compute sample weights for - // each successive downsample. We use _BitScanForward to count number of zeros - // in the low bits. Zeros indicate we can divide by two without truncating. - uint32_t AdditionalMips; - _BitScanForward((unsigned long*)&AdditionalMips, DstWidth | DstHeight); - uint32_t NumMips = 1 + (AdditionalMips > 3 ? 3 : AdditionalMips); - if (TopMip + NumMips > TexDesc.MipLevels-1) - NumMips = TexDesc.MipLevels-1 - TopMip; - - // These are clamped to 1 after computing additional mips because clamped - // dimensions should not limit us from downsampling multiple times. (E.g. - // 16x1 -> 8x1 -> 4x1 -> 2x1 -> 1x1.) - if (DstWidth == 0) - DstWidth = 1; - if (DstHeight == 0) - DstHeight = 1; - - D3D12_DESCRIPTOR_HEAP_TYPE HeapType = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; - auto DescriptorAlloc = Ctx.AllocateDynamicGPUVisibleDescriptor(HeapType, 5); - CommandContext::ShaderDescriptorHeaps Heaps(DescriptorAlloc.GetDescriptorHeap()); - ComputeCtx.SetDescriptorHeaps(Heaps); - Ctx.GetCommandList()->SetComputeRootDescriptorTable(1, DescriptorAlloc.GetGpuHandle(0)); - Ctx.GetCommandList()->SetComputeRootDescriptorTable(2, DescriptorAlloc.GetGpuHandle(1)); - struct RootCBData + const auto &ViewDesc = pTexView->GetDesc(); + for (Uint32 ArrSlice = ViewDesc.FirstArraySlice; ArrSlice < ViewDesc.FirstArraySlice + ViewDesc.NumArraySlices; ++ArrSlice) + { + for (uint32_t TopMip = 0; TopMip < TexDesc.MipLevels - 1; ) { - Uint32 SrcMipLevel; // Texture level of source mip - Uint32 NumMipLevels; // Number of OutMips to write: [1, 4] - float TexelSize[2]; // 1.0 / OutMip1.Dimensions - }CBData = {TopMip, NumMips, 1.0f / static_cast<float>(DstWidth), 1.0f / static_cast<float>(DstHeight)}; - Ctx.GetCommandList()->SetComputeRoot32BitConstants(0, 4, &CBData, 0); - - // TODO: Shouldn't we transition top mip to shader resource state? - D3D12_CPU_DESCRIPTOR_HANDLE DstDescriptorRange = DescriptorAlloc.GetCpuHandle(); - const Uint32 MaxMipsHandledByCS = 4; // Max number of mip levels processed by one CS shader invocation - UINT DstRangeSize = 1 + MaxMipsHandledByCS; - D3D12_CPU_DESCRIPTOR_HANDLE SrcDescriptorRanges[5] = {}; - SrcDescriptorRanges[0] = SRVDescriptorHandle; - UINT SrcRangeSizes[5] = {1,1,1,1,1}; - // On Resource Binding Tier 2 hardware, all descriptor tables of type CBV and UAV declared in the set - // Root Signature must be populated and initialized, even if the shaders do not need the descriptor. - // So we must populate all 4 slots even though we may actually process less than 4 mip levels - // Copy top mip level UAV descriptor handle to all unused slots - for(Uint32 u=0; u < MaxMipsHandledByCS; ++u) - SrcDescriptorRanges[1+u] = pTexD3D12->GetUAVDescriptorHandle( std::min( TopMip+u+1, TexDesc.MipLevels-1), 0); - - pd3d12Device->CopyDescriptors(1, &DstDescriptorRange, &DstRangeSize, 1+MaxMipsHandledByCS, SrcDescriptorRanges, SrcRangeSizes, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); - - ComputeCtx.Dispatch((DstWidth+7)/8, (DstHeight+7)/8); - - Ctx.InsertUAVBarrier(*pTexD3D12, *pTexD3D12); - - TopMip += NumMips; - } + uint32_t SrcWidth = TexDesc.Width >> TopMip; + uint32_t SrcHeight = TexDesc.Height >> TopMip; + uint32_t DstWidth = SrcWidth >> 1; + uint32_t DstHeight = SrcHeight >> 1; + + // Determine if the first downsample is more than 2:1. This happens whenever + // the source width or height is odd. + uint32_t NonPowerOfTwo = (SrcWidth & 1) | (SrcHeight & 1) << 1; + if (TexDesc.Format == TEX_FORMAT_RGBA8_UNORM_SRGB) + ComputeCtx.SetPipelineState(m_pGenerateMipsGammaPSO[NonPowerOfTwo]); + else + ComputeCtx.SetPipelineState(m_pGenerateMipsLinearPSO[NonPowerOfTwo]); + + // We can downsample up to four times, but if the ratio between levels is not + // exactly 2:1, we have to shift our blend weights, which gets complicated or + // expensive. Maybe we can update the code later to compute sample weights for + // each successive downsample. We use _BitScanForward to count number of zeros + // in the low bits. Zeros indicate we can divide by two without truncating. + uint32_t AdditionalMips; + _BitScanForward((unsigned long*)&AdditionalMips, DstWidth | DstHeight); + uint32_t NumMips = 1 + (AdditionalMips > 3 ? 3 : AdditionalMips); + if (TopMip + NumMips > TexDesc.MipLevels - 1) + NumMips = TexDesc.MipLevels - 1 - TopMip; + + // These are clamped to 1 after computing additional mips because clamped + // dimensions should not limit us from downsampling multiple times. (E.g. + // 16x1 -> 8x1 -> 4x1 -> 2x1 -> 1x1.) + if (DstWidth == 0) + DstWidth = 1; + if (DstHeight == 0) + DstHeight = 1; + + D3D12_DESCRIPTOR_HEAP_TYPE HeapType = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; + auto DescriptorAlloc = Ctx.AllocateDynamicGPUVisibleDescriptor(HeapType, 5); + CommandContext::ShaderDescriptorHeaps Heaps(DescriptorAlloc.GetDescriptorHeap()); + ComputeCtx.SetDescriptorHeaps(Heaps); + Ctx.GetCommandList()->SetComputeRootDescriptorTable(1, DescriptorAlloc.GetGpuHandle(0)); + Ctx.GetCommandList()->SetComputeRootDescriptorTable(2, DescriptorAlloc.GetGpuHandle(1)); + struct RootCBData + { + Uint32 SrcMipLevel; // Texture level of source mip + Uint32 NumMipLevels; // Number of OutMips to write: [1, 4] + Uint32 ArraySlice; + Uint32 Dummy; + float TexelSize[2]; // 1.0 / OutMip1.Dimensions + }CBData = { TopMip, NumMips, ArrSlice, 0, 1.0f / static_cast<float>(DstWidth), 1.0f / static_cast<float>(DstHeight) }; + Ctx.GetCommandList()->SetComputeRoot32BitConstants(0, 6, &CBData, 0); + + // TODO: Shouldn't we transition top mip to shader resource state? + D3D12_CPU_DESCRIPTOR_HANDLE DstDescriptorRange = DescriptorAlloc.GetCpuHandle(); + const Uint32 MaxMipsHandledByCS = 4; // Max number of mip levels processed by one CS shader invocation + UINT DstRangeSize = 1 + MaxMipsHandledByCS; + D3D12_CPU_DESCRIPTOR_HANDLE SrcDescriptorRanges[5] = {}; + SrcDescriptorRanges[0] = SRVDescriptorHandle; + UINT SrcRangeSizes[5] = { 1,1,1,1,1 }; + // On Resource Binding Tier 2 hardware, all descriptor tables of type CBV and UAV declared in the set + // Root Signature must be populated and initialized, even if the shaders do not need the descriptor. + // So we must populate all 4 slots even though we may actually process less than 4 mip levels + // Copy top mip level UAV descriptor handle to all unused slots + for (Uint32 u = 0; u < MaxMipsHandledByCS; ++u) + SrcDescriptorRanges[1 + u] = pTexD3D12->GetMipLevelUAV(std::min(TopMip + u + 1, TexDesc.MipLevels - 1)); + + pd3d12Device->CopyDescriptors(1, &DstDescriptorRange, &DstRangeSize, 1 + MaxMipsHandledByCS, SrcDescriptorRanges, SrcRangeSizes, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV); + + ComputeCtx.Dispatch((DstWidth + 7) / 8, (DstHeight + 7) / 8); + + Ctx.InsertUAVBarrier(*pTexD3D12, *pTexD3D12); + + TopMip += NumMips; + } + } } } diff --git a/Graphics/GraphicsEngineD3D12/src/GraphicsEngineD3D12.def b/Graphics/GraphicsEngineD3D12/src/GraphicsEngineD3D12.def index 79094c73..8355bd14 100644 --- a/Graphics/GraphicsEngineD3D12/src/GraphicsEngineD3D12.def +++ b/Graphics/GraphicsEngineD3D12/src/GraphicsEngineD3D12.def @@ -1,3 +1,2 @@ EXPORTS - CreateDeviceAndContextsD3D12 - CreateSwapChainD3D12
\ No newline at end of file + GetEngineFactoryD3D12
\ No newline at end of file diff --git a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp index c5e2cf4b..daebf3c5 100644 --- a/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/PipelineStateD3D12Impl.cpp @@ -30,6 +30,7 @@ #include "ShaderResourceBindingD3D12Impl.h" #include "CommandContext.h" #include "EngineMemory.h" +#include "StringTools.h" namespace Diligent { @@ -79,19 +80,10 @@ void PipelineStateD3D12Impl::ParseShaderResourceLayout(IShader *pShader) m_pShaderResourceLayouts[ShaderInd]->Initialize(pDeviceD3D12Impl->GetD3D12Device(), pShaderD3D12->GetShaderResources(), GetRawAllocator(), nullptr, 0, nullptr, &m_RootSig); } -PipelineStateD3D12Impl :: PipelineStateD3D12Impl(FixedBlockMemoryAllocator &PSOAllocator, RenderDeviceD3D12Impl *pDeviceD3D12, const PipelineStateDesc &PipelineDesc) : - TPipelineStateBase(PSOAllocator, pDeviceD3D12, PipelineDesc), +PipelineStateD3D12Impl :: PipelineStateD3D12Impl(IReferenceCounters *pRefCounters, RenderDeviceD3D12Impl *pDeviceD3D12, const PipelineStateDesc &PipelineDesc) : + TPipelineStateBase(pRefCounters, pDeviceD3D12, PipelineDesc), m_DummyVar(*this), m_ResourceCacheDataAllocator(GetRawAllocator(), PipelineDesc.SRBAllocationGranularity), - m_ShaderResLayoutDataAllocators - { - {GetRawAllocator(), PipelineDesc.SRBAllocationGranularity}, - {GetRawAllocator(), PipelineDesc.SRBAllocationGranularity}, - {GetRawAllocator(), PipelineDesc.SRBAllocationGranularity}, - {GetRawAllocator(), PipelineDesc.SRBAllocationGranularity}, - {GetRawAllocator(), PipelineDesc.SRBAllocationGranularity}, - {GetRawAllocator(), PipelineDesc.SRBAllocationGranularity}, - }, m_pDefaultShaderResBinding(nullptr, STDDeleter<ShaderResourceBindingD3D12Impl, FixedBlockMemoryAllocator>(pDeviceD3D12->GetSRBAllocator()) ) { auto pd3d12Device = pDeviceD3D12->GetD3D12Device(); @@ -213,9 +205,21 @@ PipelineStateD3D12Impl :: PipelineStateD3D12Impl(FixedBlockMemoryAllocator &PSOA LOG_ERROR_AND_THROW("Failed to create pipeline state"); } + if (*m_Desc.Name != 0) + { + m_pd3d12PSO->SetName(WidenString(m_Desc.Name).c_str()); + String RootSignatureDesc("Root signature for PSO \""); + RootSignatureDesc.append(m_Desc.Name); + RootSignatureDesc.push_back('\"'); + m_RootSig.GetD3D12RootSignature()->SetName(WidenString(RootSignatureDesc).c_str()); + } + + if(PipelineDesc.SRBAllocationGranularity > 1) + m_ResLayoutDataAllocators.Init(m_NumShaders, PipelineDesc.SRBAllocationGranularity); + auto &SRBAllocator = pDeviceD3D12->GetSRBAllocator(); // Default shader resource binding must be initialized after resource layouts are parsed! - m_pDefaultShaderResBinding.reset( NEW(SRBAllocator, "ShaderResourceBindingD3D12Impl instance", ShaderResourceBindingD3D12Impl, this, true) ); + m_pDefaultShaderResBinding.reset( NEW_RC_OBJ(SRBAllocator, "ShaderResourceBindingD3D12Impl instance", ShaderResourceBindingD3D12Impl, this)(this, true) ); } PipelineStateD3D12Impl::~PipelineStateD3D12Impl() @@ -257,7 +261,7 @@ void PipelineStateD3D12Impl::CreateShaderResourceBinding(IShaderResourceBinding { auto *pRenderDeviceD3D12 = ValidatedCast<RenderDeviceD3D12Impl>( GetDevice() ); auto &SRBAllocator = pRenderDeviceD3D12->GetSRBAllocator(); - auto pResBindingD3D12 = NEW(SRBAllocator, "ShaderResourceBindingD3D12Impl instance", ShaderResourceBindingD3D12Impl, this, false); + auto pResBindingD3D12 = NEW_RC_OBJ(SRBAllocator, "ShaderResourceBindingD3D12Impl instance", ShaderResourceBindingD3D12Impl)(this, false); pResBindingD3D12->QueryInterface(IID_ShaderResourceBinding, reinterpret_cast<IObject**>(ppShaderResourceBinding)); } diff --git a/Graphics/GraphicsEngineD3D12/src/RenderDeviceD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/RenderDeviceD3D12Impl.cpp index d9fc8265..63ab19af 100644 --- a/Graphics/GraphicsEngineD3D12/src/RenderDeviceD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/RenderDeviceD3D12Impl.cpp @@ -36,15 +36,13 @@ namespace Diligent { -RenderDeviceD3D12Impl :: RenderDeviceD3D12Impl(IMemoryAllocator &RawMemAllocator, const EngineD3D12Attribs &CreationAttribs, ID3D12Device *pd3d12Device, ID3D12CommandQueue *pd3d12CmdQueue, Uint32 NumDeferredContexts) : - TRenderDeviceBase(RawMemAllocator, NumDeferredContexts, sizeof(TextureD3D12Impl), sizeof(TextureViewD3D12Impl), sizeof(BufferD3D12Impl), sizeof(BufferViewD3D12Impl), sizeof(ShaderD3D12Impl), sizeof(SamplerD3D12Impl), sizeof(PipelineStateD3D12Impl), sizeof(ShaderResourceBindingD3D12Impl)), +RenderDeviceD3D12Impl :: RenderDeviceD3D12Impl(IReferenceCounters *pRefCounters, IMemoryAllocator &RawMemAllocator, const EngineD3D12Attribs &CreationAttribs, ID3D12Device *pd3d12Device, ICommandQueueD3D12 *pCmdQueue, Uint32 NumDeferredContexts) : + TRenderDeviceBase(pRefCounters, RawMemAllocator, NumDeferredContexts, sizeof(TextureD3D12Impl), sizeof(TextureViewD3D12Impl), sizeof(BufferD3D12Impl), sizeof(BufferViewD3D12Impl), sizeof(ShaderD3D12Impl), sizeof(SamplerD3D12Impl), sizeof(PipelineStateD3D12Impl), sizeof(ShaderResourceBindingD3D12Impl)), m_pd3d12Device(pd3d12Device), - m_pd3d12CmdQueue(pd3d12CmdQueue), + m_pCommandQueue(pCmdQueue), m_EngineAttribs(CreationAttribs), - m_CurrentFrameNumber(0), - m_NextCmdList(0), - m_NumCompletedCmdLists(0), - m_WaitForGPUEventHandle( CreateEvent(nullptr, false, false, nullptr) ), + m_FrameNumber(0), + m_NextCmdListNumber(0), m_CmdListManager(this), m_CPUDescriptorHeaps { @@ -66,6 +64,7 @@ RenderDeviceD3D12Impl :: RenderDeviceD3D12Impl(IMemoryAllocator &RawMemAllocator m_ContextPool(STD_ALLOCATOR_RAW_MEM(ContextPoolElemType, GetRawAllocator(), "Allocator for vector<unique_ptr<CommandContext>>")), m_AvailableContexts(STD_ALLOCATOR_RAW_MEM(CommandContext*, GetRawAllocator(), "Allocator for vector<CommandContext*>")), m_D3D12ObjReleaseQueue(STD_ALLOCATOR_RAW_MEM(ReleaseQueueElemType, GetRawAllocator(), "Allocator for queue<ReleaseQueueElemType>")), + m_StaleD3D12Objects(STD_ALLOCATOR_RAW_MEM(ReleaseQueueElemType, GetRawAllocator(), "Allocator for queue<ReleaseQueueElemType>")), m_UploadHeaps(STD_ALLOCATOR_RAW_MEM(UploadHeapPoolElemType, GetRawAllocator(), "Allocator for vector<unique_ptr<DynamicUploadHeap>>")) { m_DeviceCaps.DevType = DeviceType::D3D12; @@ -73,30 +72,20 @@ RenderDeviceD3D12Impl :: RenderDeviceD3D12Impl(IMemoryAllocator &RawMemAllocator m_DeviceCaps.MinorVersion = 0; m_DeviceCaps.bSeparableProgramSupported = True; m_DeviceCaps.bMultithreadedResourceCreationSupported = True; - - VERIFY_EXPR(m_WaitForGPUEventHandle != INVALID_HANDLE_VALUE); - - auto hr = pd3d12Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, __uuidof(m_pCompletedCmdListFence), reinterpret_cast<void**>(static_cast<ID3D12Fence**>(&m_pCompletedCmdListFence))); - VERIFY(SUCCEEDED(hr), "Failed to create the fence"); - m_pCompletedCmdListFence->SetName(L"RenderDeviceD3D12Impl::m_pCompletedCmdListFence fence"); - m_pCompletedCmdListFence->Signal(m_NumCompletedCmdLists); // 0 cmd lists are completed } RenderDeviceD3D12Impl::~RenderDeviceD3D12Impl() { // Finish current frame. This will release resources taken by previous frames, and - // will signal current frame as completed. If we do not call FinishFrame(), then - // current frame will not be signaled as completed and the second call to FinishFrame() - // will not release current frame's resources. + // will move all stale resources to the release queues. The resources will not be + // release until next call to FinishFrame() FinishFrame(); // Wait for the GPU to complete all its operations - IdleGPU(); - // Call FinishFrame() again to release all resources associated with the current frame - FinishFrame(); - CloseHandle(m_WaitForGPUEventHandle); - - //LinearAllocator::DestroyAll(); - //DynamicDescriptorHeap::DestroyAll(); + IdleGPU(true); + // Call FinishFrame() again to destroy resources in + // release queues + FinishFrame(true); + m_ContextPool.clear(); } @@ -106,38 +95,63 @@ void RenderDeviceD3D12Impl::DisposeCommandContext(CommandContext* pCtx) m_AvailableContexts.push_back(pCtx); } -void RenderDeviceD3D12Impl::CloseAndExecuteCommandContext(CommandContext *pCtx) +void RenderDeviceD3D12Impl::CloseAndExecuteCommandContext(CommandContext *pCtx, bool DiscardStaleObjects) { CComPtr<ID3D12CommandAllocator> pAllocator; auto *pCmdList = pCtx->Close(&pAllocator); - Uint64 SubmittedCmdListNum = 0; + Uint64 FenceValue = 0; + Uint64 CmdListNumber = 0; { std::lock_guard<std::mutex> LockGuard(m_CmdQueueMutex); - + auto NextFenceValue = m_pCommandQueue->GetNextFenceValue(); // Submit the command list to the queue - ID3D12CommandList *const ppCmdLists[] = {pCmdList}; - m_pd3d12CmdQueue->ExecuteCommandLists(1, ppCmdLists); - - // Increment the counter before signaling the fence. - SubmittedCmdListNum = m_NextCmdList; - Atomics::AtomicIncrement(m_NextCmdList); - - // Signal the fence value. Note that for cmd list N that has just been submitted, - // we are signaling value N+1, that has a meaning of the TOTAL NUMBER OF COMPLETED - // cmd lists rather than the index number of the LAST completed cmd list. - // This is convenient as the natural default fence value of 0 - // corresponds to no completed cmd lists - m_pd3d12CmdQueue->Signal(m_pCompletedCmdListFence, m_NextCmdList); + FenceValue = m_pCommandQueue->ExecuteCommandList(pCmdList); + VERIFY(FenceValue >= NextFenceValue, "Fence value of the executed command list is less than the next fence value previously queried through GetNextFenceValue()"); + FenceValue = std::max(FenceValue, NextFenceValue); + CmdListNumber = m_NextCmdListNumber; + Atomics::AtomicIncrement(m_NextCmdListNumber); + } + + if (DiscardStaleObjects) + { + // The following basic requirement guarantees correctness of resource deallocation: + // + // A resource is never released before the last draw command referencing it is invoked on the immediate context + // + // See http://diligentgraphics.com/diligent-engine/architecture/d3d12/managing-resource-lifetimes/ + + // Stale objects should only be discarded when submitting cmd list from + // the immediate context, otherwise the basic requirement may be violated + // as in the following scenario + // + // Signaled | | + // Fence Value | 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 number N | | + + // Move stale objects into a release queue. + // Note that objects are moved from stale list to release queue based on the + // cmd list number, not the fence value. This makes sure that basic requirement + // is met even when the fence value is not incremented while executing + // the command list (as is the case with Unity command queue). + DiscardStaleD3D12Objects(CmdListNumber, FenceValue); } // DiscardAllocator() is thread-safe - m_CmdListManager.DiscardAllocator(SubmittedCmdListNum, pAllocator); + m_CmdListManager.DiscardAllocator(FenceValue, pAllocator); - // m_NextCmdList may be different at this point, which does not matter. - // SubmittedCmdListNum stores the actual number when the command list - // was sent for executrion - pCtx->DiscardDynamicDescriptors(SubmittedCmdListNum); + pCtx->DiscardDynamicDescriptors(FenceValue); { std::lock_guard<std::mutex> LockGuard(m_ContextAllocationMutex); @@ -146,51 +160,82 @@ void RenderDeviceD3D12Impl::CloseAndExecuteCommandContext(CommandContext *pCtx) } -void RenderDeviceD3D12Impl::IdleGPU(bool ReleasePendingObjects) +void RenderDeviceD3D12Impl::IdleGPU(bool ReleaseStaleObjects) { - // We must lock the command queue to avoid other threads interfering with the GPU - std::lock_guard<std::mutex> LockGuard(m_CmdQueueMutex); - - // Simulate execution of an empty command list - - // Increment the counter before signaling the fence. - Atomics::AtomicIncrement(m_NextCmdList); - - // Signal the fence value. Note that for cmd list N that has just been submitted, - // we are signaling value N+1, that has a meaning of the TOTAL NUMBER OF COMPLETED cmd lists - m_pd3d12CmdQueue->Signal(m_pCompletedCmdListFence, m_NextCmdList); - - // Wait for the fence - m_pCompletedCmdListFence->SetEventOnCompletion(m_NextCmdList, m_WaitForGPUEventHandle); - WaitForSingleObject(m_WaitForGPUEventHandle, INFINITE); - - VERIFY(GetNumCompletedCmdLists() == m_NextCmdList, "Unexpected number of completed cmd lists"); - - // Note: release queues may now contain objects released during the current frame - // that have cmd list number (m_NextCmdList-1) - - if(ReleasePendingObjects) + Uint64 FenceValue = 0; + Uint64 CmdListNumber = 0; + { + // Lock the command queue to avoid other threads interfering with the GPU + std::lock_guard<std::mutex> LockGuard(m_CmdQueueMutex); + FenceValue = m_pCommandQueue->GetNextFenceValue(); + m_pCommandQueue->IdleGPU(); + // Increment cmd list number while keeping queue locked. + // This guarantees that any D3D12 object released after the lock + // is released, will be associated with the incremented cmd list number + CmdListNumber = m_NextCmdListNumber; + Atomics::AtomicIncrement(m_NextCmdListNumber); + } + + if (ReleaseStaleObjects) { // Do not wait until the end of the frame and force deletion. // This is necessary to release outstanding references to the // swap chain buffers when it is resized in the middle of the frame. - // Since GPU has been idled, it is safe to do so - ProcessReleaseQueue(true); + // Since GPU has been idled, it it is safe to do so + DiscardStaleD3D12Objects(CmdListNumber, FenceValue); + ProcessReleaseQueue(FenceValue); } } -Uint64 RenderDeviceD3D12Impl::GetNumCompletedCmdLists() +Bool RenderDeviceD3D12Impl::IsFenceSignaled(Uint64 FenceValue) { - auto NumCompletedCmdLists = m_pCompletedCmdListFence->GetCompletedValue(); - if(NumCompletedCmdLists > m_NumCompletedCmdLists) - m_NumCompletedCmdLists = NumCompletedCmdLists; + return FenceValue <= GetCompletedFenceValue(); +} - return m_NumCompletedCmdLists; +Uint64 RenderDeviceD3D12Impl::GetCompletedFenceValue() +{ + return m_pCommandQueue->GetCompletedFenceValue(); } -void RenderDeviceD3D12Impl::FinishFrame() +void RenderDeviceD3D12Impl::FinishFrame(bool ReleaseAllResources) { - auto NumCompletedCmdLists = GetNumCompletedCmdLists(); + { + if (auto pImmediateCtx = m_wpImmediateContext.Lock()) + { + auto pImmediateCtxD3D12 = ValidatedCast<DeviceContextD3D12Impl>(pImmediateCtx.RawPtr()); + if(pImmediateCtxD3D12->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") + } + + for (auto wpDeferredCtx : m_wpDeferredContexts) + { + if (auto pDeferredCtx = wpDeferredCtx.Lock()) + { + auto pDeferredCtxD3D12 = ValidatedCast<DeviceContextD3D12Impl>(pDeferredCtx.RawPtr()); + if(pDeferredCtxD3D12->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") + } + } + } + + auto CompletedFenceValue = ReleaseAllResources ? std::numeric_limits<Uint64>::max() : GetCompletedFenceValue(); + + // We must use NextFenceValue here, NOT current value, because the + // fence value may or may not have been incremented when the last + // command list was submitted for execution (Unity only + // increments fence value once per frame) + Uint64 NextFenceValue = 0; + Uint64 CmdListNumber = 0; + { + // Lock the command queue to avoid other threads interfering with the GPU + std::lock_guard<std::mutex> LockGuard(m_CmdQueueMutex); + NextFenceValue = m_pCommandQueue->GetNextFenceValue(); + // Increment cmd list number while keeping queue locked. + // This guarantees that any D3D12 object released after the lock + // is released, will be associated with the incremented cmd list number + CmdListNumber = m_NextCmdListNumber; + Atomics::AtomicIncrement(m_NextCmdListNumber); + } { // There is no need to lock as new heaps are only created during initialization @@ -199,15 +244,15 @@ void RenderDeviceD3D12Impl::FinishFrame() // Upload heaps are used to update resource contents as well as to allocate // space for dynamic resources. - // Initial resource data is uploaded using temporary one-time upload buffers, so can - // performed in parallel across frame boundaries + // Initial resource data is uploaded using temporary one-time upload buffers, + // so can be performed in parallel across frame boundaries for (auto &UploadHeap : m_UploadHeaps) { // Currently upload heaps are free-threaded, so other threads must not allocate // resources at the same time. This means that all dynamic buffers must be unmaped // in the same frame and all resources must be updated within boundaries of a single frame. // - // worker thread 2 | pDevice->CrateTexture(InitData) | | pDevice->CrateBuffer(InitData) | | pDevice->CrateTexture(InitData) | + // worker thread 3 | pDevice->CrateTexture(InitData) | | pDevice->CrateBuffer(InitData) | | pDevice->CrateTexture(InitData) | // // worker thread 2 | pDfrdCtx2->UpdateResource() | || // || @@ -216,7 +261,8 @@ void RenderDeviceD3D12Impl::FinishFrame() // main thread | pCtx->Map(WRITE_DISCARD )| | pCtx->UpdateResource() | || | Present() | // // - UploadHeap->FinishFrame(GetNextCmdListNumber(), NumCompletedCmdLists); + + UploadHeap->FinishFrame(NextFenceValue, CompletedFenceValue); } } @@ -224,18 +270,20 @@ void RenderDeviceD3D12Impl::FinishFrame() { // This is OK if other thread disposes descriptor heap allocation at this time // The allocation will be registered as part of the current frame - m_CPUDescriptorHeaps[CPUHeap].ReleaseStaleAllocations(NumCompletedCmdLists); + m_CPUDescriptorHeaps[CPUHeap].ReleaseStaleAllocations(CompletedFenceValue); } for(Uint32 GPUHeap=0; GPUHeap < _countof(m_GPUDescriptorHeaps); ++GPUHeap) { - m_GPUDescriptorHeaps[GPUHeap].ReleaseStaleAllocations(NumCompletedCmdLists); + m_GPUDescriptorHeaps[GPUHeap].ReleaseStaleAllocations(CompletedFenceValue); } + // Discard all remaining objects. This is important to do if there were + // no command lists submitted during the frame + DiscardStaleD3D12Objects(CmdListNumber, NextFenceValue); + ProcessReleaseQueue(CompletedFenceValue); - ProcessReleaseQueue(); - - Atomics::AtomicIncrement(m_CurrentFrameNumber); + Atomics::AtomicIncrement(m_FrameNumber); } DynamicUploadHeap* RenderDeviceD3D12Impl::RequestUploadHeap() @@ -288,22 +336,42 @@ CommandContext* RenderDeviceD3D12Impl::AllocateCommandContext(const Char *ID) void RenderDeviceD3D12Impl::SafeReleaseD3D12Object(ID3D12Object* pObj) { - std::lock_guard<std::mutex> LockGuard(m_ReleasedObjectsMutex); - m_D3D12ObjReleaseQueue.emplace_back( GetNextCmdListNumber(), CComPtr<ID3D12Object>(pObj) ); + // When D3D12 object is released, it is first moved into the + // stale objects list. The list is moved into a release queue + // when the next command list is executed. + std::lock_guard<std::mutex> LockGuard(m_StaleObjectsMutex); + m_StaleD3D12Objects.emplace_back( m_NextCmdListNumber, CComPtr<ID3D12Object>(pObj) ); +} + +void RenderDeviceD3D12Impl::DiscardStaleD3D12Objects(Uint64 CmdListNumber, Uint64 FenceValue) +{ + // Only discard these stale objects that were released before CmdListNumber + // was executed + std::lock_guard<std::mutex> StaleObjectsLock(m_StaleObjectsMutex); + std::lock_guard<std::mutex> ReleaseQueueLock(m_ReleaseQueueMutex); + while (!m_StaleD3D12Objects.empty() ) + { + auto &FirstStaleObj = m_StaleD3D12Objects.front(); + if (FirstStaleObj.first <= CmdListNumber) + { + m_D3D12ObjReleaseQueue.emplace_back(FenceValue, std::move(FirstStaleObj.second)); + m_StaleD3D12Objects.pop_front(); + } + else + break; + } } -void RenderDeviceD3D12Impl::ProcessReleaseQueue(bool ForceRelease) +void RenderDeviceD3D12Impl::ProcessReleaseQueue(Uint64 CompletedFenceValue) { - std::lock_guard<std::mutex> LockGuard(m_ReleasedObjectsMutex); + std::lock_guard<std::mutex> LockGuard(m_ReleaseQueueMutex); - auto NumCompletedCmdLists = GetNumCompletedCmdLists(); - // Release all objects whose cmd list number value < number of completed cmd lists + // Release all objects whose associated fence value is at most CompletedFenceValue // See http://diligentgraphics.com/diligent-engine/architecture/d3d12/managing-resource-lifetimes/ while (!m_D3D12ObjReleaseQueue.empty()) { auto &FirstObj = m_D3D12ObjReleaseQueue.front(); - // GPU must have been idled when ForceRelease == true - if (FirstObj.first < NumCompletedCmdLists || ForceRelease) + if (FirstObj.first <= CompletedFenceValue) m_D3D12ObjReleaseQueue.pop_front(); else break; @@ -433,20 +501,32 @@ void RenderDeviceD3D12Impl::CreatePipelineState(const PipelineStateDesc &Pipelin CreateDeviceObject("Pipeline State", PipelineDesc, ppPipelineState, [&]() { - PipelineStateD3D12Impl *pPipelineStateD3D12( NEW(m_PSOAllocator, "PipelineStateD3D12Impl instance", PipelineStateD3D12Impl, this, PipelineDesc ) ); + PipelineStateD3D12Impl *pPipelineStateD3D12( NEW_RC_OBJ(m_PSOAllocator, "PipelineStateD3D12Impl instance", PipelineStateD3D12Impl)(this, PipelineDesc ) ); pPipelineStateD3D12->QueryInterface( IID_PipelineState, reinterpret_cast<IObject**>(ppPipelineState) ); OnCreateDeviceObject( pPipelineStateD3D12 ); } ); } +void RenderDeviceD3D12Impl :: CreateBufferFromD3DResource(ID3D12Resource *pd3d12Buffer, const BufferDesc& BuffDesc, IBuffer **ppBuffer) +{ + CreateDeviceObject("buffer", BuffDesc, ppBuffer, + [&]() + { + BufferD3D12Impl *pBufferD3D12( NEW_RC_OBJ(m_BufObjAllocator, "BufferD3D12Impl instance", BufferD3D12Impl)(m_BuffViewObjAllocator, this, BuffDesc, pd3d12Buffer ) ); + pBufferD3D12->QueryInterface( IID_Buffer, reinterpret_cast<IObject**>(ppBuffer) ); + pBufferD3D12->CreateDefaultViews(); + OnCreateDeviceObject( pBufferD3D12 ); + } + ); +} void RenderDeviceD3D12Impl :: CreateBuffer(const BufferDesc& BuffDesc, const BufferData &BuffData, IBuffer **ppBuffer) { CreateDeviceObject("buffer", BuffDesc, ppBuffer, [&]() { - BufferD3D12Impl *pBufferD3D12( NEW(m_BufObjAllocator, "BufferD3D12Impl instance", BufferD3D12Impl, m_BuffViewObjAllocator, this, BuffDesc, BuffData ) ); + BufferD3D12Impl *pBufferD3D12( NEW_RC_OBJ(m_BufObjAllocator, "BufferD3D12Impl instance", BufferD3D12Impl)(m_BuffViewObjAllocator, this, BuffDesc, BuffData ) ); pBufferD3D12->QueryInterface( IID_Buffer, reinterpret_cast<IObject**>(ppBuffer) ); pBufferD3D12->CreateDefaultViews(); OnCreateDeviceObject( pBufferD3D12 ); @@ -460,7 +540,7 @@ void RenderDeviceD3D12Impl :: CreateShader(const ShaderCreationAttribs &ShaderCr CreateDeviceObject( "shader", ShaderCreationAttribs.Desc, ppShader, [&]() { - ShaderD3D12Impl *pShaderD3D12( NEW(m_ShaderObjAllocator, "ShaderD3D12Impl instance", ShaderD3D12Impl, this, ShaderCreationAttribs ) ); + ShaderD3D12Impl *pShaderD3D12( NEW_RC_OBJ(m_ShaderObjAllocator, "ShaderD3D12Impl instance", ShaderD3D12Impl)(this, ShaderCreationAttribs ) ); pShaderD3D12->QueryInterface( IID_Shader, reinterpret_cast<IObject**>(ppShader) ); OnCreateDeviceObject( pShaderD3D12 ); @@ -468,10 +548,31 @@ void RenderDeviceD3D12Impl :: CreateShader(const ShaderCreationAttribs &ShaderCr ); } -void RenderDeviceD3D12Impl::CreateTexture(TextureDesc& TexDesc, ID3D12Resource *pd3d12Texture, class TextureD3D12Impl **ppTexture) +void RenderDeviceD3D12Impl::CreateTextureFromD3DResource(ID3D12Resource *pd3d12Texture, ITexture **ppTexture) { - TextureD3D12Impl *pTextureD3D12 = NEW(m_TexObjAllocator, "TextureD3D12Impl instance", TextureD3D12Impl, m_TexViewObjAllocator, this, TexDesc, pd3d12Texture ); - pTextureD3D12->QueryInterface( IID_TextureD3D12, reinterpret_cast<IObject**>(ppTexture) ); + TextureDesc TexDesc; + TexDesc.Name = "Texture from d3d12 resource"; + CreateDeviceObject( "texture", TexDesc, ppTexture, + [&]() + { + TextureD3D12Impl *pTextureD3D12 = NEW_RC_OBJ(m_TexObjAllocator, "TextureD3D12Impl instance", TextureD3D12Impl)(m_TexViewObjAllocator, this, TexDesc, pd3d12Texture ); + + pTextureD3D12->QueryInterface( IID_Texture, reinterpret_cast<IObject**>(ppTexture) ); + pTextureD3D12->CreateDefaultViews(); + OnCreateDeviceObject( pTextureD3D12 ); + } + ); +} + +void RenderDeviceD3D12Impl::CreateTexture(const TextureDesc& TexDesc, ID3D12Resource *pd3d12Texture, class TextureD3D12Impl **ppTexture) +{ + CreateDeviceObject( "texture", TexDesc, ppTexture, + [&]() + { + TextureD3D12Impl *pTextureD3D12 = NEW_RC_OBJ(m_TexObjAllocator, "TextureD3D12Impl instance", TextureD3D12Impl)(m_TexViewObjAllocator, this, TexDesc, pd3d12Texture ); + pTextureD3D12->QueryInterface( IID_TextureD3D12, reinterpret_cast<IObject**>(ppTexture) ); + } + ); } void RenderDeviceD3D12Impl :: CreateTexture(const TextureDesc& TexDesc, const TextureData &Data, ITexture **ppTexture) @@ -479,7 +580,7 @@ void RenderDeviceD3D12Impl :: CreateTexture(const TextureDesc& TexDesc, const Te CreateDeviceObject( "texture", TexDesc, ppTexture, [&]() { - TextureD3D12Impl *pTextureD3D12 = NEW(m_TexObjAllocator, "TextureD3D12Impl instance", TextureD3D12Impl, m_TexViewObjAllocator, this, TexDesc, Data ); + TextureD3D12Impl *pTextureD3D12 = NEW_RC_OBJ(m_TexObjAllocator, "TextureD3D12Impl instance", TextureD3D12Impl)(m_TexViewObjAllocator, this, TexDesc, Data ); pTextureD3D12->QueryInterface( IID_Texture, reinterpret_cast<IObject**>(ppTexture) ); pTextureD3D12->CreateDefaultViews(); @@ -496,7 +597,7 @@ void RenderDeviceD3D12Impl :: CreateSampler(const SamplerDesc& SamplerDesc, ISam m_SamplersRegistry.Find( SamplerDesc, reinterpret_cast<IDeviceObject**>(ppSampler) ); if( *ppSampler == nullptr ) { - SamplerD3D12Impl *pSamplerD3D12( NEW(m_SamplerObjAllocator, "SamplerD3D12Impl instance", SamplerD3D12Impl, this, SamplerDesc ) ); + SamplerD3D12Impl *pSamplerD3D12( NEW_RC_OBJ(m_SamplerObjAllocator, "SamplerD3D12Impl instance", SamplerD3D12Impl)(this, SamplerDesc ) ); pSamplerD3D12->QueryInterface( IID_Sampler, reinterpret_cast<IObject**>(ppSampler) ); OnCreateDeviceObject( pSamplerD3D12 ); m_SamplersRegistry.Add( SamplerDesc, *ppSampler ); diff --git a/Graphics/GraphicsEngineD3D12/src/RenderDeviceFactoryD3D12.cpp b/Graphics/GraphicsEngineD3D12/src/RenderDeviceFactoryD3D12.cpp index db7164dc..70d482ae 100644 --- a/Graphics/GraphicsEngineD3D12/src/RenderDeviceFactoryD3D12.cpp +++ b/Graphics/GraphicsEngineD3D12/src/RenderDeviceFactoryD3D12.cpp @@ -32,12 +32,43 @@ #include "D3D12TypeConversions.h" #include "StringTools.h" #include "EngineMemory.h" +#include "CommandQueueD3D12Impl.h" #include <Windows.h> #include <dxgi1_4.h> namespace Diligent { +/// Engine factory for D3D12 implementation +class EngineFactoryD3D12Impl : public IEngineFactoryD3D12 +{ +public: + static EngineFactoryD3D12Impl* GetInstance() + { + static EngineFactoryD3D12Impl TheFactory; + return &TheFactory; + } + + void CreateDeviceAndContextsD3D12( const EngineD3D12Attribs& CreationAttribs, + IRenderDevice **ppDevice, + IDeviceContext **ppContexts, + Uint32 NumDeferredContexts)override final; + + void AttachToD3D12Device(void *pd3d12NativeDevice, + ICommandQueueD3D12 *pCommandQueue, + const EngineD3D12Attribs& EngineAttribs, + IRenderDevice **ppDevice, + IDeviceContext **ppContexts, + Uint32 NumDeferredContexts)override final; + + void CreateSwapChainD3D12( IRenderDevice *pDevice, + IDeviceContext *pImmediateContext, + const SwapChainDesc& SwapChainDesc, + void* pNativeWndHandle, + ISwapChain **ppSwapChain )override final; + +}; + void GetHardwareAdapter(IDXGIFactory2* pFactory, IDXGIAdapter1** ppAdapter) { CComPtr<IDXGIAdapter1> adapter; @@ -80,12 +111,12 @@ void GetHardwareAdapter(IDXGIFactory2* pFactory, IDXGIAdapter1** ppAdapter) /// of deferred contexts is requested, pointers to the /// contexts are written to ppContexts array starting /// at position 1 -void CreateDeviceAndContextsD3D12( const EngineD3D12Attribs& CreationAttribs, - IRenderDevice **ppDevice, - Diligent::IDeviceContext **ppContexts, - Diligent::Uint32 NumDeferredContexts) +void EngineFactoryD3D12Impl::CreateDeviceAndContextsD3D12( const EngineD3D12Attribs& CreationAttribs, + IRenderDevice **ppDevice, + IDeviceContext **ppContexts, + Uint32 NumDeferredContexts) { - VERIFY( ppDevice && ppContexts, "Null pointer is provided" ); + VERIFY( ppDevice && ppContexts, "Null pointer provided" ); if( !ppDevice || !ppContexts ) return; @@ -111,6 +142,8 @@ void CreateDeviceAndContextsD3D12( const EngineD3D12Attribs& CreationAttribs, *ppDevice = nullptr; memset(ppContexts, 0, sizeof(*ppContexts) * (1+NumDeferredContexts)); + RefCntAutoPtr<CommandQueueD3D12Impl> pCmdQueueD3D12; + CComPtr<ID3D12Device> d3d12Device; try { #if defined(_DEBUG) @@ -131,8 +164,7 @@ void CreateDeviceAndContextsD3D12( const EngineD3D12Attribs& CreationAttribs, CComPtr<IDXGIAdapter1> hardwareAdapter; GetHardwareAdapter(factory, &hardwareAdapter); - CComPtr<ID3D12Device> d3d12Device; - hr = D3D12CreateDevice(hardwareAdapter, D3D_FEATURE_LEVEL_11_0, __uuidof(d3d12Device), reinterpret_cast<void**>(static_cast<ID3D12Device**>(&d3d12Device)) ); + hr = D3D12CreateDevice(hardwareAdapter, D3D_FEATURE_LEVEL_11_0, __uuidof(d3d12Device), reinterpret_cast<void**>(static_cast<ID3D12Device**>(&d3d12Device)) ); if( FAILED(hr)) { LOG_WARNING_MESSAGE("Failed to create hardware device. Attempting to create WARP device") @@ -193,11 +225,61 @@ void CreateDeviceAndContextsD3D12( const EngineD3D12Attribs& CreationAttribs, hr = pd3d12CmdQueue->SetName(L"Main Command Queue"); VERIFY_EXPR(SUCCEEDED(hr)); + CComPtr<ID3D12Fence> pd3d12Fence; + hr = d3d12Device->CreateFence(0, D3D12_FENCE_FLAG_NONE, __uuidof(pd3d12Fence), reinterpret_cast<void**>(static_cast<ID3D12Fence**>(&pd3d12Fence))); + CHECK_D3D_RESULT_THROW(hr, "Failed to create main command queue fence"); + d3d12Device->SetName(L"Main Command Queue fence"); + auto &RawMemAllocator = GetRawAllocator(); - RenderDeviceD3D12Impl *pRenderDeviceD3D12( NEW(RawMemAllocator, "RenderDeviceD3D12Impl instance", RenderDeviceD3D12Impl, CreationAttribs, d3d12Device, pd3d12CmdQueue, NumDeferredContexts ) ); + pCmdQueueD3D12 = NEW_RC_OBJ(RawMemAllocator, "CommandQueueD3D12 instance", CommandQueueD3D12Impl)(pd3d12CmdQueue, pd3d12Fence); + } + catch( const std::runtime_error & ) + { + LOG_ERROR( "Failed to initialize D3D12 resources" ); + return; + } + + AttachToD3D12Device(d3d12Device, pCmdQueueD3D12, CreationAttribs, ppDevice, ppContexts, NumDeferredContexts); +} + + +/// Attaches to existing D3D12 device + +/// \param [in] pd3d12NativeDevice - pointer to native D3D12 device +/// \param [in] pCommandQueue - pointer to the implementation of command queue +/// \param [in] EngineAttribs - Engine creation attributes. +/// \param [out] ppDevice - Address of the memory location where pointer to +/// the created device will be written +/// \param [out] ppContexts - Address of the memory location where pointers to +/// the contexts will be written. Pointer to the immediate +/// context goes at position 0. If NumDeferredContexts > 0, +/// pointers to the deferred contexts go afterwards. +/// \param [in] NumDeferredContexts - Number of deferred contexts. If non-zero number +/// of deferred contexts is requested, pointers to the +/// contexts are written to ppContexts array starting +/// at position 1 +void EngineFactoryD3D12Impl::AttachToD3D12Device(void *pd3d12NativeDevice, + ICommandQueueD3D12 *pCommandQueue, + const EngineD3D12Attribs& EngineAttribs, + IRenderDevice **ppDevice, + IDeviceContext **ppContexts, + Uint32 NumDeferredContexts) +{ + VERIFY( pd3d12NativeDevice && pCommandQueue && ppDevice && ppContexts, "Null pointer provided" ); + if( !pd3d12NativeDevice || !pCommandQueue || !ppDevice || !ppContexts ) + return; + + *ppDevice = nullptr; + memset(ppContexts, 0, sizeof(*ppContexts) * (1+NumDeferredContexts)); + + try + { + auto &RawMemAllocator = GetRawAllocator(); + auto d3d12Device = reinterpret_cast<ID3D12Device*>(pd3d12NativeDevice); + RenderDeviceD3D12Impl *pRenderDeviceD3D12( NEW_RC_OBJ(RawMemAllocator, "RenderDeviceD3D12Impl instance", RenderDeviceD3D12Impl)(RawMemAllocator, EngineAttribs, d3d12Device, pCommandQueue, NumDeferredContexts ) ); pRenderDeviceD3D12->QueryInterface(IID_RenderDevice, reinterpret_cast<IObject**>(ppDevice) ); - RefCntAutoPtr<DeviceContextD3D12Impl> pImmediateCtxD3D12( NEW(RawMemAllocator, "DeviceContextD3D12Impl instance", DeviceContextD3D12Impl, pRenderDeviceD3D12, false, CreationAttribs, 0) ); + RefCntAutoPtr<DeviceContextD3D12Impl> pImmediateCtxD3D12( NEW_RC_OBJ(RawMemAllocator, "DeviceContextD3D12Impl instance", DeviceContextD3D12Impl)(pRenderDeviceD3D12, false, EngineAttribs, 0) ); // We must call AddRef() (implicitly through QueryInterface()) because pRenderDeviceD3D12 will // keep a weak reference to the context pImmediateCtxD3D12->QueryInterface(IID_DeviceContext, reinterpret_cast<IObject**>(ppContexts) ); @@ -205,7 +287,7 @@ void CreateDeviceAndContextsD3D12( const EngineD3D12Attribs& CreationAttribs, for (Uint32 DeferredCtx = 0; DeferredCtx < NumDeferredContexts; ++DeferredCtx) { - RefCntAutoPtr<DeviceContextD3D12Impl> pDeferredCtxD3D12( NEW(RawMemAllocator, "DeviceContextD3D12Impl instance", DeviceContextD3D12Impl, pRenderDeviceD3D12, true, CreationAttribs, 1+DeferredCtx) ); + RefCntAutoPtr<DeviceContextD3D12Impl> pDeferredCtxD3D12( NEW_RC_OBJ(RawMemAllocator, "DeviceContextD3D12Impl instance", DeviceContextD3D12Impl)(pRenderDeviceD3D12, true, EngineAttribs, 1+DeferredCtx) ); // We must call AddRef() (implicitly through QueryInterface()) because pRenderDeviceD3D12 will // keep a weak reference to the context pDeferredCtxD3D12->QueryInterface(IID_DeviceContext, reinterpret_cast<IObject**>(ppContexts + 1 + DeferredCtx) ); @@ -228,11 +310,10 @@ void CreateDeviceAndContextsD3D12( const EngineD3D12Attribs& CreationAttribs, } } - LOG_ERROR( "Failed to create device and immediate context" ); + LOG_ERROR( "Failed to create device and contexts" ); } } - /// Creates a swap chain for Direct3D12-based engine implementation /// \param [in] pDevice - Pointer to the render device @@ -246,9 +327,13 @@ void CreateDeviceAndContextsD3D12( const EngineD3D12Attribs& CreationAttribs, /// /// \param [out] ppSwapChain - Address of the memory location where pointer to the new /// swap chain will be written -void CreateSwapChainD3D12( IRenderDevice *pDevice, IDeviceContext *pImmediateContext, const SwapChainDesc& SCDesc, void* pNativeWndHandle, ISwapChain **ppSwapChain ) +void EngineFactoryD3D12Impl::CreateSwapChainD3D12( IRenderDevice *pDevice, + IDeviceContext *pImmediateContext, + const SwapChainDesc& SCDesc, + void* pNativeWndHandle, + ISwapChain **ppSwapChain ) { - VERIFY( ppSwapChain, "Null pointer is provided" ); + VERIFY( ppSwapChain, "Null pointer provided" ); if( !ppSwapChain ) return; @@ -259,7 +344,7 @@ void CreateSwapChainD3D12( IRenderDevice *pDevice, IDeviceContext *pImmediateCon auto *pDeviceD3D12 = ValidatedCast<RenderDeviceD3D12Impl>( pDevice ); auto *pDeviceContextD3D12 = ValidatedCast<DeviceContextD3D12Impl>(pImmediateContext); auto &RawMemAllocator = GetRawAllocator(); - auto *pSwapChainD3D12 = NEW(RawMemAllocator, "SwapChainD3D12Impl instance", SwapChainD3D12Impl, SCDesc, pDeviceD3D12, pDeviceContextD3D12, pNativeWndHandle); + auto *pSwapChainD3D12 = NEW_RC_OBJ(RawMemAllocator, "SwapChainD3D12Impl instance", SwapChainD3D12Impl)(SCDesc, pDeviceD3D12, pDeviceContextD3D12, pNativeWndHandle); pSwapChainD3D12->QueryInterface( IID_SwapChain, reinterpret_cast<IObject**>(ppSwapChain) ); pDeviceContextD3D12->SetSwapChain(pSwapChainD3D12); @@ -275,10 +360,10 @@ void CreateSwapChainD3D12( IRenderDevice *pDevice, IDeviceContext *pImmediateCon { auto *pDeferredCtxD3D12 = ValidatedCast<DeviceContextD3D12Impl>(pDeferredCtx.RawPtr()); pDeferredCtxD3D12->SetSwapChain(pSwapChainD3D12); - // Bind default render target - pDeferredCtxD3D12->SetRenderTargets( 0, nullptr, nullptr ); - // Set default viewport - pDeferredCtxD3D12->SetViewports( 1, nullptr, 0, 0 ); + // We cannot bind default render target here because + // there is no guarantee that deferred context will be used + // in this frame. It is an error to bind + // RTV of an inactive buffer in the swap chain } } } @@ -297,17 +382,16 @@ void CreateSwapChainD3D12( IRenderDevice *pDevice, IDeviceContext *pImmediateCon #ifdef DOXYGEN /// Loads Direct3D12-based engine implementation and exports factory functions -/// \param [out] CreateDeviceFunc - Pointer to the function that creates render device and device contexts. -/// See CreateDeviceAndContextsD3D12(). +/// \param [out] GetFactoryFunc - Pointer to the function that returns factory for D3D12 engine implementation /// \param [out] CreateSwapChainFunc - Pointer to the function that creates swap chain. -/// See CreateSwapChainD3D12(). +/// See EngineFactoryD3D12Impl. /// \remarks Depending on the configuration and platform, the function loads different dll: /// Platform\\Configuration | Debug | Release /// --------------------------|-------------------------------|---------------------------- /// x86 | GraphicsEngineD3D12_32d.dll | GraphicsEngineD3D12_32r.dll /// x64 | GraphicsEngineD3D12_64d.dll | GraphicsEngineD3D12_64r.dll /// -void LoadGraphicsEngineD3D12(CreateDeviceAndContextsD3D12Type &CreateDeviceFunc, CreateSwapChainD3D12Type &CreateSwapChainFunc) +void LoadGraphicsEngineD3D12(GetEngineFactoryD3D12Type &GetFactoryFunc) { // This function is only required because DoxyGen refuses to generate documentation for a static function when SHOW_FILES==NO #error This function must never be compiled; @@ -321,14 +405,8 @@ void LoadGraphicsEngineD3D12(CreateDeviceAndContextsD3D12Type &CreateDeviceFunc, using namespace Diligent; extern "C" { - void CreateDeviceAndContextsD3D12(const EngineD3D12Attribs& EngineAttribs, IRenderDevice **ppDevice, IDeviceContext **ppContexts, Uint32 NumDeferredContexts) - { - Diligent::CreateDeviceAndContextsD3D12(EngineAttribs, ppDevice, ppContexts, NumDeferredContexts); - } - - - void CreateSwapChainD3D12(IRenderDevice *pDevice, IDeviceContext *pImmediateContext, const SwapChainDesc& SwapChainDesc, void* pNativeWndHandle, ISwapChain **ppSwapChain) + IEngineFactoryD3D12* GetEngineFactoryD3D12() { - Diligent::CreateSwapChainD3D12(pDevice, pImmediateContext, SwapChainDesc, pNativeWndHandle, ppSwapChain); + return EngineFactoryD3D12Impl::GetInstance(); } } diff --git a/Graphics/GraphicsEngineD3D12/src/RootSignature.cpp b/Graphics/GraphicsEngineD3D12/src/RootSignature.cpp index 8be68df4..707ccd75 100644 --- a/Graphics/GraphicsEngineD3D12/src/RootSignature.cpp +++ b/Graphics/GraphicsEngineD3D12/src/RootSignature.cpp @@ -474,9 +474,6 @@ void RootSignature::Finalize(ID3D12Device *pd3d12Device) hr = pd3d12Device->CreateRootSignature(0, signature->GetBufferPointer(), signature->GetBufferSize(), __uuidof(m_pd3d12RootSignature), reinterpret_cast<void**>( static_cast<ID3D12RootSignature**>(&m_pd3d12RootSignature))); CHECK_D3D_RESULT_THROW(hr, "Failed to create root signature") - //if( *m_Desc.Name != 0) - // m_pd3d12RootSignature->SetName(WidenString(m_Desc.Name).c_str()); - bool bHasDynamicResources = m_TotalSrvCbvUavSlots[SHADER_VARIABLE_TYPE_DYNAMIC]!=0 || m_TotalSamplerSlots[SHADER_VARIABLE_TYPE_DYNAMIC]!=0; if(bHasDynamicResources) { @@ -522,9 +519,11 @@ void RootSignature::InitResourceCache(RenderDeviceD3D12Impl *pDeviceD3D12Impl, S DescriptorHeapAllocation CbcSrvUavHeapSpace, SamplerHeapSpace; if(TotalSrvCbvUavDescriptors) CbcSrvUavHeapSpace = pDeviceD3D12Impl->AllocateGPUDescriptors(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, TotalSrvCbvUavDescriptors); + VERIFY_EXPR(TotalSrvCbvUavDescriptors == 0 && CbcSrvUavHeapSpace.IsNull() || CbcSrvUavHeapSpace.GetNumHandles() == TotalSrvCbvUavDescriptors); if(TotalSamplerDescriptors) SamplerHeapSpace = pDeviceD3D12Impl->AllocateGPUDescriptors(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, TotalSamplerDescriptors); + VERIFY_EXPR(TotalSamplerDescriptors == 0 && SamplerHeapSpace.IsNull() || SamplerHeapSpace.GetNumHandles() == TotalSamplerDescriptors); // Iterate through all root static/mutable tables and assign start offsets. The tables are tightly packed, so // start offset of table N+1 is start offset of table N plus the size of table N. @@ -588,8 +587,8 @@ void RootSignature::InitResourceCache(RenderDeviceD3D12Impl *pDeviceD3D12Impl, S } #endif - VERIFY_EXPR(SrvCbvUavTblStartOffset == SrvCbvUavTblStartOffset); - VERIFY_EXPR(SamplerTblStartOffset == SamplerTblStartOffset); + VERIFY_EXPR(SrvCbvUavTblStartOffset == TotalSrvCbvUavDescriptors); + VERIFY_EXPR(SamplerTblStartOffset == TotalSamplerDescriptors); ResourceCache.SetDescriptorHeapSpace(std::move(CbcSrvUavHeapSpace), std::move(SamplerHeapSpace)); } diff --git a/Graphics/GraphicsEngineD3D12/src/SamplerD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/SamplerD3D12Impl.cpp index 0d443e85..f811373d 100644 --- a/Graphics/GraphicsEngineD3D12/src/SamplerD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/SamplerD3D12Impl.cpp @@ -29,8 +29,8 @@ namespace Diligent { -SamplerD3D12Impl::SamplerD3D12Impl(FixedBlockMemoryAllocator &SamplerObjAllocator, class RenderDeviceD3D12Impl *pRenderDeviceD3D12, const SamplerDesc& SamplerDesc) : - TSamplerBase(SamplerObjAllocator, pRenderDeviceD3D12, SamplerDesc) +SamplerD3D12Impl::SamplerD3D12Impl(IReferenceCounters *pRefCounters, class RenderDeviceD3D12Impl *pRenderDeviceD3D12, const SamplerDesc& SamplerDesc) : + TSamplerBase(pRefCounters, pRenderDeviceD3D12, SamplerDesc) { auto *pD3D12Device = pRenderDeviceD3D12->GetD3D12Device(); D3D12_SAMPLER_DESC D3D12SamplerDesc = diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderD3D12Impl.cpp index 6e2b7484..0598fcc1 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderD3D12Impl.cpp @@ -36,8 +36,8 @@ namespace Diligent { -ShaderD3D12Impl::ShaderD3D12Impl(FixedBlockMemoryAllocator& ShaderObjAllocator, RenderDeviceD3D12Impl *pRenderDeviceD3D12, const ShaderCreationAttribs &ShaderCreationAttribs) : - TShaderBase(ShaderObjAllocator, pRenderDeviceD3D12, ShaderCreationAttribs.Desc), +ShaderD3D12Impl::ShaderD3D12Impl(IReferenceCounters *pRefCounters, RenderDeviceD3D12Impl *pRenderDeviceD3D12, const ShaderCreationAttribs &ShaderCreationAttribs) : + TShaderBase(pRefCounters, pRenderDeviceD3D12, ShaderCreationAttribs.Desc), ShaderD3DBase(ShaderCreationAttribs), m_StaticResLayout(*this, GetRawAllocator()), m_DummyShaderVar(*this), diff --git a/Graphics/GraphicsEngineD3D12/src/ShaderResourceBindingD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/ShaderResourceBindingD3D12Impl.cpp index 737fd6f7..f4715f34 100644 --- a/Graphics/GraphicsEngineD3D12/src/ShaderResourceBindingD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/ShaderResourceBindingD3D12Impl.cpp @@ -30,8 +30,8 @@ namespace Diligent { -ShaderResourceBindingD3D12Impl::ShaderResourceBindingD3D12Impl( FixedBlockMemoryAllocator &SRBAllocator, PipelineStateD3D12Impl *pPSO, bool IsPSOInternal) : - TBase( SRBAllocator, pPSO, IsPSOInternal ), +ShaderResourceBindingD3D12Impl::ShaderResourceBindingD3D12Impl( IReferenceCounters *pRefCounters, PipelineStateD3D12Impl *pPSO, bool IsPSOInternal) : + TBase( pRefCounters, pPSO, IsPSOInternal ), m_ShaderResourceCache(ShaderResourceCacheD3D12::DbgCacheContentType::SRBResources) { auto *ppShaders = pPSO->GetShaders(); @@ -49,7 +49,7 @@ ShaderResourceBindingD3D12Impl::ShaderResourceBindingD3D12Impl( FixedBlockMemory auto ShaderType = pShader->GetDesc().ShaderType; auto ShaderInd = GetShaderTypeIndex(ShaderType); - auto &ShaderResLayoutDataAllocator = pPSO->GetShaderResourceLayoutDataAllocator(ShaderInd); + auto &ShaderResLayoutDataAllocator = pPSO->GetShaderResourceLayoutDataAllocator(s); // http://diligentgraphics.com/diligent-engine/architecture/d3d12/shader-resource-layout#Initializing-Resource-Layouts-in-a-Shader-Resource-Binding-Object SHADER_VARIABLE_TYPE Types[] = {SHADER_VARIABLE_TYPE_STATIC, SHADER_VARIABLE_TYPE_MUTABLE, SHADER_VARIABLE_TYPE_DYNAMIC}; diff --git a/Graphics/GraphicsEngineD3D12/src/SwapChainD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/SwapChainD3D12Impl.cpp index 2e42ca44..a0aa1e08 100644 --- a/Graphics/GraphicsEngineD3D12/src/SwapChainD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/SwapChainD3D12Impl.cpp @@ -32,12 +32,12 @@ namespace Diligent { -SwapChainD3D12Impl::SwapChainD3D12Impl(IMemoryAllocator &Allocator, +SwapChainD3D12Impl::SwapChainD3D12Impl(IReferenceCounters *pRefCounters, const SwapChainDesc& SCDesc, RenderDeviceD3D12Impl* pRenderDeviceD3D12, DeviceContextD3D12Impl* pDeviceContextD3D12, void* pNativeWndHandle) : - TSwapChainBase(Allocator, pRenderDeviceD3D12, pDeviceContextD3D12, SCDesc), + TSwapChainBase(pRefCounters, pRenderDeviceD3D12, pDeviceContextD3D12, SCDesc), m_pBackBufferRTV(STD_ALLOCATOR_RAW_MEM(RefCntAutoPtr<ITextureView>, GetRawAllocator(), "Allocator for vector<RefCntAutoPtr<ITextureView>>")) { @@ -81,7 +81,7 @@ SwapChainD3D12Impl::SwapChainD3D12Impl(IMemoryAllocator &Allocator, HRESULT hr = CreateDXGIFactory1(__uuidof(factory), reinterpret_cast<void**>(static_cast<IDXGIFactory4**>(&factory)) ); CHECK_D3D_RESULT_THROW(hr, "Failed to create DXGI factory") - auto *pd3d12CmdQueue = pRenderDeviceD3D12->GetCmdQueue(); + auto *pd3d12CmdQueue = pRenderDeviceD3D12->GetCmdQueue()->GetD3D12CommandQueue(); #if defined( PLATFORM_WIN32 ) hr = factory->CreateSwapChainForHwnd(pd3d12CmdQueue, hWnd, &swapChainDesc, nullptr, nullptr, &pSwapChain1); CHECK_D3D_RESULT_THROW( hr, "Failed to create Swap Chain" ); @@ -140,7 +140,7 @@ void SwapChainD3D12Impl::InitBuffersAndViews() RTVDesc.ViewType = TEXTURE_VIEW_RENDER_TARGET; RefCntAutoPtr<ITextureView> pRTV; pBackBufferTex->CreateView(RTVDesc, &pRTV); - m_pBackBufferRTV[backbuff] = pRTV; + m_pBackBufferRTV[backbuff] = RefCntAutoPtr<ITextureViewD3D12>(pRTV, IID_TextureViewD3D12); } TextureDesc DepthBufferDesc; @@ -158,7 +158,8 @@ void SwapChainD3D12Impl::InitBuffersAndViews() DepthBufferDesc.Name = "Main depth buffer"; RefCntAutoPtr<ITexture> pDepthBufferTex; m_pRenderDevice->CreateTexture(DepthBufferDesc, TextureData(), static_cast<ITexture**>(&pDepthBufferTex) ); - m_pDepthBufferDSV = pDepthBufferTex->GetDefaultView(TEXTURE_VIEW_DEPTH_STENCIL); + auto pDSV = pDepthBufferTex->GetDefaultView(TEXTURE_VIEW_DEPTH_STENCIL); + m_pDepthBufferDSV = RefCntAutoPtr<ITextureViewD3D12>(pDSV, IID_TextureViewD3D12); } IMPLEMENT_QUERY_INTERFACE( SwapChainD3D12Impl, IID_SwapChainD3D12, TSwapChainBase ) @@ -200,7 +201,7 @@ void SwapChainD3D12Impl::Present() // backbuffer 0 from all GPU writeable bind points. // We need to rebind all render targets to make sure that // the back buffer is not unbound - pImmediateCtxD3D12->RebindRenderTargets(); + pImmediateCtxD3D12->CommitRenderTargets(); #endif #endif } @@ -255,10 +256,10 @@ void SwapChainD3D12Impl::Resize( Uint32 NewWidth, Uint32 NewHeight ) } } -ITextureView *SwapChainD3D12Impl::GetCurrentBackBufferRTV() +ITextureViewD3D12 *SwapChainD3D12Impl::GetCurrentBackBufferRTV() { auto CurrentBackBufferIndex = m_pSwapChain->GetCurrentBackBufferIndex(); - VERIFY_EXPR(CurrentBackBufferIndex>=0 && CurrentBackBufferIndex < m_SwapChainDesc.BufferCount); + VERIFY_EXPR(CurrentBackBufferIndex >= 0 && CurrentBackBufferIndex < m_SwapChainDesc.BufferCount); return m_pBackBufferRTV[CurrentBackBufferIndex]; } diff --git a/Graphics/GraphicsEngineD3D12/src/TextureD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/TextureD3D12Impl.cpp index 8361421f..f105a218 100644 --- a/Graphics/GraphicsEngineD3D12/src/TextureD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/TextureD3D12Impl.cpp @@ -56,12 +56,12 @@ DXGI_FORMAT GetClearFormat(DXGI_FORMAT Fmt, D3D12_RESOURCE_FLAGS Flags) return Fmt; } -TextureD3D12Impl :: TextureD3D12Impl(FixedBlockMemoryAllocator &TexObjAllocator, +TextureD3D12Impl :: TextureD3D12Impl(IReferenceCounters *pRefCounters, FixedBlockMemoryAllocator &TexViewObjAllocator, RenderDeviceD3D12Impl *pRenderDeviceD3D12, const TextureDesc& TexDesc, const TextureData &InitData /*= TextureData()*/) : - TTextureBase(TexObjAllocator, TexViewObjAllocator, pRenderDeviceD3D12, TexDesc) + TTextureBase(pRefCounters, TexViewObjAllocator, pRenderDeviceD3D12, TexDesc) { if( m_Desc.Usage == USAGE_STATIC && InitData.pSubResources == nullptr ) LOG_ERROR_AND_THROW("Static Texture must be initialized with data at creation time"); @@ -94,8 +94,14 @@ TextureD3D12Impl :: TextureD3D12Impl(FixedBlockMemoryAllocator &TexObjAllocator, Desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL; if( (m_Desc.BindFlags & BIND_UNORDERED_ACCESS) || (m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) ) Desc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS; + if( (m_Desc.BindFlags & BIND_SHADER_RESOURCE) == 0 ) + Desc.Flags |= D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE; - Desc.Format = TexFormatToDXGI_Format(m_Desc.Format, m_Desc.BindFlags);; + auto Format = TexFormatToDXGI_Format(m_Desc.Format, m_Desc.BindFlags); + if (Format == DXGI_FORMAT_R8G8B8A8_UNORM_SRGB && (Desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS)) + Desc.Format = DXGI_FORMAT_R8G8B8A8_TYPELESS; + else + Desc.Format = Format; Desc.Height = (UINT)m_Desc.Height; Desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; Desc.MipLevels = static_cast<Uint16>(m_Desc.MipLevels); @@ -119,7 +125,7 @@ TextureD3D12Impl :: TextureD3D12Impl(FixedBlockMemoryAllocator &TexObjAllocator, if(m_Desc.ClearValue.Format != TEX_FORMAT_UNKNOWN) ClearValue.Format = TexFormatToDXGI_Format(m_Desc.ClearValue.Format); else - ClearValue.Format = GetClearFormat(Desc.Format, Desc.Flags); + ClearValue.Format = GetClearFormat(Format, Desc.Flags); if (Desc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) { @@ -194,7 +200,25 @@ TextureD3D12Impl :: TextureD3D12Impl(FixedBlockMemoryAllocator &TexObjAllocator, auto UploadedSize = UpdateSubresources(pInitContext->GetCommandList(), m_pd3d12Resource, UploadBuffer, 0, 0, InitData.NumSubresources, D3D12SubResData.data()); VERIFY(UploadedSize == uploadBufferSize, "Incorrect uploaded data size (", UploadedSize, "). ", uploadBufferSize, " is expected"); - pRenderDeviceD3D12->CloseAndExecuteCommandContext(pInitContext); + // 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/d3d12/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 | | + pRenderDeviceD3D12->CloseAndExecuteCommandContext(pInitContext, false); // We MUST NOT call TransitionResource() from here, because // it will call AddRef() and potentially Release(), while @@ -207,26 +231,38 @@ TextureD3D12Impl :: TextureD3D12Impl(FixedBlockMemoryAllocator &TexObjAllocator, if(m_Desc.MiscFlags & MISC_TEXTURE_FLAG_GENERATE_MIPS) { - auto NumSubresources = m_Desc.MipLevels * m_Desc.ArraySize; - m_SubresUAVs = pRenderDeviceD3D12->AllocateDescriptor(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, NumSubresources); - for(Uint32 Slice = 0; Slice < m_Desc.ArraySize; ++Slice) - for (Uint32 MipLevel = 0; MipLevel < m_Desc.MipLevels; ++MipLevel) - { - auto SubResInd = D3D12CalcSubresource(MipLevel, Slice, 0, m_Desc.MipLevels, m_Desc.ArraySize); - VERIFY_EXPR(SubResInd < m_SubresUAVs.GetNumHandles()); - - TextureViewDesc UAVDesc; - if( m_Desc.Type == RESOURCE_DIM_TEX_2D ) - UAVDesc.TextureDim = RESOURCE_DIM_TEX_2D; - else - { - LOG_ERROR("Mipmap generation is only supported for 2D textures and texture arrays") - } - UAVDesc.ViewType = TEXTURE_VIEW_UNORDERED_ACCESS; - UAVDesc.FirstArraySlice = Slice; - UAVDesc.MostDetailedMip = MipLevel; - CreateUAV( UAVDesc, m_SubresUAVs.GetCpuHandle(SubResInd) ); - } + if (m_Desc.Type != RESOURCE_DIM_TEX_2D && m_Desc.Type != RESOURCE_DIM_TEX_2D_ARRAY) + { + LOG_ERROR_AND_THROW("Mipmap generation is only supported for 2D textures and texture arrays") + } + + m_MipUAVs = pRenderDeviceD3D12->AllocateDescriptor(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, m_Desc.MipLevels); + for(Uint32 MipLevel = 0; MipLevel < m_Desc.MipLevels; ++MipLevel) + { + TextureViewDesc UAVDesc; + // Always create texture array UAV + UAVDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY; + UAVDesc.ViewType = TEXTURE_VIEW_UNORDERED_ACCESS; + UAVDesc.FirstArraySlice = 0; + UAVDesc.NumArraySlices = m_Desc.ArraySize; + UAVDesc.MostDetailedMip = MipLevel; + if (m_Desc.Format == TEX_FORMAT_RGBA8_UNORM_SRGB) + UAVDesc.Format = TEX_FORMAT_RGBA8_UNORM; + CreateUAV( UAVDesc, m_MipUAVs.GetCpuHandle(MipLevel) ); + } + + { + m_TexArraySRV = pRenderDeviceD3D12->AllocateDescriptor(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1); + TextureViewDesc TexArraySRVDesc; + // Create texture array SRV + TexArraySRVDesc.TextureDim = RESOURCE_DIM_TEX_2D_ARRAY; + TexArraySRVDesc.ViewType = TEXTURE_VIEW_SHADER_RESOURCE; + TexArraySRVDesc.FirstArraySlice = 0; + TexArraySRVDesc.NumArraySlices = m_Desc.ArraySize; + TexArraySRVDesc.MostDetailedMip = 0; + TexArraySRVDesc.NumMipLevels = m_Desc.MipLevels; + CreateSRV( TexArraySRVDesc, m_TexArraySRV.GetCpuHandle() ); + } } } @@ -236,22 +272,16 @@ static TextureDesc InitTexDescFromD3D12Resource(ID3D12Resource *pTexture, const auto ResourceDesc = pTexture->GetDesc(); TextureDesc TexDesc = SrcTexDesc; - if( TexDesc.Width != 0 && TexDesc.Width != ResourceDesc.Width) - LOG_WARNING_MESSAGE("Provided texture width (", TexDesc.Width, ") does not match the actual D3D12 resource width (", ResourceDesc.Width, ")"); - if( TexDesc.Height != 0 && TexDesc.Height != ResourceDesc.Height) - LOG_WARNING_MESSAGE("Provided texture height (", TexDesc.Height, ") does not match the actual D3D12 resource height (", ResourceDesc.Height, ")"); - if( TexDesc.ArraySize != 0 && TexDesc.ArraySize != ResourceDesc.DepthOrArraySize) - LOG_WARNING_MESSAGE("Provided texture depth or array size (", TexDesc.ArraySize, ") does not match the actual D3D12 resource depth or array size (", ResourceDesc.DepthOrArraySize, ")"); - if( TexDesc.MipLevels != 0 && TexDesc.MipLevels != ResourceDesc.MipLevels) - LOG_WARNING_MESSAGE("Provided number of mip levels (", TexDesc.MipLevels, ") does not match the actual number of mip levels in D3D12 resource (", ResourceDesc.MipLevels, ")"); + if (TexDesc.Format == TEX_FORMAT_UNKNOWN) + TexDesc.Format = DXGI_FormatToTexFormat(ResourceDesc.Format); auto RefDXGIFormat = TexFormatToDXGI_Format(TexDesc.Format); if( RefDXGIFormat != TexDesc.Format) LOG_ERROR_AND_THROW("Incorrect texture format (", GetTextureFormatAttribs(TexDesc.Format).Name, ")"); - TexDesc.Width = static_cast<Uint32>(ResourceDesc.Width); - TexDesc.Height = static_cast<Uint32>(ResourceDesc.Height); - TexDesc.ArraySize = ResourceDesc.DepthOrArraySize; - TexDesc.MipLevels = ResourceDesc.MipLevels; + TexDesc.Width = static_cast<Uint32>( ResourceDesc.Width ); + TexDesc.Height = Uint32{ ResourceDesc.Height }; + TexDesc.ArraySize = Uint32{ ResourceDesc.DepthOrArraySize }; + TexDesc.MipLevels = Uint32{ ResourceDesc.MipLevels }; switch(ResourceDesc.Dimension) { case D3D12_RESOURCE_DIMENSION_TEXTURE1D: TexDesc.Type = TexDesc.ArraySize == 1 ? RESOURCE_DIM_TEX_1D : RESOURCE_DIM_TEX_1D_ARRAY; break; @@ -263,21 +293,31 @@ static TextureDesc InitTexDescFromD3D12Resource(ID3D12Resource *pTexture, const TexDesc.Usage = USAGE_DEFAULT; TexDesc.BindFlags = 0; - if( ResourceDesc.Flags |= D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET ) + if( (ResourceDesc.Flags & D3D12_RESOURCE_FLAG_ALLOW_RENDER_TARGET) != 0 ) TexDesc.BindFlags |= BIND_RENDER_TARGET; - if( ResourceDesc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL ) + if( (ResourceDesc.Flags & D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL) != 0 ) TexDesc.BindFlags |= BIND_DEPTH_STENCIL; - if( ResourceDesc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS ) + if( (ResourceDesc.Flags & D3D12_RESOURCE_FLAG_ALLOW_UNORDERED_ACCESS) != 0 ) TexDesc.BindFlags |= BIND_UNORDERED_ACCESS; + if ((ResourceDesc.Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE) == 0) + { + auto FormatAttribs = GetTextureFormatAttribs(TexDesc.Format); + if (FormatAttribs.ComponentType != COMPONENT_TYPE_DEPTH && + FormatAttribs.ComponentType != COMPONENT_TYPE_DEPTH_STENCIL) + { + TexDesc.BindFlags |= BIND_SHADER_RESOURCE; + } + } + return TexDesc; } -TextureD3D12Impl::TextureD3D12Impl(FixedBlockMemoryAllocator &TexObjAllocator, +TextureD3D12Impl::TextureD3D12Impl(IReferenceCounters *pRefCounters, FixedBlockMemoryAllocator &TexViewObjAllocator, RenderDeviceD3D12Impl *pDeviceD3D12, const TextureDesc& TexDesc, ID3D12Resource *pTexture) : - TTextureBase(TexObjAllocator, TexViewObjAllocator, pDeviceD3D12, InitTexDescFromD3D12Resource(pTexture, TexDesc)) + TTextureBase(pRefCounters, TexViewObjAllocator, pDeviceD3D12, InitTexDescFromD3D12Resource(pTexture, TexDesc)) { m_pd3d12Resource = pTexture; } @@ -338,7 +378,8 @@ void TextureD3D12Impl::CreateViewInternal( const struct TextureViewDesc &ViewDes default: UNEXPECTED( "Unknown view type" ); break; } - auto pViewD3D12 = NEW(TexViewAllocator, "TextureViewD3D12Impl instance", TextureViewD3D12Impl, GetDevice(), UpdatedViewDesc, this, std::move(ViewHandleAlloc), bIsDefaultView ); + auto pViewD3D12 = NEW_RC_OBJ(TexViewAllocator, "TextureViewD3D12Impl instance", TextureViewD3D12Impl, bIsDefaultView ? this : nullptr) + (GetDevice(), UpdatedViewDesc, this, std::move(ViewHandleAlloc), bIsDefaultView ); VERIFY( pViewD3D12->GetDesc().ViewType == ViewDesc.ViewType, "Incorrect view type" ); if( bIsDefaultView ) @@ -363,22 +404,19 @@ TextureD3D12Impl :: ~TextureD3D12Impl() void TextureD3D12Impl::UpdateData( IDeviceContext *pContext, Uint32 MipLevel, Uint32 Slice, const Box &DstBox, const TextureSubResData &SubresData ) { TTextureBase::UpdateData( pContext, MipLevel, Slice, DstBox, SubresData ); + if (SubresData.pSrcBuffer == nullptr) + { + LOG_ERROR("D3D12 does not allow updating texture subresource from CPU memory"); + return; + } + VERIFY( m_Desc.Usage == USAGE_DEFAULT, "Only default usage resiurces can be updated with UpdateData()" ); - - LOG_ERROR_ONCE("TextureD3D12Impl::UpdateData() is not implemented"); -#if 0 - auto *pD3D12DeviceContext = static_cast<DeviceContextD3D12Impl*>(pContext)->GetD3D12DeviceContext(); - - D3D12_BOX D3D12Box; - D3D12Box.left = DstBox.MinX; - D3D12Box.right = DstBox.MaxX; - D3D12Box.top = DstBox.MinY; - D3D12Box.bottom = DstBox.MaxY; - D3D12Box.front = DstBox.MinZ; - D3D12Box.back = DstBox.MaxZ; - auto SubresIndex = D3D12CalcSubresource(MipLevel, Slice, m_Desc.MipLevels); - pD3D12DeviceContext->UpdateSubresource(m_pd3d12Resource, SubresIndex, &D3D12Box, SubresData.pData, SubresData.Stride, SubresData.DepthStride); -#endif + + auto *pCtxD3D12 = ValidatedCast<DeviceContextD3D12Impl>(pContext); + + auto DstSubResIndex = D3D12CalcSubresource(MipLevel, Slice, 0, m_Desc.MipLevels, m_Desc.ArraySize); + + pCtxD3D12->CopyTextureRegion(SubresData.pSrcBuffer, SubresData.Stride, SubresData.DepthStride, this, DstSubResIndex, DstBox); } void TextureD3D12Impl :: CopyData(IDeviceContext *pContext, @@ -415,32 +453,19 @@ void TextureD3D12Impl :: CopyData(IDeviceContext *pContext, pCtxD3D12->CopyTextureRegion(pSrcTexD3D12, SrcSubResIndex, pD3D12SrcBox, this, DstSubResIndex, DstX, DstY, DstZ); } -void TextureD3D12Impl :: Map(IDeviceContext *pContext, MAP_TYPE MapType, Uint32 MapFlags, PVoid &pMappedData) +void TextureD3D12Impl :: Map(IDeviceContext *pContext, Uint32 Subresource, MAP_TYPE MapType, Uint32 MapFlags, MappedTextureSubresource &MappedData) { - TTextureBase::Map( pContext, MapType, MapFlags, pMappedData ); + TTextureBase::Map( pContext, Subresource, MapType, MapFlags, MappedData ); LOG_ERROR_ONCE("TextureD3D12Impl::Map() is not implemented"); static char TmpDummyBuffer[1024*1024*64]; - pMappedData = TmpDummyBuffer; - - //auto *pD3D12DeviceContext = static_cast<RenderDeviceD3D12Impl*>( static_cast<CRenderDevice*>(m_pDevice) )->GetD3D12DeviceContext(); - //auto D3D12MapType = MapTypeToD3D12MapType(MapType); - //auto D3D12MapFlags = MapFlagsToD3D12MapFlags(MapFlags); - - //D3D12_MAPPED_SUBRESOURCE MappedBuff; - //pD3D12DeviceContext->Map(m_pd3d12Resource, 0, D3D12MapType, D3D12MapFlags, &MappedBuff); - - //pMappedData = MappedBuff.pData; - - //VERIFY( pMappedData, "Map failed" ); + MappedData.pData = TmpDummyBuffer; } -void TextureD3D12Impl::Unmap( IDeviceContext *pContext, MAP_TYPE MapType ) +void TextureD3D12Impl::Unmap( IDeviceContext *pContext, Uint32 Subresource, MAP_TYPE MapType, Uint32 MapFlags ) { - TTextureBase::Unmap( pContext, MapType ); + TTextureBase::Unmap( pContext, Subresource, MapType, MapFlags ); LOG_ERROR_ONCE("TextureD3D12Impl::Unmap() is not implemented"); - //auto *pD3D12DeviceContext = static_cast<RenderDeviceD3D12Impl*>( static_cast<CRenderDevice*>(m_pDevice) )->GetD3D12DeviceContext(); - //pD3D12DeviceContext->Unmap(m_pd3d12Resource, 0); } @@ -507,9 +532,4 @@ void TextureD3D12Impl::CreateUAV( TextureViewDesc &UAVDesc, D3D12_CPU_DESCRIPTOR pDeviceD3D12->CreateUnorderedAccessView( m_pd3d12Resource, nullptr, &D3D12_UAVDesc, UAVHandle ); } -D3D12_CPU_DESCRIPTOR_HANDLE TextureD3D12Impl::GetUAVDescriptorHandle(Uint32 Mip, Uint32 Slice) -{ - return m_SubresUAVs.GetCpuHandle(D3D12CalcSubresource(Mip, Slice, 0, m_Desc.MipLevels, m_Desc.ArraySize)); -} - } diff --git a/Graphics/GraphicsEngineD3D12/src/TextureViewD3D12Impl.cpp b/Graphics/GraphicsEngineD3D12/src/TextureViewD3D12Impl.cpp index 57749298..8e386fbd 100644 --- a/Graphics/GraphicsEngineD3D12/src/TextureViewD3D12Impl.cpp +++ b/Graphics/GraphicsEngineD3D12/src/TextureViewD3D12Impl.cpp @@ -28,13 +28,13 @@ namespace Diligent { -TextureViewD3D12Impl::TextureViewD3D12Impl( FixedBlockMemoryAllocator &TexViewObjAllocator, +TextureViewD3D12Impl::TextureViewD3D12Impl( IReferenceCounters *pRefCounters, IRenderDevice *pDevice, const TextureViewDesc& ViewDesc, ITexture *pTexture, DescriptorHeapAllocation &&HandleAlloc, bool bIsDefaultView ) : - TTextureViewBase( TexViewObjAllocator, pDevice, ViewDesc, pTexture, bIsDefaultView ), + TTextureViewBase( pRefCounters, pDevice, ViewDesc, pTexture, bIsDefaultView ), m_Descriptor(std::move(HandleAlloc)) { } |
