Refactor level actions to support time budget and time slicing
This commit is contained in:
@@ -18,16 +18,15 @@
|
||||
#include "Engine/Debug/Exceptions/ArgumentNullException.h"
|
||||
#include "Engine/Debug/Exceptions/InvalidOperationException.h"
|
||||
#include "Engine/Debug/Exceptions/JsonParseException.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Engine/EngineService.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Threading/JobSystem.h"
|
||||
#include "Engine/Platform/File.h"
|
||||
#include "Engine/Platform/FileSystem.h"
|
||||
#include "Engine/Profiler/ProfilerCPU.h"
|
||||
#include "Engine/Profiler/ProfilerMemory.h"
|
||||
#include "Engine/Scripting/Script.h"
|
||||
#include "Engine/Engine/Time.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MAssembly.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MClass.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MDomain.h"
|
||||
#include "Engine/Scripting/ManagedCLR/MException.h"
|
||||
@@ -78,6 +77,13 @@ enum class SceneEventType
|
||||
OnSceneUnloaded = 7,
|
||||
};
|
||||
|
||||
enum class SceneResult
|
||||
{
|
||||
Success,
|
||||
Failed,
|
||||
Wait,
|
||||
};
|
||||
|
||||
class SceneAction
|
||||
{
|
||||
public:
|
||||
@@ -85,14 +91,15 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool CanDo() const
|
||||
struct Context
|
||||
{
|
||||
return true;
|
||||
}
|
||||
// Amount of seconds that action can take to run within a budget.
|
||||
float TimeBudget = MAX_float;
|
||||
};
|
||||
|
||||
virtual bool Do() const
|
||||
virtual SceneResult Do(Context& context)
|
||||
{
|
||||
return true;
|
||||
return SceneResult::Failed;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -107,6 +114,33 @@ struct ScriptsReloadObject
|
||||
|
||||
#endif
|
||||
|
||||
// Async map loading utility for state tracking and synchronization of various load stages.
|
||||
class SceneLoader
|
||||
{
|
||||
public:
|
||||
enum Stages
|
||||
{
|
||||
Init,
|
||||
Spawn,
|
||||
SetupPrefabs,
|
||||
Deserialize,
|
||||
SetupTransforms,
|
||||
BeginPlay,
|
||||
Loaded,
|
||||
} Stage = Init;
|
||||
bool AsyncLoad;
|
||||
bool AsyncJobs;
|
||||
float TotalTime = 0.0f;
|
||||
|
||||
SceneLoader(bool asyncLoad = false)
|
||||
: AsyncLoad(true)
|
||||
, AsyncJobs(JobSystem::GetThreadsCount() > 1)
|
||||
{
|
||||
}
|
||||
|
||||
SceneResult Tick(rapidjson_flax::Value& data, int32 engineBuild, Scene** outScene, const String* assetPath, float* timeBudget);
|
||||
};
|
||||
|
||||
namespace LevelImpl
|
||||
{
|
||||
Array<SceneAction*> _sceneActions;
|
||||
@@ -119,6 +153,10 @@ namespace LevelImpl
|
||||
void CallSceneEvent(SceneEventType eventType, Scene* scene, Guid sceneId);
|
||||
|
||||
void flushActions();
|
||||
SceneResult loadScene(SceneLoader& loader, JsonAsset* sceneAsset);
|
||||
SceneResult loadScene(SceneLoader& loader, const BytesContainer& sceneData, Scene** outScene = nullptr);
|
||||
SceneResult loadScene(SceneLoader& loader, rapidjson_flax::Document& document, Scene** outScene = nullptr);
|
||||
SceneResult loadScene(SceneLoader& loader, rapidjson_flax::Value& data, int32 engineBuild, Scene** outScene = nullptr, const String* assetPath = nullptr, float* timeBudget = nullptr);
|
||||
bool unloadScene(Scene* scene);
|
||||
bool unloadScenes();
|
||||
bool saveScene(Scene* scene);
|
||||
@@ -151,6 +189,7 @@ LevelService LevelServiceInstanceService;
|
||||
CriticalSection Level::ScenesLock;
|
||||
Array<Scene*> Level::Scenes;
|
||||
bool Level::TickEnabled = true;
|
||||
float Level::StreamingFrameBudget = 0.3f;
|
||||
Delegate<Actor*> Level::ActorSpawned;
|
||||
Delegate<Actor*> Level::ActorDeleted;
|
||||
Delegate<Actor*, Actor*> Level::ActorParentChanged;
|
||||
@@ -394,40 +433,22 @@ class LoadSceneAction : public SceneAction
|
||||
public:
|
||||
Guid SceneId;
|
||||
AssetReference<JsonAsset> SceneAsset;
|
||||
SceneLoader Loader;
|
||||
|
||||
LoadSceneAction(const Guid& sceneId, JsonAsset* sceneAsset)
|
||||
LoadSceneAction(const Guid& sceneId, JsonAsset* sceneAsset, bool async)
|
||||
: Loader(async)
|
||||
{
|
||||
SceneId = sceneId;
|
||||
SceneAsset = sceneAsset;
|
||||
}
|
||||
|
||||
bool CanDo() const override
|
||||
SceneResult Do(Context& context) override
|
||||
{
|
||||
return SceneAsset == nullptr || SceneAsset->IsLoaded();
|
||||
}
|
||||
|
||||
bool Do() const override
|
||||
{
|
||||
// Now to deserialize scene in a proper way we need to load scripting
|
||||
if (!Scripting::IsEveryAssemblyLoaded())
|
||||
{
|
||||
LOG(Error, "Scripts must be compiled without any errors in order to load a scene.");
|
||||
#if USE_EDITOR
|
||||
Platform::Error(TEXT("Scripts must be compiled without any errors in order to load a scene. Please fix it."));
|
||||
#endif
|
||||
CallSceneEvent(SceneEventType::OnSceneLoadError, nullptr, SceneId);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Load scene
|
||||
if (Level::loadScene(SceneAsset))
|
||||
{
|
||||
LOG(Error, "Failed to deserialize scene {0}", SceneId);
|
||||
CallSceneEvent(SceneEventType::OnSceneLoadError, nullptr, SceneId);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
if (SceneAsset == nullptr)
|
||||
return SceneResult::Failed;
|
||||
if (!SceneAsset->IsLoaded())
|
||||
return SceneResult::Wait;
|
||||
return LevelImpl::loadScene(Loader, SceneAsset);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -441,12 +462,12 @@ public:
|
||||
TargetScene = scene->GetID();
|
||||
}
|
||||
|
||||
bool Do() const override
|
||||
SceneResult Do(Context& context) override
|
||||
{
|
||||
auto scene = Level::FindScene(TargetScene);
|
||||
if (!scene)
|
||||
return true;
|
||||
return unloadScene(scene);
|
||||
return SceneResult::Failed;
|
||||
return unloadScene(scene) ? SceneResult::Failed : SceneResult::Success;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -457,9 +478,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
bool Do() const override
|
||||
SceneResult Do(Context& context) override
|
||||
{
|
||||
return unloadScenes();
|
||||
return unloadScenes() ? SceneResult::Failed : SceneResult::Success;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -475,14 +496,14 @@ public:
|
||||
PrettyJson = prettyJson;
|
||||
}
|
||||
|
||||
bool Do() const override
|
||||
SceneResult Do(Context& context) override
|
||||
{
|
||||
if (saveScene(TargetScene))
|
||||
{
|
||||
LOG(Error, "Failed to save scene {0}", TargetScene ? TargetScene->GetName() : String::Empty);
|
||||
return true;
|
||||
return SceneResult::Failed;
|
||||
}
|
||||
return false;
|
||||
return SceneResult::Success;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -495,7 +516,7 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
bool Do() const override
|
||||
SceneResult Do(Context& context) override
|
||||
{
|
||||
// Reloading scripts workflow:
|
||||
// - save scenes (to temporary files)
|
||||
@@ -556,7 +577,7 @@ public:
|
||||
{
|
||||
LOG(Error, "Failed to save scene '{0}' for scripts reload.", scenes[i].Name);
|
||||
CallSceneEvent(SceneEventType::OnSceneSaveError, scene, scene->GetID());
|
||||
return true;
|
||||
return SceneResult::Failed;
|
||||
}
|
||||
CallSceneEvent(SceneEventType::OnSceneSaved, scene, scene->GetID());
|
||||
}
|
||||
@@ -601,16 +622,17 @@ public:
|
||||
}
|
||||
if (document.HasParseError())
|
||||
{
|
||||
LOG(Error, "Failed to deserialize scene {0}. Result: {1}", scenes[i].Name, GetParseError_En(document.GetParseError()));
|
||||
return true;
|
||||
LOG(Error, "Failed to deserialize scene {0}. SceneResult: {1}", scenes[i].Name, GetParseError_En(document.GetParseError()));
|
||||
return SceneResult::Failed;
|
||||
}
|
||||
|
||||
// Load scene
|
||||
if (Level::loadScene(document))
|
||||
SceneLoader loader;
|
||||
if (LevelImpl::loadScene(loader, document) != SceneResult::Success)
|
||||
{
|
||||
LOG(Error, "Failed to deserialize scene {0}", scenes[i].Name);
|
||||
CallSceneEvent(SceneEventType::OnSceneLoadError, nullptr, scenes[i].ID);
|
||||
return true;
|
||||
return SceneResult::Failed;
|
||||
}
|
||||
}
|
||||
scenes.Resize(0);
|
||||
@@ -619,7 +641,7 @@ public:
|
||||
LOG(Info, "Scripts reloading end. Total time: {0}ms", static_cast<int32>((DateTime::NowUTC() - startTime).GetTotalMilliseconds()));
|
||||
Level::ScriptsReloadEnd();
|
||||
|
||||
return false;
|
||||
return SceneResult::Success;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -651,9 +673,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
bool Do() const override
|
||||
SceneResult Do(Context& context) override
|
||||
{
|
||||
return spawnActor(TargetActor, ParentActor);
|
||||
return spawnActor(TargetActor, ParentActor) ? SceneResult::Failed : SceneResult::Success;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -667,9 +689,9 @@ public:
|
||||
{
|
||||
}
|
||||
|
||||
bool Do() const override
|
||||
SceneResult Do(Context& context) override
|
||||
{
|
||||
return deleteActor(TargetActor);
|
||||
return deleteActor(TargetActor) ? SceneResult::Failed : SceneResult::Success;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -767,13 +789,29 @@ void Level::callActorEvent(ActorEventType eventType, Actor* a, Actor* b)
|
||||
|
||||
void LevelImpl::flushActions()
|
||||
{
|
||||
ScopeLock lock(_sceneActionsLocker);
|
||||
// Calculate time budget for the streaming (relative to the game frame rate to scale across different devices)
|
||||
SceneAction::Context context;
|
||||
float targetFps = 60;
|
||||
if (Time::UpdateFPS > ZeroTolerance)
|
||||
targetFps = Time::UpdateFPS;
|
||||
else if (Engine::GetFramesPerSecond() > 0)
|
||||
targetFps = (float)Engine::GetFramesPerSecond();
|
||||
context.TimeBudget = Level::StreamingFrameBudget / targetFps;
|
||||
|
||||
while (_sceneActions.HasItems() && _sceneActions.First()->CanDo())
|
||||
// Runs actions in order
|
||||
ScopeLock lock(_sceneActionsLocker);
|
||||
for (int32 i = 0; i < _sceneActions.Count() && context.TimeBudget >= 0.0; i++)
|
||||
{
|
||||
const auto action = _sceneActions.Dequeue();
|
||||
action->Do();
|
||||
Delete(action);
|
||||
auto action = _sceneActions[0];
|
||||
Stopwatch time;
|
||||
auto result = action->Do(context);
|
||||
time.Stop();
|
||||
context.TimeBudget -= time.GetTotalSeconds();
|
||||
if (result != SceneResult::Wait)
|
||||
{
|
||||
_sceneActions.RemoveAtKeepOrder(i--);
|
||||
Delete(action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -823,25 +861,25 @@ bool LevelImpl::unloadScenes()
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Level::loadScene(JsonAsset* sceneAsset)
|
||||
SceneResult LevelImpl::loadScene(SceneLoader& loader, JsonAsset* sceneAsset)
|
||||
{
|
||||
// Keep reference to the asset (prevent unloading during action)
|
||||
AssetReference<JsonAsset> ref = sceneAsset;
|
||||
if (sceneAsset == nullptr || sceneAsset->WaitForLoaded())
|
||||
{
|
||||
LOG(Error, "Cannot load scene asset.");
|
||||
return true;
|
||||
return SceneResult::Failed;
|
||||
}
|
||||
|
||||
return loadScene(*sceneAsset->Data, sceneAsset->DataEngineBuild, nullptr, &sceneAsset->GetPath());
|
||||
return loadScene(loader, *sceneAsset->Data, sceneAsset->DataEngineBuild, nullptr, &sceneAsset->GetPath());
|
||||
}
|
||||
|
||||
bool Level::loadScene(const BytesContainer& sceneData, Scene** outScene)
|
||||
SceneResult LevelImpl::loadScene(SceneLoader& loader, const BytesContainer& sceneData, Scene** outScene)
|
||||
{
|
||||
if (sceneData.IsInvalid())
|
||||
{
|
||||
LOG(Error, "Missing scene data.");
|
||||
return true;
|
||||
return SceneResult::Failed;
|
||||
}
|
||||
PROFILE_MEM(Level);
|
||||
|
||||
@@ -854,34 +892,48 @@ bool Level::loadScene(const BytesContainer& sceneData, Scene** outScene)
|
||||
if (document.HasParseError())
|
||||
{
|
||||
Log::JsonParseException(document.GetParseError(), document.GetErrorOffset());
|
||||
return true;
|
||||
return SceneResult::Failed;
|
||||
}
|
||||
|
||||
ScopeLock lock(ScenesLock);
|
||||
return loadScene(document, outScene);
|
||||
ScopeLock lock(Level::ScenesLock);
|
||||
return loadScene(loader, document, outScene);
|
||||
}
|
||||
|
||||
bool Level::loadScene(rapidjson_flax::Document& document, Scene** outScene)
|
||||
SceneResult LevelImpl::loadScene(SceneLoader& loader, rapidjson_flax::Document& document, Scene** outScene)
|
||||
{
|
||||
auto data = document.FindMember("Data");
|
||||
if (data == document.MemberEnd())
|
||||
{
|
||||
LOG(Error, "Missing Data member.");
|
||||
return true;
|
||||
return SceneResult::Failed;
|
||||
}
|
||||
const int32 saveEngineBuild = JsonTools::GetInt(document, "EngineBuild", 0);
|
||||
return loadScene(data->value, saveEngineBuild, outScene);
|
||||
return loadScene(loader, data->value, saveEngineBuild, outScene);
|
||||
}
|
||||
|
||||
bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** outScene, const String* assetPath)
|
||||
SceneResult LevelImpl::loadScene(SceneLoader& loader, rapidjson_flax::Value& data, int32 engineBuild, Scene** outScene, const String* assetPath, float* timeBudget)
|
||||
{
|
||||
PROFILE_CPU_NAMED("Level.LoadScene");
|
||||
PROFILE_MEM(Level);
|
||||
if (outScene)
|
||||
*outScene = nullptr;
|
||||
#if USE_EDITOR
|
||||
ContentDeprecated::Clear();
|
||||
#endif
|
||||
SceneResult result = SceneResult::Success;
|
||||
while ((!timeBudget || *timeBudget > 0.0f) && loader.Stage != SceneLoader::Loaded && result == SceneResult::Success)
|
||||
{
|
||||
Stopwatch time;
|
||||
result = loader.Tick(data, engineBuild, outScene, assetPath, timeBudget);
|
||||
time.Stop();
|
||||
const float delta = time.GetTotalSeconds();
|
||||
loader.TotalTime += delta;
|
||||
if (timeBudget)
|
||||
*timeBudget -= delta;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
SceneResult SceneLoader::Tick(rapidjson_flax::Value& data, int32 engineBuild, Scene** outScene, const String* assetPath, float* timeBudget)
|
||||
{
|
||||
LOG(Info, "Loading scene...");
|
||||
Stopwatch stopwatch;
|
||||
_lastSceneLoadTime = DateTime::Now();
|
||||
@@ -900,19 +952,19 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
MessageBox::Show(TEXT("Failed to load scripts.\n\nCannot load scene without game script modules.\n\nSee logs for more info."), TEXT("Missing game modules"), MessageBoxButtons::OK, MessageBoxIcon::Error);
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
return SceneResult::Failed;
|
||||
}
|
||||
|
||||
// Peek meta
|
||||
if (engineBuild < 6000)
|
||||
{
|
||||
LOG(Error, "Invalid serialized engine build.");
|
||||
return true;
|
||||
return SceneResult::Failed;
|
||||
}
|
||||
if (!data.IsArray())
|
||||
{
|
||||
LOG(Error, "Invalid Data member.");
|
||||
return true;
|
||||
return SceneResult::Failed;
|
||||
}
|
||||
|
||||
// Peek scene node value (it's the first actor serialized)
|
||||
@@ -920,16 +972,16 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
if (!sceneId.IsValid())
|
||||
{
|
||||
LOG(Error, "Invalid scene id.");
|
||||
return true;
|
||||
return SceneResult::Failed;
|
||||
}
|
||||
auto modifier = Cache::ISerializeModifier.Get();
|
||||
modifier->EngineBuild = engineBuild;
|
||||
|
||||
// Skip is that scene is already loaded
|
||||
if (FindScene(sceneId) != nullptr)
|
||||
if (Level::FindScene(sceneId) != nullptr)
|
||||
{
|
||||
LOG(Info, "Scene {0} is already loaded.", sceneId);
|
||||
return false;
|
||||
return SceneResult::Failed;
|
||||
}
|
||||
|
||||
// Create scene actor
|
||||
@@ -958,7 +1010,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
SceneObject** objects = sceneObjects->Get();
|
||||
if (context.Async)
|
||||
{
|
||||
ScenesLock.Unlock(); // Unlock scenes from Main Thread so Job Threads can use it to safely setup actors hierarchy (see Actor::Deserialize)
|
||||
Level::ScenesLock.Unlock(); // Unlock scenes from Main Thread so Job Threads can use it to safely setup actors hierarchy (see Actor::Deserialize)
|
||||
JobSystem::Execute([&](int32 i)
|
||||
{
|
||||
PROFILE_MEM(Level);
|
||||
@@ -979,7 +1031,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
else
|
||||
SceneObjectsFactory::HandleObjectDeserializationError(stream);
|
||||
}, dataCount - 1);
|
||||
ScenesLock.Lock();
|
||||
Level::ScenesLock.Lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1015,7 +1067,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
// TODO: - add _loadNoAsync flag to SceneObject or Actor to handle non-async loading for those types (eg. UIControl/UICanvas)
|
||||
if (context.Async)
|
||||
{
|
||||
ScenesLock.Unlock(); // Unlock scenes from Main Thread so Job Threads can use it to safely setup actors hierarchy (see Actor::Deserialize)
|
||||
Level::ScenesLock.Unlock(); // Unlock scenes from Main Thread so Job Threads can use it to safely setup actors hierarchy (see Actor::Deserialize)
|
||||
#if USE_EDITOR
|
||||
volatile int64 deprecated = 0;
|
||||
#endif
|
||||
@@ -1039,7 +1091,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
if (deprecated != 0)
|
||||
ContentDeprecated::Mark();
|
||||
#endif
|
||||
ScenesLock.Lock();
|
||||
Level::ScenesLock.Lock();
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1114,13 +1166,15 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
{
|
||||
PROFILE_CPU_NAMED("BeginPlay");
|
||||
|
||||
ScopeLock lock(ScenesLock);
|
||||
Scenes.Add(scene);
|
||||
ScopeLock lock(Level::ScenesLock);
|
||||
Level::Scenes.Add(scene);
|
||||
SceneBeginData beginData;
|
||||
scene->BeginPlay(&beginData);
|
||||
beginData.OnDone();
|
||||
}
|
||||
|
||||
Stage = Loaded;
|
||||
|
||||
// Fire event
|
||||
CallSceneEvent(SceneEventType::OnSceneLoaded, scene, sceneId);
|
||||
|
||||
@@ -1150,7 +1204,7 @@ bool Level::loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** ou
|
||||
}
|
||||
#endif
|
||||
|
||||
return false;
|
||||
return SceneResult::Success;
|
||||
}
|
||||
|
||||
bool LevelImpl::saveScene(Scene* scene)
|
||||
@@ -1271,7 +1325,8 @@ bool LevelImpl::saveScene(Scene* scene, rapidjson_flax::StringBuffer& outBuffer,
|
||||
bool Level::SaveScene(Scene* scene, bool prettyJson)
|
||||
{
|
||||
ScopeLock lock(_sceneActionsLocker);
|
||||
return SaveSceneAction(scene, prettyJson).Do();
|
||||
SceneAction::Context context;
|
||||
return SaveSceneAction(scene, prettyJson).Do(context) != SceneResult::Success;
|
||||
}
|
||||
|
||||
bool Level::SaveSceneToBytes(Scene* scene, rapidjson_flax::StringBuffer& outData, bool prettyJson)
|
||||
@@ -1317,9 +1372,10 @@ void Level::SaveSceneAsync(Scene* scene)
|
||||
bool Level::SaveAllScenes()
|
||||
{
|
||||
ScopeLock lock(_sceneActionsLocker);
|
||||
SceneAction::Context context;
|
||||
for (int32 i = 0; i < Scenes.Count(); i++)
|
||||
{
|
||||
if (SaveSceneAction(Scenes[i]).Do())
|
||||
if (SaveSceneAction(Scenes[i]).Do(context) != SceneResult::Success)
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -1369,7 +1425,8 @@ bool Level::LoadScene(const Guid& id)
|
||||
|
||||
// Load scene
|
||||
ScopeLock lock(ScenesLock);
|
||||
if (loadScene(sceneAsset))
|
||||
SceneLoader loader;
|
||||
if (loadScene(loader, sceneAsset) != SceneResult::Success)
|
||||
{
|
||||
LOG(Error, "Failed to deserialize scene {0}", id);
|
||||
CallSceneEvent(SceneEventType::OnSceneLoadError, nullptr, id);
|
||||
@@ -1381,7 +1438,8 @@ bool Level::LoadScene(const Guid& id)
|
||||
Scene* Level::LoadSceneFromBytes(const BytesContainer& data)
|
||||
{
|
||||
Scene* scene = nullptr;
|
||||
if (loadScene(data, &scene))
|
||||
SceneLoader loader;
|
||||
if (loadScene(loader, data, &scene) != SceneResult::Success)
|
||||
{
|
||||
LOG(Error, "Failed to deserialize scene from bytes");
|
||||
CallSceneEvent(SceneEventType::OnSceneLoadError, nullptr, Guid::Empty);
|
||||
@@ -1391,7 +1449,6 @@ Scene* Level::LoadSceneFromBytes(const BytesContainer& data)
|
||||
|
||||
bool Level::LoadSceneAsync(const Guid& id)
|
||||
{
|
||||
// Check ID
|
||||
if (!id.IsValid())
|
||||
{
|
||||
Log::ArgumentException();
|
||||
@@ -1407,7 +1464,7 @@ bool Level::LoadSceneAsync(const Guid& id)
|
||||
}
|
||||
|
||||
ScopeLock lock(_sceneActionsLocker);
|
||||
_sceneActions.Enqueue(New<LoadSceneAction>(id, sceneAsset));
|
||||
_sceneActions.Enqueue(New<LoadSceneAction>(id, sceneAsset, true));
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -48,7 +48,12 @@ public:
|
||||
/// <summary>
|
||||
/// True if game objects (actors and scripts) can receive a tick during engine Update/LateUpdate/FixedUpdate events. Can be used to temporarily disable gameplay logic updating.
|
||||
/// </summary>
|
||||
API_FIELD() static bool TickEnabled;
|
||||
API_FIELD(Attributes="DebugCommand") static bool TickEnabled;
|
||||
|
||||
/// <summary>
|
||||
/// Fraction of the frame budget to limit time spent on levels streaming. For example, value of 0.3 means that 30% of frame time can be spent on levels loading within a single frame (eg. 0.3 at 60fps is 4.8ms budget).
|
||||
/// </summary>
|
||||
API_FIELD(Attributes="DebugCommand") static float StreamingFrameBudget;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
@@ -547,10 +552,4 @@ private:
|
||||
};
|
||||
|
||||
static void callActorEvent(ActorEventType eventType, Actor* a, Actor* b);
|
||||
|
||||
// All loadScene assume that ScenesLock has been taken by the calling thread
|
||||
static bool loadScene(JsonAsset* sceneAsset);
|
||||
static bool loadScene(const BytesContainer& sceneData, Scene** outScene = nullptr);
|
||||
static bool loadScene(rapidjson_flax::Document& document, Scene** outScene = nullptr);
|
||||
static bool loadScene(rapidjson_flax::Value& data, int32 engineBuild, Scene** outScene = nullptr, const String* assetPath = nullptr);
|
||||
};
|
||||
|
||||
@@ -19,6 +19,7 @@ API_CLASS() class FLAXENGINE_API Scene : public Actor
|
||||
{
|
||||
friend class Level;
|
||||
friend class ReloadScriptsAction;
|
||||
friend class SceneLoader;
|
||||
DECLARE_SCENE_OBJECT(Scene);
|
||||
|
||||
/// <summary>
|
||||
|
||||
Reference in New Issue
Block a user