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