diff options
| author | assiduous <assiduous@diligentgraphics.com> | 2020-04-08 20:47:14 +0000 |
|---|---|---|
| committer | assiduous <assiduous@diligentgraphics.com> | 2020-04-08 20:47:14 +0000 |
| commit | 0729ade33fb0f255214af2b228141731bac7a6c9 (patch) | |
| tree | 024f326bd3aaed7f1fa2d3c27aa72c83314156e6 /Graphics/GraphicsTools | |
| parent | Math lib: added RGBA8Unorm_To_F4Color and F4Color_To_RGBA8Unorm functions (diff) | |
| download | DiligentCore-0729ade33fb0f255214af2b228141731bac7a6c9.tar.gz DiligentCore-0729ade33fb0f255214af2b228141731bac7a6c9.zip | |
Updated ITextureUploader to allow synchronous execution
Diffstat (limited to 'Graphics/GraphicsTools')
7 files changed, 436 insertions, 233 deletions
diff --git a/Graphics/GraphicsTools/interface/TextureUploader.hpp b/Graphics/GraphicsTools/interface/TextureUploader.hpp index 021dbf67..fa4374b6 100644 --- a/Graphics/GraphicsTools/interface/TextureUploader.hpp +++ b/Graphics/GraphicsTools/interface/TextureUploader.hpp @@ -34,6 +34,8 @@ namespace Diligent { // clang-format off + +/// Upload buffer description struct UploadBufferDesc { Uint32 Width = 0; @@ -61,29 +63,84 @@ public: virtual const UploadBufferDesc& GetDesc() const = 0; }; +/// Texture uploader description. struct TextureUploaderDesc { }; + +/// Texture uploader statistics. struct TextureUploaderStats { Uint32 NumPendingOperations = 0; }; +/// Asynchronous texture uplader class ITextureUploader : public IObject { public: + /// Executes pending render-thread operations virtual void RenderThreadUpdate(IDeviceContext* pContext) = 0; - virtual void AllocateUploadBuffer(const UploadBufferDesc& Desc, - bool IsRenderThread, + + /// Allocates upload buffer + + /// \param [in] pContext - Pointer to the device context when the method is executed by + /// render thread, or null when it is called from a worker thread, + /// see remarks. + /// \param [in] Desc - Buffer description, see Diligent::UploadBufferDesc. + /// \param [out] ppBuffer - Memory address where pointer to the created upload buffer + /// object will be written to. + /// + /// \remarks When the method is called from a worker thread (pContext is null), + /// it may enqueue a render-thread operation and block until the operation is + /// complete. If in this case the method is in fact called from the render thread, + /// it may never return causing a deadlock. Always provide non-null device context + /// when calling the method from the render thread. On the other hand, always + /// pass null when calling the method from a worker thread to avoid + /// synchronization issues, which may result in an undefined behavior. + /// + /// The method can be safely called from multiple threads simultaneously. + /// However, if pContext is not null, the application is responsible for synchronizing + /// the access to the context. + virtual void AllocateUploadBuffer(IDeviceContext* pContext, + const UploadBufferDesc& Desc, IUploadBuffer** ppBuffer) = 0; - virtual void ScheduleGPUCopy(ITexture* pDstTexture, - Uint32 ArraySlice, - Uint32 MipLevel, - IUploadBuffer* pUploadBuffer) = 0; - virtual void RecycleBuffer(IUploadBuffer* pUploadBuffer) = 0; + + /// Schedules a GPU copy or executes the copy immediately. + + /// \param [in] pContext - Pointer to the device context when the method is executed by + /// render thread, or null when it is called from a worker thread, + /// see remarks. + /// \param [in] pDstTexture - Destination texture for copy operation. + /// \param [in] ArraySlice - Destination array slice. When multiple slices + /// are copied, the starting slice. + /// \param [in] MipLevel - Destination mip level. When multiple mip levels are copied, + /// the starting mip level. + /// \param [in] pUploadBuffer - Upload buffer to copy data from. + /// + /// \remarks When the method is called from a worker thread (pContext is null), + /// it may enqueue a render-thread operation and block until the operation is + /// complete. If in this case the method is in fact called from the render thread, + /// it may never return causing a deadlock. Always provide non-null device context + /// when calling the method from the render thread. On the other hand, always + /// pass null when calling the method from a worker thread to avoid + /// synchronization issues, which may result in an undefined behavior. + virtual void ScheduleGPUCopy(IDeviceContext* pContext, + ITexture* pDstTexture, + Uint32 ArraySlice, + Uint32 MipLevel, + IUploadBuffer* pUploadBuffer) = 0; + + + /// Recycles upload buffer to make it available for future operations. + + /// \param [in] pUploadBuffer - Upload buffer to recycle. + virtual void RecycleBuffer(IUploadBuffer* pUploadBuffer) = 0; + + + /// Returns texture uploader statistics, see Diligent::TextureUploaderStats. virtual TextureUploaderStats GetStats() = 0; }; diff --git a/Graphics/GraphicsTools/interface/TextureUploaderD3D11.hpp b/Graphics/GraphicsTools/interface/TextureUploaderD3D11.hpp index fab896bd..313daa02 100644 --- a/Graphics/GraphicsTools/interface/TextureUploaderD3D11.hpp +++ b/Graphics/GraphicsTools/interface/TextureUploaderD3D11.hpp @@ -42,14 +42,15 @@ public: virtual void RenderThreadUpdate(IDeviceContext* pContext) override final; - virtual void AllocateUploadBuffer(const UploadBufferDesc& Desc, - bool IsRenderThread, + virtual void AllocateUploadBuffer(IDeviceContext* pContext, + const UploadBufferDesc& Desc, IUploadBuffer** ppBuffer) override final; - virtual void ScheduleGPUCopy(ITexture* pDstTexture, - Uint32 ArraySlice, - Uint32 MipLevel, - IUploadBuffer* pUploadBuffer) override final; + virtual void ScheduleGPUCopy(IDeviceContext* pContext, + ITexture* pDstTexture, + Uint32 ArraySlice, + Uint32 MipLevel, + IUploadBuffer* pUploadBuffer) override final; virtual void RecycleBuffer(IUploadBuffer* pUploadBuffer) override final; diff --git a/Graphics/GraphicsTools/interface/TextureUploaderD3D12_Vk.hpp b/Graphics/GraphicsTools/interface/TextureUploaderD3D12_Vk.hpp index 4fac42f3..d8e0af1c 100644 --- a/Graphics/GraphicsTools/interface/TextureUploaderD3D12_Vk.hpp +++ b/Graphics/GraphicsTools/interface/TextureUploaderD3D12_Vk.hpp @@ -42,14 +42,15 @@ public: virtual void RenderThreadUpdate(IDeviceContext* pContext) override final; - virtual void AllocateUploadBuffer(const UploadBufferDesc& Desc, - bool IsRenderThread, + virtual void AllocateUploadBuffer(IDeviceContext* pContext, + const UploadBufferDesc& Desc, IUploadBuffer** ppBuffer) override final; - virtual void ScheduleGPUCopy(ITexture* pDstTexture, - Uint32 ArraySlice, - Uint32 MipLevel, - IUploadBuffer* pUploadBuffer) override final; + virtual void ScheduleGPUCopy(IDeviceContext* pContext, + ITexture* pDstTexture, + Uint32 ArraySlice, + Uint32 MipLevel, + IUploadBuffer* pUploadBuffer) override final; virtual void RecycleBuffer(IUploadBuffer* pUploadBuffer) override final; diff --git a/Graphics/GraphicsTools/interface/TextureUploaderGL.hpp b/Graphics/GraphicsTools/interface/TextureUploaderGL.hpp index 20be3213..2361c0a5 100644 --- a/Graphics/GraphicsTools/interface/TextureUploaderGL.hpp +++ b/Graphics/GraphicsTools/interface/TextureUploaderGL.hpp @@ -42,14 +42,15 @@ public: virtual void RenderThreadUpdate(IDeviceContext* pContext) override final; - virtual void AllocateUploadBuffer(const UploadBufferDesc& Desc, - bool IsRenderThread, + virtual void AllocateUploadBuffer(IDeviceContext* pContext, + const UploadBufferDesc& Desc, IUploadBuffer** ppBuffer) override final; - virtual void ScheduleGPUCopy(ITexture* pDstTexture, - Uint32 ArraySlice, - Uint32 MipLevel, - IUploadBuffer* pUploadBuffer) override final; + virtual void ScheduleGPUCopy(IDeviceContext* pContext, + ITexture* pDstTexture, + Uint32 ArraySlice, + Uint32 MipLevel, + IUploadBuffer* pUploadBuffer) override final; virtual void RecycleBuffer(IUploadBuffer* pUploadBuffer) override final; diff --git a/Graphics/GraphicsTools/src/TextureUploaderD3D11.cpp b/Graphics/GraphicsTools/src/TextureUploaderD3D11.cpp index 624a615a..5e47e3a7 100644 --- a/Graphics/GraphicsTools/src/TextureUploaderD3D11.cpp +++ b/Graphics/GraphicsTools/src/TextureUploaderD3D11.cpp @@ -117,8 +117,7 @@ struct TextureUploaderD3D11::InternalData enum Operation { Map, - Copy, - MapAndCache + Copy } operation; RefCntAutoPtr<UploadBufferD3D11> pUploadBuffer; CComPtr<ID3D11Resource> pd3d11NativeDstTexture; @@ -168,6 +167,23 @@ struct TextureUploaderD3D11::InternalData m_PendingOperations.emplace_back(Op, pUploadBuffer); } + void Execute(ID3D11DeviceContext* pd3d11NativeCtx, PendingBufferOperation& OperationInfo, bool ExecuteImmediately); + + void ExecuteImmediately(IDeviceContext* pContext, PendingBufferOperation& OperationInfo) + { + RefCntAutoPtr<IDeviceContextD3D11> pContextD3D11(pContext, IID_DeviceContextD3D11); + if (pContextD3D11) + { + auto* pd3d11NativeCtx = pContextD3D11->GetD3D11DeviceContext(); + Execute(pd3d11NativeCtx, OperationInfo, true /*ExecuteImmediately*/); + } + else + { + UNEXPECTED("Failed to query IID_DeviceContextD3D11 interface from the device context. " + "Is it really Diligent::IDeviceContextD3D11 interface?"); + } + } + std::mutex m_PendingOperationsMtx; std::vector<PendingBufferOperation> m_PendingOperations; std::vector<PendingBufferOperation> m_InWorkOperations; @@ -199,7 +215,8 @@ TextureUploaderD3D11::~TextureUploaderD3D11() { const auto& desc = BuffQueueIt.first; auto& FmtInfo = m_pDevice->GetTextureFormatInfo(desc.Format); - LOG_INFO_MESSAGE("TextureUploaderD3D11: releasing ", BuffQueueIt.second.size(), ' ', desc.Width, 'x', desc.Height, 'x', desc.Depth, ' ', FmtInfo.Name, " staging texture(s) "); + LOG_INFO_MESSAGE("TextureUploaderD3D11: releasing ", BuffQueueIt.second.size(), ' ', desc.Width, 'x', desc.Height, 'x', desc.Depth, ' ', FmtInfo.Name, + " staging texture", (BuffQueueIt.second.size() != 1 ? "s" : "")); } } } @@ -213,105 +230,114 @@ void TextureUploaderD3D11::RenderThreadUpdate(IDeviceContext* pContext) auto* pd3d11NativeCtx = pContextD3D11->GetD3D11DeviceContext(); - for (auto& OperationInfo : m_pInternalData->m_InWorkOperations) + for (auto& Operation : m_pInternalData->m_InWorkOperations) { - auto& pBuffer = OperationInfo.pUploadBuffer; - const auto& UploadBuffDesc = pBuffer->GetDesc(); + m_pInternalData->Execute(pd3d11NativeCtx, Operation, false /*ExecuteImmediately*/); + } + + m_pInternalData->m_InWorkOperations.clear(); + } +} + +void TextureUploaderD3D11::InternalData::Execute(ID3D11DeviceContext* pd3d11NativeCtx, + PendingBufferOperation& OperationInfo, + bool ExecuteImmediately) +{ + auto& pBuffer = OperationInfo.pUploadBuffer; + const auto& UploadBuffDesc = pBuffer->GetDesc(); - switch (OperationInfo.operation) + switch (OperationInfo.operation) + { + case InternalData::PendingBufferOperation::Map: + { + bool AllMapped = true; + for (Uint32 Slice = 0; Slice < UploadBuffDesc.ArraySize; ++Slice) { - case InternalData::PendingBufferOperation::MapAndCache: - case InternalData::PendingBufferOperation::Map: + for (Uint32 Mip = 0; Mip < UploadBuffDesc.MipLevels; ++Mip) { - bool AllMapped = true; - for (Uint32 Slice = 0; Slice < UploadBuffDesc.ArraySize; ++Slice) + if (!pBuffer->IsMapped(Mip, Slice)) { - for (Uint32 Mip = 0; Mip < UploadBuffDesc.MipLevels; ++Mip) + D3D11_MAPPED_SUBRESOURCE MappedData; + + UINT Subres = D3D11CalcSubresource(static_cast<UINT>(Mip), static_cast<UINT>(Slice), static_cast<UINT>(UploadBuffDesc.MipLevels)); + auto hr = pd3d11NativeCtx->Map(pBuffer->GetStagingTex(), Subres, D3D11_MAP_WRITE, + ExecuteImmediately ? 0 : D3D11_MAP_FLAG_DO_NOT_WAIT, + &MappedData); + if (SUCCEEDED(hr)) { - if (!pBuffer->IsMapped(Mip, Slice)) - { - D3D11_MAPPED_SUBRESOURCE MappedData; - - UINT Subres = D3D11CalcSubresource(static_cast<UINT>(Mip), static_cast<UINT>(Slice), static_cast<UINT>(UploadBuffDesc.MipLevels)); - auto hr = pd3d11NativeCtx->Map(pBuffer->GetStagingTex(), Subres, D3D11_MAP_WRITE, D3D11_MAP_FLAG_DO_NOT_WAIT, &MappedData); - if (SUCCEEDED(hr)) - { - pBuffer->SetMappedData(Mip, Slice, MappedTextureSubresource{MappedData.pData, MappedData.RowPitch, MappedData.DepthPitch}); - } - else - { - if (hr == DXGI_ERROR_WAS_STILL_DRAWING) - { - AllMapped = false; - } - else - { - LOG_ERROR("Unknown DX error when mapping staging texture: ", hr); - } - } - } + pBuffer->SetMappedData(Mip, Slice, MappedTextureSubresource{MappedData.pData, MappedData.RowPitch, MappedData.DepthPitch}); } - } - - if (AllMapped) - { - pBuffer->SignalMapped(); - if (OperationInfo.operation == InternalData::PendingBufferOperation::MapAndCache) + else { - std::lock_guard<std::mutex> CacheLock(m_pInternalData->m_UploadBuffCacheMtx); - auto& Cache = m_pInternalData->m_UploadBufferCache; - Cache[pBuffer->GetDesc()].emplace_back(std::move(pBuffer)); + VERIFY_EXPR(!ExecuteImmediately); + if (hr == DXGI_ERROR_WAS_STILL_DRAWING) + { + AllMapped = false; + } + else + { + LOG_ERROR("Unknown DX error when mapping staging texture: ", hr); + } } } - else - { - m_pInternalData->EnqueMap(pBuffer, OperationInfo.operation); - } } - break; + } - case InternalData::PendingBufferOperation::Copy: - { - VERIFY(pBuffer->DbgIsMapped(), "Upload buffer must be copied only after it has been mapped"); - // Unmap all subresources first to avoid D3D11 warnings - for (Uint32 Subres = 0; Subres < UploadBuffDesc.MipLevels * UploadBuffDesc.ArraySize; ++Subres) - { - pd3d11NativeCtx->Unmap(pBuffer->GetStagingTex(), Subres); - } + if (AllMapped) + { + pBuffer->SignalMapped(); + } + else + { + VERIFY_EXPR(!ExecuteImmediately); + EnqueMap(pBuffer, OperationInfo.operation); + } + } + break; - for (Uint32 Slice = 0; Slice < UploadBuffDesc.ArraySize; ++Slice) - { - for (Uint32 Mip = 0; Mip < UploadBuffDesc.MipLevels; ++Mip) - { - UINT SrcSubres = D3D11CalcSubresource( - static_cast<UINT>(Mip), - static_cast<UINT>(Slice), - static_cast<UINT>(UploadBuffDesc.MipLevels)); - UINT DstSubres = D3D11CalcSubresource( - static_cast<UINT>(OperationInfo.DstMip + Mip), - static_cast<UINT>(OperationInfo.DstSlice + Slice), - static_cast<UINT>(OperationInfo.DstMipLevels)); - pd3d11NativeCtx->CopySubresourceRegion(OperationInfo.pd3d11NativeDstTexture, DstSubres, - 0, 0, 0, // DstX, DstY, DstZ - pBuffer->GetStagingTex(), - SrcSubres, - nullptr // pSrcBox - ); - } - } - pBuffer->SignalCopyScheduled(); + case InternalData::PendingBufferOperation::Copy: + { + VERIFY(pBuffer->DbgIsMapped(), "Upload buffer must be copied only after it has been mapped"); + // Unmap all subresources first to avoid D3D11 warnings + for (Uint32 Subres = 0; Subres < UploadBuffDesc.MipLevels * UploadBuffDesc.ArraySize; ++Subres) + { + pd3d11NativeCtx->Unmap(pBuffer->GetStagingTex(), Subres); + } + + for (Uint32 Slice = 0; Slice < UploadBuffDesc.ArraySize; ++Slice) + { + for (Uint32 Mip = 0; Mip < UploadBuffDesc.MipLevels; ++Mip) + { + UINT SrcSubres = D3D11CalcSubresource( + static_cast<UINT>(Mip), + static_cast<UINT>(Slice), + static_cast<UINT>(UploadBuffDesc.MipLevels)); + UINT DstSubres = D3D11CalcSubresource( + static_cast<UINT>(OperationInfo.DstMip + Mip), + static_cast<UINT>(OperationInfo.DstSlice + Slice), + static_cast<UINT>(OperationInfo.DstMipLevels)); + pd3d11NativeCtx->CopySubresourceRegion(OperationInfo.pd3d11NativeDstTexture, DstSubres, + 0, 0, 0, // DstX, DstY, DstZ + pBuffer->GetStagingTex(), + SrcSubres, + nullptr // pSrcBox + ); } - break; } + pBuffer->SignalCopyScheduled(); } - m_pInternalData->m_InWorkOperations.clear(); + break; } } -void TextureUploaderD3D11::AllocateUploadBuffer(const UploadBufferDesc& Desc, bool IsRenderThread, IUploadBuffer** ppBuffer) +void TextureUploaderD3D11::AllocateUploadBuffer(IDeviceContext* pContext, + const UploadBufferDesc& Desc, + IUploadBuffer** ppBuffer) { *ppBuffer = nullptr; + RefCntAutoPtr<UploadBufferD3D11> pUploadBuffer; + { std::lock_guard<std::mutex> CacheLock(m_pInternalData->m_UploadBuffCacheMtx); @@ -324,14 +350,14 @@ void TextureUploaderD3D11::AllocateUploadBuffer(const UploadBufferDesc& Desc, bo auto& Deque = DequeIt->second; if (!Deque.empty()) { - *ppBuffer = Deque.front().Detach(); + pUploadBuffer = std::move(Deque.front()); Deque.pop_front(); } } } } - if (*ppBuffer == nullptr) + if (!pUploadBuffer) { // clang-format off D3D11_TEXTURE2D_DESC StagingTexDesc = @@ -361,23 +387,57 @@ void TextureUploaderD3D11::AllocateUploadBuffer(const UploadBufferDesc& Desc, bo LOG_INFO_MESSAGE("TextureUploaderD3D11: created ", Desc.Width, 'x', Desc.Height, 'x', Desc.Depth, ' ', Desc.MipLevels, "-mip ", m_pDevice->GetTextureFormatInfo(Desc.Format).Name, " staging texture"); - RefCntAutoPtr<UploadBufferD3D11> pUploadBuffer(MakeNewRCObj<UploadBufferD3D11>()(Desc, pStagingTex)); - m_pInternalData->EnqueMap(pUploadBuffer, InternalData::PendingBufferOperation::Map); - pUploadBuffer->WaitForMap(); - *ppBuffer = pUploadBuffer.Detach(); + pUploadBuffer = MakeNewRCObj<UploadBufferD3D11>()(Desc, pStagingTex); } + + if (pUploadBuffer) + { + if (pContext != nullptr) + { + // Main thread + InternalData::PendingBufferOperation MapOp{InternalData::PendingBufferOperation::Map, pUploadBuffer}; + m_pInternalData->ExecuteImmediately(pContext, MapOp); + } + else + { + // Worker thread + m_pInternalData->EnqueMap(pUploadBuffer, InternalData::PendingBufferOperation::Map); + pUploadBuffer->WaitForMap(); + } + } + + *ppBuffer = pUploadBuffer.Detach(); } -void TextureUploaderD3D11::ScheduleGPUCopy(ITexture* pDstTexture, - Uint32 ArraySlice, - Uint32 MipLevel, - IUploadBuffer* pUploadBuffer) +void TextureUploaderD3D11::ScheduleGPUCopy(IDeviceContext* pContext, + ITexture* pDstTexture, + Uint32 ArraySlice, + Uint32 MipLevel, + IUploadBuffer* pUploadBuffer) { auto* pUploadBufferD3D11 = ValidatedCast<UploadBufferD3D11>(pUploadBuffer); RefCntAutoPtr<ITextureD3D11> pDstTexD3D11(pDstTexture, IID_TextureD3D11); auto* pd3d11NativeDstTex = pDstTexD3D11->GetD3D11Texture(); const auto& DstTexDesc = pDstTexture->GetDesc(); - m_pInternalData->EnqueCopy(pUploadBufferD3D11, pd3d11NativeDstTex, MipLevel, ArraySlice, DstTexDesc.MipLevels); + if (pContext != nullptr) + { + // Main thread + InternalData::PendingBufferOperation CopyOp // + { + InternalData::PendingBufferOperation::Copy, + pUploadBufferD3D11, + pd3d11NativeDstTex, + MipLevel, + ArraySlice, + DstTexDesc.MipLevels // + }; + m_pInternalData->ExecuteImmediately(pContext, CopyOp); + } + else + { + // Worker thread + m_pInternalData->EnqueCopy(pUploadBufferD3D11, pd3d11NativeDstTex, MipLevel, ArraySlice, DstTexDesc.MipLevels); + } } void TextureUploaderD3D11::RecycleBuffer(IUploadBuffer* pUploadBuffer) @@ -386,21 +446,16 @@ void TextureUploaderD3D11::RecycleBuffer(IUploadBuffer* pUploadBuffer) VERIFY(pUploadBufferD3D11->DbgIsCopyScheduled(), "Upload buffer must be recycled only after copy operation has been scheduled on the GPU"); pUploadBufferD3D11->Reset(); - m_pInternalData->EnqueMap(pUploadBufferD3D11, InternalData::PendingBufferOperation::MapAndCache); + std::lock_guard<std::mutex> CacheLock(m_pInternalData->m_UploadBuffCacheMtx); + m_pInternalData->m_UploadBufferCache[pUploadBufferD3D11->GetDesc()].emplace_back(pUploadBufferD3D11); } TextureUploaderStats TextureUploaderD3D11::GetStats() { TextureUploaderStats Stats; std::lock_guard<std::mutex> QueueLock(m_pInternalData->m_PendingOperationsMtx); - for (auto& OperationInfo : m_pInternalData->m_PendingOperations) - { - // Do not count MapAndCache operations as they are performed as part of the - // buffer recycling. - if (OperationInfo.operation == InternalData::PendingBufferOperation::Map || - OperationInfo.operation == InternalData::PendingBufferOperation::Copy) - ++Stats.NumPendingOperations; - } + Stats.NumPendingOperations = static_cast<Uint32>(m_pInternalData->m_PendingOperations.size()); + return Stats; } diff --git a/Graphics/GraphicsTools/src/TextureUploaderD3D12_Vk.cpp b/Graphics/GraphicsTools/src/TextureUploaderD3D12_Vk.cpp index 59c550dc..043e05f6 100644 --- a/Graphics/GraphicsTools/src/TextureUploaderD3D12_Vk.cpp +++ b/Graphics/GraphicsTools/src/TextureUploaderD3D12_Vk.cpp @@ -257,6 +257,8 @@ struct TextureUploaderD3D12_Vk::InternalData return static_cast<Uint32>(m_PendingOperations.size()); } + void Execute(IDeviceContext* pContext, PendingBufferOperation& OperationInfo); + private: std::mutex m_PendingOperationsMtx; std::vector<PendingBufferOperation> m_PendingOperations; @@ -296,51 +298,9 @@ void TextureUploaderD3D12_Vk::RenderThreadUpdate(IDeviceContext* pContext) Uint32 NumCopyOperations = 0; for (auto& OperationInfo : InWorkOperations) { - auto& pUploadTex = OperationInfo.pUploadTexture; - const auto& StagingTexDesc = pUploadTex->GetDesc(); - - switch (OperationInfo.operation) - { - case InternalData::PendingBufferOperation::Map: - { - for (Uint32 Slice = 0; Slice < StagingTexDesc.ArraySize; ++Slice) - { - for (Uint32 Mip = 0; Mip < StagingTexDesc.MipLevels; ++Mip) - { - pUploadTex->Map(pContext, Mip, Slice); - } - } - pUploadTex->SignalMapped(); - } - break; - - case InternalData::PendingBufferOperation::Copy: - { - VERIFY(pUploadTex->DbgIsMapped(), "Upload texture must be copied only after it has been mapped"); - for (Uint32 Slice = 0; Slice < StagingTexDesc.ArraySize; ++Slice) - { - for (Uint32 Mip = 0; Mip < StagingTexDesc.MipLevels; ++Mip) - { - pUploadTex->Unmap(pContext, Mip, Slice); - - CopyTextureAttribs CopyInfo // - { - pUploadTex->GetStagingTexture(), - RESOURCE_STATE_TRANSITION_MODE_TRANSITION, - OperationInfo.pDstTexture, - RESOURCE_STATE_TRANSITION_MODE_TRANSITION // - }; - CopyInfo.SrcMipLevel = Mip; - CopyInfo.SrcSlice = Slice; - CopyInfo.DstMipLevel = OperationInfo.DstMip + Mip; - CopyInfo.DstSlice = OperationInfo.DstSlice + Slice; - pContext->CopyTexture(CopyInfo); - } - } - ++NumCopyOperations; - } - break; - } + m_pInternalData->Execute(pContext, OperationInfo); + if (OperationInfo.operation == InternalData::PendingBufferOperation::Copy) + ++NumCopyOperations; } if (NumCopyOperations > 0) @@ -363,7 +323,59 @@ void TextureUploaderD3D12_Vk::RenderThreadUpdate(IDeviceContext* pContext) m_pInternalData->UpdatedCompletedFenceValue(); } -void TextureUploaderD3D12_Vk::AllocateUploadBuffer(const UploadBufferDesc& Desc, bool IsRenderThread, IUploadBuffer** ppBuffer) + +void TextureUploaderD3D12_Vk::InternalData::Execute(IDeviceContext* pContext, + PendingBufferOperation& OperationInfo) +{ + auto& pUploadTex = OperationInfo.pUploadTexture; + const auto& StagingTexDesc = pUploadTex->GetDesc(); + + switch (OperationInfo.operation) + { + case InternalData::PendingBufferOperation::Map: + { + for (Uint32 Slice = 0; Slice < StagingTexDesc.ArraySize; ++Slice) + { + for (Uint32 Mip = 0; Mip < StagingTexDesc.MipLevels; ++Mip) + { + pUploadTex->Map(pContext, Mip, Slice); + } + } + pUploadTex->SignalMapped(); + } + break; + + case InternalData::PendingBufferOperation::Copy: + { + VERIFY(pUploadTex->DbgIsMapped(), "Upload texture must be copied only after it has been mapped"); + for (Uint32 Slice = 0; Slice < StagingTexDesc.ArraySize; ++Slice) + { + for (Uint32 Mip = 0; Mip < StagingTexDesc.MipLevels; ++Mip) + { + pUploadTex->Unmap(pContext, Mip, Slice); + + CopyTextureAttribs CopyInfo // + { + pUploadTex->GetStagingTexture(), + RESOURCE_STATE_TRANSITION_MODE_TRANSITION, + OperationInfo.pDstTexture, + RESOURCE_STATE_TRANSITION_MODE_TRANSITION // + }; + CopyInfo.SrcMipLevel = Mip; + CopyInfo.SrcSlice = Slice; + CopyInfo.DstMipLevel = OperationInfo.DstMip + Mip; + CopyInfo.DstSlice = OperationInfo.DstSlice + Slice; + pContext->CopyTexture(CopyInfo); + } + } + } + break; + } +} + +void TextureUploaderD3D12_Vk::AllocateUploadBuffer(IDeviceContext* pContext, + const UploadBufferDesc& Desc, + IUploadBuffer** ppBuffer) { RefCntAutoPtr<UploadTexture> pUploadTexture = m_pInternalData->FindCachedUploadTexture(Desc); @@ -388,18 +400,53 @@ void TextureUploaderD3D12_Vk::AllocateUploadBuffer(const UploadBufferDesc& Desc, pUploadTexture = MakeNewRCObj<UploadTexture>()(Desc, pStagingTexture); } - m_pInternalData->EnqueMap(pUploadTexture); - pUploadTexture->WaitForMap(); + if (pContext != nullptr) + { + // Render thread + InternalData::PendingBufferOperation MapOp{InternalData::PendingBufferOperation::Operation::Map, pUploadTexture}; + m_pInternalData->Execute(pContext, MapOp); + } + else + { + // Worker thread + m_pInternalData->EnqueMap(pUploadTexture); + pUploadTexture->WaitForMap(); + } *ppBuffer = pUploadTexture.Detach(); } -void TextureUploaderD3D12_Vk::ScheduleGPUCopy(ITexture* pDstTexture, - Uint32 ArraySlice, - Uint32 MipLevel, - IUploadBuffer* pUploadBuffer) +void TextureUploaderD3D12_Vk::ScheduleGPUCopy(IDeviceContext* pContext, + ITexture* pDstTexture, + Uint32 ArraySlice, + Uint32 MipLevel, + IUploadBuffer* pUploadBuffer) { auto* pUploadTexture = ValidatedCast<UploadTexture>(pUploadBuffer); - m_pInternalData->EnqueCopy(pUploadTexture, pDstTexture, ArraySlice, MipLevel); + if (pContext != nullptr) + { + // Render thread + InternalData::PendingBufferOperation CopyOp // + { + InternalData::PendingBufferOperation::Operation::Copy, + pUploadTexture, + pDstTexture, + ArraySlice, + MipLevel // + }; + m_pInternalData->Execute(pContext, CopyOp); + + // 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); + pUploadTexture->SignalCopyScheduled(SignaledFenceValue); + // This must be called by the same thread that signals the fence + m_pInternalData->UpdatedCompletedFenceValue(); + } + else + { + // Worker thread + m_pInternalData->EnqueCopy(pUploadTexture, pDstTexture, ArraySlice, MipLevel); + } } void TextureUploaderD3D12_Vk::RecycleBuffer(IUploadBuffer* pUploadBuffer) diff --git a/Graphics/GraphicsTools/src/TextureUploaderGL.cpp b/Graphics/GraphicsTools/src/TextureUploaderGL.cpp index bd8bb95c..485d5f8c 100644 --- a/Graphics/GraphicsTools/src/TextureUploaderGL.cpp +++ b/Graphics/GraphicsTools/src/TextureUploaderGL.cpp @@ -196,6 +196,10 @@ struct TextureUploaderGL::InternalData // clang-format on }; + void Execute(IRenderDevice* pDevice, + IDeviceContext* pContext, + PendingBufferOperation& OperationInfo); + std::mutex m_PendingOperationsMtx; std::vector<PendingBufferOperation> m_PendingOperations; std::vector<PendingBufferOperation> m_InWorkOperations; @@ -228,7 +232,7 @@ TextureUploaderGL::~TextureUploaderGL() const auto& desc = BuffQueueIt.first; auto& FmtInfo = m_pDevice->GetTextureFormatInfo(desc.Format); LOG_INFO_MESSAGE("TextureUploaderGL: releasing ", BuffQueueIt.second.size(), ' ', desc.Width, 'x', - desc.Height, 'x', desc.Depth, ' ', FmtInfo.Name, " upload buffer(s) "); + desc.Height, 'x', desc.Depth, ' ', FmtInfo.Name, " upload buffer", (BuffQueueIt.second.size() != 1 ? "s" : "")); } } } @@ -240,63 +244,72 @@ void TextureUploaderGL::RenderThreadUpdate(IDeviceContext* pContext) { for (auto& OperationInfo : m_pInternalData->m_InWorkOperations) { - auto& pBuffer = OperationInfo.pUploadBuffer; - const auto& UploadBuffDesc = pBuffer->GetDesc(); + m_pInternalData->Execute(m_pDevice, pContext, OperationInfo); + } + m_pInternalData->m_InWorkOperations.clear(); + } +} + +void TextureUploaderGL::InternalData::Execute(IRenderDevice* pDevice, + IDeviceContext* pContext, + PendingBufferOperation& OperationInfo) +{ + auto& pBuffer = OperationInfo.pUploadBuffer; + const auto& UploadBuffDesc = pBuffer->GetDesc(); - switch (OperationInfo.operation) + switch (OperationInfo.operation) + { + case InternalData::PendingBufferOperation::Map: + { + if (pBuffer->m_pStagingBuffer == nullptr) { - case InternalData::PendingBufferOperation::Map: - { - if (pBuffer->m_pStagingBuffer == nullptr) - { - BufferDesc BuffDesc; - BuffDesc.Name = "Staging buffer for UploadBufferGL"; - BuffDesc.CPUAccessFlags = CPU_ACCESS_WRITE; - BuffDesc.Usage = USAGE_STAGING; - BuffDesc.uiSizeInBytes = pBuffer->GetTotalSize(); - RefCntAutoPtr<IBuffer> pStagingBuffer; - m_pDevice->CreateBuffer(BuffDesc, nullptr, &pBuffer->m_pStagingBuffer); - } - - PVoid CpuAddress = nullptr; - pContext->MapBuffer(pBuffer->m_pStagingBuffer, MAP_WRITE, MAP_FLAG_DISCARD, CpuAddress); - pBuffer->SetDataPtr(reinterpret_cast<Uint8*>(CpuAddress)); - - pBuffer->SignalMapped(); - } - break; + BufferDesc BuffDesc; + BuffDesc.Name = "Staging buffer for UploadBufferGL"; + BuffDesc.CPUAccessFlags = CPU_ACCESS_WRITE; + BuffDesc.Usage = USAGE_STAGING; + BuffDesc.uiSizeInBytes = pBuffer->GetTotalSize(); + RefCntAutoPtr<IBuffer> pStagingBuffer; + pDevice->CreateBuffer(BuffDesc, nullptr, &pBuffer->m_pStagingBuffer); + } + + PVoid CpuAddress = nullptr; + pContext->MapBuffer(pBuffer->m_pStagingBuffer, MAP_WRITE, MAP_FLAG_DISCARD, CpuAddress); + pBuffer->SetDataPtr(reinterpret_cast<Uint8*>(CpuAddress)); + + pBuffer->SignalMapped(); + } + break; - case InternalData::PendingBufferOperation::Copy: + case InternalData::PendingBufferOperation::Copy: + { + const auto& TexDesc = OperationInfo.pDstTexture->GetDesc(); + pContext->UnmapBuffer(pBuffer->m_pStagingBuffer, MAP_WRITE); + for (Uint32 Slice = 0; Slice < UploadBuffDesc.ArraySize; ++Slice) + { + for (Uint32 Mip = 0; Mip < UploadBuffDesc.MipLevels; ++Mip) { - const auto& TexDesc = OperationInfo.pDstTexture->GetDesc(); - pContext->UnmapBuffer(pBuffer->m_pStagingBuffer, MAP_WRITE); - for (Uint32 Slice = 0; Slice < UploadBuffDesc.ArraySize; ++Slice) - { - for (Uint32 Mip = 0; Mip < UploadBuffDesc.MipLevels; ++Mip) - { - auto SrcOffset = pBuffer->GetOffset(Mip, Slice); - auto SrcStride = pBuffer->GetMappedData(Mip, Slice).Stride; - - TextureSubResData SubResData(pBuffer->m_pStagingBuffer, SrcOffset, SrcStride); - - auto MipLevelProps = GetMipLevelProperties(TexDesc, Mip); - Box DstBox; - DstBox.MaxX = MipLevelProps.LogicalWidth; - DstBox.MaxY = MipLevelProps.LogicalHeight; - pContext->UpdateTexture(OperationInfo.pDstTexture, OperationInfo.DstMip + Mip, OperationInfo.DstSlice + Slice, DstBox, - SubResData, RESOURCE_STATE_TRANSITION_MODE_TRANSITION, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); - } - } - pBuffer->SignalCopyScheduled(); + auto SrcOffset = pBuffer->GetOffset(Mip, Slice); + auto SrcStride = pBuffer->GetMappedData(Mip, Slice).Stride; + + TextureSubResData SubResData(pBuffer->m_pStagingBuffer, SrcOffset, SrcStride); + + auto MipLevelProps = GetMipLevelProperties(TexDesc, Mip); + Box DstBox; + DstBox.MaxX = MipLevelProps.LogicalWidth; + DstBox.MaxY = MipLevelProps.LogicalHeight; + pContext->UpdateTexture(OperationInfo.pDstTexture, OperationInfo.DstMip + Mip, OperationInfo.DstSlice + Slice, DstBox, + SubResData, RESOURCE_STATE_TRANSITION_MODE_TRANSITION, RESOURCE_STATE_TRANSITION_MODE_TRANSITION); } - break; } + pBuffer->SignalCopyScheduled(); } - m_pInternalData->m_InWorkOperations.clear(); + break; } } -void TextureUploaderGL::AllocateUploadBuffer(const UploadBufferDesc& Desc, bool IsRenderThread, IUploadBuffer** ppBuffer) +void TextureUploaderGL::AllocateUploadBuffer(IDeviceContext* pContext, + const UploadBufferDesc& Desc, + IUploadBuffer** ppBuffer) { *ppBuffer = nullptr; RefCntAutoPtr<UploadBufferGL> pUploadBuffer; @@ -327,18 +340,46 @@ void TextureUploaderGL::AllocateUploadBuffer(const UploadBufferDesc& Desc, bool m_pDevice->GetTextureFormatInfo(Desc.Format).Name, " texture"); } - m_pInternalData->EnqueMap(pUploadBuffer); - pUploadBuffer->WaitForMap(); + if (pContext != nullptr) + { + // Render thread + InternalData::PendingBufferOperation MapOp{InternalData::PendingBufferOperation::Operation::Map, pUploadBuffer}; + m_pInternalData->Execute(m_pDevice, pContext, MapOp); + } + else + { + // Worker thread + m_pInternalData->EnqueMap(pUploadBuffer); + pUploadBuffer->WaitForMap(); + } *ppBuffer = pUploadBuffer.Detach(); } -void TextureUploaderGL::ScheduleGPUCopy(ITexture* pDstTexture, - Uint32 ArraySlice, - Uint32 MipLevel, - IUploadBuffer* pUploadBuffer) +void TextureUploaderGL::ScheduleGPUCopy(IDeviceContext* pContext, + ITexture* pDstTexture, + Uint32 ArraySlice, + Uint32 MipLevel, + IUploadBuffer* pUploadBuffer) { auto* pUploadBufferGL = ValidatedCast<UploadBufferGL>(pUploadBuffer); - m_pInternalData->EnqueCopy(pUploadBufferGL, pDstTexture, ArraySlice, MipLevel); + if (pContext != nullptr) + { + // Render thread + InternalData::PendingBufferOperation CopyOp // + { + InternalData::PendingBufferOperation::Operation::Copy, + pUploadBufferGL, + pDstTexture, + ArraySlice, + MipLevel // + }; + m_pInternalData->Execute(m_pDevice, pContext, CopyOp); + } + else + { + // Worker thread + m_pInternalData->EnqueCopy(pUploadBufferGL, pDstTexture, ArraySlice, MipLevel); + } } void TextureUploaderGL::RecycleBuffer(IUploadBuffer* pUploadBuffer) |
