2929#include < memory>
3030#include < cmath>
3131#include < limits>
32+ #include < atomic>
3233
3334#include " GLTFLoader.hpp"
3435#include " MapHelper.hpp"
@@ -384,6 +385,8 @@ struct TextureInitData : public ObjectBase<IObject>
384385
385386 RefCntAutoPtr<ITexture> pStagingTex;
386387
388+ std::atomic<Uint32> NumPendingUploads{0 };
389+
387390 void GenerateMipLevels (Uint32 StartMipLevel)
388391 {
389392 VERIFY_EXPR (StartMipLevel > 0 );
@@ -724,9 +727,117 @@ float Model::GetTextureAlphaCutoffValue(int TextureIndex) const
724727 return std::max (AlphaCutoff, 0 .f );
725728}
726729
730+ void ScheduleAtlasUpdate (IGPUUploadManager* pUploadMgr, ITextureAtlasSuballocation* pAtlasSuballocation, ITextureLoader* pTexLoader)
731+ {
732+ const TextureDesc& SrcTexDesc = pTexLoader->GetTextureDesc ();
733+ const TextureDesc& AtlasDesc = pAtlasSuballocation->GetAtlas ()->GetAtlasDesc ();
734+ const TextureFormatAttribs& FmtAttribs = GetTextureFormatAttribs (SrcTexDesc.Format );
735+ const uint2 Origin = pAtlasSuballocation->GetOrigin ();
736+
737+ const Uint32 SrcMips = std::min (AtlasDesc.MipLevels , SrcTexDesc.MipLevels );
738+ for (Uint32 mip = 0 ; mip < SrcMips; ++mip)
739+ {
740+ const MipLevelProperties MipProps = GetMipLevelProperties (SrcTexDesc, mip);
741+ if (FmtAttribs.ComponentType == COMPONENT_TYPE_COMPRESSED)
742+ {
743+ // Do not copy mip levels that are smaller than the block size
744+ if (MipProps.LogicalWidth < FmtAttribs.BlockWidth ||
745+ MipProps.LogicalHeight < FmtAttribs.BlockHeight )
746+ break ;
747+ }
748+
749+ const TextureSubResData& SubResData = pTexLoader->GetSubresourceData (mip);
750+ ScheduleTextureUpdateInfo UpdateInfo;
751+ UpdateInfo.Format = AtlasDesc.Format ;
752+ UpdateInfo.pSrcData = SubResData.pData ;
753+ UpdateInfo.Stride = SubResData.Stride ;
754+ UpdateInfo.DepthStride = SubResData.DepthStride ;
755+ UpdateInfo.DstBox = {0 , MipProps.LogicalWidth , 0 , MipProps.LogicalHeight };
756+ UpdateInfo.DstSlice = pAtlasSuballocation->GetSlice ();
757+ UpdateInfo.DstMipLevel = mip;
758+
759+ UpdateInfo.DstBox .MinX = Origin.x >> mip;
760+ UpdateInfo.DstBox .MinY = Origin.y >> mip;
761+ UpdateInfo.DstBox .MaxX = UpdateInfo.DstBox .MinX + MipProps.LogicalWidth ;
762+ UpdateInfo.DstBox .MaxY = UpdateInfo.DstBox .MinY + MipProps.LogicalHeight ;
763+
764+ pAtlasSuballocation->AddRef ();
765+ UpdateInfo.pCopyTextureData = pAtlasSuballocation;
766+
767+ TextureInitData* pInitData = static_cast <TextureInitData*>(pAtlasSuballocation->GetUserData ());
768+ VERIFY_EXPR (pInitData != nullptr );
769+ pInitData->NumPendingUploads .fetch_add (1 );
770+
771+ UpdateInfo.CopyTexture =
772+ [](IDeviceContext* pContext,
773+ Uint32 DstMipLevel,
774+ Uint32 DstSlice,
775+ const Box& DstBox,
776+ const TextureSubResData& SrcData,
777+ void * pUserData) {
778+ ITextureAtlasSuballocation* pAtlasSuballocation = static_cast <ITextureAtlasSuballocation*>(pUserData);
779+ if (pContext != nullptr )
780+ {
781+ pContext->UpdateTexture (pAtlasSuballocation->GetAtlas ()->GetTexture (), DstMipLevel, DstSlice, DstBox, SrcData,
782+ RESOURCE_STATE_TRANSITION_MODE_VERIFY,
783+ RESOURCE_STATE_TRANSITION_MODE_TRANSITION);
784+ }
785+ TextureInitData* pInitData = static_cast <TextureInitData*>(pAtlasSuballocation->GetUserData ());
786+ VERIFY_EXPR (pInitData != nullptr );
787+ pInitData->NumPendingUploads .fetch_sub (1 );
788+ pAtlasSuballocation->Release ();
789+ };
790+
791+ UpdateInfo.CopyD3D11Texture =
792+ [](IDeviceContext* pContext,
793+ Uint32 DstMipLevel,
794+ Uint32 DstSlice,
795+ const Box& DstBox,
796+ ITexture* pSrcTexture,
797+ Uint32 SrcX,
798+ Uint32 SrcY,
799+ void * pUserData) {
800+ ITextureAtlasSuballocation* pAtlasSuballocation = static_cast <ITextureAtlasSuballocation*>(pUserData);
801+ if (pContext != nullptr )
802+ {
803+ CopyTextureAttribs CopyAttribs;
804+ CopyAttribs.pSrcTexture = pSrcTexture;
805+ CopyAttribs.pDstTexture = pAtlasSuballocation->GetAtlas ()->GetTexture ();
806+ CopyAttribs.DstMipLevel = DstMipLevel;
807+ CopyAttribs.DstSlice = DstSlice;
808+ CopyAttribs.DstX = DstBox.MinX ;
809+ CopyAttribs.DstY = DstBox.MinY ;
810+ CopyAttribs.DstZ = DstBox.MinZ ;
811+
812+ const TextureFormatAttribs& FmtAttribs = GetTextureFormatAttribs (CopyAttribs.pDstTexture ->GetDesc ().Format );
813+
814+ Box SrcBox;
815+ SrcBox.MinX = SrcX;
816+ SrcBox.MinY = SrcY;
817+ SrcBox.MaxX = AlignUp (SrcX + DstBox.Width (), FmtAttribs.BlockWidth );
818+ SrcBox.MaxY = AlignUp (SrcY + DstBox.Height (), FmtAttribs.BlockHeight );
819+
820+ CopyAttribs.pSrcBox = &SrcBox;
821+
822+ CopyAttribs.SrcTextureTransitionMode = RESOURCE_STATE_TRANSITION_MODE_VERIFY;
823+ CopyAttribs.DstTextureTransitionMode = RESOURCE_STATE_TRANSITION_MODE_TRANSITION;
824+
825+ pContext->CopyTexture (CopyAttribs);
826+ }
827+ TextureInitData* pInitData = static_cast <TextureInitData*>(pAtlasSuballocation->GetUserData ());
828+ VERIFY_EXPR (pInitData != nullptr );
829+ pInitData->NumPendingUploads .fetch_sub (1 );
830+ pAtlasSuballocation->Release ();
831+ };
832+
833+ pUploadMgr->ScheduleTextureUpdate (UpdateInfo);
834+ }
835+ }
836+
727837Uint32 Model::AddTexture (IRenderDevice* pDevice,
728838 TextureCacheType* pTextureCache,
729839 ResourceManager* pResourceMgr,
840+ IGPUUploadManager* pUploadMgr,
730841 const ImageData& Image,
731842 int GltfSamplerId,
732843 const std::string& CacheId)
@@ -907,9 +1018,16 @@ Uint32 Model::AddTexture(IRenderDevice* pDevice,
9071018 // is added to the cache. This is all OK though.
9081019 TexInfo.pAtlasSuballocation = pResourceMgr->AllocateTextureSpace (TexDesc.Format , TexDesc.Width , TexDesc.Height , CacheId.c_str (), pTexInitData);
9091020
910- // NB: create staging texture to save work in the main thread when
911- // this function is called from a worker thread
912- pTexLoader->CreateTexture (pDevice, &pTexInitData->pStagingTex );
1021+ if (pUploadMgr != nullptr )
1022+ {
1023+ ScheduleAtlasUpdate (pUploadMgr, TexInfo.pAtlasSuballocation , pTexLoader);
1024+ }
1025+ else
1026+ {
1027+ // NB: create staging texture to save work in the main thread when
1028+ // this function is called from a worker thread
1029+ pTexLoader->CreateTexture (pDevice, &pTexInitData->pStagingTex );
1030+ }
9131031 }
9141032 }
9151033 }
@@ -994,7 +1112,8 @@ void Model::LoadTextures(IRenderDevice* pDevice,
9941112 const tinygltf::Model& gltf_model,
9951113 const std::string& BaseDir,
9961114 TextureCacheType* pTextureCache,
997- ResourceManager* pResourceMgr)
1115+ ResourceManager* pResourceMgr,
1116+ IGPUUploadManager* pUploadMgr)
9981117{
9991118 Textures.reserve (gltf_model.textures .size ());
10001119 for (const tinygltf::Texture& gltf_tex : gltf_model.textures )
@@ -1011,12 +1130,13 @@ void Model::LoadTextures(IRenderDevice* pDevice,
10111130 Image.pData = gltf_image.image .data ();
10121131 Image.DataSize = gltf_image.image .size ();
10131132
1014- AddTexture (pDevice, pTextureCache, pResourceMgr, Image, gltf_tex.sampler , CacheId);
1133+ AddTexture (pDevice, pTextureCache, pResourceMgr, pUploadMgr, Image, gltf_tex.sampler , CacheId);
10151134 }
10161135}
10171136
10181137bool Model::PrepareTextureGPUData (IRenderDevice* pDevice, IDeviceContext* pCtx, std::vector<StateTransitionDesc>& Barriers)
10191138{
1139+ bool AllTexturesReady = true ;
10201140 for (Uint32 i = 0 ; i < Textures.size (); ++i)
10211141 {
10221142 TextureInfo& DstTexInfo = Textures[i];
@@ -1027,6 +1147,12 @@ bool Model::PrepareTextureGPUData(IRenderDevice* pDevice, IDeviceContext* pCtx,
10271147 {
10281148 pTexture = DstTexInfo.pAtlasSuballocation ->GetAtlas ()->Update (pDevice, pCtx);
10291149 pInitData = ClassPtrCast<TextureInitData>(DstTexInfo.pAtlasSuballocation ->GetUserData ());
1150+ if (pInitData && pInitData->NumPendingUploads .load () > 0 )
1151+ {
1152+ // Wait until uploads are scheduled for all mip levels.
1153+ AllTexturesReady = false ;
1154+ continue ;
1155+ }
10301156 // User data is only set when the allocation is created, so no other
10311157 // thread can call SetUserData() in parallel.
10321158 DstTexInfo.pAtlasSuballocation ->SetUserData (nullptr );
@@ -1133,7 +1259,7 @@ bool Model::PrepareTextureGPUData(IRenderDevice* pDevice, IDeviceContext* pCtx,
11331259 }
11341260 }
11351261
1136- return true ;
1262+ return AllTexturesReady ;
11371263}
11381264
11391265bool Model::PrepareIndexGPUData (IRenderDevice* pDevice, IDeviceContext* pCtx, std::vector<StateTransitionDesc>& Barriers)
@@ -2115,7 +2241,7 @@ void Model::LoadFromFile(IRenderDevice* pDevice,
21152241 // Load materials first as the LoadTextures() function needs them to determine the alpha-cut value.
21162242 LoadMaterials (gltf_model, CI.MaterialLoadCallback );
21172243 LoadTextureSamplers (pDevice, gltf_model);
2118- LoadTextures (pDevice, gltf_model, LoaderData.BaseDir , pTextureCache, pResourceMgr);
2244+ LoadTextures (pDevice, gltf_model, LoaderData.BaseDir , pTextureCache, pResourceMgr, CI. pUploadMgr );
21192245
21202246 ModelBuilder Builder{CI, *this };
21212247 Builder.Execute (TinyGltfModelWrapper{gltf_model}, CI.SceneId , pDevice);
0 commit comments