Skip to content

Commit 65d1173

Browse files
GPUUploadManager: implement texture upload support in Direct3D11
1 parent c076d58 commit 65d1173

File tree

4 files changed

+619
-129
lines changed

4 files changed

+619
-129
lines changed

Graphics/GraphicsTools/include/GPUUploadManagerImpl.hpp

Lines changed: 89 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -35,11 +35,12 @@
3535
#include "RefCntAutoPtr.hpp"
3636
#include "MPSCQueue.hpp"
3737
#include "ThreadSignal.hpp"
38+
#include "DynamicAtlasManager.hpp"
3839

3940
#include <memory>
4041
#include <vector>
41-
#include <array>
4242
#include <mutex>
43+
#include <shared_mutex>
4344
#include <map>
4445
#include <unordered_map>
4546
#include <variant>
@@ -64,14 +65,23 @@ class GPUUploadManagerImpl final : public ObjectBase<IGPUUploadManager>
6465

6566
virtual void DILIGENT_CALL_TYPE ScheduleTextureUpdate(const ScheduleTextureUpdateInfo& UpdateInfo) override final;
6667

67-
virtual void DILIGENT_CALL_TYPE GetStats(GPUUploadManagerStats& Stats) const override final;
68+
virtual void DILIGENT_CALL_TYPE GetStats(GPUUploadManagerStats& Stats) override final;
6869

70+
private:
71+
class UploadStream;
72+
73+
public:
6974
class Page
7075
{
7176
public:
7277
explicit Page(Uint32 Size, bool PersistentMapped = false) noexcept;
7378

74-
Page(size_t StreamIndex, IRenderDevice* pDevice, Uint32 Size);
79+
Page(UploadStream& Stream, IRenderDevice* pDevice, Uint32 Size);
80+
81+
// Special page for texture updates in Direct3D11, which require a staging texture.
82+
// The texture dimensions are Size x Size.
83+
Page(UploadStream& Stream, IRenderDevice* pDevice, Uint32 Size, TEXTURE_FORMAT Format);
84+
7585
~Page();
7686

7787
enum class WritingStatus
@@ -143,7 +153,7 @@ class GPUUploadManagerImpl final : public ObjectBase<IGPUUploadManager>
143153
// Returns true if the page was not previously enqueued, false otherwise.
144154
bool TryEnqueue();
145155

146-
size_t GetStreamIndex() const { return m_StreamIdx; }
156+
UploadStream* GetStream() const { return m_pStream; }
147157

148158
Uint64 GetFenceValue() const { return m_FenceValue; }
149159
Uint32 GetSize() const { return m_Size; }
@@ -179,13 +189,40 @@ class GPUUploadManagerImpl final : public ObjectBase<IGPUUploadManager>
179189

180190
WritingStatus EndWriting();
181191

192+
void UnmapStagingResource(IDeviceContext* pContext);
193+
182194
private:
183-
const size_t m_StreamIdx = 0;
195+
UploadStream* const m_pStream = nullptr;
196+
184197
const Uint32 m_Size = 0;
185198
const bool m_PersistentMapped = false;
186199

187200
RefCntAutoPtr<IBuffer> m_pStagingBuffer;
188201

202+
// Direct3D11 does not support buffer to texture copies, so we need a
203+
// staging texture to perform texture updates.
204+
struct StagingTextureAtlas
205+
{
206+
StagingTextureAtlas(IRenderDevice* pDevice, Uint32 Width, Uint32 Height, TEXTURE_FORMAT Format, const std::string& Name);
207+
~StagingTextureAtlas();
208+
209+
void* Map(IDeviceContext* pContext);
210+
void Unmap(IDeviceContext* pContext);
211+
void Reset();
212+
213+
DynamicAtlasManager::Region Allocate(Uint32 Width, Uint32 Height);
214+
215+
RefCntAutoPtr<ITexture> pTex;
216+
217+
Uint32 RowStride = 0;
218+
Uint32 DepthStride = 0;
219+
220+
private:
221+
std::mutex MgrMtx;
222+
DynamicAtlasManager Mgr;
223+
};
224+
std::unique_ptr<StagingTextureAtlas> m_pStagingAtlas;
225+
189226
void* m_pData = nullptr;
190227

191228
std::atomic<Uint32> m_Offset{0};
@@ -217,11 +254,14 @@ class GPUUploadManagerImpl final : public ObjectBase<IGPUUploadManager>
217254
PendingBufferOp() noexcept = default;
218255
};
219256

220-
221257
struct PendingTextureOp
222258
{
223259
RefCntAutoPtr<ITexture> pDstTexture;
224260

261+
// For Direct3D11, coordinates of the source region within the staging texture.
262+
Uint32 SrcX = 0;
263+
Uint32 SrcY = 0;
264+
225265
Uint32 SrcOffset = 0;
226266
Uint32 SrcStride = 0;
227267
Uint32 SrcDepthStride = 0;
@@ -230,20 +270,21 @@ class GPUUploadManagerImpl final : public ObjectBase<IGPUUploadManager>
230270
Uint32 DstSlice = 0;
231271
Box DstBox;
232272

233-
CopyStagingTextureCallbackType CopyTexture = nullptr;
234-
void* pCopyTextureData = nullptr;
273+
CopyStagingTextureCallbackType CopyTexture = nullptr;
274+
CopyStagingD3D11TextureCallbackType CopyD3D11Texture = nullptr;
275+
void* pCopyTextureData = nullptr;
235276

236277
GPUTextureUploadEnqueuedCallbackType UploadEnqueued = nullptr;
237278
void* pUploadEnqueuedData = nullptr;
238279
};
239280

240281
using PendingOp = std::variant<PendingBufferOp, PendingTextureOp>;
241282
MPSCQueue<PendingOp> m_PendingOps;
283+
284+
static std::atomic<int> sm_PageCounter;
242285
};
243286

244287
private:
245-
class UploadStream;
246-
247288
void ReclaimCompletedPages(IDeviceContext* pContext);
248289
void ProcessPendingPages(IDeviceContext* pContext);
249290

@@ -285,10 +326,11 @@ class GPUUploadManagerImpl final : public ObjectBase<IGPUUploadManager>
285326
{
286327
public:
287328
UploadStream(GPUUploadManagerImpl& Mgr,
288-
size_t StreamIdx,
329+
IDeviceContext* pContext,
289330
Uint32 PageSize,
290331
Uint32 MaxPageCount,
291-
Uint32 InitialPageCount) noexcept;
332+
Uint32 InitialPageCount,
333+
TEXTURE_FORMAT Format = TEX_FORMAT_UNKNOWN) noexcept;
292334

293335
Page* CreatePage(IDeviceContext* pContext, Uint32 RequiredSize = 0);
294336
Page* AcquireFreePage(IDeviceContext* pContext, Uint32 RequiredSize = 0);
@@ -313,9 +355,9 @@ class GPUUploadManagerImpl final : public ObjectBase<IGPUUploadManager>
313355

314356
private:
315357
GPUUploadManagerImpl& m_Mgr;
316-
const size_t m_StreamIdx;
317358
const Uint32 m_PageSize;
318359
const Uint32 m_MaxPageCount;
360+
const TEXTURE_FORMAT m_Format;
319361

320362
std::atomic<Page*> m_pCurrentPage{nullptr};
321363

@@ -336,20 +378,48 @@ class GPUUploadManagerImpl final : public ObjectBase<IGPUUploadManager>
336378
Uint32 m_PeakPageCount = 0;
337379
};
338380

339-
enum class UploadStreamType : Uint32
381+
using UploadStreamUniquePtr = std::unique_ptr<UploadStream>;
382+
std::vector<UploadStreamUniquePtr> m_Streams;
383+
384+
UploadStream* m_pNormalStream = nullptr;
385+
UploadStream* m_pLargeStream = nullptr;
386+
387+
class TextureUploadStreams
340388
{
341-
Normal = 0,
342-
Large = 1,
343-
Count
389+
public:
390+
TextureUploadStreams(GPUUploadManagerImpl& Mgr, Uint32 PageSizeInBytes, Uint32 MaxPageCount) :
391+
m_Mgr{Mgr},
392+
m_PageSizeInBytes{PageSizeInBytes},
393+
m_MaxPageCount{MaxPageCount}
394+
{}
395+
396+
UploadStream* GetStreamForFormat(IDeviceContext* pContext, TEXTURE_FORMAT Format);
397+
398+
void MoveNewStreamsToManager();
399+
void SetStopping();
400+
401+
private:
402+
GPUUploadManagerImpl& m_Mgr;
403+
const Uint32 m_PageSizeInBytes;
404+
const Uint32 m_MaxPageCount;
405+
406+
std::shared_mutex m_Mtx;
407+
std::unordered_map<TEXTURE_FORMAT, UploadStream*> m_StreamsByFormat;
408+
409+
// New streams created by worker threads that are not yet added to parent m_Streams vector.
410+
std::mutex m_NewStreamsMtx;
411+
std::vector<std::unique_ptr<UploadStream>> m_NewStreams;
412+
std::atomic<bool> m_HasNewStreams{false};
413+
std::atomic<bool> m_Stopping{false};
344414
};
345-
std::array<UploadStream, static_cast<size_t>(UploadStreamType::Count)> m_Streams;
415+
std::unique_ptr<TextureUploadStreams> m_pTextureStreams;
346416

347417
// The number of running ScheduleBufferUpdate operations.
348418
std::atomic<Uint32> m_NumRunningUpdates{0};
349419
std::atomic<bool> m_Stopping{false};
350420
Threading::Signal m_LastRunningThreadFinishedSignal;
351421

352-
mutable std::array<GPUUploadManagerStreamStats, static_cast<size_t>(UploadStreamType::Count)> m_StreamStats;
422+
std::vector<GPUUploadManagerStreamStats> m_StreamStats;
353423
};
354424

355425
} // namespace Diligent

Graphics/GraphicsTools/interface/GPUUploadManager.h

Lines changed: 40 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,7 @@ typedef void (*GPUBufferUploadEnqueuedCallbackType)(IBuffer* pDstBuffer,
121121
/// The callback is expected to perform the copy operation itself, using the provided parameters, and schedule it for execution on the GPU.
122122
///
123123
/// \param [in] pContext - Device context to use for scheduling the copy operation.
124-
/// \param [in] pSrcBuffer - Source buffer containing the data to copy. The buffer is guaranteed to be valid for the duration of the callback.
124+
/// \param [in] pSrcBuffer - Source buffer containing the data to copy.
125125
/// \param [in] SrcOffset - Offset in the source buffer where the data to copy starts.
126126
/// \param [in] NumBytes - Number of bytes to copy.
127127
/// \param [in] pUserData - User-provided pointer passed to ScheduleBufferUpdate().
@@ -315,7 +315,7 @@ typedef void (*GPUTextureUploadEnqueuedCallbackType)(ITexture* pDstTexture,
315315
/// \param [in] DstMipLevel - Destination mip level in the texture where the update will be applied.
316316
/// \param [in] DstSlice - Destination array slice in the texture where the update will be applied.
317317
/// \param [in] DstBox - Destination box in the texture where the update will be applied.
318-
/// \param [in] SrcData - Source data description. The manager is guaranteed to keep the source data valid for the duration of the callback.
318+
/// \param [in] SrcData - Source data description.
319319
/// \param [in] pUserData - User-provided pointer passed to ScheduleTextureUpdate().
320320
///
321321
/// If the copy operation was not scheduled by the time the manager is destroyed,
@@ -340,6 +340,28 @@ typedef void (*CopyStagingTextureCallbackType)(IDeviceContext* pCont
340340
void* pUserData);
341341

342342

343+
/// Direct3D11-specific callback function type for copying texture data.
344+
/// This callback is invoked on the render thread when the manager needs to perform the copy operation for a texture update.
345+
/// The callback is expected to perform the copy operation itself, using the provided parameters, and schedule it for execution on the GPU.
346+
///
347+
/// \param [in] pContext - Device context to use for scheduling the copy operation.
348+
/// \param [in] DstMipLevel - Destination mip level in the texture where the update will be applied.
349+
/// \param [in] DstSlice - Destination array slice in the texture where the update will be applied.
350+
/// \param [in] DstBox - Destination box in the texture where the update will be applied.
351+
/// \param [in] pSrcTexture - Pointer to the staging texture containing the source data.
352+
/// \param [in] SrcX - X offset in the staging texture where the source data starts.
353+
/// \param [in] SrcY - Y offset in the staging texture where the source data starts.
354+
/// \param [in] pUserData - User-provided pointer passed to ScheduleTextureUpdate().
355+
typedef void (*CopyStagingD3D11TextureCallbackType)(IDeviceContext* pContext,
356+
Uint32 DstMipLevel,
357+
Uint32 DstSlice,
358+
const Box REF DstBox,
359+
ITexture* pSrcTexture,
360+
Uint32 SrcX,
361+
Uint32 SrcY,
362+
void* pUserData);
363+
364+
343365
/// Structure describing a texture update operation to be scheduled by IGPUUploadManager::ScheduleTextureUpdate().
344366
struct ScheduleTextureUpdateInfo
345367
{
@@ -391,8 +413,16 @@ struct ScheduleTextureUpdateInfo
391413
/// from the source data to the destination texture using its internal staging buffer and copy command.
392414
/// If the callback is provided, it must perform the copy operation itself. The manager will pass the
393415
/// necessary parameters to the callback.
416+
///
417+
/// \note This callback is used on all graphics backends, except Direct3D11 because it does not support copying from a
418+
/// buffer to a texture. For Direct3D11, the CopyD3D11Texture callback is used instead, which provides a pointer
419+
/// to the staging texture
394420
CopyStagingTextureCallbackType CopyTexture DEFAULT_INITIALIZER(nullptr);
395421

422+
/// Optional Direct3D11-specific callback to perform the copy operation.
423+
/// This callback is only called for Direct3D11 backend.
424+
CopyStagingD3D11TextureCallbackType CopyD3D11Texture DEFAULT_INITIALIZER(nullptr);
425+
396426
/// Optional pointer to user data that will be passed to the CopyTexture callback.
397427
void* pCopyTextureData DEFAULT_INITIALIZER(nullptr);
398428

@@ -412,6 +442,8 @@ typedef struct ScheduleTextureUpdateInfo ScheduleTextureUpdateInfo;
412442
struct GPUUploadManagerBucketInfo
413443
{
414444
/// Page size in bytes.
445+
/// For Direct3D11 texture upload stream, this is the upload texture size (width and height).
446+
/// Otherwise, this is the size of the upload page in bytes.
415447
Uint32 PageSize DEFAULT_INITIALIZER(0);
416448

417449
/// Number of pages currently in the manager.
@@ -423,7 +455,12 @@ typedef struct GPUUploadManagerBucketInfo GPUUploadManagerBucketInfo;
423455
/// GPU upload manager stream statistics.
424456
struct GPUUploadManagerStreamStats
425457
{
458+
/// For Direct3D11 only: texture format for texture upload stream.
459+
TEXTURE_FORMAT Format DEFAULT_INITIALIZER(TEX_FORMAT_UNKNOWN);
460+
426461
/// Page size in bytes for this stream.
462+
/// If Format is not TEX_FORMAT_UNKNOWN, this is the upload texture size (width and height).
463+
/// Otherwise, this is the size of the upload page in bytes.
427464
Uint32 PageSize DEFAULT_INITIALIZER(0);
428465

429466
/// The number of pages in the manager.
@@ -529,7 +566,7 @@ DILIGENT_BEGIN_INTERFACE(IGPUUploadManager, IObject)
529566
/// Pointers returned in the Stats structure are valid only until the next call to GetStats()
530567
/// and must not be used after that.
531568
VIRTUAL void METHOD(GetStats)(THIS_
532-
GPUUploadManagerStats REF Stats) CONST PURE;
569+
GPUUploadManagerStats REF Stats) PURE;
533570
};
534571
DILIGENT_END_INTERFACE
535572

0 commit comments

Comments
 (0)