@@ -291,8 +291,7 @@ TEST(GPUUploadManagerTest, Writer_ScheduleBufferUpdateParallel)
291291
292292 std::vector<std::thread> threads;
293293
294- const size_t kNumThreads = std::max (4u , std::thread::hardware_concurrency ());
295- const size_t kNumIterations = (kNumUpdates + kNumThreads - 1 ) / kNumThreads ;
294+ const size_t kNumThreads = std::max (4u , std::thread::hardware_concurrency ());
296295
297296 std::atomic<size_t > UpdatesScheduled{0 };
298297 std::atomic<Uint32> DstOffset{0 };
@@ -302,26 +301,27 @@ TEST(GPUUploadManagerTest, Writer_ScheduleBufferUpdateParallel)
302301 [&](size_t ThreadId) {
303302 StartSignal.Wait (true , static_cast <int >(kNumThreads ));
304303
305- for ( size_t i = 0 ; i < kNumIterations ; ++i )
304+ while ( true )
306305 {
306+ Uint32 Offset = DstOffset.fetch_add (kUpdateSize );
307+ if (Offset >= kPageSize )
308+ break ;
309+
307310 GPUUploadManagerImpl::Page::Writer Writer = Page.TryBeginWriting ();
308311 EXPECT_TRUE (Writer) << " Writer should be valid because the page should not be sealed yet" ;
309312 if (!Writer)
310313 break ;
311314
312- Uint32 Offset = DstOffset.fetch_add (kUpdateSize );
313- if (Offset < kPageSize )
315+ ScheduleBufferUpdateInfo UpdateInfo;
316+ UpdateInfo.pDstBuffer = pBuffer;
317+ UpdateInfo.pSrcData = &BufferData[Offset];
318+ UpdateInfo.DstOffset = Offset;
319+ UpdateInfo.NumBytes = kUpdateSize ;
320+ if (Writer.ScheduleBufferUpdate (UpdateInfo))
314321 {
315- ScheduleBufferUpdateInfo UpdateInfo;
316- UpdateInfo.pDstBuffer = pBuffer;
317- UpdateInfo.pSrcData = &BufferData[Offset];
318- UpdateInfo.DstOffset = Offset;
319- UpdateInfo.NumBytes = kUpdateSize ;
320- if (Writer.ScheduleBufferUpdate (UpdateInfo))
321- {
322- UpdatesScheduled.fetch_add (1 );
323- }
322+ UpdatesScheduled.fetch_add (1 );
324323 }
324+
325325 Writer.EndWriting ();
326326 }
327327 },
@@ -1022,6 +1022,7 @@ void TestWriterScheduleTextureUpdates(Uint32 Flags = TEST_TEXTURE_UPDATES_FLAGS_
10221022 ASSERT_TRUE (pTexture != nullptr );
10231023
10241024 const std::vector<std::vector<Uint8>> SubresData = GenerateTextureSubresData (TexDesc);
1025+ const std::vector<Uint8>& Mip0Data = SubresData[0 ];
10251026
10261027 const Uint32 ElementSize = GetTextureFormatAttribs (TexDesc.Format ).GetElementSize ();
10271028
@@ -1035,7 +1036,6 @@ void TestWriterScheduleTextureUpdates(Uint32 Flags = TEST_TEXTURE_UPDATES_FLAGS_
10351036 Page = std::make_unique<GPUUploadManagerImpl::Page>(nullptr , pDevice, TexDesc.Width * TexDesc.Height * ElementSize);
10361037 }
10371038
1038- const std::vector<Uint8>& Mip0Data = SubresData[0 ];
10391039 Page->Reset (pContext);
10401040 Page->Unseal ();
10411041
@@ -1199,6 +1199,110 @@ TEST(GPUUploadManagerTest, Writer_ScheduleTextureUpdates_WithWriteAndCopyCallbac
11991199 TestWriterScheduleTextureUpdates (TEST_TEXTURE_UPDATES_FLAGS_USE_WRITE_DATA_CALLBACK | TEST_TEXTURE_UPDATES_FLAGS_USE_COPY_CALLBACK);
12001200}
12011201
1202+ TEST (GPUUploadManagerTest, Writer_ScheduleTextureUpdateParallel)
1203+ {
1204+ GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance ();
1205+ IRenderDevice* pDevice = pEnv->GetDevice ();
1206+ IDeviceContext* pContext = pEnv->GetDeviceContext ();
1207+
1208+ GPUTestingEnvironment::ScopedReset AutoReset;
1209+
1210+ TextureDesc TexDesc;
1211+ TexDesc.Name = " GPUUploadManagerTest texture" ;
1212+ TexDesc.Type = RESOURCE_DIM_TEX_2D;
1213+ TexDesc.Format = TEX_FORMAT_RGBA8_UNORM;
1214+ TexDesc.Usage = USAGE_DEFAULT;
1215+ TexDesc.Width = 2048 ;
1216+ TexDesc.Height = 2048 ;
1217+ TexDesc.MipLevels = 1 ;
1218+ TexDesc.BindFlags = BIND_SHADER_RESOURCE;
1219+
1220+ RefCntAutoPtr<ITexture> pTexture;
1221+ pDevice->CreateTexture (TexDesc, nullptr , &pTexture);
1222+ ASSERT_TRUE (pTexture != nullptr );
1223+
1224+ const std::vector<std::vector<Uint8>> SubresData = GenerateTextureSubresData (TexDesc);
1225+ const std::vector<Uint8>& Mip0Data = SubresData[0 ];
1226+
1227+ const Uint32 ElementSize = GetTextureFormatAttribs (TexDesc.Format ).GetElementSize ();
1228+
1229+ std::unique_ptr<GPUUploadManagerImpl::Page> Page;
1230+ if (pDevice->GetDeviceInfo ().Type == RENDER_DEVICE_TYPE_D3D11)
1231+ {
1232+ Page = std::make_unique<GPUUploadManagerImpl::Page>(nullptr , pDevice, TexDesc.Width , TexDesc.Format );
1233+ }
1234+ else
1235+ {
1236+ Page = std::make_unique<GPUUploadManagerImpl::Page>(nullptr , pDevice, TexDesc.Width * TexDesc.Height * ElementSize);
1237+ }
1238+
1239+ Page->Reset (pContext);
1240+ Page->Unseal ();
1241+ Threading::Signal StartSignal;
1242+
1243+ std::vector<std::thread> threads;
1244+
1245+ const Uint32 kUpdateWidth = 512 ;
1246+ const Uint32 kUpdateHeight = 16 ;
1247+ const Uint32 NumUpdates = (TexDesc.Width / kUpdateWidth ) * (TexDesc.Height / kUpdateHeight );
1248+ const Uint32 NumUpdatesPerRow = TexDesc.Width / kUpdateWidth ;
1249+
1250+ const size_t kNumThreads = std::max (4u , std::thread::hardware_concurrency ());
1251+
1252+ std::atomic<Uint32> UpdateIndex{0 };
1253+ std::atomic<size_t > UpdatesScheduled{0 };
1254+ std::atomic<Uint32> DstOffset{0 };
1255+ for (size_t t = 0 ; t < kNumThreads ; ++t)
1256+ {
1257+ threads.emplace_back (
1258+ [&](size_t ThreadId) {
1259+ StartSignal.Wait (true , static_cast <int >(kNumThreads ));
1260+
1261+ while (true )
1262+ {
1263+ Uint32 UpdateId = UpdateIndex.fetch_add (1 );
1264+ if (UpdateId >= NumUpdates)
1265+ break ;
1266+
1267+ GPUUploadManagerImpl::Page::Writer Writer = Page->TryBeginWriting ();
1268+ EXPECT_TRUE (Writer) << " Writer should be valid because the page should not be sealed yet" ;
1269+ if (!Writer)
1270+ break ;
1271+
1272+ ScheduleTextureUpdateInfo UpdateInfo;
1273+ UpdateInfo.pDstTexture = pTexture;
1274+ UpdateInfo.Stride = TexDesc.Width * ElementSize;
1275+ UpdateInfo.DstBox .MinX = (UpdateId % NumUpdatesPerRow) * kUpdateWidth ;
1276+ UpdateInfo.DstBox .MinY = (UpdateId / NumUpdatesPerRow) * kUpdateHeight ;
1277+ UpdateInfo.DstBox .MaxX = UpdateInfo.DstBox .MinX + kUpdateWidth ;
1278+ UpdateInfo.DstBox .MaxY = UpdateInfo.DstBox .MinY + kUpdateHeight ;
1279+ UpdateInfo.pSrcData = &Mip0Data[(UpdateInfo.DstBox .MinX + UpdateInfo.DstBox .MinY * TexDesc.Width ) * ElementSize];
1280+
1281+ BufferToTextureCopyInfo CopyInfo = GetBufferToTextureCopyInfo (TexDesc.Format , UpdateInfo.DstBox , 1024 );
1282+ if (Writer.ScheduleTextureUpdate (UpdateInfo, CopyInfo, 1024 ))
1283+ {
1284+ UpdatesScheduled.fetch_add (1 );
1285+ }
1286+ Writer.EndWriting ();
1287+ }
1288+ },
1289+ t);
1290+ }
1291+
1292+ StartSignal.Trigger (true );
1293+
1294+ for (auto & thread : threads)
1295+ thread.join ();
1296+
1297+ EXPECT_EQ (Page->GetNumPendingOps (), NumUpdates);
1298+ EXPECT_EQ (UpdatesScheduled.load (), NumUpdates) << " Should be able to schedule updates until the page size is reached" ;
1299+ EXPECT_EQ (Page->TrySeal (), GPUUploadManagerImpl::Page::SealStatus::Ready) << " Page should be ready for sealing after all updates are scheduled" ;
1300+ Page->ExecutePendingOps (pContext, 1 );
1301+ Page->ReleaseStagingBuffer (pContext);
1302+
1303+ VerifyTextureContents (pTexture, SubresData);
1304+ }
1305+
12021306void TestTextureUpdates (TEXTURE_FORMAT Format, RESOURCE_DIMENSION Type, Uint32 Flags = TEST_TEXTURE_UPDATES_FLAGS_NONE)
12031307{
12041308 GPUTestingEnvironment* pEnv = GPUTestingEnvironment::GetInstance ();
0 commit comments