summaryrefslogtreecommitdiffstats
path: root/Graphics/GraphicsTools
diff options
context:
space:
mode:
authorassiduous <assiduous@diligentgraphics.com>2020-04-08 20:47:14 +0000
committerassiduous <assiduous@diligentgraphics.com>2020-04-08 20:47:14 +0000
commit0729ade33fb0f255214af2b228141731bac7a6c9 (patch)
tree024f326bd3aaed7f1fa2d3c27aa72c83314156e6 /Graphics/GraphicsTools
parentMath lib: added RGBA8Unorm_To_F4Color and F4Color_To_RGBA8Unorm functions (diff)
downloadDiligentCore-0729ade33fb0f255214af2b228141731bac7a6c9.tar.gz
DiligentCore-0729ade33fb0f255214af2b228141731bac7a6c9.zip
Updated ITextureUploader to allow synchronous execution
Diffstat (limited to 'Graphics/GraphicsTools')
-rw-r--r--Graphics/GraphicsTools/interface/TextureUploader.hpp71
-rw-r--r--Graphics/GraphicsTools/interface/TextureUploaderD3D11.hpp13
-rw-r--r--Graphics/GraphicsTools/interface/TextureUploaderD3D12_Vk.hpp13
-rw-r--r--Graphics/GraphicsTools/interface/TextureUploaderGL.hpp13
-rw-r--r--Graphics/GraphicsTools/src/TextureUploaderD3D11.cpp255
-rw-r--r--Graphics/GraphicsTools/src/TextureUploaderD3D12_Vk.cpp153
-rw-r--r--Graphics/GraphicsTools/src/TextureUploaderGL.cpp151
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)