Optimize SceneObjectsFactory::SynchronizeNewPrefabInstances when loading large scenes

This commit is contained in:
Wojtek Figat
2024-08-16 14:49:43 +02:00
parent 330d769bdc
commit c4c25fc21f
3 changed files with 31 additions and 16 deletions

View File

@@ -148,6 +148,10 @@ Asset::LoadResult Prefab::loadAsset()
ObjectsDataCache.Add(objectId, &objData);
ObjectsCount++;
Guid parentID;
if (JsonTools::GetGuidIfValid(parentID, objData, "ParentID"))
ObjectsHierarchyCache[parentID].Add(objectId);
Guid prefabId = JsonTools::GetGuid(objData, "PrefabID");
if (prefabId.IsValid() && !NestedPrefabs.Contains(prefabId))
{
@@ -186,6 +190,8 @@ void Prefab::unload(bool isReloading)
NestedPrefabs.Resize(0);
ObjectsDataCache.Clear();
ObjectsDataCache.SetCapacity(0);
ObjectsHierarchyCache.Clear();
ObjectsHierarchyCache.SetCapacity(0);
ObjectsCache.Clear();
ObjectsCache.SetCapacity(0);
if (_defaultInstance)

View File

@@ -41,6 +41,11 @@ public:
/// </summary>
Dictionary<Guid, const ISerializable::DeserializeStream*> ObjectsDataCache;
/// <summary>
/// The object hierarchy cache that maps the PrefabObjectID into the list of children (identified also by PrefabObjectID). Objects without any children are not included for sake of optimization. Used for quick validation of the structure of loaded prefab instances. Valid only if asset is loaded.
/// </summary>
Dictionary<Guid, Array<Guid>> ObjectsHierarchyCache;
/// <summary>
/// The objects cache maps the id of the object contained in the prefab asset (actor or script) to the default instance deserialized from prefab data. Valid only if asset is loaded and GetDefaultInstance was called.
/// </summary>

View File

@@ -719,26 +719,21 @@ void SceneObjectsFactory::SynchronizePrefabInstances(Context& context, PrefabSyn
void SceneObjectsFactory::SynchronizeNewPrefabInstances(Context& context, PrefabSyncData& data, Prefab* prefab, Actor* actor, const Guid& actorPrefabObjectId, int32 i, const ISerializable::DeserializeStream& stream)
{
// Check for RemovedObjects list
// Use cached acceleration structure for prefab hierarchy validation
const auto* hierarchy = prefab->ObjectsHierarchyCache.TryGet(actorPrefabObjectId);
if (!hierarchy)
return;
const Guid actorId = actor->GetID();
const auto removedObjects = SERIALIZE_FIND_MEMBER(stream, "RemovedObjects");
// Check if the given actor has new children or scripts added (inside the prefab that it uses)
// TODO: consider caching prefab objects structure maybe to boost this logic?
for (auto it = prefab->ObjectsDataCache.Begin(); it.IsNotEnd(); ++it)
for (const Guid& prefabObjectId : *hierarchy)
{
// Use only objects that are linked to the current actor
const Guid parentId = JsonTools::GetGuid(*it->Value, "ParentID");
if (parentId != actorPrefabObjectId)
continue;
// Skip if object was marked to be removed per instance
const Guid prefabObjectId = JsonTools::GetGuid(*it->Value, "ID");
if (removedObjects != stream.MemberEnd())
{
auto& list = removedObjects->value;
const int32 size = static_cast<int32>(list.Size());
const rapidjson::SizeType size = list.Size();
bool removed = false;
for (int32 j = 0; j < size; j++)
for (rapidjson::SizeType j = 0; j < size; j++)
{
if (JsonTools::GetGuid(list[j]) == prefabObjectId)
{
@@ -754,10 +749,19 @@ void SceneObjectsFactory::SynchronizeNewPrefabInstances(Context& context, Prefab
bool spawned = false;
int32 childSearchStart = i + 1; // Objects are serialized with parent followed by its children
int32 instanceIndex = -1;
if (context.ObjectToInstance.TryGet(actor->GetID(), instanceIndex) && context.Instances[instanceIndex].Prefab == prefab)
if (context.ObjectToInstance.TryGet(actorId, instanceIndex) && context.Instances[instanceIndex].Prefab == prefab)
{
// Quickly check if that object exists
auto& prefabInstance = context.Instances[instanceIndex];
Guid id;
int32 idInstanceIndex;
if (prefabInstance.IdsMapping.TryGet(prefabObjectId, id) &&
context.ObjectToInstance.TryGet(id, idInstanceIndex) &&
idInstanceIndex == instanceIndex)
continue;
// Start searching from the beginning of that prefab instance (eg. in case prefab objects were reordered)
childSearchStart = Math::Min(childSearchStart, context.Instances[instanceIndex].StatIndex);
childSearchStart = Math::Min(childSearchStart, prefabInstance.StatIndex);
}
for (int32 j = childSearchStart; j < data.InitialCount; j++)
{
@@ -773,7 +777,7 @@ void SceneObjectsFactory::SynchronizeNewPrefabInstances(Context& context, Prefab
// Map prefab object ID to this actor's prefab instance so the new objects gets added to it
context.SetupIdsMapping(actor, data.Modifier);
data.Modifier->IdsMapping[actorPrefabObjectId] = actor->GetID();
data.Modifier->IdsMapping[actorPrefabObjectId] = actorId;
Scripting::ObjectsLookupIdMapping.Set(&data.Modifier->IdsMapping);
// Create instance (including all children)