Fix binary asset dependencies tracking when dependent asset gets loaded later on

#3951

Adds additional cache for dependencies tracking to update them when other asset gets loaded later on.
Remove critical-section from `LoadAssetTask` that was causing some deadlocks (data access is already atomic there).
This commit is contained in:
2026-03-31 12:03:18 +02:00
parent 8b383d4dbe
commit c6204fc274
5 changed files with 67 additions and 3 deletions
+5
View File
@@ -84,6 +84,11 @@ bool BinaryAsset::Init(AssetInitData& initData)
{ {
asset->_dependantAssets.Add(this); asset->_dependantAssets.Add(this);
} }
else
{
// Dependency is not yet loaded to keep track this link to act when it's loaded
Content::onAssetDepend(this, e.First);
}
} }
#endif #endif
+1
View File
@@ -22,6 +22,7 @@
API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API BinaryAsset : public Asset API_CLASS(Abstract, NoSpawn) class FLAXENGINE_API BinaryAsset : public Asset
{ {
DECLARE_ASSET_HEADER(BinaryAsset); DECLARE_ASSET_HEADER(BinaryAsset);
friend Content;
protected: protected:
AssetHeader _header; AssetHeader _header;
FlaxStorageReference _storageRef; // Allow asset to have missing storage reference but only before asset is loaded or if it's virtual FlaxStorageReference _storageRef; // Allow asset to have missing storage reference but only before asset is loaded or if it's virtual
+55 -1
View File
@@ -94,6 +94,9 @@ namespace
DateTime LastWorkspaceDiscovery; DateTime LastWorkspaceDiscovery;
CriticalSection WorkspaceDiscoveryLocker; CriticalSection WorkspaceDiscoveryLocker;
#endif #endif
#if USE_EDITOR
Dictionary<Guid, HashSet<BinaryAsset*>> PendingDependencies;
#endif
} }
#if ENABLE_ASSETS_DISCOVERY #if ENABLE_ASSETS_DISCOVERY
@@ -157,6 +160,9 @@ void ContentService::Update()
{ {
auto asset = LoadedAssetsToInvoke.Dequeue(); auto asset = LoadedAssetsToInvoke.Dequeue();
asset->onLoaded_MainThread(); asset->onLoaded_MainThread();
#if USE_EDITOR
Content::onAddDependencies(asset);
#endif
} }
} }
@@ -1027,10 +1033,17 @@ bool Content::CloneAssetFile(const StringView& dstPath, const StringView& srcPat
FileSystem::DeleteFile(tmpPath); FileSystem::DeleteFile(tmpPath);
// Reload storage // Reload storage
if (auto storage = ContentStorageManager::GetStorage(dstPath, false)) auto storage = ContentStorageManager::GetStorage(dstPath, false);
if (storage && storage->IsLoaded())
{ {
storage->Reload(); storage->Reload();
} }
else if (auto dependencies = PendingDependencies.TryGet(dstId))
{
// Destination storage is not loaded but there are other assets that depend on it so update them
for (const auto& e : *dependencies)
e.Item->OnDependencyModified(nullptr);
}
} }
} }
else else
@@ -1218,6 +1231,9 @@ void Content::tryCallOnLoaded(Asset* asset)
{ {
LoadedAssetsToInvoke.RemoveAtKeepOrder(index); LoadedAssetsToInvoke.RemoveAtKeepOrder(index);
asset->onLoaded_MainThread(); asset->onLoaded_MainThread();
#if USE_EDITOR
onAddDependencies(asset);
#endif
} }
} }
@@ -1235,6 +1251,10 @@ void Content::onAssetUnload(Asset* asset)
Assets.Remove(asset->GetID()); Assets.Remove(asset->GetID());
UnloadQueue.Remove(asset); UnloadQueue.Remove(asset);
LoadedAssetsToInvoke.Remove(asset); LoadedAssetsToInvoke.Remove(asset);
#if USE_EDITOR
for (auto& e : PendingDependencies)
e.Value.Remove(asset);
#endif
} }
void Content::onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId) void Content::onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId)
@@ -1242,8 +1262,42 @@ void Content::onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId
ScopeLock locker(AssetsLocker); ScopeLock locker(AssetsLocker);
Assets.Remove(oldId); Assets.Remove(oldId);
Assets.Add(newId, asset); Assets.Add(newId, asset);
#if USE_EDITOR
if (PendingDependencies.ContainsKey(oldId))
{
auto deps = MoveTemp(PendingDependencies[oldId]);
PendingDependencies.Remove(oldId);
PendingDependencies.Add(newId, MoveTemp(deps));
}
#endif
} }
#if USE_EDITOR
void Content::onAssetDepend(BinaryAsset* asset, const Guid& otherId)
{
ScopeLock locker(AssetsLocker);
PendingDependencies[otherId].Add(asset);
}
void Content::onAddDependencies(Asset* asset)
{
auto it = PendingDependencies.Find(asset->GetID());
if (it.IsNotEnd())
{
auto& dependencies = it->Value;
auto binaryAsset = Asset::Cast<BinaryAsset>(asset);
if (binaryAsset)
{
for (const auto& e : dependencies)
binaryAsset->_dependantAssets.Add(e.Item);
}
PendingDependencies.Remove(it);
}
}
#endif
bool Content::IsAssetTypeIdInvalid(const ScriptingTypeHandle& type, const ScriptingTypeHandle& assetType) bool Content::IsAssetTypeIdInvalid(const ScriptingTypeHandle& type, const ScriptingTypeHandle& assetType)
{ {
// Skip if no restrictions for the type // Skip if no restrictions for the type
+6
View File
@@ -389,6 +389,12 @@ private:
static void onAssetLoaded(Asset* asset); static void onAssetLoaded(Asset* asset);
static void onAssetUnload(Asset* asset); static void onAssetUnload(Asset* asset);
static void onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId); static void onAssetChangeId(Asset* asset, const Guid& oldId, const Guid& newId);
#if USE_EDITOR
friend class BinaryAsset;
friend class ContentService;
static void onAssetDepend(BinaryAsset* asset, const Guid& otherId);
static void onAddDependencies(Asset* asset);
#endif
static void deleteFileSafety(const StringView& path, const Guid& id); static void deleteFileSafety(const StringView& path, const Guid& id);
// Internal bindings // Internal bindings
@@ -80,7 +80,6 @@ private:
auto asset = Asset.Get(); auto asset = Asset.Get();
if (asset) if (asset)
{ {
asset->Locker.Lock();
Task* task = (Task*)Platform::AtomicRead(&asset->_loadingTask); Task* task = (Task*)Platform::AtomicRead(&asset->_loadingTask);
if (task) if (task)
{ {
@@ -99,7 +98,6 @@ private:
task = task->GetContinueWithTask(); task = task->GetContinueWithTask();
} while (task); } while (task);
} }
asset->Locker.Unlock();
} }
} }
}; };