Optimize scenes loading with Job System
This commit is contained in:
@@ -8,6 +8,7 @@ CollectionPoolCache<ISerializeModifier, Cache::ISerializeModifierClearCallback>
|
|||||||
void Cache::ISerializeModifierClearCallback(::ISerializeModifier* obj)
|
void Cache::ISerializeModifierClearCallback(::ISerializeModifier* obj)
|
||||||
{
|
{
|
||||||
obj->EngineBuild = FLAXENGINE_VERSION_BUILD;
|
obj->EngineBuild = FLAXENGINE_VERSION_BUILD;
|
||||||
|
obj->CurrentInstance = -1;
|
||||||
obj->IdsMapping.Clear();
|
obj->IdsMapping.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ public:
|
|||||||
private:
|
private:
|
||||||
CollectionPoolCache* _pool;
|
CollectionPoolCache* _pool;
|
||||||
|
|
||||||
ScopeCache(CollectionPoolCache* pool, T* value)
|
FORCE_INLINE ScopeCache(CollectionPoolCache* pool, T* value)
|
||||||
{
|
{
|
||||||
_pool = pool;
|
_pool = pool;
|
||||||
Value = value;
|
Value = value;
|
||||||
@@ -71,7 +71,7 @@ public:
|
|||||||
|
|
||||||
~ScopeCache()
|
~ScopeCache()
|
||||||
{
|
{
|
||||||
_pool->Release(Value);
|
_pool->Put(Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
T* operator->()
|
T* operator->()
|
||||||
@@ -99,13 +99,6 @@ private:
|
|||||||
CriticalSection _locker;
|
CriticalSection _locker;
|
||||||
Array<T*, InlinedAllocation<64>> _pool;
|
Array<T*, InlinedAllocation<64>> _pool;
|
||||||
|
|
||||||
void Release(T* value)
|
|
||||||
{
|
|
||||||
_locker.Lock();
|
|
||||||
_pool.Add(value);
|
|
||||||
_locker.Unlock();
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Finalizes an instance of the <see cref="CollectionPoolCache"/> class.
|
/// Finalizes an instance of the <see cref="CollectionPoolCache"/> class.
|
||||||
@@ -120,7 +113,16 @@ public:
|
|||||||
/// Gets the collection instance from the pool. Can reuse the object from the pool or create a new one. Returns collection is always cleared and ready to use.
|
/// Gets the collection instance from the pool. Can reuse the object from the pool or create a new one. Returns collection is always cleared and ready to use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns>The collection (cleared).</returns>
|
/// <returns>The collection (cleared).</returns>
|
||||||
ScopeCache Get()
|
FORCE_INLINE ScopeCache Get()
|
||||||
|
{
|
||||||
|
return ScopeCache(this, GetUnscoped());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the collection instance from the pool. Can reuse the object from the pool or create a new one. Returns collection is always cleared and ready to use.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>The collection (cleared).</returns>
|
||||||
|
T* GetUnscoped()
|
||||||
{
|
{
|
||||||
T* result;
|
T* result;
|
||||||
_locker.Lock();
|
_locker.Lock();
|
||||||
@@ -129,10 +131,18 @@ public:
|
|||||||
else
|
else
|
||||||
result = CreateCallback();
|
result = CreateCallback();
|
||||||
_locker.Unlock();
|
_locker.Unlock();
|
||||||
|
|
||||||
ClearCallback(result);
|
ClearCallback(result);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
return ScopeCache(this, result);
|
/// <summary>
|
||||||
|
/// Puts the collection value back to the pool.
|
||||||
|
/// </summary>
|
||||||
|
void Put(T* value)
|
||||||
|
{
|
||||||
|
_locker.Lock();
|
||||||
|
_pool.Add(value);
|
||||||
|
_locker.Unlock();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -785,12 +785,11 @@ public:
|
|||||||
/// <param name="other">The other collection to clone.</param>
|
/// <param name="other">The other collection to clone.</param>
|
||||||
void Clone(const Dictionary& other)
|
void Clone(const Dictionary& other)
|
||||||
{
|
{
|
||||||
|
// TODO: if both key and value are POD types then use raw memory copy for buckets
|
||||||
Clear();
|
Clear();
|
||||||
SetCapacity(other.Capacity(), false);
|
EnsureCapacity(other.Capacity(), false);
|
||||||
for (Iterator i = other.Begin(); i != other.End(); ++i)
|
for (Iterator i = other.Begin(); i != other.End(); ++i)
|
||||||
Add(i);
|
Add(i);
|
||||||
ASSERT(Count() == other.Count());
|
|
||||||
ASSERT(Capacity() == other.Capacity());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -1013,6 +1013,7 @@ void Actor::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier)
|
|||||||
const auto parent = Scripting::FindObject<Actor>(parentId);
|
const auto parent = Scripting::FindObject<Actor>(parentId);
|
||||||
if (_parent != parent)
|
if (_parent != parent)
|
||||||
{
|
{
|
||||||
|
ScopeLock lock(Level::ScenesLock);
|
||||||
if (IsDuringPlay())
|
if (IsDuringPlay())
|
||||||
{
|
{
|
||||||
SetParent(parent, false, false);
|
SetParent(parent, false, false);
|
||||||
|
|||||||
@@ -18,6 +18,7 @@
|
|||||||
#include "Engine/Debug/Exceptions/JsonParseException.h"
|
#include "Engine/Debug/Exceptions/JsonParseException.h"
|
||||||
#include "Engine/Engine/EngineService.h"
|
#include "Engine/Engine/EngineService.h"
|
||||||
#include "Engine/Threading/Threading.h"
|
#include "Engine/Threading/Threading.h"
|
||||||
|
#include "Engine/Threading/JobSystem.h"
|
||||||
#include "Engine/Platform/File.h"
|
#include "Engine/Platform/File.h"
|
||||||
#include "Engine/Platform/FileSystem.h"
|
#include "Engine/Platform/FileSystem.h"
|
||||||
#include "Engine/Profiler/ProfilerCPU.h"
|
#include "Engine/Profiler/ProfilerCPU.h"
|
||||||
@@ -932,11 +933,9 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
|||||||
LOG(Error, "Invalid Data member.");
|
LOG(Error, "Invalid Data member.");
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
int32 objectsCount = data.Size();
|
|
||||||
|
|
||||||
// Peek scene node value (it's the first actor serialized)
|
// Peek scene node value (it's the first actor serialized)
|
||||||
auto& sceneValue = data[0];
|
auto sceneId = JsonTools::GetGuid(data[0], "ID");
|
||||||
auto sceneId = JsonTools::GetGuid(sceneValue, "ID");
|
|
||||||
if (!sceneId.IsValid())
|
if (!sceneId.IsValid())
|
||||||
{
|
{
|
||||||
LOG(Error, "Invalid scene id.");
|
LOG(Error, "Invalid scene id.");
|
||||||
@@ -957,59 +956,103 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
|||||||
auto scene = New<Scene>(ScriptingObjectSpawnParams(sceneId, Scene::TypeInitializer));
|
auto scene = New<Scene>(ScriptingObjectSpawnParams(sceneId, Scene::TypeInitializer));
|
||||||
scene->LoadTime = startTime;
|
scene->LoadTime = startTime;
|
||||||
scene->RegisterObject();
|
scene->RegisterObject();
|
||||||
scene->Deserialize(sceneValue, modifier.Value);
|
scene->Deserialize(data[0], modifier.Value);
|
||||||
|
|
||||||
// Fire event
|
// Fire event
|
||||||
CallSceneEvent(SceneEventType::OnSceneLoading, scene, sceneId);
|
CallSceneEvent(SceneEventType::OnSceneLoading, scene, sceneId);
|
||||||
|
|
||||||
// Loaded scene objects list
|
// Loaded scene objects list
|
||||||
CollectionPoolCache<ActorsCache::SceneObjectsListType>::ScopeCache sceneObjects = ActorsCache::SceneObjectsListCache.Get();
|
CollectionPoolCache<ActorsCache::SceneObjectsListType>::ScopeCache sceneObjects = ActorsCache::SceneObjectsListCache.Get();
|
||||||
|
const int32 objectsCount = (int32)data.Size();
|
||||||
sceneObjects->Resize(objectsCount);
|
sceneObjects->Resize(objectsCount);
|
||||||
sceneObjects->At(0) = scene;
|
sceneObjects->At(0) = scene;
|
||||||
|
|
||||||
|
// Spawn all scene objects
|
||||||
SceneObjectsFactory::Context context(modifier.Value);
|
SceneObjectsFactory::Context context(modifier.Value);
|
||||||
|
context.Async = JobSystem::GetThreadsCount() > 1 && objectsCount > 10;
|
||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Spawn");
|
PROFILE_CPU_NAMED("Spawn");
|
||||||
|
SceneObject** objects = sceneObjects->Get();
|
||||||
// Spawn all scene objects
|
if (context.Async)
|
||||||
for (int32 i = 1; i < objectsCount; i++) // start from 1. at index [0] was scene
|
|
||||||
{
|
{
|
||||||
auto& stream = data[i];
|
JobSystem::Execute([&](int32 i)
|
||||||
auto obj = SceneObjectsFactory::Spawn(context, stream);
|
{
|
||||||
sceneObjects->At(i) = obj;
|
i++; // Start from 1. at index [0] was scene
|
||||||
if (obj)
|
auto& stream = data[i];
|
||||||
obj->RegisterObject();
|
auto obj = SceneObjectsFactory::Spawn(context, stream);
|
||||||
else
|
objects[i] = obj;
|
||||||
SceneObjectsFactory::HandleObjectDeserializationError(stream);
|
if (obj)
|
||||||
|
{
|
||||||
|
obj->RegisterObject();
|
||||||
|
#if USE_EDITOR
|
||||||
|
// Auto-create C# objects for all actors in Editor during scene load when running in async (so main thread already has all of them)
|
||||||
|
obj->CreateManaged();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
SceneObjectsFactory::HandleObjectDeserializationError(stream);
|
||||||
|
}, objectsCount - 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (int32 i = 1; i < objectsCount; i++) // start from 1. at index [0] was scene
|
||||||
|
{
|
||||||
|
auto& stream = data[i];
|
||||||
|
auto obj = SceneObjectsFactory::Spawn(context, stream);
|
||||||
|
sceneObjects->At(i) = obj;
|
||||||
|
if (obj)
|
||||||
|
obj->RegisterObject();
|
||||||
|
else
|
||||||
|
SceneObjectsFactory::HandleObjectDeserializationError(stream);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Capture prefab instances in a scene to restore any missing objects (eg. newly added objects to prefab that are missing in scene file)
|
||||||
SceneObjectsFactory::PrefabSyncData prefabSyncData(*sceneObjects.Value, data, modifier.Value);
|
SceneObjectsFactory::PrefabSyncData prefabSyncData(*sceneObjects.Value, data, modifier.Value);
|
||||||
|
|
||||||
SceneObjectsFactory::SetupPrefabInstances(context, prefabSyncData);
|
SceneObjectsFactory::SetupPrefabInstances(context, prefabSyncData);
|
||||||
|
|
||||||
// TODO: resave and force sync scenes during game cooking so this step could be skipped in game
|
// TODO: resave and force sync scenes during game cooking so this step could be skipped in game
|
||||||
SceneObjectsFactory::SynchronizeNewPrefabInstances(context, prefabSyncData);
|
SceneObjectsFactory::SynchronizeNewPrefabInstances(context, prefabSyncData);
|
||||||
|
|
||||||
// /\ all above this has to be done on an any thread
|
// /\ all above this has to be done on an any thread
|
||||||
// \/ all below this has to be done on multiple threads at once
|
// \/ all below this has to be done on multiple threads at once
|
||||||
|
|
||||||
|
// Load all scene objects
|
||||||
{
|
{
|
||||||
PROFILE_CPU_NAMED("Deserialize");
|
PROFILE_CPU_NAMED("Deserialize");
|
||||||
|
|
||||||
// TODO: at this point we would probably spawn a few thread pool tasks which will load deserialize scene object but only if scene is big enough
|
|
||||||
|
|
||||||
// Load all scene objects
|
|
||||||
Scripting::ObjectsLookupIdMapping.Set(&modifier.Value->IdsMapping);
|
|
||||||
SceneObject** objects = sceneObjects->Get();
|
SceneObject** objects = sceneObjects->Get();
|
||||||
for (int32 i = 1; i < objectsCount; i++) // start from 1. at index [0] was scene
|
bool wasAsync = context.Async;
|
||||||
|
context.Async = false; // TODO: fix Actor's Scripts and Children order when loading objects data out of order via async jobs
|
||||||
|
if (context.Async)
|
||||||
{
|
{
|
||||||
auto& objData = data[i];
|
ScenesLock.Unlock(); // Unlock scenes from Main Thread so Job Threads can use it to safely setup actors hierarchy (see Actor::Deserialize)
|
||||||
auto obj = objects[i];
|
JobSystem::Execute([&](int32 i)
|
||||||
if (obj)
|
{
|
||||||
SceneObjectsFactory::Deserialize(context, obj, objData);
|
i++; // Start from 1. at index [0] was scene
|
||||||
|
auto obj = objects[i];
|
||||||
|
if (obj)
|
||||||
|
{
|
||||||
|
auto& idMapping = Scripting::ObjectsLookupIdMapping.Get();
|
||||||
|
idMapping = &context.GetModifier()->IdsMapping;
|
||||||
|
SceneObjectsFactory::Deserialize(context, obj, data[i]);
|
||||||
|
idMapping = nullptr;
|
||||||
|
}
|
||||||
|
}, objectsCount - 1);
|
||||||
|
ScenesLock.Lock();
|
||||||
}
|
}
|
||||||
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
else
|
||||||
|
{
|
||||||
|
Scripting::ObjectsLookupIdMapping.Set(&modifier.Value->IdsMapping);
|
||||||
|
for (int32 i = 1; i < objectsCount; i++) // start from 1. at index [0] was scene
|
||||||
|
{
|
||||||
|
auto& objData = data[i];
|
||||||
|
auto obj = objects[i];
|
||||||
|
if (obj)
|
||||||
|
SceneObjectsFactory::Deserialize(context, obj, objData);
|
||||||
|
}
|
||||||
|
Scripting::ObjectsLookupIdMapping.Set(nullptr);
|
||||||
|
}
|
||||||
|
context.Async = wasAsync;
|
||||||
}
|
}
|
||||||
|
|
||||||
// /\ all above this has to be done on multiple threads at once
|
// /\ all above this has to be done on multiple threads at once
|
||||||
@@ -1031,7 +1074,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
|||||||
PROFILE_CPU_NAMED("Initialize");
|
PROFILE_CPU_NAMED("Initialize");
|
||||||
|
|
||||||
SceneObject** objects = sceneObjects->Get();
|
SceneObject** objects = sceneObjects->Get();
|
||||||
for (int32 i = 0; i < sceneObjects->Count(); i++)
|
for (int32 i = 0; i < objectsCount; i++)
|
||||||
{
|
{
|
||||||
SceneObject* obj = objects[i];
|
SceneObject* obj = objects[i];
|
||||||
if (obj)
|
if (obj)
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
#include "Engine/Level/Actor.h"
|
#include "Engine/Level/Actor.h"
|
||||||
#include "Engine/Level/Prefabs/Prefab.h"
|
#include "Engine/Level/Prefabs/Prefab.h"
|
||||||
#include "Engine/Content/Content.h"
|
#include "Engine/Content/Content.h"
|
||||||
|
#include "Engine/Core/Cache.h"
|
||||||
#include "Engine/Core/Log.h"
|
#include "Engine/Core/Log.h"
|
||||||
#include "Engine/Scripting/Scripting.h"
|
#include "Engine/Scripting/Scripting.h"
|
||||||
#include "Engine/Serialization/JsonTools.h"
|
#include "Engine/Serialization/JsonTools.h"
|
||||||
@@ -18,16 +19,49 @@ SceneObjectsFactory::Context::Context(ISerializeModifier* modifier)
|
|||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneObjectsFactory::Context::SetupIdsMapping(const SceneObject* obj)
|
SceneObjectsFactory::Context::~Context()
|
||||||
|
{
|
||||||
|
if (Async)
|
||||||
|
{
|
||||||
|
Array<ISerializeModifier*, FixedAllocation<PLATFORM_THREADS_LIMIT>> modifiers;
|
||||||
|
Modifiers.GetValues(modifiers);
|
||||||
|
for (ISerializeModifier* e : modifiers)
|
||||||
|
Cache::ISerializeModifier.Put(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ISerializeModifier* SceneObjectsFactory::Context::GetModifier()
|
||||||
|
{
|
||||||
|
ISerializeModifier* modifier = Modifier;
|
||||||
|
if (Async)
|
||||||
|
{
|
||||||
|
// When using context in async then use one ISerializeModifier per-thread
|
||||||
|
ISerializeModifier*& modifierThread = Modifiers.Get();
|
||||||
|
if (!modifierThread)
|
||||||
|
{
|
||||||
|
modifierThread = Cache::ISerializeModifier.GetUnscoped();
|
||||||
|
Modifiers.Set(modifierThread);
|
||||||
|
Locker.Lock();
|
||||||
|
modifierThread->EngineBuild = modifier->EngineBuild;
|
||||||
|
modifierThread->CurrentInstance = modifier->CurrentInstance;
|
||||||
|
modifierThread->IdsMapping = modifier->IdsMapping;
|
||||||
|
Locker.Unlock();
|
||||||
|
}
|
||||||
|
modifier = modifierThread;
|
||||||
|
}
|
||||||
|
return modifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
void SceneObjectsFactory::Context::SetupIdsMapping(const SceneObject* obj, ISerializeModifier* modifier)
|
||||||
{
|
{
|
||||||
int32 instanceIndex;
|
int32 instanceIndex;
|
||||||
if (ObjectToInstance.TryGet(obj->GetID(), instanceIndex) && instanceIndex != CurrentInstance)
|
if (ObjectToInstance.TryGet(obj->GetID(), instanceIndex) && instanceIndex != modifier->CurrentInstance)
|
||||||
{
|
{
|
||||||
// Apply the current prefab instance objects ids table to resolve references inside a prefab properly
|
// Apply the current prefab instance objects ids table to resolve references inside a prefab properly
|
||||||
CurrentInstance = instanceIndex;
|
modifier->CurrentInstance = instanceIndex;
|
||||||
auto& instance = Instances[instanceIndex];
|
auto& instance = Instances[instanceIndex];
|
||||||
for (auto& e : instance.IdsMapping)
|
for (auto& e : instance.IdsMapping)
|
||||||
Modifier->IdsMapping[e.Key] = e.Value;
|
modifier->IdsMapping[e.Key] = e.Value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -35,13 +69,13 @@ SceneObject* SceneObjectsFactory::Spawn(Context& context, const ISerializable::D
|
|||||||
{
|
{
|
||||||
// Get object id
|
// Get object id
|
||||||
Guid id = JsonTools::GetGuid(stream, "ID");
|
Guid id = JsonTools::GetGuid(stream, "ID");
|
||||||
context.Modifier->IdsMapping.TryGet(id, id);
|
ISerializeModifier* modifier = context.GetModifier();
|
||||||
|
modifier->IdsMapping.TryGet(id, id);
|
||||||
if (!id.IsValid())
|
if (!id.IsValid())
|
||||||
{
|
{
|
||||||
LOG(Warning, "Invalid object id.");
|
LOG(Warning, "Invalid object id.");
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
SceneObject* obj = nullptr;
|
SceneObject* obj = nullptr;
|
||||||
|
|
||||||
// Check for prefab instance
|
// Check for prefab instance
|
||||||
@@ -78,7 +112,7 @@ SceneObject* SceneObjectsFactory::Spawn(Context& context, const ISerializable::D
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Map prefab object ID to the deserialized instance ID
|
// Map prefab object ID to the deserialized instance ID
|
||||||
context.Modifier->IdsMapping[prefabObjectId] = id;
|
modifier->IdsMapping[prefabObjectId] = id;
|
||||||
|
|
||||||
// Create prefab instance (recursive prefab loading to support nested prefabs)
|
// Create prefab instance (recursive prefab loading to support nested prefabs)
|
||||||
obj = Spawn(context, *prefabData);
|
obj = Spawn(context, *prefabData);
|
||||||
@@ -169,6 +203,7 @@ void SceneObjectsFactory::Deserialize(Context& context, SceneObject* obj, ISeria
|
|||||||
#if ENABLE_ASSERTION
|
#if ENABLE_ASSERTION
|
||||||
CHECK(obj);
|
CHECK(obj);
|
||||||
#endif
|
#endif
|
||||||
|
ISerializeModifier* modifier = context.GetModifier();
|
||||||
|
|
||||||
// Check for prefab instance
|
// Check for prefab instance
|
||||||
Guid prefabObjectId;
|
Guid prefabObjectId;
|
||||||
@@ -204,24 +239,16 @@ void SceneObjectsFactory::Deserialize(Context& context, SceneObject* obj, ISeria
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Deserialize prefab data (recursive prefab loading to support nested prefabs)
|
// Deserialize prefab data (recursive prefab loading to support nested prefabs)
|
||||||
const auto prevVersion = context.Modifier->EngineBuild;
|
const auto prevVersion = modifier->EngineBuild;
|
||||||
context.Modifier->EngineBuild = prefab->DataEngineBuild;
|
modifier->EngineBuild = prefab->DataEngineBuild;
|
||||||
Deserialize(context, obj, *(ISerializable::DeserializeStream*)prefabData);
|
Deserialize(context, obj, *(ISerializable::DeserializeStream*)prefabData);
|
||||||
context.Modifier->EngineBuild = prevVersion;
|
modifier->EngineBuild = prevVersion;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32 instanceIndex;
|
context.SetupIdsMapping(obj, modifier);
|
||||||
if (context.ObjectToInstance.TryGet(obj->GetID(), instanceIndex) && instanceIndex != context.CurrentInstance)
|
|
||||||
{
|
|
||||||
// Apply the current prefab instance objects ids table to resolve references inside a prefab properly
|
|
||||||
context.CurrentInstance = instanceIndex;
|
|
||||||
auto& instance = context.Instances[instanceIndex];
|
|
||||||
for (auto& e : instance.IdsMapping)
|
|
||||||
context.Modifier->IdsMapping[e.Key] = e.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load data
|
// Load data
|
||||||
obj->Deserialize(stream, context.Modifier);
|
obj->Deserialize(stream, modifier);
|
||||||
}
|
}
|
||||||
|
|
||||||
void SceneObjectsFactory::HandleObjectDeserializationError(const ISerializable::DeserializeStream& value)
|
void SceneObjectsFactory::HandleObjectDeserializationError(const ISerializable::DeserializeStream& value)
|
||||||
@@ -518,7 +545,7 @@ void SceneObjectsFactory::SynchronizePrefabInstances(Context& context, PrefabSyn
|
|||||||
LOG(Info, "Object {0} has invalid parent object {4} -> {5} (PrefabObjectID: {1}, PrefabID: {2}, Path: {3})", obj->GetSceneObjectId(), prefabObjectId, prefab->GetID(), prefab->GetPath(), parentPrefabObjectId, actualParentPrefabId);
|
LOG(Info, "Object {0} has invalid parent object {4} -> {5} (PrefabObjectID: {1}, PrefabID: {2}, Path: {3})", obj->GetSceneObjectId(), prefabObjectId, prefab->GetID(), prefab->GetPath(), parentPrefabObjectId, actualParentPrefabId);
|
||||||
|
|
||||||
// Map actual prefab object id to the current scene objects collection
|
// Map actual prefab object id to the current scene objects collection
|
||||||
context.SetupIdsMapping(obj);
|
context.SetupIdsMapping(obj, data.Modifier);
|
||||||
data.Modifier->IdsMapping.TryGet(actualParentPrefabId, actualParentPrefabId);
|
data.Modifier->IdsMapping.TryGet(actualParentPrefabId, actualParentPrefabId);
|
||||||
|
|
||||||
// Find parent
|
// Find parent
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include "SceneObject.h"
|
#include "SceneObject.h"
|
||||||
#include "Engine/Core/Collections/Dictionary.h"
|
#include "Engine/Core/Collections/Dictionary.h"
|
||||||
|
#include "Engine/Platform/CriticalSection.h"
|
||||||
|
#include "Engine/Threading/ThreadLocal.h"
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Helper class for scene objects creation and deserialization utilities.
|
/// Helper class for scene objects creation and deserialization utilities.
|
||||||
@@ -21,13 +23,17 @@ public:
|
|||||||
struct Context
|
struct Context
|
||||||
{
|
{
|
||||||
ISerializeModifier* Modifier;
|
ISerializeModifier* Modifier;
|
||||||
int32 CurrentInstance = -1;
|
bool Async = false;
|
||||||
Array<PrefabInstance> Instances;
|
Array<PrefabInstance> Instances;
|
||||||
Dictionary<Guid, int32> ObjectToInstance;
|
Dictionary<Guid, int32> ObjectToInstance;
|
||||||
|
CriticalSection Locker;
|
||||||
|
ThreadLocal<ISerializeModifier*> Modifiers;
|
||||||
|
|
||||||
Context(ISerializeModifier* modifier);
|
Context(ISerializeModifier* modifier);
|
||||||
|
~Context();
|
||||||
|
|
||||||
void SetupIdsMapping(const SceneObject* obj);
|
ISerializeModifier* GetModifier();
|
||||||
|
void SetupIdsMapping(const SceneObject* obj, ISerializeModifier* modifier);
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -327,6 +327,7 @@ void Script::Deserialize(DeserializeStream& stream, ISerializeModifier* modifier
|
|||||||
const auto parent = Scripting::FindObject<Actor>(parentId);
|
const auto parent = Scripting::FindObject<Actor>(parentId);
|
||||||
if (_parent != parent)
|
if (_parent != parent)
|
||||||
{
|
{
|
||||||
|
ScopeLock lock(Level::ScenesLock);
|
||||||
if (IsDuringPlay())
|
if (IsDuringPlay())
|
||||||
{
|
{
|
||||||
SetParent(parent, false);
|
SetParent(parent, false);
|
||||||
|
|||||||
@@ -18,6 +18,9 @@ public:
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
uint32 EngineBuild = FLAXENGINE_VERSION_BUILD;
|
uint32 EngineBuild = FLAXENGINE_VERSION_BUILD;
|
||||||
|
|
||||||
|
// Utility for scene deserialization to track currently mapped in Prefab Instance object IDs into IdsMapping.
|
||||||
|
int32 CurrentInstance = -1;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The object IDs mapping. Key is a serialized object id, value is mapped value to use.
|
/// The object IDs mapping. Key is a serialized object id, value is mapped value to use.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -228,6 +228,7 @@ int32 JobSystemThread::Run()
|
|||||||
|
|
||||||
void JobSystem::Execute(const Function<void(int32)>& job, int32 jobCount)
|
void JobSystem::Execute(const Function<void(int32)>& job, int32 jobCount)
|
||||||
{
|
{
|
||||||
|
#if JOB_SYSTEM_ENABLED
|
||||||
// TODO: disable async if called on job thread? or maybe Wait should handle waiting in job thread to do the processing?
|
// TODO: disable async if called on job thread? or maybe Wait should handle waiting in job thread to do the processing?
|
||||||
if (jobCount > 1)
|
if (jobCount > 1)
|
||||||
{
|
{
|
||||||
@@ -235,7 +236,8 @@ void JobSystem::Execute(const Function<void(int32)>& job, int32 jobCount)
|
|||||||
const int64 jobWaitHandle = Dispatch(job, jobCount);
|
const int64 jobWaitHandle = Dispatch(job, jobCount);
|
||||||
Wait(jobWaitHandle);
|
Wait(jobWaitHandle);
|
||||||
}
|
}
|
||||||
else if (jobCount > 0)
|
else
|
||||||
|
#endif
|
||||||
{
|
{
|
||||||
// Sync
|
// Sync
|
||||||
for (int32 i = 0; i < jobCount; i++)
|
for (int32 i = 0; i < jobCount; i++)
|
||||||
|
|||||||
@@ -65,10 +65,10 @@ public:
|
|||||||
template<typename AllocationType = HeapAllocation>
|
template<typename AllocationType = HeapAllocation>
|
||||||
void GetValues(Array<T, AllocationType>& result) const
|
void GetValues(Array<T, AllocationType>& result) const
|
||||||
{
|
{
|
||||||
result.EnsureCapacity(MaxThreads);
|
|
||||||
for (int32 i = 0; i < MaxThreads; i++)
|
for (int32 i = 0; i < MaxThreads; i++)
|
||||||
{
|
{
|
||||||
result.Add(_buckets[i].Value);
|
if (Platform::AtomicRead((int64 volatile*)&_buckets[i].ThreadID) != 0)
|
||||||
|
result.Add(_buckets[i].Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user