diff options
| author | Egor Yusov <egor.yusov@gmail.com> | 2019-04-16 04:42:28 +0000 |
|---|---|---|
| committer | Egor Yusov <egor.yusov@gmail.com> | 2019-04-16 04:42:28 +0000 |
| commit | a08fc052ea6190738cb1408644b51d7de760eee1 (patch) | |
| tree | d387bf07c4219de9137330bde5ce7c2041db0e02 /Graphics/GraphicsTools | |
| parent | Fixed issue with FenceVkImpl not waiting for Vulkan fences to signal when bei... (diff) | |
| download | DiligentCore-a08fc052ea6190738cb1408644b51d7de760eee1.tar.gz DiligentCore-a08fc052ea6190738cb1408644b51d7de760eee1.zip | |
Reworked TextureUploaderD3D12 to work in D3D12 and Vulkan modes
Diffstat (limited to 'Graphics/GraphicsTools')
| -rw-r--r-- | Graphics/GraphicsTools/CMakeLists.txt | 7 | ||||
| -rw-r--r-- | Graphics/GraphicsTools/include/TextureUploaderD3D11.h | 17 | ||||
| -rw-r--r-- | Graphics/GraphicsTools/include/TextureUploaderD3D12_Vk.h (renamed from Graphics/GraphicsTools/include/TextureUploaderD3D12.h) | 21 | ||||
| -rw-r--r-- | Graphics/GraphicsTools/include/TextureUploaderGL.h | 17 | ||||
| -rw-r--r-- | Graphics/GraphicsTools/src/TextureUploader.cpp | 5 | ||||
| -rw-r--r-- | Graphics/GraphicsTools/src/TextureUploaderD3D12.cpp | 284 | ||||
| -rw-r--r-- | Graphics/GraphicsTools/src/TextureUploaderD3D12_Vk.cpp | 368 |
7 files changed, 412 insertions, 307 deletions
diff --git a/Graphics/GraphicsTools/CMakeLists.txt b/Graphics/GraphicsTools/CMakeLists.txt index 05061415..f2908cb0 100644 --- a/Graphics/GraphicsTools/CMakeLists.txt +++ b/Graphics/GraphicsTools/CMakeLists.txt @@ -27,10 +27,9 @@ if(D3D11_SUPPORTED) list(APPEND DEPENDENCIES GraphicsEngineD3D11Interface) endif() -if(D3D12_SUPPORTED) - list(APPEND SOURCE src/TextureUploaderD3D12.cpp) - list(APPEND INCLUDE include/TextureUploaderD3D12.h) - list(APPEND DEPENDENCIES GraphicsEngineD3D12Interface) +if(D3D12_SUPPORTED OR VULKAN_SUPPORTED) + list(APPEND SOURCE src/TextureUploaderD3D12_Vk.cpp) + list(APPEND INCLUDE include/TextureUploaderD3D12_Vk.h) endif() if(GL_SUPPORTED OR GLES_SUPPORTED) diff --git a/Graphics/GraphicsTools/include/TextureUploaderD3D11.h b/Graphics/GraphicsTools/include/TextureUploaderD3D11.h index dc14fd75..f6cec59d 100644 --- a/Graphics/GraphicsTools/include/TextureUploaderD3D11.h +++ b/Graphics/GraphicsTools/include/TextureUploaderD3D11.h @@ -30,13 +30,20 @@ namespace Diligent class TextureUploaderD3D11 : public TextureUploaderBase { public: - TextureUploaderD3D11(IReferenceCounters *pRefCounters, IRenderDevice *pDevice, const TextureUploaderDesc Desc); + TextureUploaderD3D11(IReferenceCounters* pRefCounters, + IRenderDevice* pDevice, + const TextureUploaderDesc Desc); ~TextureUploaderD3D11(); - virtual void RenderThreadUpdate(IDeviceContext *pContext)override final; + virtual void RenderThreadUpdate(IDeviceContext* pContext)override final; - virtual void AllocateUploadBuffer(const UploadBufferDesc& Desc, bool IsRenderThread, IUploadBuffer **ppBuffer)override final; - virtual void ScheduleGPUCopy(ITexture *pDstTexture, Uint32 ArraySlice, Uint32 MipLevel, IUploadBuffer *pUploadBuffer)override final; - virtual void RecycleBuffer(IUploadBuffer *pUploadBuffer)override final; + virtual void AllocateUploadBuffer(const UploadBufferDesc& Desc, + bool IsRenderThread, + IUploadBuffer** ppBuffer)override final; + virtual void ScheduleGPUCopy(ITexture* pDstTexture, + Uint32 ArraySlice, + Uint32 MipLevel, + IUploadBuffer* pUploadBuffer)override final; + virtual void RecycleBuffer(IUploadBuffer* pUploadBuffer)override final; private: struct InternalData; diff --git a/Graphics/GraphicsTools/include/TextureUploaderD3D12.h b/Graphics/GraphicsTools/include/TextureUploaderD3D12_Vk.h index c2ca81e5..585d0f37 100644 --- a/Graphics/GraphicsTools/include/TextureUploaderD3D12.h +++ b/Graphics/GraphicsTools/include/TextureUploaderD3D12_Vk.h @@ -27,16 +27,23 @@ namespace Diligent { - class TextureUploaderD3D12 : public TextureUploaderBase + class TextureUploaderD3D12_Vk : public TextureUploaderBase { public: - TextureUploaderD3D12(IReferenceCounters *pRefCounters, IRenderDevice *pDevice, const TextureUploaderDesc Desc); - ~TextureUploaderD3D12(); - virtual void RenderThreadUpdate(IDeviceContext *pContext)override final; + TextureUploaderD3D12_Vk(IReferenceCounters* pRefCounters, + IRenderDevice* pDevice, + const TextureUploaderDesc Desc); + ~TextureUploaderD3D12_Vk(); + virtual void RenderThreadUpdate(IDeviceContext* pContext)override final; - virtual void AllocateUploadBuffer(const UploadBufferDesc& Desc, bool IsRenderThread, IUploadBuffer **ppBuffer)override final; - virtual void ScheduleGPUCopy(ITexture *pDstTexture, Uint32 ArraySlice, Uint32 MipLevel, IUploadBuffer *pUploadBuffer)override final; - virtual void RecycleBuffer(IUploadBuffer *pUploadBuffer)override final; + virtual void AllocateUploadBuffer(const UploadBufferDesc& Desc, + bool IsRenderThread, + IUploadBuffer** ppBuffer)override final; + virtual void ScheduleGPUCopy(ITexture* pDstTexture, + Uint32 ArraySlice, + Uint32 MipLevel, + IUploadBuffer* pUploadBuffer)override final; + virtual void RecycleBuffer(IUploadBuffer* pUploadBuffer)override final; private: struct InternalData; diff --git a/Graphics/GraphicsTools/include/TextureUploaderGL.h b/Graphics/GraphicsTools/include/TextureUploaderGL.h index d186b20c..fd9d77e4 100644 --- a/Graphics/GraphicsTools/include/TextureUploaderGL.h +++ b/Graphics/GraphicsTools/include/TextureUploaderGL.h @@ -30,13 +30,20 @@ namespace Diligent class TextureUploaderGL : public TextureUploaderBase { public: - TextureUploaderGL(IReferenceCounters *pRefCounters, IRenderDevice *pDevice, const TextureUploaderDesc Desc); + TextureUploaderGL(IReferenceCounters* pRefCounters, + IRenderDevice* pDevice, + const TextureUploaderDesc Desc); ~TextureUploaderGL(); - virtual void RenderThreadUpdate(IDeviceContext *pContext)override final; + virtual void RenderThreadUpdate(IDeviceContext* pContext)override final; - virtual void AllocateUploadBuffer(const UploadBufferDesc& Desc, bool IsRenderThread, IUploadBuffer **ppBuffer)override final; - virtual void ScheduleGPUCopy(ITexture *pDstTexture, Uint32 ArraySlice, Uint32 MipLevel, IUploadBuffer *pUploadBuffer)override final; - virtual void RecycleBuffer(IUploadBuffer *pUploadBuffer)override final; + virtual void AllocateUploadBuffer(const UploadBufferDesc& Desc, + bool IsRenderThread, + IUploadBuffer** ppBuffer)override final; + virtual void ScheduleGPUCopy(ITexture* pDstTexture, + Uint32 ArraySlice, + Uint32 MipLevel, + IUploadBuffer* pUploadBuffer)override final; + virtual void RecycleBuffer(IUploadBuffer* pUploadBuffer)override final; private: struct InternalData; diff --git a/Graphics/GraphicsTools/src/TextureUploader.cpp b/Graphics/GraphicsTools/src/TextureUploader.cpp index 46b96cff..ed355677 100644 --- a/Graphics/GraphicsTools/src/TextureUploader.cpp +++ b/Graphics/GraphicsTools/src/TextureUploader.cpp @@ -23,7 +23,7 @@ #include "pch.h" #include "TextureUploaderD3D11.h" -#include "TextureUploaderD3D12.h" +#include "TextureUploaderD3D12_Vk.h" #include "TextureUploaderGL.h" namespace Diligent @@ -38,7 +38,8 @@ namespace Diligent break; case DeviceType::D3D12: - *ppUploader = MakeNewRCObj<TextureUploaderD3D12>()( pDevice, Desc ); + case DeviceType::Vulkan: + *ppUploader = MakeNewRCObj<TextureUploaderD3D12_Vk>()( pDevice, Desc ); break; case DeviceType::OpenGLES: diff --git a/Graphics/GraphicsTools/src/TextureUploaderD3D12.cpp b/Graphics/GraphicsTools/src/TextureUploaderD3D12.cpp deleted file mode 100644 index 71f612cb..00000000 --- a/Graphics/GraphicsTools/src/TextureUploaderD3D12.cpp +++ /dev/null @@ -1,284 +0,0 @@ -/* Copyright 2015-2019 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 <atlbase.h> -#include <mutex> -#include <unordered_map> -#include <deque> -#include <vector> -#include <d3d12.h> - -#include "TextureUploaderD3D12.h" -#include "RenderDeviceD3D12.h" -#include "BufferD3D12.h" -#include "TextureD3D12.h" -#include "ThreadSignal.h" - -namespace Diligent -{ - - class UploadBufferD3D12 : public UploadBufferBase - { - public: - UploadBufferD3D12(IReferenceCounters *pRefCounters, - IRenderDeviceD3D12 *pRenderDeviceD3D12, - const UploadBufferDesc &Desc, - IBuffer *pStagingBuffer, - void *pData, - size_t RowStride, - size_t DepthStride) : - UploadBufferBase(pRefCounters, Desc), - m_pDeviceD3D12(pRenderDeviceD3D12), - m_pStagingBuffer(pStagingBuffer) - { - m_pData = pData; - m_RowStride = RowStride; - m_DepthStride = DepthStride; - } - - ~UploadBufferD3D12() - { - RefCntAutoPtr<IBufferD3D12> pBufferD3D12(m_pStagingBuffer, IID_BufferD3D12); - size_t DataStartOffset; - auto* pd3d12Buff = pBufferD3D12->GetD3D12Buffer(DataStartOffset, nullptr); - pd3d12Buff->Unmap(0, nullptr); - LOG_INFO_MESSAGE("Releasing staging buffer of size ", m_pStagingBuffer->GetDesc().uiSizeInBytes); - } - - void SignalCopyScheduled() - { - m_CopyScheduledSignal.Trigger(); - } - - void Reset() - { - m_CopyScheduledSignal.Reset(); - } - - virtual void WaitForCopyScheduled()override final - { - m_CopyScheduledSignal.Wait(); - } - - IBuffer* GetStagingBuffer() { return m_pStagingBuffer; } - - bool DbgIsCopyScheduled()const { return m_CopyScheduledSignal.IsTriggered(); } - private: - - ThreadingTools::Signal m_CopyScheduledSignal; - - RefCntAutoPtr<IBuffer> m_pStagingBuffer; - RefCntAutoPtr<IRenderDeviceD3D12> m_pDeviceD3D12; - }; - - struct TextureUploaderD3D12::InternalData - { - InternalData(IRenderDevice *pDevice) : - m_pDeviceD3D12(pDevice, IID_RenderDeviceD3D12) - { - m_pd3d12NativeDevice = m_pDeviceD3D12->GetD3D12Device(); - } - - CComPtr<ID3D12Device> m_pd3d12NativeDevice; - RefCntAutoPtr<IRenderDeviceD3D12> m_pDeviceD3D12; - - void SwapMapQueues() - { - std::lock_guard<std::mutex> QueueLock(m_PendingOperationsMtx); - m_PendingOperations.swap(m_InWorkOperations); - } - - void EnqueCopy(UploadBufferD3D12 *pUploadBuffer, ITextureD3D12 *pDstTex, Uint32 dstSlice, Uint32 dstMip) - { - std::lock_guard<std::mutex> QueueLock(m_PendingOperationsMtx); - m_PendingOperations.emplace_back(PendingBufferOperation::Operation::Copy, pUploadBuffer, pDstTex, dstSlice, dstMip); - } - - std::mutex m_PendingOperationsMtx; - struct PendingBufferOperation - { - enum Operation - { - Copy - }operation; - RefCntAutoPtr<UploadBufferD3D12> pUploadBuffer; - RefCntAutoPtr<ITextureD3D12> pDstTexture; - Uint32 DstSlice = 0; - Uint32 DstMip = 0; - - PendingBufferOperation(Operation op, UploadBufferD3D12* pBuff) : - operation(op), - pUploadBuffer(pBuff) - {} - PendingBufferOperation(Operation op, UploadBufferD3D12* pBuff, ITextureD3D12 *pDstTex, Uint32 dstSlice, Uint32 dstMip) : - operation(op), - pUploadBuffer(pBuff), - pDstTexture(pDstTex), - DstSlice(dstSlice), - DstMip(dstMip) - {} - }; - std::vector< PendingBufferOperation > m_PendingOperations; - std::vector< PendingBufferOperation > m_InWorkOperations; - - std::mutex m_UploadBuffCacheMtx; - std::unordered_map< UploadBufferDesc, std::deque< std::pair<Uint64, RefCntAutoPtr<UploadBufferD3D12> > > > m_UploadBufferCache; - }; - - TextureUploaderD3D12::TextureUploaderD3D12(IReferenceCounters *pRefCounters, IRenderDevice *pDevice, const TextureUploaderDesc Desc) : - TextureUploaderBase(pRefCounters, pDevice, Desc), - m_pInternalData(new InternalData(pDevice)) - { - } - - TextureUploaderD3D12::~TextureUploaderD3D12() - { - for (auto BuffQueueIt : m_pInternalData->m_UploadBufferCache) - { - if (BuffQueueIt.second.size()) - { - const auto &desc = BuffQueueIt.first; - auto &FmtInfo = m_pDevice->GetTextureFormatInfo(desc.Format); - LOG_INFO_MESSAGE("TextureUploaderD3D12: releasing ", BuffQueueIt.second.size(), ' ', desc.Width, 'x', desc.Height, 'x', desc.Depth, ' ', FmtInfo.Name, " upload buffer(s) "); - } - } - } - - void TextureUploaderD3D12::RenderThreadUpdate(IDeviceContext *pContext) - { - m_pInternalData->SwapMapQueues(); - if (!m_pInternalData->m_InWorkOperations.empty()) - { - for (auto &OperationInfo : m_pInternalData->m_InWorkOperations) - { - auto &pBuffer = OperationInfo.pUploadBuffer; - - switch (OperationInfo.operation) - { - case InternalData::PendingBufferOperation::Copy: - { - TextureSubResData SubResData(pBuffer->GetStagingBuffer(), 0, static_cast<Uint32>(pBuffer->GetRowStride())); - const auto &TexDesc = OperationInfo.pDstTexture->GetDesc(); - Box DstBox; - DstBox.MaxX = TexDesc.Width; - DstBox.MaxY = TexDesc.Height; - // UpdateTexture() transitions dst subresource to COPY_DEST state and then transitions back to original state - pContext->UpdateTexture(OperationInfo.pDstTexture, OperationInfo.DstMip, OperationInfo.DstSlice, DstBox, - SubResData, RESOURCE_STATE_TRANSITION_MODE_TRANSITION, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); - pBuffer->SignalCopyScheduled(); - } - break; - } - } - - m_pInternalData->m_InWorkOperations.clear(); - } - } - - void TextureUploaderD3D12::AllocateUploadBuffer(const UploadBufferDesc& Desc, bool IsRenderThread, IUploadBuffer **ppBuffer) - { - *ppBuffer = nullptr; - - { - std::lock_guard<std::mutex> CacheLock(m_pInternalData->m_UploadBuffCacheMtx); - auto &Cache = m_pInternalData->m_UploadBufferCache; - if (!Cache.empty()) - { - auto DequeIt = Cache.find(Desc); - if (DequeIt != Cache.end()) - { - auto &Deque = DequeIt->second; - if (!Deque.empty()) - { - auto &FrontBuff = Deque.front(); - if (m_pInternalData->m_pDeviceD3D12->IsFenceSignaled(0, FrontBuff.first)) - { - *ppBuffer = FrontBuff.second.Detach(); - Deque.pop_front(); - } - } - } - } - } - - // No available buffer found in the cache - if(*ppBuffer == nullptr) - { - BufferDesc BuffDesc; - BuffDesc.Name = "Staging buffer for UploadBufferD3D12"; - BuffDesc.CPUAccessFlags = CPU_ACCESS_WRITE; - BuffDesc.Usage = USAGE_STAGING; - - const auto &TexFmtInfo = m_pDevice->GetTextureFormatInfo(Desc.Format); - Uint32 RowStride = Desc.Width * Uint32{TexFmtInfo.ComponentSize} * Uint32{TexFmtInfo.NumComponents}; - static_assert((D3D12_TEXTURE_DATA_PITCH_ALIGNMENT & (D3D12_TEXTURE_DATA_PITCH_ALIGNMENT - 1)) == 0, "Alginment is expected to be power of 2"); - Uint32 AlignmentMask = D3D12_TEXTURE_DATA_PITCH_ALIGNMENT-1; - RowStride = (RowStride + AlignmentMask) & (~AlignmentMask); - - BuffDesc.uiSizeInBytes = Desc.Height * RowStride; - RefCntAutoPtr<IBuffer> pStagingBuffer; - m_pDevice->CreateBuffer(BuffDesc, nullptr, &pStagingBuffer); - - PVoid CpuVirtualAddress = nullptr; - RefCntAutoPtr<IBufferD3D12> pStagingBufferD3D12(pStagingBuffer, IID_BufferD3D12); - size_t DataStartOffset; - auto* pd3d12Buff = pStagingBufferD3D12->GetD3D12Buffer(DataStartOffset, nullptr); - pd3d12Buff->Map(0, nullptr, &CpuVirtualAddress); - if (CpuVirtualAddress == nullptr) - { - LOG_ERROR_MESSAGE("Failed to map upload buffer"); - return; - } - - LOG_INFO_MESSAGE("Created staging buffer of size ", BuffDesc.uiSizeInBytes); - - RefCntAutoPtr<UploadBufferD3D12> pUploadBuffer(MakeNewRCObj<UploadBufferD3D12>()(m_pInternalData->m_pDeviceD3D12, Desc, pStagingBuffer, CpuVirtualAddress, RowStride, 0)); - *ppBuffer = pUploadBuffer.Detach(); - } - } - - void TextureUploaderD3D12::ScheduleGPUCopy(ITexture *pDstTexture, - Uint32 ArraySlice, - Uint32 MipLevel, - IUploadBuffer *pUploadBuffer) - { - auto *pUploadBufferD3D12 = ValidatedCast<UploadBufferD3D12>(pUploadBuffer); - RefCntAutoPtr<ITextureD3D12> pDstTexD3D12(pDstTexture, IID_TextureD3D12); - m_pInternalData->EnqueCopy(pUploadBufferD3D12, pDstTexD3D12, ArraySlice, MipLevel); - } - - void TextureUploaderD3D12::RecycleBuffer(IUploadBuffer *pUploadBuffer) - { - auto *pUploadBufferD3D12 = ValidatedCast<UploadBufferD3D12>(pUploadBuffer); - VERIFY(pUploadBufferD3D12->DbgIsCopyScheduled(), "Upload buffer must be recycled only after copy operation has been scheduled on the GPU"); - pUploadBufferD3D12->Reset(); - - std::lock_guard<std::mutex> CacheLock(m_pInternalData->m_UploadBuffCacheMtx); - auto &Cache = m_pInternalData->m_UploadBufferCache; - auto &Deque = Cache[pUploadBufferD3D12->GetDesc()]; - Uint64 FenceValue = m_pInternalData->m_pDeviceD3D12->GetNextFenceValue(0); - Deque.emplace_back( FenceValue, pUploadBufferD3D12 ); - } -} diff --git a/Graphics/GraphicsTools/src/TextureUploaderD3D12_Vk.cpp b/Graphics/GraphicsTools/src/TextureUploaderD3D12_Vk.cpp new file mode 100644 index 00000000..b3e843b7 --- /dev/null +++ b/Graphics/GraphicsTools/src/TextureUploaderD3D12_Vk.cpp @@ -0,0 +1,368 @@ +/* Copyright 2015-2019 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 <atlbase.h> +#include <mutex> +#include <unordered_map> +#include <deque> +#include <vector> + +#include "TextureUploaderD3D12_Vk.h" +#include "ThreadSignal.h" +#include "GraphicsAccessories.h" + +namespace Diligent +{ + +namespace +{ + +class UploadTexture : public UploadBufferBase +{ +public: + UploadTexture(IReferenceCounters* pRefCounters, + const UploadBufferDesc& Desc, + ITexture* pStagingTexture) : + UploadBufferBase (pRefCounters, Desc), + m_pStagingTexture (pStagingTexture) + { + } + + ~UploadTexture() + { + DEV_CHECK_ERR(m_pData == nullptr, "Releasing mapped staging texture"); + } + + void WaitForMap() + { + m_TextureMappedSignal.Wait(); + } + + void SignalMapped() + { + m_TextureMappedSignal.Trigger(); + } + + void SignalCopyScheduled(Uint64 FenceValue) + { + m_CopyScheduledFenceValue = FenceValue; + m_CopyScheduledSignal.Trigger(); + } + + void Unmap(IDeviceContext* pDeviceContext) + { + pDeviceContext->UnmapTextureSubresource(m_pStagingTexture, 0, 0); + m_pData = nullptr; + m_RowStride = 0; + m_DepthStride = 0; + } + + void Map(IDeviceContext* pDeviceContext) + { + VERIFY(m_pData == nullptr, "Staging texture is already mapped"); + MappedTextureSubresource MappedData; + pDeviceContext->MapTextureSubresource(m_pStagingTexture, 0, 0, MAP_WRITE, MAP_FLAG_DO_NOT_SYNCHRONIZE, nullptr, MappedData); + m_pData = MappedData.pData; + m_RowStride = MappedData.Stride; + m_DepthStride = MappedData.DepthStride; + } + + void Reset() + { + m_CopyScheduledSignal.Reset(); + m_TextureMappedSignal.Reset(); + m_CopyScheduledFenceValue = 0; + } + + virtual void WaitForCopyScheduled()override final + { + m_CopyScheduledSignal.Wait(); + } + + ITexture* GetStagingTexture() { return m_pStagingTexture; } + + bool DbgIsCopyScheduled()const + { + return m_CopyScheduledSignal.IsTriggered(); + } + + bool DbgIsMapped() + { + return m_TextureMappedSignal.IsTriggered(); + } + + Uint64 GetCopyScheduledFenceValue()const + { + VERIFY(m_CopyScheduledFenceValue != 0, "Fence value has not been initialized"); + return m_CopyScheduledFenceValue; + } + +private: + ThreadingTools::Signal m_CopyScheduledSignal; + ThreadingTools::Signal m_TextureMappedSignal; + + RefCntAutoPtr<ITexture> m_pStagingTexture; + Uint64 m_CopyScheduledFenceValue = 0; +}; + +} // namespace + + +struct TextureUploaderD3D12_Vk::InternalData +{ + struct PendingBufferOperation + { + enum Operation + { + Copy, + Map + }operation; + RefCntAutoPtr<UploadTexture> pUploadTexture; + RefCntAutoPtr<ITexture> pDstTexture; + Uint32 DstSlice = 0; + Uint32 DstMip = 0; + + PendingBufferOperation(Operation op, UploadTexture* pUploadTex) : + operation (op), + pUploadTexture(pUploadTex) + {} + PendingBufferOperation(Operation op, UploadTexture* pUploadTex, ITexture* pDstTex, Uint32 dstSlice, Uint32 dstMip) : + operation (op), + pUploadTexture (pUploadTex), + pDstTexture (pDstTex), + DstSlice (dstSlice), + DstMip (dstMip) + {} + }; + + InternalData(IRenderDevice* pDevice) + { + FenceDesc fenceDesc; + fenceDesc.Name = "Texture uploader sync fence"; + pDevice->CreateFence(fenceDesc, &m_pFence); + } + + ~InternalData() + { + for (auto it : m_UploadTexturesCache) + { + if (it.second.size()) + { + const auto& desc = it.first; + auto& FmtInfo = GetTextureFormatAttribs(desc.Format); + LOG_INFO_MESSAGE("TextureUploaderD3D12_Vk: releasing ", it.second.size(), ' ', desc.Width, 'x', desc.Height, 'x', desc.Depth, ' ', FmtInfo.Name, " upload buffer(s) "); + } + } + } + + std::vector<PendingBufferOperation>& SwapMapQueues() + { + std::lock_guard<std::mutex> QueueLock(m_PendingOperationsMtx); + m_PendingOperations.swap(m_InWorkOperations); + return m_InWorkOperations; + } + + void EnqueCopy(UploadTexture* pUploadBuffer, ITexture* pDstTex, Uint32 dstSlice, Uint32 dstMip) + { + std::lock_guard<std::mutex> QueueLock(m_PendingOperationsMtx); + m_PendingOperations.emplace_back(PendingBufferOperation::Operation::Copy, pUploadBuffer, pDstTex, dstSlice, dstMip); + } + + void EnqueMap(UploadTexture* pUploadBuffer) + { + std::lock_guard<std::mutex> QueueLock(m_PendingOperationsMtx); + m_PendingOperations.emplace_back(PendingBufferOperation::Operation::Map, pUploadBuffer); + } + + Uint64 SignalFence(IDeviceContext* pContext) + { + // Fences can't be accessed from multiple threads simultaneously even + // when protected by mutex + auto FenceValue = m_NextFenceValue++; + pContext->SignalFence(m_pFence, FenceValue); + return FenceValue; + } + + void UpdatedCompletedFenceValue() + { + // Fences can't be accessed from multiple threads simultaneously even + // when protected by mutex + m_CompletedFenceValue = m_pFence->GetCompletedValue(); + } + + RefCntAutoPtr<UploadTexture> FindCachedUploadTexture(const UploadBufferDesc& Desc) + { + RefCntAutoPtr<UploadTexture> pUploadTexture; + std::lock_guard<std::mutex> CacheLock(m_UploadTexturesCacheMtx); + auto DequeIt = m_UploadTexturesCache.find(Desc); + if (DequeIt != m_UploadTexturesCache.end()) + { + auto& Deque = DequeIt->second; + if (!Deque.empty()) + { + auto& FrontBuff = Deque.front(); + if (FrontBuff->GetCopyScheduledFenceValue() <= m_CompletedFenceValue) + { + pUploadTexture = std::move(FrontBuff); + Deque.pop_front(); + pUploadTexture->Reset(); + } + } + } + + return pUploadTexture; + } + + void RecycleUploadTexture(UploadTexture* pUploadTexture) + { + std::lock_guard<std::mutex> CacheLock(m_UploadTexturesCacheMtx); + auto& Deque = m_UploadTexturesCache[pUploadTexture->GetDesc()]; + Deque.emplace_back(pUploadTexture); + } + +private: + std::mutex m_PendingOperationsMtx; + std::vector<PendingBufferOperation> m_PendingOperations; + std::vector<PendingBufferOperation> m_InWorkOperations; + + std::mutex m_UploadTexturesCacheMtx; + std::unordered_map< UploadBufferDesc, std::deque< RefCntAutoPtr<UploadTexture> > > m_UploadTexturesCache; + + RefCntAutoPtr<IFence> m_pFence; + Uint64 m_NextFenceValue = 1; + Uint64 m_CompletedFenceValue = 0; +}; + +TextureUploaderD3D12_Vk::TextureUploaderD3D12_Vk(IReferenceCounters* pRefCounters, IRenderDevice* pDevice, const TextureUploaderDesc Desc) : + TextureUploaderBase(pRefCounters, pDevice, Desc), + m_pInternalData(new InternalData(pDevice)) +{ +} + +TextureUploaderD3D12_Vk::~TextureUploaderD3D12_Vk() +{ +} + +void TextureUploaderD3D12_Vk::RenderThreadUpdate(IDeviceContext* pContext) +{ + auto& InWorkOperations = m_pInternalData->SwapMapQueues(); + if (!InWorkOperations.empty()) + { + for (auto& OperationInfo : InWorkOperations) + { + auto& pUploadTex = OperationInfo.pUploadTexture; + + switch (OperationInfo.operation) + { + case InternalData::PendingBufferOperation::Map: + { + pUploadTex->Map(pContext); + pUploadTex->SignalMapped(); + } + break; + + case InternalData::PendingBufferOperation::Copy: + { + VERIFY(pUploadTex->DbgIsMapped(), "Upload texture must be copied only after it has been mapped"); + pUploadTex->Unmap(pContext); + + CopyTextureAttribs CopyInfo + { + pUploadTex->GetStagingTexture(), + RESOURCE_STATE_TRANSITION_MODE_TRANSITION, + OperationInfo.pDstTexture, + RESOURCE_STATE_TRANSITION_MODE_TRANSITION + }; + CopyInfo.DstSlice = OperationInfo.DstSlice; + CopyInfo.DstMipLevel = OperationInfo.DstMip; + pContext->CopyTexture(CopyInfo); + } + break; + } + } + + // The buffer may be recycled immediately after the copy scheduled is signaled, + // so we must signal the fence first. + auto SignaledFenceValue = m_pInternalData->SignalFence(pContext); + + for (auto& OperationInfo : InWorkOperations) + { + if (OperationInfo.operation == InternalData::PendingBufferOperation::Copy) + OperationInfo.pUploadTexture->SignalCopyScheduled(SignaledFenceValue); + } + + InWorkOperations.clear(); + } + + // This must be called by the same thread that signals the fence + m_pInternalData->UpdatedCompletedFenceValue(); +} + +void TextureUploaderD3D12_Vk::AllocateUploadBuffer(const UploadBufferDesc& Desc, bool IsRenderThread, IUploadBuffer** ppBuffer) +{ + RefCntAutoPtr<UploadTexture> pUploadTexture = m_pInternalData->FindCachedUploadTexture(Desc); + + // No available buffer found in the cache + if (!pUploadTexture) + { + TextureDesc StagingTexDesc; + StagingTexDesc.Type = RESOURCE_DIM_TEX_2D; + StagingTexDesc.Width = Desc.Width; + StagingTexDesc.Height = Desc.Height; + StagingTexDesc.Format = Desc.Format; + StagingTexDesc.CPUAccessFlags = CPU_ACCESS_WRITE; + StagingTexDesc.Usage = USAGE_STAGING; + + RefCntAutoPtr<ITexture> pStagingTexture; + m_pDevice->CreateTexture(StagingTexDesc, nullptr, &pStagingTexture); + + LOG_INFO_MESSAGE("Created ", Desc.Width, "x", Desc.Height, " ", GetTextureFormatAttribs(Desc.Format).Name, " staging texture"); + + pUploadTexture = MakeNewRCObj<UploadTexture>()(Desc, pStagingTexture); + } + + m_pInternalData->EnqueMap(pUploadTexture); + pUploadTexture->WaitForMap(); + *ppBuffer = pUploadTexture.Detach(); +} + +void TextureUploaderD3D12_Vk::ScheduleGPUCopy(ITexture* pDstTexture, + Uint32 ArraySlice, + Uint32 MipLevel, + IUploadBuffer* pUploadBuffer) +{ + auto* pUploadTexture = ValidatedCast<UploadTexture>(pUploadBuffer); + m_pInternalData->EnqueCopy(pUploadTexture, pDstTexture, ArraySlice, MipLevel); +} + +void TextureUploaderD3D12_Vk::RecycleBuffer(IUploadBuffer* pUploadBuffer) +{ + auto* pUploadTexture = ValidatedCast<UploadTexture>(pUploadBuffer); + VERIFY(pUploadTexture->DbgIsCopyScheduled(), "Upload buffer must be recycled only after copy operation has been scheduled on the GPU"); + + m_pInternalData->RecycleUploadTexture(pUploadTexture); +} + +} // namespace Diligent |
