Skip to content

Commit b1db8d5

Browse files
Radient: move raw mesh cache key generation to worker thread
Remove user-provided mesh cache keys and derive raw mesh cache keys from copied source data during async mesh loading. Fold raw mesh payload creation into the mesh cache factory and update tests for async mesh resolution.
1 parent ece5682 commit b1db8d5

6 files changed

Lines changed: 187 additions & 88 deletions

File tree

Radient/include/Assets/RadientMeshSource.hpp

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333

3434
#include "../../../PBR/interface/PBR_Renderer.hpp"
3535

36+
#include <string>
3637
#include <unordered_map>
3738
#include <vector>
3839

@@ -131,6 +132,8 @@ class RadientMeshSource final
131132
RADIENT_STATUS PackIndexData(PackDestination Destination) const;
132133
RADIENT_STATUS PackVertexData(Uint32 VertexBufferIndex, PackDestination Destination) const;
133134

135+
std::string MakeCacheKey() const;
136+
134137
private:
135138
struct SrcAttributeData
136139
{
@@ -154,9 +157,10 @@ class RadientMeshSource final
154157

155158
PBR_Renderer::PSO_FLAGS m_VertexAttribFlags = PBR_Renderer::PSO_FLAG_NONE;
156159

157-
Uint32 m_VertexCount = 0;
158-
Uint32 m_IndexCount = 0;
159-
Uint32 m_ActiveVertexBufferMask = 0;
160+
Uint32 m_VertexCount = 0;
161+
Uint32 m_IndexCount = 0;
162+
Uint32 m_ActiveVertexBufferMask = 0;
163+
RADIENT_INDEX_TYPE m_IndexType = RADIENT_INDEX_TYPE_NONE;
160164

161165
std::vector<GLTF::VertexAttributeDesc> m_DstAttributes;
162166
std::vector<Uint32> m_VertexStrides;

Radient/interface/RadientAssets.h

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -178,10 +178,6 @@ struct RadientMeshCreateInfo
178178

179179
/// Number of primitives.
180180
Uint32 PrimitiveCount DEFAULT_INITIALIZER(0);
181-
182-
/// Optional cache key. When non-empty, live mesh assets created with the
183-
/// same key are reused instead of creating a new asset.
184-
const Char* CacheKey DEFAULT_INITIALIZER(nullptr);
185181
};
186182
typedef struct RadientMeshCreateInfo RadientMeshCreateInfo;
187183

Radient/src/Assets/RadientMeshAssetManager.cpp

Lines changed: 57 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -492,13 +492,6 @@ void LoadMeshFromSource(MeshPayloadImpl& Mesh,
492492
SetLoadStatus(Status);
493493
}
494494

495-
RefCntAutoPtr<MeshAssetImpl> CreateMeshAsset(std::atomic<RadientHandle>& NextAssetID,
496-
RefCntAutoPtr<MeshPayloadImpl>&& pPayload)
497-
{
498-
const RadientHandle AssetID = NextAssetID.fetch_add(1, std::memory_order_relaxed);
499-
return MeshAssetImpl::Create(MakeRadientAssetURI("mesh", AssetID), std::move(pPayload));
500-
}
501-
502495
RefCntAutoPtr<MeshAssetImpl> CreateCachedMeshAsset(const char* CacheKey,
503496
RefCntAutoPtr<MeshPayloadImpl>&& pPayload)
504497
{
@@ -554,85 +547,76 @@ RADIENT_STATUS RadientMeshAssetManager::CreateMesh(const RadientMeshCreateInfo&
554547
if (!ValidateMeshCreateInfo(MeshCI))
555548
return RADIENT_STATUS_INVALID_ARGUMENT;
556549

550+
if (m_pThreadPool == nullptr)
551+
return RADIENT_STATUS_INVALID_OPERATION;
552+
557553
const bool CanUploadMesh =
558554
m_pDevice != nullptr &&
559555
m_pResourceManager != nullptr &&
560556
m_pUploadManager != nullptr;
561-
if (CanUploadMesh && m_pThreadPool == nullptr)
562-
return RADIENT_STATUS_INVALID_OPERATION;
563-
564-
RADIENT_STATUS SourceStatus = RADIENT_STATUS_OK;
565557

566-
auto CreateMeshPayloadFromCI =
567-
[this, &MeshCI, CanUploadMesh, &SourceStatus]() -> RefCntAutoPtr<MeshPayloadImpl> {
568-
std::unique_ptr<RadientMeshSource> pMeshSource = std::make_unique<RadientMeshSource>(MeshCI);
569-
SourceStatus = pMeshSource->GetStatus();
570-
if (RADIENT_FAILED(SourceStatus))
571-
return {};
558+
RefCntAutoPtr<MeshAssetImpl> pMeshAsset =
559+
MeshAssetImpl::Create(MakeRadientAssetURI("mesh", m_NextAssetID.fetch_add(1, std::memory_order_relaxed)));
560+
VERIFY_EXPR(pMeshAsset != nullptr);
561+
if (!pMeshAsset)
562+
return RADIENT_STATUS_INVALID_OPERATION;
572563

573-
MeshStorage MeshData{CanUploadMesh ? RADIENT_STATUS_PENDING : RADIENT_STATUS_INVALID_OPERATION};
574-
MeshData.GPUResourcesReady.store(false, std::memory_order_release);
564+
std::unique_ptr<RadientMeshSource> pMeshSource = std::make_unique<RadientMeshSource>(MeshCI);
575565

576-
RefCntAutoPtr<MeshPayloadImpl> pMeshPayload = MeshPayloadImpl::Create(MeshAssetStorage{std::move(MeshData)});
577-
VERIFY_EXPR(pMeshPayload != nullptr);
566+
pMeshAsset->QueryInterface(IID_RadientMeshAsset, ppMesh);
578567

579-
if (CanUploadMesh)
568+
EnqueueAsyncWork(
569+
m_pThreadPool,
570+
[pMeshAsset,
571+
MeshCache = m_MeshCache.GetAccessor(),
572+
CanUploadMesh,
573+
pDevice = m_pDevice,
574+
pResourceManager = m_pResourceManager,
575+
pUploadManager = m_pUploadManager,
576+
pMeshSource = std::move(pMeshSource)](Uint32) mutable //
580577
{
581-
EnqueueAsyncWork(
582-
m_pThreadPool,
583-
[pMeshPayload,
584-
pDevice = m_pDevice,
585-
pResourceManager = m_pResourceManager,
586-
pUploadManager = m_pUploadManager,
587-
pMeshSource = std::move(pMeshSource)](Uint32) mutable //
588-
{
589-
LoadMeshFromSource(*pMeshPayload, std::move(pMeshSource), pDevice, pResourceManager, pUploadManager);
590-
return ASYNC_TASK_STATUS_COMPLETE;
591-
});
592-
}
593-
594-
return pMeshPayload;
595-
};
596-
597-
const bool HasCacheKey = MeshCI.CacheKey != nullptr && MeshCI.CacheKey[0] != '\0';
598-
RefCntAutoPtr<MeshPayloadImpl> pMeshPayload;
599-
if (HasCacheKey)
600-
{
601-
auto [pCachedMeshPayload, PayloadCreated] =
602-
m_MeshCache.GetOrCreate(
603-
MeshCI.CacheKey,
604-
[&CreateMeshPayloadFromCI]() {
605-
return CreateMeshPayloadFromCI();
606-
});
578+
const RADIENT_STATUS SourceStatus = pMeshSource->GetStatus();
579+
if (RADIENT_FAILED(SourceStatus))
580+
{
581+
pMeshAsset->Fail(SourceStatus);
582+
return ASYNC_TASK_STATUS_COMPLETE;
583+
}
607584

608-
(void)PayloadCreated;
585+
const std::string CacheKey = pMeshSource->MakeCacheKey();
586+
if (CacheKey.empty())
587+
{
588+
pMeshAsset->Fail(RADIENT_STATUS_INVALID_OPERATION);
589+
return ASYNC_TASK_STATUS_COMPLETE;
590+
}
609591

610-
if (!pCachedMeshPayload)
611-
{
612-
return RADIENT_FAILED(SourceStatus) ? SourceStatus : RADIENT_STATUS_INVALID_OPERATION;
613-
}
592+
auto [pMeshPayload, PayloadCreated] =
593+
MeshCache.GetOrCreate(
594+
CacheKey.c_str(),
595+
[CanUploadMesh]() {
596+
MeshStorage MeshData{CanUploadMesh ? RADIENT_STATUS_PENDING : RADIENT_STATUS_INVALID_OPERATION};
597+
MeshData.GPUResourcesReady.store(false, std::memory_order_release);
598+
return MeshPayloadImpl::Create(MeshAssetStorage{std::move(MeshData)});
599+
});
614600

615-
RefCntAutoPtr<MeshAssetImpl> pMeshAsset = CreateCachedMeshAsset(MeshCI.CacheKey, std::move(pCachedMeshPayload));
616-
if (!pMeshAsset)
617-
return RADIENT_STATUS_INVALID_OPERATION;
601+
if (!pMeshPayload)
602+
{
603+
pMeshAsset->Fail(RADIENT_STATUS_INVALID_OPERATION);
604+
return ASYNC_TASK_STATUS_COMPLETE;
605+
}
618606

619-
pMeshAsset->QueryInterface(IID_RadientMeshAsset, ppMesh);
620-
return RADIENT_STATUS_OK;
621-
}
607+
MeshPayloadImpl* const pMeshPayloadRaw = pMeshPayload.RawPtr();
608+
pMeshAsset->Resolve(std::move(pMeshPayload));
622609

623-
pMeshPayload = CreateMeshPayloadFromCI();
624-
if (!pMeshPayload)
625-
{
626-
return RADIENT_FAILED(SourceStatus) ? SourceStatus : RADIENT_STATUS_INVALID_OPERATION;
627-
}
610+
if (!PayloadCreated)
611+
return ASYNC_TASK_STATUS_COMPLETE;
628612

629-
RefCntAutoPtr<MeshAssetImpl> pMeshAsset = CreateMeshAsset(m_NextAssetID, std::move(pMeshPayload));
630-
if (!pMeshAsset)
631-
return RADIENT_STATUS_INVALID_OPERATION;
613+
if (CanUploadMesh)
614+
LoadMeshFromSource(*pMeshPayloadRaw, std::move(pMeshSource), pDevice, pResourceManager, pUploadManager);
632615

633-
pMeshAsset->QueryInterface(IID_RadientMeshAsset, ppMesh);
616+
return ASYNC_TASK_STATUS_COMPLETE;
617+
});
634618

635-
return RADIENT_STATUS_OK;
619+
return pMeshAsset->GetResolveStatus();
636620
}
637621

638622
RADIENT_STATUS RadientMeshAssetManager::CreateMeshFromGLTFMesh(IRadientSceneAsset* pModel,
@@ -712,6 +696,10 @@ RADIENT_STATUS RadientMeshAssetManager::GetLoadStatus(IRadientAsset* pMeshAsset)
712696
if (!pMesh)
713697
return RADIENT_STATUS_INVALID_ARGUMENT;
714698

699+
const RADIENT_STATUS ResolveStatus = pMesh->GetResolveStatus();
700+
if (ResolveStatus != RADIENT_STATUS_OK)
701+
return ResolveStatus;
702+
715703
const MeshAssetStorage& Storage = pMesh->GetStorage();
716704
if (const MeshStorage* pMeshStorage = std::get_if<MeshStorage>(&Storage))
717705
return pMeshStorage->LoadStatus.load(std::memory_order_acquire);

Radient/src/Assets/RadientMeshSource.cpp

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "GraphicsAccessories.hpp"
3232

3333
#include <algorithm>
34+
#include <cstdint>
3435
#include <cstring>
3536
#include <limits>
3637
#include <utility>
@@ -69,6 +70,25 @@ bool IsActiveVertexBuffer(Uint32 ActiveVertexBufferMask, Uint32 BufferIndex)
6970
(ActiveVertexBufferMask & (Uint32{1} << BufferIndex)) != 0;
7071
}
7172

73+
void HashMaterial(std::size_t& Hash, IRadientMaterialAsset* pMaterial)
74+
{
75+
HashCombine(Hash, pMaterial != nullptr);
76+
if (pMaterial == nullptr)
77+
return;
78+
79+
const RadientAssetReference& MaterialRef = pMaterial->GetReference();
80+
if (MaterialRef.URI != nullptr && MaterialRef.URI[0] != '\0')
81+
{
82+
HashCombine(Hash,
83+
CStringHash<Char>{}(MaterialRef.URI),
84+
MaterialRef.Version);
85+
}
86+
else
87+
{
88+
HashCombine(Hash, reinterpret_cast<uintptr_t>(pMaterial));
89+
}
90+
}
91+
7292
bool ValidateRadientMeshCI(const RadientMeshCreateInfo& MeshCI)
7393
{
7494
if (MeshCI.VertexCount == 0 ||
@@ -122,6 +142,7 @@ RadientMeshSource::RadientMeshSource(const RadientMeshCreateInfo& MeshCI)
122142

123143
m_VertexCount = MeshCI.VertexCount;
124144
m_IndexCount = MeshCI.IndexCount;
145+
m_IndexType = MeshCI.IndexType;
125146

126147
auto AddAttribute =
127148
[this](const char* Name, VALUE_TYPE Type, Uint8 NumComponents, bool IsNormalized, const auto* pSrcData) //
@@ -158,6 +179,67 @@ RadientMeshSource::RadientMeshSource(const RadientMeshCreateInfo& MeshCI)
158179
m_PrimitiveMaterials.emplace_back(MeshCI.pPrimitives[PrimitiveIndex].pMaterial);
159180
}
160181

182+
std::string RadientMeshSource::MakeCacheKey() const
183+
{
184+
if (RADIENT_FAILED(m_Status))
185+
return {};
186+
187+
std::size_t Hash = 0;
188+
HashCombine(Hash,
189+
Uint32{1}, // Raw mesh cache key version.
190+
m_VertexCount,
191+
m_IndexCount,
192+
m_IndexType);
193+
194+
auto HashSourceAttribute =
195+
[this, &Hash](const char* Name, Uint32 AttributeId) //
196+
{
197+
HashCombine(Hash, AttributeId);
198+
199+
const auto SrcAttribIt = m_SrcAttributes.find(Name);
200+
const bool HasAttrib = SrcAttribIt != m_SrcAttributes.end();
201+
HashCombine(Hash, HasAttrib);
202+
if (!HasAttrib)
203+
return;
204+
205+
const SrcAttributeData& SrcAttrib = SrcAttribIt->second;
206+
HashCombine(Hash,
207+
SrcAttrib.Type,
208+
SrcAttrib.NumComponents,
209+
SrcAttrib.IsNormalized,
210+
SrcAttrib.ElementSize,
211+
SrcAttrib.Bytes.size(),
212+
ComputeHashRaw(SrcAttrib.Bytes.data(), SrcAttrib.Bytes.size()));
213+
};
214+
215+
HashSourceAttribute(GLTF::PositionAttributeName, 0);
216+
HashSourceAttribute(GLTF::NormalAttributeName, 1);
217+
HashSourceAttribute(GLTF::TangentAttributeName, 2);
218+
HashSourceAttribute(GLTF::Texcoord0AttributeName, 3);
219+
HashSourceAttribute(GLTF::VertexColorAttributeName, 4);
220+
HashSourceAttribute(GLTF::JointsAttributeName, 5);
221+
HashSourceAttribute(GLTF::WeightsAttributeName, 6);
222+
223+
HashCombine(Hash, m_Indices16.size());
224+
if (!m_Indices16.empty())
225+
HashCombine(Hash, ComputeHashRaw(m_Indices16.data(), m_Indices16.size() * sizeof(m_Indices16[0])));
226+
227+
HashCombine(Hash, m_Indices32.size());
228+
if (!m_Indices32.empty())
229+
HashCombine(Hash, ComputeHashRaw(m_Indices32.data(), m_Indices32.size() * sizeof(m_Indices32[0])));
230+
231+
HashCombine(Hash, m_Primitives.size());
232+
for (const RadientMeshPrimitiveCreateInfo& Primitive : m_Primitives)
233+
{
234+
HashCombine(Hash,
235+
Primitive.FirstIndex,
236+
Primitive.IndexCount);
237+
HashMaterial(Hash, Primitive.pMaterial);
238+
}
239+
240+
return std::string{"raw-mesh:"} + std::to_string(Hash);
241+
}
242+
161243
RADIENT_STATUS RadientMeshSource::SetVertexAttributes(const GLTF::VertexAttributeDesc* pDstAttributes,
162244
Uint32 NumDstAttributes)
163245
{

Tests/RadientTest/src/RadientMeshPrimitivesTest.cpp

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "RadientTestAssetHelpers.hpp"
3232

3333
#include "ObjectBase.hpp"
34+
#include "ThreadPool.hpp"
3435

3536
#include <array>
3637
#include <vector>
@@ -43,7 +44,21 @@ namespace
4344

4445
RefCntAutoPtr<RadientAssetManagerImpl> CreateAssetManager()
4546
{
46-
return RadientAssetManagerImpl::Create({});
47+
RefCntAutoPtr<IThreadPool> pThreadPool = CreateThreadPool(ThreadPoolCreateInfo{0});
48+
RadientAssetManagerImpl::CreateInfo CreateInfo;
49+
CreateInfo.pThreadPool = pThreadPool;
50+
return RadientAssetManagerImpl::Create(CreateInfo);
51+
}
52+
53+
void ExpectCreateMeshAccepted(RADIENT_STATUS Status)
54+
{
55+
EXPECT_TRUE(Status == RADIENT_STATUS_OK || Status == RADIENT_STATUS_PENDING);
56+
}
57+
58+
void ExpectMeshLoadFinished(IRadientAssetManager* pAssetManager, IRadientMeshAsset* pMesh)
59+
{
60+
const RADIENT_STATUS Status = pAssetManager->WaitForAssetLoad(pMesh);
61+
EXPECT_TRUE(Status == RADIENT_STATUS_OK || Status == RADIENT_STATUS_INVALID_OPERATION);
4762
}
4863

4964
void ExpectValidMeshAsset(IRadientMeshAsset* pMesh)
@@ -143,8 +158,9 @@ TEST(RadientMeshPrimitivesTest, CreateCubeMesh)
143158
CubeCI.Subdivisions = 2;
144159

145160
RefCntAutoPtr<IRadientMeshAsset> pMesh;
146-
EXPECT_EQ(CreateRadientCubeMesh(pAssetManager, CubeCI, &pMesh), RADIENT_STATUS_OK);
161+
ExpectCreateMeshAccepted(CreateRadientCubeMesh(pAssetManager, CubeCI, &pMesh));
147162
ExpectValidMeshAsset(pMesh);
163+
ExpectMeshLoadFinished(pAssetManager, pMesh);
148164
}
149165

150166
TEST(RadientMeshPrimitivesTest, CreateCubeMeshWithFaceColors)
@@ -192,8 +208,9 @@ TEST(RadientMeshPrimitivesTest, CreateSphereMesh)
192208
SphereCI.Subdivisions = 4;
193209

194210
RefCntAutoPtr<IRadientMeshAsset> pMesh;
195-
EXPECT_EQ(CreateRadientSphereMesh(pAssetManager, SphereCI, &pMesh), RADIENT_STATUS_OK);
211+
ExpectCreateMeshAccepted(CreateRadientSphereMesh(pAssetManager, SphereCI, &pMesh));
196212
ExpectValidMeshAsset(pMesh);
213+
ExpectMeshLoadFinished(pAssetManager, pMesh);
197214
}
198215

199216
TEST(RadientMeshPrimitivesTest, RejectInvalidArguments)

0 commit comments

Comments
 (0)