Skip to content

Commit 3dd1349

Browse files
TextureUploader: add option to use CPU memory
1 parent f896969 commit 3dd1349

File tree

7 files changed

+399
-173
lines changed

7 files changed

+399
-173
lines changed

Graphics/GraphicsTools/interface/TextureUploader.hpp

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,6 @@
3333
namespace Diligent
3434
{
3535

36-
3736
/// Upload buffer description
3837
struct UploadBufferDesc
3938
{
@@ -63,9 +62,38 @@ class IUploadBuffer : public IObject
6362
virtual const UploadBufferDesc& GetDesc() const = 0;
6463
};
6564

65+
66+
// clang-format off
67+
68+
/// Texture uploader mode
69+
DILIGENT_TYPED_ENUM(TEXTURE_UPLOADER_MODE, Uint8)
70+
{
71+
/// Use staging buffers for texture uploads.
72+
73+
/// In this mode, the uploader creates a staging resource in GPU memory,
74+
/// maps it for CPU access, and returns the mapped pointer to the application
75+
/// through the IUploadBuffer::GetMappedData() method. When the application
76+
/// schedules a GPU copy, the uploader unmaps the staging resource and
77+
/// issues a GPU copy command from the staging resource to the destination texture.
78+
TEXTURE_UPLOADER_MODE_STAGING_RESOURCE = 0,
79+
80+
81+
/// Use CPU memory for texture uploads.
82+
83+
/// In this mode, the uploader allocates CPU memory for texture data and returns
84+
/// the pointer to the application through the IUploadBuffer::GetMappedData() method.
85+
/// When the application schedules a GPU copy, the uploader uses the appropriate
86+
/// update method (e.g. IDeviceContext::UpdateTexture) to copy data from CPU memory.
87+
TEXTURE_UPLOADER_MODE_CPU_MEMORY = 1
88+
};
89+
90+
// clang-format on
91+
6692
/// Texture uploader description.
6793
struct TextureUploaderDesc
6894
{
95+
/// Texture uploader mode, see Diligent::TEXTURE_UPLOADER_MODE.
96+
TEXTURE_UPLOADER_MODE Mode = TEXTURE_UPLOADER_MODE_STAGING_RESOURCE;
6997
};
7098

7199

Graphics/GraphicsTools/interface/TextureUploaderBase.hpp

Lines changed: 58 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include "../../../Common/interface/ObjectBase.hpp"
3434
#include "../../../Common/interface/HashUtils.hpp"
3535
#include "../../../Common/interface/RefCntAutoPtr.hpp"
36+
#include "../../GraphicsAccessories/interface/GraphicsAccessories.hpp"
3637

3738
namespace std
3839
{
@@ -50,16 +51,61 @@ struct hash<Diligent::UploadBufferDesc>
5051

5152
namespace Diligent
5253
{
54+
5355
class UploadBufferBase : public ObjectBase<IUploadBuffer>
5456
{
5557
public:
56-
UploadBufferBase(IReferenceCounters* pRefCounters, const UploadBufferDesc& Desc) :
58+
UploadBufferBase(IReferenceCounters* pRefCounters,
59+
const UploadBufferDesc& Desc,
60+
bool AllocateStagingData = false) :
5761
// clang-format off
5862
ObjectBase<IUploadBuffer>{pRefCounters},
5963
m_Desc {Desc},
6064
m_MappedData (size_t{m_Desc.ArraySize} * size_t{m_Desc.MipLevels})
6165
// clang-format on
6266
{
67+
if (AllocateStagingData)
68+
{
69+
TextureDesc StagingTexDesc;
70+
StagingTexDesc.Width = Desc.Width;
71+
StagingTexDesc.Height = Desc.Height;
72+
if (Desc.Depth > 1)
73+
{
74+
StagingTexDesc.Depth = Desc.Depth;
75+
VERIFY(Desc.ArraySize == 1, "3D textures cannot have array size greater than 1");
76+
StagingTexDesc.Type = RESOURCE_DIM_TEX_3D;
77+
}
78+
else if (Desc.ArraySize > 1)
79+
{
80+
StagingTexDesc.ArraySize = Desc.ArraySize;
81+
StagingTexDesc.Type = RESOURCE_DIM_TEX_2D_ARRAY;
82+
}
83+
else
84+
{
85+
StagingTexDesc.Type = RESOURCE_DIM_TEX_2D;
86+
}
87+
StagingTexDesc.MipLevels = Desc.MipLevels;
88+
StagingTexDesc.Format = Desc.Format;
89+
90+
constexpr Uint32 Alignment = 4;
91+
const Uint64 StagingTextureDataSize = GetStagingTextureDataSize(StagingTexDesc, Alignment);
92+
m_StagingData.resize(static_cast<size_t>(StagingTextureDataSize));
93+
94+
for (Uint32 Slice = 0; Slice < Desc.ArraySize; ++Slice)
95+
{
96+
for (Uint32 Mip = 0; Mip < Desc.MipLevels; ++Mip)
97+
{
98+
const Uint64 SubresOffset = GetStagingTextureSubresourceOffset(StagingTexDesc, Slice, Mip, Alignment);
99+
const MipLevelProperties MipProps = GetMipLevelProperties(StagingTexDesc, Mip);
100+
101+
MappedTextureSubresource MappedData;
102+
MappedData.pData = &m_StagingData[static_cast<size_t>(SubresOffset)];
103+
MappedData.Stride = MipProps.RowSize;
104+
MappedData.DepthStride = MipProps.DepthSliceSize;
105+
SetMappedData(Mip, Slice, MappedData);
106+
}
107+
}
108+
}
63109
}
64110

65111
virtual MappedTextureSubresource GetMappedData(Uint32 Mip, Uint32 Slice) override final
@@ -83,20 +129,27 @@ class UploadBufferBase : public ObjectBase<IUploadBuffer>
83129

84130
void Reset()
85131
{
86-
for (auto& MappedData : m_MappedData)
87-
MappedData = MappedTextureSubresource{};
132+
if (!HasStagingData())
133+
{
134+
for (auto& MappedData : m_MappedData)
135+
MappedData = MappedTextureSubresource{};
136+
}
88137
}
89138

139+
bool HasStagingData() const { return !m_StagingData.empty(); }
140+
90141
protected:
91142
const UploadBufferDesc m_Desc;
92143
std::vector<MappedTextureSubresource> m_MappedData;
144+
std::vector<Uint8> m_StagingData;
93145
};
94146

95147
class TextureUploaderBase : public ObjectBase<ITextureUploader>
96148
{
97149
public:
98-
TextureUploaderBase(IReferenceCounters* pRefCounters, IRenderDevice* pDevice, const TextureUploaderDesc Desc) :
150+
TextureUploaderBase(IReferenceCounters* pRefCounters, IRenderDevice* pDevice, const TextureUploaderDesc& Desc) :
99151
ObjectBase<ITextureUploader>{pRefCounters},
152+
m_Desc{Desc},
100153
m_pDevice{pDevice}
101154
{}
102155

@@ -142,6 +195,7 @@ class TextureUploaderBase : public ObjectBase<ITextureUploader>
142195
};
143196

144197
protected:
198+
const TextureUploaderDesc m_Desc;
145199
RefCntAutoPtr<IRenderDevice> m_pDevice;
146200
};
147201

Graphics/GraphicsTools/src/TextureUploaderD3D11.cpp

Lines changed: 70 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class UploadBufferD3D11 : public UploadBufferBase
5454
{
5555
public:
5656
UploadBufferD3D11(IReferenceCounters* pRefCounters, const UploadBufferDesc& Desc, ID3D11Texture2D* pStagingTexture) :
57-
UploadBufferBase{pRefCounters, Desc},
57+
UploadBufferBase{pRefCounters, Desc, /* AllocateStagingData = */ pStagingTexture == nullptr},
5858
m_pStagingTexture{pStagingTexture}
5959
{}
6060

@@ -65,7 +65,10 @@ class UploadBufferD3D11 : public UploadBufferBase
6565
// http://en.cppreference.com/w/cpp/thread/condition_variable
6666
void WaitForMap()
6767
{
68-
m_BufferMappedSignal.Wait();
68+
if (!HasStagingData())
69+
{
70+
m_BufferMappedSignal.Wait();
71+
}
6972
}
7073

7174
void SignalMapped()
@@ -275,11 +278,13 @@ void TextureUploaderD3D11::InternalData::Execute(ID3D11DeviceContext* pd3d11N
275278
{
276279
RefCntAutoPtr<UploadBufferD3D11>& pBuffer = Operation.pUploadBuffer;
277280
const UploadBufferDesc& UploadBuffDesc = pBuffer->GetDesc();
281+
ID3D11Texture2D* pStagingTex = pBuffer->GetStagingTex();
278282

279283
switch (Operation.OpType)
280284
{
281285
case InternalData::PendingBufferOperation::Type::Map:
282286
{
287+
VERIFY_EXPR(pStagingTex != nullptr);
283288
bool AllMapped = true;
284289
for (Uint32 Slice = 0; Slice < UploadBuffDesc.ArraySize; ++Slice)
285290
{
@@ -291,7 +296,7 @@ void TextureUploaderD3D11::InternalData::Execute(ID3D11DeviceContext* pd3d11N
291296

292297
UINT Subres = D3D11CalcSubresource(static_cast<UINT>(Mip), static_cast<UINT>(Slice), static_cast<UINT>(UploadBuffDesc.MipLevels));
293298

294-
HRESULT hr = pd3d11NativeCtx->Map(pBuffer->GetStagingTex(), Subres, D3D11_MAP_WRITE,
299+
HRESULT hr = pd3d11NativeCtx->Map(pStagingTex, Subres, D3D11_MAP_WRITE,
295300
ExecuteImmediately ? 0 : D3D11_MAP_FLAG_DO_NOT_WAIT,
296301
&MappedData);
297302
if (SUCCEEDED(hr))
@@ -329,10 +334,13 @@ void TextureUploaderD3D11::InternalData::Execute(ID3D11DeviceContext* pd3d11N
329334
case InternalData::PendingBufferOperation::Type::Copy:
330335
{
331336
VERIFY(pBuffer->DbgIsMapped(), "Upload buffer must be copied only after it has been mapped");
332-
// Unmap all subresources first to avoid D3D11 warnings
333-
for (Uint32 Subres = 0; Subres < UploadBuffDesc.MipLevels * UploadBuffDesc.ArraySize; ++Subres)
337+
if (pStagingTex != nullptr)
334338
{
335-
pd3d11NativeCtx->Unmap(pBuffer->GetStagingTex(), Subres);
339+
// Unmap all subresources first to avoid D3D11 warnings
340+
for (Uint32 Subres = 0; Subres < UploadBuffDesc.MipLevels * UploadBuffDesc.ArraySize; ++Subres)
341+
{
342+
pd3d11NativeCtx->Unmap(pBuffer->GetStagingTex(), Subres);
343+
}
336344
}
337345

338346
for (Uint32 Slice = 0; Slice < UploadBuffDesc.ArraySize; ++Slice)
@@ -347,12 +355,26 @@ void TextureUploaderD3D11::InternalData::Execute(ID3D11DeviceContext* pd3d11N
347355
static_cast<UINT>(Operation.DstMip + Mip),
348356
static_cast<UINT>(Operation.DstSlice + Slice),
349357
static_cast<UINT>(Operation.DstMipLevels));
350-
pd3d11NativeCtx->CopySubresourceRegion(Operation.pd3d11NativeDstTexture, DstSubres,
351-
0, 0, 0, // DstX, DstY, DstZ
352-
pBuffer->GetStagingTex(),
353-
SrcSubres,
354-
nullptr // pSrcBox
355-
);
358+
if (pStagingTex != nullptr)
359+
{
360+
pd3d11NativeCtx->CopySubresourceRegion(Operation.pd3d11NativeDstTexture, DstSubres,
361+
0, 0, 0, // DstX, DstY, DstZ
362+
pBuffer->GetStagingTex(),
363+
SrcSubres,
364+
nullptr // pSrcBox
365+
);
366+
}
367+
else
368+
{
369+
const MappedTextureSubresource& MappedData = pBuffer->GetMappedData(Mip, Slice);
370+
pd3d11NativeCtx->UpdateSubresource(
371+
Operation.pd3d11NativeDstTexture,
372+
DstSubres,
373+
nullptr, // pDstBox
374+
pBuffer->GetMappedData(Mip, Slice).pData,
375+
static_cast<UINT>(MappedData.Stride),
376+
static_cast<UINT>(MappedData.DepthStride));
377+
}
356378
}
357379
}
358380
pBuffer->SignalCopyScheduled();
@@ -395,51 +417,59 @@ void TextureUploaderD3D11::AllocateUploadBuffer(IDeviceContext* pContext
395417

396418
if (!pUploadBuffer)
397419
{
398-
// clang-format off
399-
D3D11_TEXTURE2D_DESC StagingTexDesc =
400-
{
401-
static_cast<UINT>(Desc.Width),
402-
static_cast<UINT>(Desc.Height),
403-
static_cast<UINT>(Desc.MipLevels),
404-
static_cast<UINT>(Desc.ArraySize),
405-
TexFormatToDXGI_Format(Desc.Format),
406-
{1, 0}, // DXGI_SAMPLE_DESC SampleDesc;
407-
D3D11_USAGE_STAGING,
408-
0, // UINT BindFlags;
409-
D3D11_CPU_ACCESS_WRITE, // UINT CPUAccessFlags;
410-
0, // UINT MiscFlags;
411-
};
412-
// clang-format on
413-
414420
CComPtr<ID3D11Texture2D> pStagingTex;
415421

416-
HRESULT hr = m_pInternalData->m_pd3d11NativeDevice->CreateTexture2D(&StagingTexDesc, nullptr, &pStagingTex);
417-
if (FAILED(hr))
422+
if (m_Desc.Mode == TEXTURE_UPLOADER_MODE_STAGING_RESOURCE)
418423
{
419-
LOG_ERROR_MESSAGE("Failed to allocate staging D3D11 texture");
420-
return;
424+
D3D11_TEXTURE2D_DESC StagingTexDesc =
425+
{
426+
static_cast<UINT>(Desc.Width),
427+
static_cast<UINT>(Desc.Height),
428+
static_cast<UINT>(Desc.MipLevels),
429+
static_cast<UINT>(Desc.ArraySize),
430+
TexFormatToDXGI_Format(Desc.Format),
431+
{1, 0}, // DXGI_SAMPLE_DESC SampleDesc;
432+
D3D11_USAGE_STAGING,
433+
0, // UINT BindFlags;
434+
D3D11_CPU_ACCESS_WRITE, // UINT CPUAccessFlags;
435+
0, // UINT MiscFlags;
436+
};
437+
438+
HRESULT hr = m_pInternalData->m_pd3d11NativeDevice->CreateTexture2D(&StagingTexDesc, nullptr, &pStagingTex);
439+
if (FAILED(hr))
440+
{
441+
LOG_ERROR_MESSAGE("Failed to allocate staging D3D11 texture");
442+
return;
443+
}
421444
}
422445

423446
LOG_INFO_MESSAGE("TextureUploaderD3D11: created ", Desc.Width, 'x', Desc.Height, 'x', Desc.Depth, ' ',
424447
Desc.MipLevels, "-mip ", Desc.ArraySize, "-slice ",
425-
m_pDevice->GetTextureFormatInfo(Desc.Format).Name, " staging texture");
448+
m_pDevice->GetTextureFormatInfo(Desc.Format).Name, pStagingTex ? " staging texture" : " CPU upload buffer");
426449

427450
pUploadBuffer = MakeNewRCObj<UploadBufferD3D11>()(Desc, pStagingTex);
428451
}
429452

430453
if (pUploadBuffer)
431454
{
432-
if (pContext != nullptr)
455+
if (m_Desc.Mode == TEXTURE_UPLOADER_MODE_STAGING_RESOURCE)
433456
{
434-
// Main thread
435-
InternalData::PendingBufferOperation MapOp{InternalData::PendingBufferOperation::Type::Map, pUploadBuffer};
436-
m_pInternalData->ExecuteImmediately(pContext, MapOp);
457+
if (pContext != nullptr)
458+
{
459+
// Main thread
460+
InternalData::PendingBufferOperation MapOp{InternalData::PendingBufferOperation::Type::Map, pUploadBuffer};
461+
m_pInternalData->ExecuteImmediately(pContext, MapOp);
462+
}
463+
else
464+
{
465+
// Worker thread
466+
m_pInternalData->EnqueueMap(pUploadBuffer, InternalData::PendingBufferOperation::Type::Map);
467+
pUploadBuffer->WaitForMap();
468+
}
437469
}
438470
else
439471
{
440-
// Worker thread
441-
m_pInternalData->EnqueueMap(pUploadBuffer, InternalData::PendingBufferOperation::Type::Map);
442-
pUploadBuffer->WaitForMap();
472+
pUploadBuffer->SignalMapped();
443473
}
444474
}
445475

0 commit comments

Comments
 (0)