Skip to content

Commit 8dec46c

Browse files
GLTFLoader: add asynchronous texture upload support via GPUUploadManager
1 parent 159f2be commit 8dec46c

File tree

2 files changed

+136
-8
lines changed

2 files changed

+136
-8
lines changed

AssetLoader/interface/GLTFLoader.hpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,7 @@ struct Model
10931093
Uint32 AddTexture(IRenderDevice* pDevice,
10941094
TextureCacheType* pTextureCache,
10951095
ResourceManager* pResourceMgr,
1096+
IGPUUploadManager* pUploadMgr,
10961097
const ImageData& Image,
10971098
int GltfSamplerId,
10981099
const std::string& CacheId);
@@ -1162,7 +1163,8 @@ struct Model
11621163
const tinygltf::Model& gltf_model,
11631164
const std::string& BaseDir,
11641165
TextureCacheType* pTextureCache,
1165-
ResourceManager* pResourceMgr);
1166+
ResourceManager* pResourceMgr,
1167+
IGPUUploadManager* pUploadMgr);
11661168

11671169
void LoadTextureSamplers(IRenderDevice* pDevice, const tinygltf::Model& gltf_model);
11681170
void LoadMaterials(const tinygltf::Model& gltf_model, const ModelCreateInfo::MaterialLoadCallbackType& MaterialLoadCallback);

AssetLoader/src/GLTFLoader.cpp

Lines changed: 133 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
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+
727837
Uint32 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

10181137
bool 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

11391265
bool 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

Comments
 (0)