diff --git a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp index 908fa88c0..a5abf2221 100644 --- a/Source/Engine/Level/Prefabs/Prefab.Apply.cpp +++ b/Source/Engine/Level/Prefabs/Prefab.Apply.cpp @@ -131,6 +131,8 @@ public: Dictionary PrefabInstanceIdToDataIndex; public: + typedef Array PrefabInstancesData; + /// /// Collects all the valid prefab instances to update on prefab data synchronization. /// @@ -138,15 +140,15 @@ public: /// The prefab asset identifier. /// The default instance (prefab internal, can be null). /// The target actor (optional actor to skip for counting, can be null). - static void CollectPrefabInstances(Array& prefabInstancesData, const Guid& prefabId, Actor* defaultInstance, Actor* targetActor); + static void CollectPrefabInstances(PrefabInstancesData& prefabInstancesData, const Guid& prefabId, Actor* defaultInstance, Actor* targetActor); /// /// Serializes all the prefab instances local changes to restore on prefab data synchronization. /// /// The prefab instances data. /// The temporary json buffer (cleared before and after usage). - /// The scene objects collection cache (cleared before and after usage). - static void SerializePrefabInstances(Array& prefabInstancesData, rapidjson_flax::StringBuffer& tmpBuffer, SceneObjectsListCacheType& sceneObjects); + /// Source prefab. + static void SerializePrefabInstances(PrefabInstancesData& prefabInstancesData, rapidjson_flax::StringBuffer& tmpBuffer, const Prefab* prefab); /// /// Synchronizes the prefab instances by applying changes from the diff data and restoring the local changes captured by SerializePrefabInstances. @@ -158,7 +160,7 @@ public: /// The hash table that maps the prefab object id to json data for the given prefab object. /// The collection of the new prefab objects ids added to prefab during changes synchronization or modifications apply. /// True if failed, otherwise false. - static bool SynchronizePrefabInstances(Array& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, const IdToDataLookupType& prefabObjectIdToDiffData, const Array& newPrefabObjectIds); + static bool SynchronizePrefabInstances(PrefabInstancesData& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, const IdToDataLookupType& prefabObjectIdToDiffData, const Array& newPrefabObjectIds); /// /// Synchronizes the prefab instances by applying changes from the diff data and restoring the local changes captured by SerializePrefabInstances. @@ -171,10 +173,10 @@ public: /// Collection with ids of the objects (actors and scripts) from the prefab before changes apply. Used to find new objects or old objects and use this information during changes sync (eg. generate ids for the new objects to prevent ids collisions). /// Collection with ids of the objects (actors and scripts) from the prefab after changes apply. Used to find new objects or old objects and use this information during changes sync (eg. generate ids for the new objects to prevent ids collisions). /// True if failed, otherwise false. - static bool SynchronizePrefabInstances(Array& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, rapidjson_flax::StringBuffer& tmpBuffer, const Array& oldObjectsIds, const Array& newObjectIds); + static bool SynchronizePrefabInstances(PrefabInstancesData& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, rapidjson_flax::StringBuffer& tmpBuffer, const Array& oldObjectsIds, const Array& newObjectIds); }; -void PrefabInstanceData::CollectPrefabInstances(Array& prefabInstancesData, const Guid& prefabId, Actor* defaultInstance, Actor* targetActor) +void PrefabInstanceData::CollectPrefabInstances(PrefabInstancesData& prefabInstancesData, const Guid& prefabId, Actor* defaultInstance, Actor* targetActor) { ScopeLock lock(PrefabManager::PrefabsReferencesLocker); if (PrefabManager::PrefabsReferences.ContainsKey(prefabId)) @@ -207,11 +209,12 @@ void PrefabInstanceData::CollectPrefabInstances(Array& prefa } } -void PrefabInstanceData::SerializePrefabInstances(Array& prefabInstancesData, rapidjson_flax::StringBuffer& tmpBuffer, SceneObjectsListCacheType& sceneObjects) +void PrefabInstanceData::SerializePrefabInstances(PrefabInstancesData& prefabInstancesData, rapidjson_flax::StringBuffer& tmpBuffer, const Prefab* prefab) { if (prefabInstancesData.IsEmpty()) return; - + CollectionPoolCache::ScopeCache sceneObjects = ActorsCache::SceneObjectsListCache.Get(); + sceneObjects->EnsureCapacity(prefab->ObjectsCount * 4); for (int32 dataIndex = 0; dataIndex < prefabInstancesData.Count(); dataIndex++) { auto& instance = prefabInstancesData[dataIndex]; @@ -253,12 +256,10 @@ void PrefabInstanceData::SerializePrefabInstances(Array& pre instance.PrefabInstanceIdToDataIndex.Add(obj->GetSceneObjectId(), i); } } - - sceneObjects->Clear(); tmpBuffer.Clear(); } -bool PrefabInstanceData::SynchronizePrefabInstances(Array& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, const IdToDataLookupType& prefabObjectIdToDiffData, const Array& newPrefabObjectIds) +bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, const IdToDataLookupType& prefabObjectIdToDiffData, const Array& newPrefabObjectIds) { for (int32 instanceIndex = 0; instanceIndex < prefabInstancesData.Count(); instanceIndex++) { @@ -492,7 +493,7 @@ bool PrefabInstanceData::SynchronizePrefabInstances(Array& p return false; } -bool PrefabInstanceData::SynchronizePrefabInstances(Array& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, rapidjson_flax::StringBuffer& tmpBuffer, const Array& oldObjectsIds, const Array& newObjectIds) +bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, rapidjson_flax::StringBuffer& tmpBuffer, const Array& oldObjectsIds, const Array& newObjectIds) { if (prefabInstancesData.IsEmpty()) return false; @@ -588,8 +589,6 @@ bool FindCyclicReferences(Actor* actor, const Guid& prefabRootId) bool Prefab::ApplyAll(Actor* targetActor) { - // TODO: use more cached dictionaries and other collections containers to prevent memory allocations during apply (optimize for apply 10 times per second the same prefab on many changes in editor) - PROFILE_CPU(); const auto startTime = DateTime::NowUTC(); @@ -695,22 +694,40 @@ bool Prefab::ApplyAll(Actor* targetActor) ObjectsRemovalService::Flush(); + // Collect existing prefab instances (this and nested ones) to cache 'before' state used later to restore it + PrefabInstancesData thisPrefabInstancesData; + Array allPrefabsInstancesData; + { + PROFILE_CPU_NAMED("Prefab.CachePrefabInstancesData"); + + rapidjson_flax::StringBuffer dataBuffer; + PrefabInstanceData::CollectPrefabInstances(thisPrefabInstancesData, GetID(), _defaultInstance, targetActor); + PrefabInstanceData::SerializePrefabInstances(thisPrefabInstancesData, dataBuffer, this); + + allPrefabsInstancesData.Resize(allPrefabs.Count()); + for (int32 i = 0; i < allPrefabs.Count(); i++) + { + Prefab* prefab = allPrefabs[i]; + PrefabInstanceData::CollectPrefabInstances(allPrefabsInstancesData[i], prefab->GetID(), prefab->GetDefaultInstance(), prefab->GetDefaultInstance()); + PrefabInstanceData::SerializePrefabInstances(allPrefabsInstancesData[i], dataBuffer, prefab); + } + } + // Use internal call to improve shared collections memory sharing - if (ApplyAllInternal(targetActor, true)) + if (ApplyAllInternal(targetActor, true, thisPrefabInstancesData)) return true; - SyncNestedPrefabs(allPrefabs); + SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData); const auto endTime = DateTime::NowUTC(); LOG(Info, "Prefab updated! {0} ms", (int32)(endTime - startTime).GetTotalMilliseconds()); return false; } -bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPrefab) +bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPrefab, PrefabInstancesData& prefabInstancesData) { PROFILE_CPU_NAMED("Prefab.Apply"); ScopeLock lock(Locker); - const auto prefabId = GetID(); // Gather all scene objects in target instance (reused later) @@ -719,9 +736,7 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr SceneQuery::GetAllSerializableSceneObjects(targetActor, *targetObjects.Value); if (PrefabManager::FilterPrefabInstancesToSave(prefabId, *targetObjects.Value)) return true; - LOG(Info, "Applying prefab changes from actor {0} (total objects count: {2}) to {1}...", targetActor->ToString(), ToString(), targetObjects->Count()); - Array oldObjectsIds = ObjectsIds; // Serialize to json data @@ -809,17 +824,8 @@ bool Prefab::ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPr // TODO: what if user applied prefab with references to the other objects from scene? clear them or what? JsonTools::ChangeIds(diffDataDocument, objectInstanceIdToPrefabObjectId); } - dataBuffer.Clear(); - - // Fetch all existing prefab instances - Array prefabInstancesData; - PrefabInstanceData::CollectPrefabInstances(prefabInstancesData, prefabId, _defaultInstance, targetActor); - - // Serialize all prefab instances data (temporary storage) CollectionPoolCache::ScopeCache sceneObjects = ActorsCache::SceneObjectsListCache.Get(); - sceneObjects->EnsureCapacity(ObjectsCount * 4); - PrefabInstanceData::SerializePrefabInstances(prefabInstancesData, dataBuffer, sceneObjects); // Destroy default instance and some cache data in Prefab DeleteDefaultInstance(); @@ -1204,18 +1210,7 @@ bool Prefab::UpdateInternal(const Array& defaultInstanceObjects, r return false; } -bool Prefab::SyncChanges(const NestedPrefabsList& allPrefabs) -{ - // Use internal call to improve shared collections memory sharing - if (SyncChangesInternal()) - return true; - - SyncNestedPrefabs(allPrefabs); - - return false; -} - -bool Prefab::SyncChangesInternal() +bool Prefab::SyncChangesInternal(PrefabInstancesData& prefabInstancesData) { PROFILE_CPU_NAMED("Prefab.SyncChanges"); @@ -1252,10 +1247,10 @@ bool Prefab::SyncChangesInternal() AutoActorCleanup cleanupDefaultInstance(targetActor); // Apply changes - return ApplyAllInternal(targetActor, false); + return ApplyAllInternal(targetActor, false, prefabInstancesData); } -void Prefab::SyncNestedPrefabs(const NestedPrefabsList& allPrefabs) +void Prefab::SyncNestedPrefabs(const NestedPrefabsList& allPrefabs, Array& allPrefabsInstancesData) const { PROFILE_CPU(); LOG(Info, "Updating referencing prefabs"); @@ -1274,9 +1269,13 @@ void Prefab::SyncNestedPrefabs(const NestedPrefabsList& allPrefabs) continue; } - if (nestedPrefab->NestedPrefabs.Contains(GetID())) + const int32 nestedPrefabIndex = nestedPrefab->NestedPrefabs.Find(GetID()); + if (nestedPrefabIndex != -1) { - nestedPrefab->SyncChanges(allPrefabs); + if (nestedPrefab->SyncChangesInternal(allPrefabsInstancesData[nestedPrefabIndex])) + continue; + + nestedPrefab->SyncNestedPrefabs(allPrefabs, allPrefabsInstancesData); ObjectsRemovalService::Flush(); } diff --git a/Source/Engine/Level/Prefabs/Prefab.h b/Source/Engine/Level/Prefabs/Prefab.h index 2f105e9ce..ed82473c9 100644 --- a/Source/Engine/Level/Prefabs/Prefab.h +++ b/Source/Engine/Level/Prefabs/Prefab.h @@ -82,12 +82,12 @@ public: private: #if USE_EDITOR + typedef Array PrefabInstancesData; typedef Array> NestedPrefabsList; - bool ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPrefab); + bool ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPrefab, PrefabInstancesData& prefabInstancesData); bool UpdateInternal(const Array& defaultInstanceObjects, rapidjson_flax::StringBuffer& tmpBuffer); - bool SyncChanges(const NestedPrefabsList& allPrefabs); - bool SyncChangesInternal(); - void SyncNestedPrefabs(const NestedPrefabsList& allPrefabs); + bool SyncChangesInternal(PrefabInstancesData& prefabInstancesData); + void SyncNestedPrefabs(const NestedPrefabsList& allPrefabs, Array& allPrefabsInstancesData) const; #endif void DeleteDefaultInstance(); diff --git a/Source/Engine/Level/SceneQuery.cpp b/Source/Engine/Level/SceneQuery.cpp index b990daaa8..7bee0e510 100644 --- a/Source/Engine/Level/SceneQuery.cpp +++ b/Source/Engine/Level/SceneQuery.cpp @@ -7,17 +7,14 @@ Actor* SceneQuery::RaycastScene(const Ray& ray) { PROFILE_CPU(); - #if SCENE_QUERIES_WITH_LOCK ScopeLock lock(Level::ScenesLock); #endif - Actor* target; Actor* minTarget = nullptr; Real distance; Vector3 normal; Real minDistance = MAX_Real; - for (int32 i = 0; i < Level::Scenes.Count(); i++) { target = Level::Scenes[i]->Intersects(ray, distance, normal); @@ -30,7 +27,6 @@ Actor* SceneQuery::RaycastScene(const Ray& ray) } } } - return minTarget; } @@ -38,16 +34,13 @@ bool GetAllSceneObjectsQuery(Actor* actor, Array& objects) { objects.Add(actor); objects.Add(reinterpret_cast(actor->Scripts.Get()), actor->Scripts.Count()); - return true; } void SceneQuery::GetAllSceneObjects(Actor* root, Array& objects) { ASSERT(root); - PROFILE_CPU(); - Function&)> func(GetAllSceneObjectsQuery); root->TreeExecuteChildren&>(func, objects); } @@ -56,19 +49,15 @@ bool GetAllSerializableSceneObjectsQuery(Actor* actor, Array& obje { if (EnumHasAnyFlags(actor->HideFlags, HideFlags::DontSave)) return false; - objects.Add(actor); objects.Add(reinterpret_cast(actor->Scripts.Get()), actor->Scripts.Count()); - return true; } void SceneQuery::GetAllSerializableSceneObjects(Actor* root, Array& objects) { ASSERT(root); - PROFILE_CPU(); - Function&)> func(GetAllSerializableSceneObjectsQuery); root->TreeExecute&>(func, objects); } @@ -82,9 +71,7 @@ bool GetAllActorsQuery(Actor* actor, Array& actors) void SceneQuery::GetAllActors(Actor* root, Array& actors) { PROFILE_CPU(); - ASSERT(root); - Function&)> func(GetAllActorsQuery); root->TreeExecuteChildren&>(func, actors); } @@ -92,11 +79,9 @@ void SceneQuery::GetAllActors(Actor* root, Array& actors) void SceneQuery::GetAllActors(Array& actors) { PROFILE_CPU(); - #if SCENE_QUERIES_WITH_LOCK ScopeLock lock(Level::ScenesLock); #endif - for (int32 i = 0; i < Level::Scenes.Count(); i++) GetAllActors(Level::Scenes[i], actors); }