Skip to content

Commit

Permalink
Hydrogent: improved texture loader memory usage throttling
Browse files Browse the repository at this point in the history
  • Loading branch information
TheMostDiligent committed Nov 7, 2024
1 parent 4a1ddf1 commit 5373177
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 48 deletions.
26 changes: 15 additions & 11 deletions Hydrogent/include/HnTextureRegistry.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
#include "../../../DiligentCore/Common/interface/ObjectsRegistry.hpp"
#include "../../../DiligentTools/TextureLoader/interface/TextureLoader.h"

#include "HnTextureUtils.hpp"

namespace Diligent
{

Expand All @@ -56,7 +58,6 @@ namespace USD
{

struct HnTextureIdentifier;
struct HnLoadTextureResult;

class HnTextureRegistry final : public std::enable_shared_from_this<HnTextureRegistry>
{
Expand Down Expand Up @@ -140,13 +141,15 @@ class HnTextureRegistry final : public std::enable_shared_from_this<HnTextureReg
const pxr::HdSamplerParameters& SamplerParams,
bool IsAsync);

using CreateTextureLoaderCallbackType = std::function<HnLoadTextureResult(Int64)>;

// Allocates texture handle for the specified texture file path.
// If the texture is not loaded, calls CreateLoader() to create the texture loader.
TextureHandleSharedPtr Allocate(const pxr::TfToken& FilePath,
const TextureComponentMapping& Swizzle,
const pxr::HdSamplerParameters& SamplerParams,
bool IsAsync,
std::function<HnLoadTextureResult()> CreateLoader);
TextureHandleSharedPtr Allocate(const pxr::TfToken& FilePath,
const TextureComponentMapping& Swizzle,
const pxr::HdSamplerParameters& SamplerParams,
bool IsAsync,
CreateTextureLoaderCallbackType CreateLoader);

TextureHandleSharedPtr Get(const pxr::TfToken& Path)
{
Expand Down Expand Up @@ -188,11 +191,12 @@ class HnTextureRegistry final : public std::enable_shared_from_this<HnTextureReg
UsageStats GetUsageStats() const;

private:
void LoadTexture(const pxr::TfToken Key,
const pxr::TfToken& FilePath,
const pxr::HdSamplerParameters& SamplerParams,
std::function<HnLoadTextureResult()> CreateLoader,
std::shared_ptr<TextureHandle> TexHandle);
HN_LOAD_TEXTURE_STATUS LoadTexture(const pxr::TfToken Key,
const pxr::TfToken& FilePath,
const pxr::HdSamplerParameters& SamplerParams,
Int64 MemoryBudget,
CreateTextureLoaderCallbackType CreateLoader,
std::shared_ptr<TextureHandle> TexHandle);

void OnDestroyHandle(const TextureHandle& Handle);

Expand Down
2 changes: 1 addition & 1 deletion Hydrogent/include/HnTextureUtils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ struct HnLoadTextureResult

HnLoadTextureResult LoadTextureFromSdfPath(const char* SdfPath,
const TextureLoadInfo& LoadInfo,
Uint64 MemoryBudget = 0);
Int64 MemoryBudget = 0);

Int64 GetTextureLoaderMemoryUsage();

Expand Down
2 changes: 1 addition & 1 deletion Hydrogent/src/HnMaterial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ HnTextureRegistry::TextureHandleSharedPtr HnMaterial::GetDefaultTexture(HnTextur
/*IsAsync = */ false, // Allocate default textures synchronously to make
// them immediately available after the first Sync
// for the fallback material.
[&]() {
[&](Int64 /*MemoryBudget*/) {
RefCntAutoPtr<Image> pImage = CreateDefaultImage(DefaultTexName);

TextureLoadInfo LoadInfo{Name.GetText()};
Expand Down
58 changes: 30 additions & 28 deletions Hydrogent/src/HnTextureRegistry.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -253,24 +253,28 @@ void HnTextureRegistry::Commit(IDeviceContext* pContext)
}
}

void HnTextureRegistry::LoadTexture(const pxr::TfToken Key,
const pxr::TfToken& FilePath,
const pxr::HdSamplerParameters& SamplerParams,
std::function<HnLoadTextureResult()> CreateLoader,
std::shared_ptr<TextureHandle> TexHandle)
HN_LOAD_TEXTURE_STATUS HnTextureRegistry::LoadTexture(const pxr::TfToken Key,
const pxr::TfToken& FilePath,
const pxr::HdSamplerParameters& SamplerParams,
Int64 MemoryBudget,
CreateTextureLoaderCallbackType CreateLoader,
std::shared_ptr<TextureHandle> TexHandle)
{
HnLoadTextureResult LoadResult = CreateLoader();
HnLoadTextureResult LoadResult = CreateLoader(MemoryBudget);
if (!LoadResult)
{
LOG_ERROR_MESSAGE("Failed to create texture loader for texture ", FilePath);
if (LoadResult.LoadStatus != HN_LOAD_TEXTURE_STATUS_BUDGET_EXCEEDED)
{
LOG_ERROR_MESSAGE("Failed to create texture loader for texture ", FilePath);

// Since we don't add the handle to the pending list, we need to decrement the counter here
m_NumTexturesLoading.fetch_add(-1);
// Since we don't add the handle to the pending list, we need to decrement the counter here
m_NumTexturesLoading.fetch_add(-1);

// Set the m_IsInitialized flag or the handle will be stuck in the loading state
TexHandle->m_IsInitialized.store(true);
// Set the m_IsInitialized flag or the handle will be stuck in the loading state
TexHandle->m_IsInitialized.store(true);
}

return;
return LoadResult.LoadStatus;
}
RefCntAutoPtr<ITextureLoader> pLoader = std::move(LoadResult.pLoader);

Expand Down Expand Up @@ -319,13 +323,15 @@ void HnTextureRegistry::LoadTexture(const pxr::TfToken Key,
std::lock_guard<std::mutex> Lock{m_PendingTexturesMtx};
m_PendingTextures.emplace(Key, PendingTextureInfo{std::move(pLoader), SamplerParams, std::move(TexHandle)});
}

return LoadResult.LoadStatus;
}

HnTextureRegistry::TextureHandleSharedPtr HnTextureRegistry::Allocate(const pxr::TfToken& FilePath,
const TextureComponentMapping& Swizzle,
const pxr::HdSamplerParameters& SamplerParams,
bool IsAsync,
std::function<HnLoadTextureResult()> CreateLoader)
HnTextureRegistry::TextureHandleSharedPtr HnTextureRegistry::Allocate(const pxr::TfToken& FilePath,
const TextureComponentMapping& Swizzle,
const pxr::HdSamplerParameters& SamplerParams,
bool IsAsync,
CreateTextureLoaderCallbackType CreateLoader)
{
const pxr::TfToken Key{FilePath.GetString() + '.' + GetTextureComponentMappingString(Swizzle)};
return m_Cache.Get(
Expand Down Expand Up @@ -357,14 +363,10 @@ HnTextureRegistry::TextureHandleSharedPtr HnTextureRegistry::Allocate(const pxr:
RefCntAutoPtr<IAsyncTask> pTask = EnqueueAsyncWork(
m_pThreadPool,
[=](Uint32 ThreadId) {
if (m_LoadBudget != 0 && GetTextureLoaderMemoryUsage() > m_LoadBudget)
{
// Reschedule the task
return ASYNC_TASK_STATUS_NOT_STARTED;
}

LoadTexture(Key, FilePath, SamplerParams, CreateLoader, TexHandle);
return ASYNC_TASK_STATUS_COMPLETE;
HN_LOAD_TEXTURE_STATUS LoadStatus = LoadTexture(Key, FilePath, SamplerParams, m_LoadBudget, CreateLoader, TexHandle);
return LoadStatus != HN_LOAD_TEXTURE_STATUS_BUDGET_EXCEEDED ?
ASYNC_TASK_STATUS_COMPLETE :
ASYNC_TASK_STATUS_NOT_STARTED; // Reschedule the task
},
Priority);

Expand All @@ -375,7 +377,7 @@ HnTextureRegistry::TextureHandleSharedPtr HnTextureRegistry::Allocate(const pxr:
}
else
{
LoadTexture(Key, FilePath, SamplerParams, CreateLoader, TexHandle);
LoadTexture(Key, FilePath, SamplerParams, 0, CreateLoader, TexHandle);
}

return TexHandle;
Expand All @@ -394,7 +396,7 @@ HnTextureRegistry::TextureHandleSharedPtr HnTextureRegistry::Allocate(const HnTe
}

return Allocate(TexId.FilePath, TexId.SubtextureId.Swizzle, SamplerParams, IsAsync,
[TexId, Format, CompressMode = m_CompressMode]() {
[TexId, Format, CompressMode = m_CompressMode](Int64 MemoryBudget) {
TextureLoadInfo LoadInfo;
LoadInfo.Name = TexId.FilePath.GetText();
LoadInfo.Format = Format;
Expand All @@ -405,7 +407,7 @@ HnTextureRegistry::TextureHandleSharedPtr HnTextureRegistry::Allocate(const HnTe
LoadInfo.CompressMode = CompressMode;
LoadInfo.UniformImageClipDim = 32;

return LoadTextureFromSdfPath(TexId.FilePath.GetText(), LoadInfo);
return LoadTextureFromSdfPath(TexId.FilePath.GetText(), LoadInfo, MemoryBudget);
});
}

Expand Down
27 changes: 20 additions & 7 deletions Hydrogent/src/HnTextureUtils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -199,16 +199,22 @@ std::atomic<int64_t> AssetDataContainer::s_AssetDataSize{0};

HnLoadTextureResult LoadTextureFromSdfPath(const char* SdfPath,
const TextureLoadInfo& _LoadInfo,
Uint64 MemoryBudget)
Int64 MemoryBudget)
{
// Check memory budget before opening the asset
if (MemoryBudget != 0 && GetTextureLoaderMemoryUsage() > MemoryBudget)
{
// Reschedule the task
return {HN_LOAD_TEXTURE_STATUS_BUDGET_EXCEEDED};
}

pxr::ArResolvedPath ResolvedPath{SdfPath};
if (ResolvedPath.empty())
{
return {HN_LOAD_TEXTURE_STATUS_INVALID_PATH};
}

std::shared_ptr<const char> Buffer;
size_t Size = 0;
RefCntAutoPtr<IDataBlob> pAssetData;
{
const pxr::ArResolver& Resolver = pxr::ArGetResolver();
std::shared_ptr<pxr::ArAsset> Asset = Resolver.OpenAsset(ResolvedPath);
Expand All @@ -217,17 +223,24 @@ HnLoadTextureResult LoadTextureFromSdfPath(const char* SdfPath,
return {HN_LOAD_TEXTURE_STATUS_ASSET_NOT_FOUND};
}

Buffer = Asset->GetBuffer();
std::shared_ptr<const char> Buffer = Asset->GetBuffer();
if (!Buffer)
{
return {HN_LOAD_TEXTURE_STATUS_EMPTY_ASSET};
}

Size = Asset->GetSize();
// Create asset data container to account for the asset memory usage
pAssetData = AssetDataContainer::Create(std::move(Buffer), Asset->GetSize());
if (MemoryBudget != 0)
{
size_t LoaderMemoryReq = GetTextureLoaderMemoryRequirement(pAssetData->GetConstDataPtr(), pAssetData->GetSize(), _LoadInfo);
if (GetTextureLoaderMemoryUsage() + static_cast<Int64>(LoaderMemoryReq) > MemoryBudget)
{
return {HN_LOAD_TEXTURE_STATUS_BUDGET_EXCEEDED};
}
}
}

RefCntAutoPtr<IDataBlob> pAssetData = AssetDataContainer::Create(std::move(Buffer), Size);

TextureLoadInfo LoadInfo = _LoadInfo;
LoadInfo.pAllocator = &TextureMemoryAllocator::Get();

Expand Down

0 comments on commit 5373177

Please sign in to comment.