Skip to content

Commit c74061b

Browse files
GPUUploadManager: add parallel updates test
1 parent 8e6acba commit c74061b

2 files changed

Lines changed: 106 additions & 1 deletion

File tree

Graphics/GraphicsTools/src/GPUUploadManagerImpl.cpp

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -391,21 +391,27 @@ void GPUUploadManagerImpl::ScheduleBufferUpdate(IDeviceContext* pC
391391
Page::Writer Writer = P->TryBeginWriting();
392392
if (!Writer)
393393
{
394+
// The page is sealed, so we need to rotate to a new page and try again
394395
UpdatePendingSizeAndTryRotate(P);
395396
continue;
396397
}
397398

398399
const bool UpdateScheduled = Writer.ScheduleBufferUpdate(pDstBuffer, DstOffset, NumBytes, pSrcData, Callback, pCallbackData);
399400
if (Writer.EndWriting() == Page::WritingStatus::LastWriterSealed)
400401
{
401-
// We were the last writer
402+
// We were the last writer - enqueue the page whether the update was successfully scheduled or not,
403+
// because the page is sealed and can't be written to anymore.
402404
TryEnqueuePage(P);
403405
}
404406

405407
if (UpdateScheduled)
406408
{
407409
if (!IsFirstAttempt)
410+
{
411+
// If we had to rotate the page at least once, we need to subtract the update size from the total pending update size
412+
// as the update was successfully scheduled on the page.
408413
m_TotalPendingUpdateSize.fetch_sub(NumBytes, std::memory_order_relaxed);
414+
}
409415
break;
410416
}
411417
else

Tests/DiligentCoreAPITest/src/GPUUploadManagerTest.cpp

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,8 @@ TEST(GPUUploadManagerTest, Creation)
4848
IRenderDevice* pDevice = pEnv->GetDevice();
4949
IDeviceContext* pContext = pEnv->GetDeviceContext();
5050

51+
GPUTestingEnvironment::ScopedReset AutoReset;
52+
5153
RefCntAutoPtr<IGPUUploadManager> pUploadManager;
5254
GPUUploadManagerCreateInfo CreateInfo{pDevice, pContext};
5355
CreateGPUUploadManager(CreateInfo, &pUploadManager);
@@ -60,6 +62,8 @@ void VerifyBufferContents(IBuffer* pBuffer, const std::vector<Uint8>& ExpectedDa
6062
IRenderDevice* pDevice = pEnv->GetDevice();
6163
IDeviceContext* pContext = pEnv->GetDeviceContext();
6264

65+
GPUTestingEnvironment::ScopedReset AutoReset;
66+
6367
BufferDesc Desc = pBuffer->GetDesc();
6468
Desc.Name = "GPUUploadManagerTest readback buffer";
6569
Desc.Usage = USAGE_STAGING;
@@ -77,6 +81,7 @@ void VerifyBufferContents(IBuffer* pBuffer, const std::vector<Uint8>& ExpectedDa
7781
void* pBufferData = nullptr;
7882
pContext->MapBuffer(pReadbackBuffer, MAP_READ, MAP_FLAG_DO_NOT_WAIT, pBufferData);
7983
ASSERT_NE(pBufferData, nullptr);
84+
pContext->UnmapBuffer(pReadbackBuffer, MAP_READ);
8085

8186
EXPECT_TRUE(std::memcmp(pBufferData, ExpectedData.data(), ExpectedData.size()) == 0) << "Buffer contents do not match expected data";
8287
}
@@ -87,6 +92,8 @@ TEST(GPUUploadManagerTest, ScheduleUpdates)
8792
IRenderDevice* pDevice = pEnv->GetDevice();
8893
IDeviceContext* pContext = pEnv->GetDeviceContext();
8994

95+
GPUTestingEnvironment::ScopedReset AutoReset;
96+
9097
RefCntAutoPtr<IGPUUploadManager> pUploadManager;
9198
GPUUploadManagerCreateInfo CreateInfo{pDevice, pContext, 1024};
9299
CreateGPUUploadManager(CreateInfo, &pUploadManager);
@@ -118,4 +125,96 @@ TEST(GPUUploadManagerTest, ScheduleUpdates)
118125
VerifyBufferContents(pBuffer, BufferData);
119126
}
120127

128+
TEST(GPUUploadManagerTest, ParallelUpdates)
129+
{
130+
GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance();
131+
IRenderDevice* pDevice = pEnv->GetDevice();
132+
IDeviceContext* pContext = pEnv->GetDeviceContext();
133+
134+
RefCntAutoPtr<IGPUUploadManager> pUploadManager;
135+
GPUUploadManagerCreateInfo CreateInfo{pDevice, pContext, 16384};
136+
CreateGPUUploadManager(CreateInfo, &pUploadManager);
137+
ASSERT_TRUE(pUploadManager != nullptr);
138+
139+
std::vector<Uint8> BufferData(4 << 20);
140+
for (size_t i = 0; i < BufferData.size(); ++i)
141+
{
142+
BufferData[i] = static_cast<Uint8>(i % 256);
143+
}
144+
145+
BufferDesc Desc;
146+
Desc.Name = "GPUUploadManagerTest buffer";
147+
Desc.Size = BufferData.size();
148+
Desc.Usage = USAGE_DEFAULT;
149+
Desc.BindFlags = BIND_VERTEX_BUFFER;
150+
151+
RefCntAutoPtr<IBuffer> pBuffer;
152+
pDevice->CreateBuffer(Desc, nullptr, &pBuffer);
153+
ASSERT_TRUE(pBuffer);
154+
155+
const size_t kNumThreads = std::max(2u, std::thread::hardware_concurrency() - 1);
156+
LOG_INFO_MESSAGE("Number of threads: ", kNumThreads);
157+
158+
std::atomic<Uint32> CurrOffset{0};
159+
std::atomic<Uint32> NumUpdatesScheduled{0};
160+
std::atomic<Uint32> NumThreadsCompleted{0};
161+
162+
std::vector<std::thread> Threads;
163+
for (size_t t = 0; t < kNumThreads; ++t)
164+
{
165+
Threads.emplace_back(
166+
[&]() {
167+
while (true)
168+
{
169+
const Uint32 UpdateSize = 64;
170+
171+
Uint32 Offset = CurrOffset.fetch_add(UpdateSize);
172+
if (Offset >= BufferData.size())
173+
break;
174+
pUploadManager->ScheduleBufferUpdate(nullptr, pBuffer, Offset, UpdateSize, &BufferData[Offset]);
175+
NumUpdatesScheduled.fetch_add(1);
176+
}
177+
NumThreadsCompleted.fetch_add(1);
178+
});
179+
}
180+
181+
const Uint32 NumUpdatesToRenderThreadUpdate = 256;
182+
183+
Uint32 LastNumUpdatesScheduled = 0;
184+
Uint32 NumIterationsWithoutUpdate = 0;
185+
Uint32 NumRenderThreadUpdates = 0;
186+
while (NumThreadsCompleted.load() < kNumThreads)
187+
{
188+
if (LastNumUpdatesScheduled == NumUpdatesScheduled.load())
189+
{
190+
++NumIterationsWithoutUpdate;
191+
}
192+
else
193+
{
194+
LastNumUpdatesScheduled = NumUpdatesScheduled.load();
195+
NumIterationsWithoutUpdate = 0;
196+
}
197+
198+
if (NumUpdatesScheduled.load() >= NumUpdatesToRenderThreadUpdate || NumIterationsWithoutUpdate >= 100)
199+
{
200+
pUploadManager->RenderThreadUpdate(pContext);
201+
NumUpdatesScheduled.fetch_sub(NumUpdatesToRenderThreadUpdate);
202+
pContext->Flush();
203+
pContext->FinishFrame();
204+
++NumRenderThreadUpdates;
205+
}
206+
}
207+
208+
LOG_INFO_MESSAGE("Total render thread updates: ", NumRenderThreadUpdates);
209+
210+
pUploadManager->RenderThreadUpdate(pContext);
211+
212+
for (std::thread& thread : Threads)
213+
{
214+
thread.join();
215+
}
216+
217+
VerifyBufferContents(pBuffer, BufferData);
218+
}
219+
121220
} // namespace

0 commit comments

Comments
 (0)