Fix existing nested prefabs sync applying when updating base prefab changes
#1015
This commit is contained in:
@@ -131,6 +131,8 @@ public:
|
||||
Dictionary<Guid, int32> PrefabInstanceIdToDataIndex;
|
||||
|
||||
public:
|
||||
typedef Array<PrefabInstanceData> PrefabInstancesData;
|
||||
|
||||
/// <summary>
|
||||
/// Collects all the valid prefab instances to update on prefab data synchronization.
|
||||
/// </summary>
|
||||
@@ -138,15 +140,15 @@ public:
|
||||
/// <param name="prefabId">The prefab asset identifier.</param>
|
||||
/// <param name="defaultInstance">The default instance (prefab internal, can be null).</param>
|
||||
/// <param name="targetActor">The target actor (optional actor to skip for counting, can be null).</param>
|
||||
static void CollectPrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, const Guid& prefabId, Actor* defaultInstance, Actor* targetActor);
|
||||
static void CollectPrefabInstances(PrefabInstancesData& prefabInstancesData, const Guid& prefabId, Actor* defaultInstance, Actor* targetActor);
|
||||
|
||||
/// <summary>
|
||||
/// Serializes all the prefab instances local changes to restore on prefab data synchronization.
|
||||
/// </summary>
|
||||
/// <param name="prefabInstancesData">The prefab instances data.</param>
|
||||
/// <param name="tmpBuffer">The temporary json buffer (cleared before and after usage).</param>
|
||||
/// <param name="sceneObjects">The scene objects collection cache (cleared before and after usage).</param>
|
||||
static void SerializePrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, rapidjson_flax::StringBuffer& tmpBuffer, SceneObjectsListCacheType& sceneObjects);
|
||||
/// <param name="prefab">Source prefab.</param>
|
||||
static void SerializePrefabInstances(PrefabInstancesData& prefabInstancesData, rapidjson_flax::StringBuffer& tmpBuffer, const Prefab* prefab);
|
||||
|
||||
/// <summary>
|
||||
/// Synchronizes the prefab instances by applying changes from the diff data and restoring the local changes captured by SerializePrefabInstances.
|
||||
@@ -158,7 +160,7 @@ public:
|
||||
/// <param name="prefabObjectIdToDiffData">The hash table that maps the prefab object id to json data for the given prefab object.</param>
|
||||
/// <param name="newPrefabObjectIds">The collection of the new prefab objects ids added to prefab during changes synchronization or modifications apply.</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
static bool SynchronizePrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, const IdToDataLookupType& prefabObjectIdToDiffData, const Array<Guid>& newPrefabObjectIds);
|
||||
static bool SynchronizePrefabInstances(PrefabInstancesData& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, const IdToDataLookupType& prefabObjectIdToDiffData, const Array<Guid>& newPrefabObjectIds);
|
||||
|
||||
/// <summary>
|
||||
/// Synchronizes the prefab instances by applying changes from the diff data and restoring the local changes captured by SerializePrefabInstances.
|
||||
@@ -171,10 +173,10 @@ public:
|
||||
/// <param name="oldObjectsIds">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).</param>
|
||||
/// <param name="newObjectIds">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).</param>
|
||||
/// <returns>True if failed, otherwise false.</returns>
|
||||
static bool SynchronizePrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, rapidjson_flax::StringBuffer& tmpBuffer, const Array<Guid>& oldObjectsIds, const Array<Guid>& newObjectIds);
|
||||
static bool SynchronizePrefabInstances(PrefabInstancesData& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, rapidjson_flax::StringBuffer& tmpBuffer, const Array<Guid>& oldObjectsIds, const Array<Guid>& newObjectIds);
|
||||
};
|
||||
|
||||
void PrefabInstanceData::CollectPrefabInstances(Array<PrefabInstanceData>& 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<PrefabInstanceData>& prefa
|
||||
}
|
||||
}
|
||||
|
||||
void PrefabInstanceData::SerializePrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, rapidjson_flax::StringBuffer& tmpBuffer, SceneObjectsListCacheType& sceneObjects)
|
||||
void PrefabInstanceData::SerializePrefabInstances(PrefabInstancesData& prefabInstancesData, rapidjson_flax::StringBuffer& tmpBuffer, const Prefab* prefab)
|
||||
{
|
||||
if (prefabInstancesData.IsEmpty())
|
||||
return;
|
||||
|
||||
CollectionPoolCache<ActorsCache::SceneObjectsListType>::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<PrefabInstanceData>& pre
|
||||
instance.PrefabInstanceIdToDataIndex.Add(obj->GetSceneObjectId(), i);
|
||||
}
|
||||
}
|
||||
|
||||
sceneObjects->Clear();
|
||||
tmpBuffer.Clear();
|
||||
}
|
||||
|
||||
bool PrefabInstanceData::SynchronizePrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, const IdToDataLookupType& prefabObjectIdToDiffData, const Array<Guid>& newPrefabObjectIds)
|
||||
bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, const IdToDataLookupType& prefabObjectIdToDiffData, const Array<Guid>& newPrefabObjectIds)
|
||||
{
|
||||
for (int32 instanceIndex = 0; instanceIndex < prefabInstancesData.Count(); instanceIndex++)
|
||||
{
|
||||
@@ -492,7 +493,7 @@ bool PrefabInstanceData::SynchronizePrefabInstances(Array<PrefabInstanceData>& p
|
||||
return false;
|
||||
}
|
||||
|
||||
bool PrefabInstanceData::SynchronizePrefabInstances(Array<PrefabInstanceData>& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, rapidjson_flax::StringBuffer& tmpBuffer, const Array<Guid>& oldObjectsIds, const Array<Guid>& newObjectIds)
|
||||
bool PrefabInstanceData::SynchronizePrefabInstances(PrefabInstancesData& prefabInstancesData, Actor* defaultInstance, SceneObjectsListCacheType& sceneObjects, const Guid& prefabId, rapidjson_flax::StringBuffer& tmpBuffer, const Array<Guid>& oldObjectsIds, const Array<Guid>& 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<PrefabInstancesData> 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<Guid> 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<PrefabInstanceData> prefabInstancesData;
|
||||
PrefabInstanceData::CollectPrefabInstances(prefabInstancesData, prefabId, _defaultInstance, targetActor);
|
||||
|
||||
// Serialize all prefab instances data (temporary storage)
|
||||
CollectionPoolCache<ActorsCache::SceneObjectsListType>::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<SceneObject*>& 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<PrefabInstancesData>& 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();
|
||||
}
|
||||
|
||||
@@ -82,12 +82,12 @@ public:
|
||||
|
||||
private:
|
||||
#if USE_EDITOR
|
||||
typedef Array<class PrefabInstanceData> PrefabInstancesData;
|
||||
typedef Array<AssetReference<Prefab>> NestedPrefabsList;
|
||||
bool ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPrefab);
|
||||
bool ApplyAllInternal(Actor* targetActor, bool linkTargetActorObjectToPrefab, PrefabInstancesData& prefabInstancesData);
|
||||
bool UpdateInternal(const Array<SceneObject*>& 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<PrefabInstancesData>& allPrefabsInstancesData) const;
|
||||
#endif
|
||||
void DeleteDefaultInstance();
|
||||
|
||||
|
||||
@@ -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<SceneObject*>& objects)
|
||||
{
|
||||
objects.Add(actor);
|
||||
objects.Add(reinterpret_cast<SceneObject* const*>(actor->Scripts.Get()), actor->Scripts.Count());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SceneQuery::GetAllSceneObjects(Actor* root, Array<SceneObject*>& objects)
|
||||
{
|
||||
ASSERT(root);
|
||||
|
||||
PROFILE_CPU();
|
||||
|
||||
Function<bool(Actor*, Array<SceneObject*>&)> func(GetAllSceneObjectsQuery);
|
||||
root->TreeExecuteChildren<Array<SceneObject*>&>(func, objects);
|
||||
}
|
||||
@@ -56,19 +49,15 @@ bool GetAllSerializableSceneObjectsQuery(Actor* actor, Array<SceneObject*>& obje
|
||||
{
|
||||
if (EnumHasAnyFlags(actor->HideFlags, HideFlags::DontSave))
|
||||
return false;
|
||||
|
||||
objects.Add(actor);
|
||||
objects.Add(reinterpret_cast<SceneObject* const*>(actor->Scripts.Get()), actor->Scripts.Count());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void SceneQuery::GetAllSerializableSceneObjects(Actor* root, Array<SceneObject*>& objects)
|
||||
{
|
||||
ASSERT(root);
|
||||
|
||||
PROFILE_CPU();
|
||||
|
||||
Function<bool(Actor*, Array<SceneObject*>&)> func(GetAllSerializableSceneObjectsQuery);
|
||||
root->TreeExecute<Array<SceneObject*>&>(func, objects);
|
||||
}
|
||||
@@ -82,9 +71,7 @@ bool GetAllActorsQuery(Actor* actor, Array<Actor*>& actors)
|
||||
void SceneQuery::GetAllActors(Actor* root, Array<Actor*>& actors)
|
||||
{
|
||||
PROFILE_CPU();
|
||||
|
||||
ASSERT(root);
|
||||
|
||||
Function<bool(Actor*, Array<Actor*>&)> func(GetAllActorsQuery);
|
||||
root->TreeExecuteChildren<Array<Actor*>&>(func, actors);
|
||||
}
|
||||
@@ -92,11 +79,9 @@ void SceneQuery::GetAllActors(Actor* root, Array<Actor*>& actors)
|
||||
void SceneQuery::GetAllActors(Array<Actor*>& 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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user