diff --git a/Source/Engine/Content/Asset.h b/Source/Engine/Content/Asset.h
index c838eddf8..32f2a5e31 100644
--- a/Source/Engine/Content/Asset.h
+++ b/Source/Engine/Content/Asset.h
@@ -127,7 +127,7 @@ public:
///
/// Gets the path to the asset storage file. In Editor, it reflects the actual file, in cooked Game, it fakes the Editor path to be informative for developers.
///
- API_PROPERTY() virtual const String& GetPath() const = 0;
+ API_PROPERTY() virtual StringView GetPath() const = 0;
///
/// Gets the asset type name.
diff --git a/Source/Engine/Content/Assets/Model.cpp b/Source/Engine/Content/Assets/Model.cpp
index df95e58b7..47335ae1f 100644
--- a/Source/Engine/Content/Assets/Model.cpp
+++ b/Source/Engine/Content/Assets/Model.cpp
@@ -619,7 +619,7 @@ Asset::LoadResult Model::load()
{
String name;
#if !BUILD_RELEASE
- name = GetPath() + TEXT(".SDF");
+ name = String(GetPath()) + TEXT(".SDF");
#endif
SDF.Texture = GPUDevice::Instance->CreateTexture(name);
}
diff --git a/Source/Engine/Content/BinaryAsset.cpp b/Source/Engine/Content/BinaryAsset.cpp
index 9e7e51113..cb7b951f6 100644
--- a/Source/Engine/Content/BinaryAsset.cpp
+++ b/Source/Engine/Content/BinaryAsset.cpp
@@ -464,10 +464,10 @@ void BinaryAsset::OnDeleteObject()
#endif
-const String& BinaryAsset::GetPath() const
+StringView BinaryAsset::GetPath() const
{
#if USE_EDITOR
- return Storage ? Storage->GetPath() : String::Empty;
+ return Storage ? Storage->GetPath() : StringView::Empty;
#else
// In build all assets are packed into packages so use ID for original path lookup
return Content::GetRegistry()->GetEditorAssetPath(_id);
diff --git a/Source/Engine/Content/BinaryAsset.h b/Source/Engine/Content/BinaryAsset.h
index cd9f078ca..39c34588e 100644
--- a/Source/Engine/Content/BinaryAsset.h
+++ b/Source/Engine/Content/BinaryAsset.h
@@ -292,7 +292,7 @@ public:
#if USE_EDITOR
void OnDeleteObject() override;
#endif
- const String& GetPath() const final override;
+ StringView GetPath() const final override;
uint64 GetMemoryUsage() const override;
protected:
diff --git a/Source/Engine/Content/Cache/AssetsCache.cpp b/Source/Engine/Content/Cache/AssetsCache.cpp
index bd1474afa..2a2d252b6 100644
--- a/Source/Engine/Content/Cache/AssetsCache.cpp
+++ b/Source/Engine/Content/Cache/AssetsCache.cpp
@@ -10,12 +10,24 @@
#include "Engine/Serialization/FileWriteStream.h"
#include "Engine/Serialization/FileReadStream.h"
#include "Engine/Content/Content.h"
-#include "Engine/Content/Storage/ContentStorageManager.h"
-#include "Engine/Content/Storage/JsonStorageProxy.h"
#include "Engine/Profiler/ProfilerCPU.h"
-#include "Engine/Threading/Threading.h"
#include "Engine/Engine/Globals.h"
#include "FlaxEngine.Gen.h"
+#if ASSETS_CACHE_EDITABLE
+#include "Engine/Content/Storage/ContentStorageManager.h"
+#include "Engine/Content/Storage/JsonStorageProxy.h"
+#include "Engine/Threading/Threading.h"
+#define ASSETS_CACHE_LOCK() ScopeLock lock(_locker)
+#else
+#define ASSETS_CACHE_LOCK()
+#endif
+
+int32 AssetsCache::Size() const
+{
+ ASSETS_CACHE_LOCK();
+ const int32 result = _registry.Count();
+ return result;
+}
void AssetsCache::Init()
{
@@ -72,7 +84,7 @@ void AssetsCache::Init()
return;
}
- ScopeLock lock(_locker);
+ ASSETS_CACHE_LOCK();
_isDirty = false;
// Load elements count
@@ -127,6 +139,16 @@ void AssetsCache::Init()
_pathsMapping.Add(mappedPath, id);
}
+#if !USE_EDITOR && !BUILD_RELEASE
+ // Build inverse path mapping in development builds for faster GetEditorAssetPath (eg. used by PROFILE_CPU_ASSET)
+ _pathsMappingInv.Clear();
+ _pathsMappingInv.EnsureCapacity(count);
+ for (auto& mapping : _pathsMapping)
+ {
+ _pathsMappingInv.Add(mapping.Value, StringView(mapping.Key));
+ }
+#endif
+
// Check errors
const bool hasError = stream->HasError();
deleteStream.Delete();
@@ -154,7 +176,7 @@ bool AssetsCache::Save()
if (!_isDirty && FileSystem::FileExists(_path))
return false;
- ScopeLock lock(_locker);
+ ASSETS_CACHE_LOCK();
if (Save(_path, _registry, _pathsMapping))
return true;
@@ -223,12 +245,16 @@ bool AssetsCache::Save(const StringView& path, const Registry& entries, const Pa
return false;
}
-const String& AssetsCache::GetEditorAssetPath(const Guid& id) const
+StringView AssetsCache::GetEditorAssetPath(const Guid& id) const
{
- ScopeLock lock(_locker);
+ ASSETS_CACHE_LOCK();
#if USE_EDITOR
auto e = _registry.TryGet(id);
return e ? e->Info.Path : String::Empty;
+#elif !BUILD_RELEASE
+ StringView result;
+ _pathsMappingInv.TryGet(id, result);
+ return result;
#else
for (auto& e : _pathsMapping)
{
@@ -242,10 +268,8 @@ const String& AssetsCache::GetEditorAssetPath(const Guid& id) const
bool AssetsCache::FindAsset(const StringView& path, AssetInfo& info)
{
PROFILE_CPU();
-
bool result = false;
-
- ScopeLock lock(_locker);
+ ASSETS_CACHE_LOCK();
// Check if asset has direct mapping to id (used for some cooked assets)
Guid id;
@@ -294,7 +318,7 @@ bool AssetsCache::FindAsset(const Guid& id, AssetInfo& info)
{
PROFILE_CPU();
bool result = false;
- ScopeLock lock(_locker);
+ ASSETS_CACHE_LOCK();
auto e = _registry.TryGet(id);
if (e != nullptr)
{
@@ -316,14 +340,14 @@ bool AssetsCache::FindAsset(const Guid& id, AssetInfo& info)
void AssetsCache::GetAll(Array& result) const
{
PROFILE_CPU();
- ScopeLock lock(_locker);
+ ASSETS_CACHE_LOCK();
_registry.GetKeys(result);
}
void AssetsCache::GetAllByTypeName(const StringView& typeName, Array& result) const
{
PROFILE_CPU();
- ScopeLock lock(_locker);
+ ASSETS_CACHE_LOCK();
for (auto i = _registry.Begin(); i.IsNotEnd(); ++i)
{
if (i->Value.Info.TypeName == typeName)
@@ -331,6 +355,8 @@ void AssetsCache::GetAllByTypeName(const StringView& typeName, Array& resu
}
}
+#if ASSETS_CACHE_EDITABLE
+
void AssetsCache::RegisterAssets(FlaxStorage* storage)
{
PROFILE_CPU();
@@ -342,7 +368,7 @@ void AssetsCache::RegisterAssets(FlaxStorage* storage)
storage->GetEntries(entries);
ASSERT(entries.HasItems());
- ScopeLock lock(_locker);
+ ASSETS_CACHE_LOCK();
auto storagePath = storage->GetPath();
// Remove all old entries from that location
@@ -440,7 +466,7 @@ void AssetsCache::RegisterAssets(const FlaxStorageReference& storage)
void AssetsCache::RegisterAsset(const Guid& id, const String& typeName, const StringView& path)
{
PROFILE_CPU();
- ScopeLock lock(_locker);
+ ASSETS_CACHE_LOCK();
// Check if asset has been already added to the registry
bool isMissing = true;
@@ -492,8 +518,7 @@ void AssetsCache::RegisterAsset(const Guid& id, const String& typeName, const St
bool AssetsCache::DeleteAsset(const StringView& path, AssetInfo* info)
{
bool result = false;
- _locker.Lock();
-
+ ASSETS_CACHE_LOCK();
for (auto i = _registry.Begin(); i.IsNotEnd(); ++i)
{
if (i->Value.Info.Path == path)
@@ -506,16 +531,13 @@ bool AssetsCache::DeleteAsset(const StringView& path, AssetInfo* info)
break;
}
}
-
- _locker.Unlock();
return result;
}
bool AssetsCache::DeleteAsset(const Guid& id, AssetInfo* info)
{
bool result = false;
- _locker.Lock();
-
+ ASSETS_CACHE_LOCK();
const auto e = _registry.TryGet(id);
if (e != nullptr)
{
@@ -525,16 +547,13 @@ bool AssetsCache::DeleteAsset(const Guid& id, AssetInfo* info)
_isDirty = true;
result = true;
}
-
- _locker.Unlock();
return result;
}
bool AssetsCache::RenameAsset(const StringView& oldPath, const StringView& newPath)
{
bool result = false;
- _locker.Lock();
-
+ ASSETS_CACHE_LOCK();
for (auto i = _registry.Begin(); i.IsNotEnd(); ++i)
{
if (i->Value.Info.Path == oldPath)
@@ -545,11 +564,11 @@ bool AssetsCache::RenameAsset(const StringView& oldPath, const StringView& newPa
break;
}
}
-
- _locker.Unlock();
return result;
}
+#endif
+
bool AssetsCache::IsEntryValid(Entry& e)
{
#if ENABLE_ASSETS_DISCOVERY
diff --git a/Source/Engine/Content/Cache/AssetsCache.h b/Source/Engine/Content/Cache/AssetsCache.h
index 217d56ebd..42f05a2aa 100644
--- a/Source/Engine/Content/Cache/AssetsCache.h
+++ b/Source/Engine/Content/Cache/AssetsCache.h
@@ -16,6 +16,9 @@ struct AssetHeader;
struct FlaxStorageReference;
class FlaxStorage;
+// In cooked game all assets are there and all access to registry is read-only so can be multithreaded
+#define ASSETS_CACHE_EDITABLE (USE_EDITOR)
+
///
/// Assets cache flags.
///
@@ -75,22 +78,21 @@ public:
private:
bool _isDirty = false;
+#if ASSETS_CACHE_EDITABLE
CriticalSection _locker;
+#endif
Registry _registry;
PathsMapping _pathsMapping;
+#if !USE_EDITOR && !BUILD_RELEASE
+ Dictionary _pathsMappingInv;
+#endif
String _path;
public:
///
/// Gets amount of registered assets.
///
- int32 Size() const
- {
- _locker.Lock();
- const int32 result = _registry.Count();
- _locker.Unlock();
- return result;
- }
+ int32 Size() const;
public:
///
@@ -116,11 +118,11 @@ public:
public:
///
- /// Finds the asset path by id. In editor it returns the actual asset path, at runtime it returns the mapped asset path.
+ /// Finds the asset path by id. In editor, it returns the actual asset path, at runtime it returns the mapped asset path.
///
/// The asset id.
/// The asset path, or empty if failed to find.
- const String& GetEditorAssetPath(const Guid& id) const;
+ StringView GetEditorAssetPath(const Guid& id) const;
///
/// Finds the asset info by path.
@@ -173,6 +175,7 @@ public:
/// The result array.
void GetAllByTypeName(const StringView& typeName, Array& result) const;
+#if ASSETS_CACHE_EDITABLE
///
/// Register assets in the cache
///
@@ -223,6 +226,7 @@ public:
/// New path
/// True if has been deleted, otherwise false
bool RenameAsset(const StringView& oldPath, const StringView& newPath);
+#endif
///
/// Determines whether cached asset entry is valid.
diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp
index 1a397a2e7..b82a329a8 100644
--- a/Source/Engine/Content/Content.cpp
+++ b/Source/Engine/Content/Content.cpp
@@ -521,7 +521,7 @@ bool Content::GetAssetInfo(const StringView& path, AssetInfo& info)
#endif
}
-String Content::GetEditorAssetPath(const Guid& id)
+StringView Content::GetEditorAssetPath(const Guid& id)
{
return Cache.GetEditorAssetPath(id);
}
@@ -749,6 +749,7 @@ void Content::DeleteAsset(const StringView& path)
return;
}
+#if USE_EDITOR
ScopeLock locker(AssetsLocker);
// Remove from registry
@@ -765,6 +766,7 @@ void Content::DeleteAsset(const StringView& path)
// Delete file
deleteFileSafety(path, info.ID);
+#endif
}
void Content::deleteFileSafety(const StringView& path, const Guid& id)
diff --git a/Source/Engine/Content/Content.h b/Source/Engine/Content/Content.h
index 15ace944a..f6dcf59f0 100644
--- a/Source/Engine/Content/Content.h
+++ b/Source/Engine/Content/Content.h
@@ -75,7 +75,7 @@ public:
///
/// The asset id.
/// The asset path, or empty if failed to find.
- API_FUNCTION() static String GetEditorAssetPath(const Guid& id);
+ API_FUNCTION() static StringView GetEditorAssetPath(const Guid& id);
///
/// Finds all the asset IDs. Uses asset registry.
diff --git a/Source/Engine/Content/JsonAsset.cpp b/Source/Engine/Content/JsonAsset.cpp
index 1aa434c41..4b5ee7b63 100644
--- a/Source/Engine/Content/JsonAsset.cpp
+++ b/Source/Engine/Content/JsonAsset.cpp
@@ -91,7 +91,7 @@ void JsonAssetBase::OnGetData(rapidjson_flax::StringBuffer& buffer) const
Data->Accept(writerObj.GetWriter());
}
-const String& JsonAssetBase::GetPath() const
+StringView JsonAssetBase::GetPath() const
{
#if USE_EDITOR
return _path;
diff --git a/Source/Engine/Content/JsonAsset.h b/Source/Engine/Content/JsonAsset.h
index 11c13ce80..e27c8dafb 100644
--- a/Source/Engine/Content/JsonAsset.h
+++ b/Source/Engine/Content/JsonAsset.h
@@ -88,7 +88,7 @@ protected:
public:
// [Asset]
- const String& GetPath() const override;
+ StringView GetPath() const override;
uint64 GetMemoryUsage() const override;
#if USE_EDITOR
void GetReferences(Array& assets, Array& files) const override;
diff --git a/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h b/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h
index 5c9ab5604..45f8f53f7 100644
--- a/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h
+++ b/Source/Engine/Content/Loading/Tasks/LoadAssetDataTask.h
@@ -36,7 +36,7 @@ public:
// [ContentLoadTask]
String ToString() const override
{
- return String::Format(TEXT("Load Asset Data Task ({}, {}, {})"), (int32)GetState(), _chunks, _asset ? _asset->GetPath() : String::Empty);
+ return String::Format(TEXT("Load Asset Data Task ({}, {}, {})"), (int32)GetState(), _chunks, _asset ? _asset->GetPath() : StringView::Empty);
}
bool HasReference(Object* obj) const override
{
diff --git a/Source/Engine/Level/Level.cpp b/Source/Engine/Level/Level.cpp
index 49bef81c3..ddb871846 100644
--- a/Source/Engine/Level/Level.cpp
+++ b/Source/Engine/Level/Level.cpp
@@ -134,7 +134,7 @@ public:
struct Args
{
rapidjson_flax::Value& Data;
- const String* AssetPath;
+ StringView AssetPath;
int32 EngineBuild;
float TimeBudget;
};
@@ -222,7 +222,7 @@ namespace LevelImpl
SceneResult loadScene(SceneLoader& loader, JsonAsset* sceneAsset, float* timeBudget = nullptr);
SceneResult loadScene(SceneLoader& loader, const BytesContainer& sceneData, Scene** outScene = nullptr, float* timeBudget = nullptr);
SceneResult loadScene(SceneLoader& loader, rapidjson_flax::Document& document, Scene** outScene = nullptr, float* timeBudget = nullptr);
- SceneResult loadScene(SceneLoader& loader, rapidjson_flax::Value& data, int32 engineBuild, Scene** outScene = nullptr, const String* assetPath = nullptr, float* timeBudget = nullptr);
+ SceneResult loadScene(SceneLoader& loader, rapidjson_flax::Value& data, int32 engineBuild, Scene** outScene = nullptr, StringView assetPath = StringView(), float* timeBudget = nullptr);
bool unloadScene(Scene* scene);
bool unloadScenes();
bool saveScene(Scene* scene);
@@ -959,7 +959,7 @@ SceneResult LevelImpl::loadScene(SceneLoader& loader, JsonAsset* sceneAsset, flo
return SceneResult::Failed;
}
- return loadScene(loader, *sceneAsset->Data, sceneAsset->DataEngineBuild, nullptr, &sceneAsset->GetPath(), timeBudget);
+ return loadScene(loader, *sceneAsset->Data, sceneAsset->DataEngineBuild, nullptr, sceneAsset->GetPath(), timeBudget);
}
SceneResult LevelImpl::loadScene(SceneLoader& loader, const BytesContainer& sceneData, Scene** outScene, float* timeBudget)
@@ -999,7 +999,7 @@ SceneResult LevelImpl::loadScene(SceneLoader& loader, rapidjson_flax::Document&
return loadScene(loader, data->value, saveEngineBuild, outScene, nullptr, timeBudget);
}
-SceneResult LevelImpl::loadScene(SceneLoader& loader, rapidjson_flax::Value& data, int32 engineBuild, Scene** outScene, const String* assetPath, float* timeBudget)
+SceneResult LevelImpl::loadScene(SceneLoader& loader, rapidjson_flax::Value& data, int32 engineBuild, Scene** outScene, StringView assetPath, float* timeBudget)
{
PROFILE_CPU_NAMED("Level.LoadScene");
PROFILE_MEM(Level);
@@ -1401,12 +1401,12 @@ SceneResult SceneLoader::OnEnd(Args& args)
LOG(Error, "Failed to resave asset '{}'", prefab->GetPath());
}
}
- if (ContentDeprecated::Clear() && args.AssetPath)
+ if (ContentDeprecated::Clear() && args.AssetPath != StringView())
{
- LOG(Info, "Resaving asset '{}' that uses deprecated data format", *args.AssetPath);
- if (saveScene(Scene, *args.AssetPath))
+ LOG(Info, "Resaving asset '{}' that uses deprecated data format", args.AssetPath);
+ if (saveScene(Scene, args.AssetPath))
{
- LOG(Error, "Failed to resave asset '{}'", *args.AssetPath);
+ LOG(Error, "Failed to resave asset '{}'", args.AssetPath);
}
}
#endif