Skip to content

Commit 5b6c97d

Browse files
GPUUploadManagerImpl::Page: support persistent mapping
Add GPUUploadManager creation test.
1 parent e6144b8 commit 5b6c97d

3 files changed

Lines changed: 100 additions & 9 deletions

File tree

Graphics/GraphicsTools/include/GPUUploadManagerImpl.hpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class GPUUploadManagerImpl final : public ObjectBase<IGPUUploadManager>
6363
class Page
6464
{
6565
public:
66-
explicit Page(Uint32 Size) noexcept;
66+
explicit Page(Uint32 Size, bool PersistentMapped = false) noexcept;
6767

6868
Page(IRenderDevice* pDevice,
6969
IDeviceContext* pContext,
@@ -149,6 +149,8 @@ class GPUUploadManagerImpl final : public ObjectBase<IGPUUploadManager>
149149
// Returns true if the page is sealed for new writes. This is used for testing and debugging purposes.
150150
bool DbgIsSealed() const { return (m_State.load(std::memory_order_relaxed) & SEALED_BIT) != 0; }
151151

152+
void ReleaseStagingBuffer(IDeviceContext* pContext);
153+
152154
private:
153155
// Schedules a buffer update operation on the page.
154156
// Returns true if the operation was successfully scheduled, and false otherwise.
@@ -162,7 +164,8 @@ class GPUUploadManagerImpl final : public ObjectBase<IGPUUploadManager>
162164
WritingStatus EndWriting();
163165

164166
private:
165-
const Uint32 m_Size = 0;
167+
const Uint32 m_Size = 0;
168+
const bool m_PersistentMapped = false;
166169

167170
RefCntAutoPtr<IBuffer> m_pStagingBuffer;
168171

@@ -202,7 +205,8 @@ class GPUUploadManagerImpl final : public ObjectBase<IGPUUploadManager>
202205
private:
203206
const Uint32 m_PageSize;
204207

205-
RefCntAutoPtr<IRenderDevice> m_pDevice;
208+
RefCntAutoPtr<IRenderDevice> m_pDevice;
209+
RefCntAutoPtr<IDeviceContext> m_pContext;
206210

207211
// Pages that are pending for execution.
208212
MPSCQueue<Page*> m_PendingPages;

Graphics/GraphicsTools/src/GPUUploadManagerImpl.cpp

Lines changed: 37 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,24 @@ GPUUploadManagerImpl::Page::Writer::~Writer()
8080
}
8181
}
8282

83-
GPUUploadManagerImpl::Page::Page(Uint32 Size) noexcept :
84-
m_Size{Size}
83+
GPUUploadManagerImpl::Page::Page(Uint32 Size, bool PersistentMapped) noexcept :
84+
m_Size{Size},
85+
m_PersistentMapped{PersistentMapped}
8586
{}
8687

88+
inline bool PersistentMapSupported(IRenderDevice* pDevice)
89+
{
90+
RENDER_DEVICE_TYPE DeviceType = pDevice->GetDeviceInfo().Type;
91+
return DeviceType == RENDER_DEVICE_TYPE_D3D12 || DeviceType == RENDER_DEVICE_TYPE_VULKAN;
92+
}
93+
8794
GPUUploadManagerImpl::Page::Page(IRenderDevice* pDevice,
8895
IDeviceContext* pContext,
8996
Uint32 Size) :
90-
Page{Size}
97+
Page{
98+
Size,
99+
PersistentMapSupported(pDevice),
100+
}
91101
{
92102
static std::atomic<int> PageCounter{0};
93103
const std::string Name = "GPUUploadManagerImpl page " + std::to_string(PageCounter.fetch_add(1));
@@ -221,7 +231,7 @@ void GPUUploadManagerImpl::Page::ExecutePendingOps(IDeviceContext* pContext, Uin
221231
VERIFY(DbgIsSealed(), "Page must be sealed before executing pending operations");
222232
VERIFY(DbgGetWriterCount() == 0, "All writers must finish before executing pending operations");
223233

224-
if (m_pData != nullptr)
234+
if (m_pData != nullptr && !m_PersistentMapped)
225235
{
226236
VERIFY_EXPR(pContext != nullptr);
227237
pContext->UnmapBuffer(m_pStagingBuffer, MAP_WRITE);
@@ -258,7 +268,10 @@ void GPUUploadManagerImpl::Page::Reset(IDeviceContext* pContext)
258268

259269
if (pContext != nullptr)
260270
{
261-
pContext->MapBuffer(m_pStagingBuffer, MAP_WRITE, MAP_FLAG_NONE, m_pData);
271+
if (!m_PersistentMapped)
272+
{
273+
pContext->MapBuffer(m_pStagingBuffer, MAP_WRITE, MAP_FLAG_NONE, m_pData);
274+
}
262275
VERIFY_EXPR(m_pData != nullptr);
263276
}
264277
}
@@ -272,11 +285,23 @@ bool GPUUploadManagerImpl::Page::TryEnqueue()
272285
return m_Enqueued.compare_exchange_strong(Expected, true, std::memory_order_acq_rel);
273286
}
274287

288+
void GPUUploadManagerImpl::Page::ReleaseStagingBuffer(IDeviceContext* pContext)
289+
{
290+
if (m_pData != nullptr)
291+
{
292+
VERIFY_EXPR(pContext != nullptr);
293+
pContext->UnmapBuffer(m_pStagingBuffer, MAP_WRITE);
294+
m_pData = nullptr;
295+
}
296+
m_pStagingBuffer.Release();
297+
}
298+
275299

276300
GPUUploadManagerImpl::GPUUploadManagerImpl(IReferenceCounters* pRefCounters, const GPUUploadManagerCreateInfo& CI) :
277301
TBase{pRefCounters},
278302
m_PageSize{AlignUpToPowerOfTwo(CI.PageSize)},
279-
m_pDevice{CI.pDevice}
303+
m_pDevice{CI.pDevice},
304+
m_pContext{CI.pContext}
280305
{
281306
FenceDesc Desc;
282307
Desc.Name = "GPU upload manager fence";
@@ -289,11 +314,17 @@ GPUUploadManagerImpl::GPUUploadManagerImpl(IReferenceCounters* pRefCounters, con
289314

290315
GPUUploadManagerImpl::~GPUUploadManagerImpl()
291316
{
317+
for (std::unique_ptr<Page>& P : m_Pages)
318+
{
319+
P->ReleaseStagingBuffer(m_pContext);
320+
}
292321
}
293322

294323
void GPUUploadManagerImpl::RenderThreadUpdate(IDeviceContext* pContext)
295324
{
325+
DEV_CHECK_ERR(pContext == m_pContext, "The context passed to RenderThreadUpdate must be the same as the one used to create the GPUUploadManagerImpl");
296326
(void)m_NextFenceValue;
327+
(void)m_FreePagesMtx;
297328
}
298329

299330
void GPUUploadManagerImpl::ScheduleBufferUpdate(IBuffer* pDstBuffer,
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
/*
2+
* Copyright 2026 Diligent Graphics LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*
16+
* In no event and under no legal theory, whether in tort (including negligence),
17+
* contract, or otherwise, unless required by applicable law (such as deliberate
18+
* and grossly negligent acts) or agreed to in writing, shall any Contributor be
19+
* liable for any damages, including any direct, indirect, special, incidental,
20+
* or consequential damages of any character arising as a result of this License or
21+
* out of the use or inability to use the software (including but not limited to damages
22+
* for loss of goodwill, work stoppage, computer failure or malfunction, or any and
23+
* all other commercial damages or losses), even if such Contributor has been advised
24+
* of the possibility of such damages.
25+
*/
26+
27+
#include "GPUUploadManager.h"
28+
#include "GPUTestingEnvironment.hpp"
29+
#include "ThreadSignal.hpp"
30+
#include "CallbackWrapper.hpp"
31+
32+
#include "gtest/gtest.h"
33+
34+
#include <atomic>
35+
#include <thread>
36+
#include <array>
37+
38+
using namespace Diligent;
39+
using namespace Diligent::Testing;
40+
41+
namespace
42+
{
43+
44+
TEST(GPUUploadManagerTest, Creation)
45+
{
46+
GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance();
47+
IRenderDevice* pDevice = pEnv->GetDevice();
48+
IDeviceContext* pContext = pEnv->GetDeviceContext();
49+
50+
RefCntAutoPtr<IGPUUploadManager> pUploadManager;
51+
GPUUploadManagerCreateInfo CreateInfo{pDevice, pContext};
52+
CreateGPUUploadManager(CreateInfo, &pUploadManager);
53+
ASSERT_TRUE(pUploadManager != nullptr);
54+
}
55+
56+
} // namespace

0 commit comments

Comments
 (0)