Add engine fatal error types handling
Add general out-of-memory handling Add safety memory buffer for crash or out of memory handling Refactor Globals exit/error state to be in Engine class
This commit is contained in:
@@ -46,14 +46,7 @@ public:
|
||||
}
|
||||
|
||||
// Create new chunk
|
||||
const auto chunk = New<FlaxChunk>();
|
||||
Output.Header.Chunks[index] = chunk;
|
||||
|
||||
if (chunk == nullptr)
|
||||
{
|
||||
OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
Output.Header.Chunks[index] = New<FlaxChunk>();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -151,14 +151,7 @@ bool CreateAssetContext::AllocateChunk(int32 index)
|
||||
}
|
||||
|
||||
// Create new chunk
|
||||
const auto chunk = New<FlaxChunk>();
|
||||
Data.Header.Chunks[index] = chunk;
|
||||
|
||||
if (chunk == nullptr)
|
||||
{
|
||||
OUT_OF_MEMORY;
|
||||
}
|
||||
|
||||
Data.Header.Chunks[index] = New<FlaxChunk>();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -146,8 +146,10 @@ void Log::Logger::Write(const StringView& msg)
|
||||
#endif
|
||||
}
|
||||
|
||||
#if !BUILD_RELEASE
|
||||
// Send message to platform logging
|
||||
Platform::Log(msg);
|
||||
#endif
|
||||
|
||||
// Write message to log file
|
||||
constexpr int32 LogMaxWriteSize = 1 * 1024 * 1024; // 1GB
|
||||
|
||||
@@ -148,27 +148,17 @@ public:
|
||||
{
|
||||
ASSERT_LOW_LAYER(!_data);
|
||||
_data = static_cast<T*>(Allocator::Allocate(capacity * sizeof(T)));
|
||||
#if ENABLE_ASSERTION
|
||||
if (!_data)
|
||||
OUT_OF_MEMORY;
|
||||
#endif
|
||||
}
|
||||
|
||||
FORCE_INLINE void Relocate(const int32 capacity, int32 oldCount, int32 newCount)
|
||||
{
|
||||
T* newData = capacity != 0 ? static_cast<T*>(Allocator::Allocate(capacity * sizeof(T))) : nullptr;
|
||||
#if ENABLE_ASSERTION
|
||||
if (!newData && capacity != 0)
|
||||
OUT_OF_MEMORY;
|
||||
#endif
|
||||
|
||||
if (oldCount)
|
||||
{
|
||||
if (newCount > 0)
|
||||
Memory::MoveItems(newData, _data, newCount);
|
||||
Memory::DestructItems(_data, oldCount);
|
||||
}
|
||||
|
||||
Allocator::Free(_data);
|
||||
_data = newData;
|
||||
}
|
||||
|
||||
@@ -71,6 +71,9 @@ Action Engine::Draw;
|
||||
Action Engine::Pause;
|
||||
Action Engine::Unpause;
|
||||
Action Engine::RequestingExit;
|
||||
FatalErrorType Engine::FatalError = FatalErrorType::None;
|
||||
bool Engine::IsRequestingExit = false;
|
||||
int32 Engine::ExitCode = 0;
|
||||
Window* Engine::MainWindow = nullptr;
|
||||
|
||||
int32 Engine::Main(const Char* cmdLine)
|
||||
@@ -201,7 +204,7 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
PROFILE_CPU_NAMED("Platform.Tick");
|
||||
Platform::Tick();
|
||||
}
|
||||
|
||||
|
||||
// Update game logic
|
||||
if (Time::OnBeginUpdate(time))
|
||||
{
|
||||
@@ -236,12 +239,13 @@ int32 Engine::Main(const Char* cmdLine)
|
||||
FileSystem::DeleteDirectory(Globals::TemporaryFolder);
|
||||
}
|
||||
|
||||
return Globals::ExitCode;
|
||||
return ExitCode;
|
||||
}
|
||||
|
||||
void Engine::Exit(int32 exitCode)
|
||||
void Engine::Exit(int32 exitCode, FatalErrorType error)
|
||||
{
|
||||
ASSERT(IsInMainThread());
|
||||
FatalError = error;
|
||||
|
||||
// Call on exit event
|
||||
OnExit();
|
||||
@@ -250,23 +254,23 @@ void Engine::Exit(int32 exitCode)
|
||||
exit(exitCode);
|
||||
}
|
||||
|
||||
void Engine::RequestExit(int32 exitCode)
|
||||
void Engine::RequestExit(int32 exitCode, FatalErrorType error)
|
||||
{
|
||||
if (Globals::IsRequestingExit)
|
||||
if (IsRequestingExit)
|
||||
return;
|
||||
#if USE_EDITOR
|
||||
// Send to editor (will leave play mode if need to)
|
||||
if (Editor::Managed->OnAppExit())
|
||||
{
|
||||
Globals::IsRequestingExit = true;
|
||||
Globals::ExitCode = exitCode;
|
||||
RequestingExit();
|
||||
}
|
||||
#else
|
||||
if (!Editor::Managed->OnAppExit())
|
||||
return;
|
||||
#endif
|
||||
IsRequestingExit = true;
|
||||
ExitCode = exitCode;
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS;
|
||||
Globals::IsRequestingExit = true;
|
||||
Globals::ExitCode = exitCode;
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS;
|
||||
FatalError = error;
|
||||
RequestingExit();
|
||||
#endif
|
||||
}
|
||||
|
||||
void Engine::OnFixedUpdate()
|
||||
@@ -407,7 +411,7 @@ bool Engine::IsReady()
|
||||
|
||||
bool Engine::ShouldExit()
|
||||
{
|
||||
return Globals::IsRequestingExit;
|
||||
return IsRequestingExit;
|
||||
}
|
||||
|
||||
bool Engine::IsEditor()
|
||||
|
||||
@@ -14,9 +14,9 @@ class JsonAsset;
|
||||
/// </summary>
|
||||
API_CLASS(Static) class FLAXENGINE_API Engine
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Engine);
|
||||
public:
|
||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Engine);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// The engine start time (local time).
|
||||
/// </summary>
|
||||
@@ -38,7 +38,6 @@ public:
|
||||
API_FIELD(ReadOnly) static uint64 FrameCount;
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Event called on engine fixed update.
|
||||
/// </summary>
|
||||
@@ -84,8 +83,22 @@ public:
|
||||
/// </summary>
|
||||
API_EVENT() static Action RequestingExit;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// The current state of the fatal error. Set to None if no error occurred yet.
|
||||
/// </summary>
|
||||
API_FIELD(ReadOnly) static FatalErrorType FatalError;
|
||||
|
||||
/// <summary>
|
||||
/// Flags set to true if engine needs to be closed (exit is pending). Use FatalError to determinate the exit reason (specific error or normal shutdown).
|
||||
/// </summary>
|
||||
API_FIELD(ReadOnly) static bool IsRequestingExit;
|
||||
|
||||
/// <summary>
|
||||
/// The current process exit code (pending to return).
|
||||
/// </summary>
|
||||
static int32 ExitCode;
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// The main engine function (must be called from platform specific entry point).
|
||||
/// </summary>
|
||||
@@ -97,16 +110,17 @@ public:
|
||||
/// Exits the engine.
|
||||
/// </summary>
|
||||
/// <param name="exitCode">The exit code.</param>
|
||||
API_FUNCTION(Attributes="DebugCommand") static void Exit(int32 exitCode = -1);
|
||||
/// <param name="error">The fatal error type (or None on graceful exit).</param>
|
||||
API_FUNCTION(Attributes="DebugCommand") static void Exit(int32 exitCode = -1, FatalErrorType error = FatalErrorType::None);
|
||||
|
||||
/// <summary>
|
||||
/// Requests normal engine exit.
|
||||
/// </summary>
|
||||
/// <param name="exitCode">The exit code.</param>
|
||||
API_FUNCTION() static void RequestExit(int32 exitCode = 0);
|
||||
/// <param name="error">The fatal error type (or None on graceful exit).</param>
|
||||
API_FUNCTION() static void RequestExit(int32 exitCode = 0, FatalErrorType error = FatalErrorType::None);
|
||||
|
||||
public:
|
||||
|
||||
/// <summary>
|
||||
/// Fixed update callback used by the physics simulation (fixed stepping).
|
||||
/// </summary>
|
||||
@@ -138,7 +152,6 @@ public:
|
||||
static void OnExit();
|
||||
|
||||
public:
|
||||
|
||||
// Returns true if engine is running without main window (aka headless mode).
|
||||
API_PROPERTY() static bool IsHeadless();
|
||||
|
||||
@@ -184,7 +197,6 @@ public:
|
||||
API_PROPERTY() static bool HasGameViewportFocus();
|
||||
|
||||
private:
|
||||
|
||||
static void OnPause();
|
||||
static void OnUnpause();
|
||||
};
|
||||
|
||||
@@ -18,9 +18,11 @@ String Globals::ProjectContentFolder;
|
||||
#if USE_MONO
|
||||
String Globals::MonoPath;
|
||||
#endif
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS;
|
||||
bool Globals::FatalErrorOccurred;
|
||||
bool Globals::IsRequestingExit;
|
||||
int32 Globals::ExitCode;
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS;
|
||||
uint64 Globals::MainThreadID;
|
||||
String Globals::EngineVersion(TEXT(FLAXENGINE_VERSION_TEXT));
|
||||
int32 Globals::EngineBuildNumber = FLAXENGINE_VERSION_BUILD;
|
||||
|
||||
@@ -10,8 +10,9 @@
|
||||
/// </summary>
|
||||
API_CLASS(Static, Attributes="DebugCommand") class FLAXENGINE_API Globals
|
||||
{
|
||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Globals);
|
||||
DECLARE_SCRIPTING_TYPE_NO_SPAWN(Globals);
|
||||
|
||||
public:
|
||||
// Paths
|
||||
|
||||
// Main engine directory path.
|
||||
@@ -34,7 +35,6 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(Globals);
|
||||
API_FIELD(ReadOnly) static String BinariesFolder;
|
||||
|
||||
#if USE_EDITOR
|
||||
|
||||
// Project specific cache folder path (editor-only).
|
||||
API_FIELD(ReadOnly) static String ProjectCacheFolder;
|
||||
|
||||
@@ -43,43 +43,60 @@ DECLARE_SCRIPTING_TYPE_NO_SPAWN(Globals);
|
||||
|
||||
// Game source code directory path (editor-only).
|
||||
API_FIELD(ReadOnly) static String ProjectSourceFolder;
|
||||
|
||||
#endif
|
||||
|
||||
// Project content directory path
|
||||
// Project content directory path.
|
||||
API_FIELD(ReadOnly) static String ProjectContentFolder;
|
||||
|
||||
#if USE_MONO
|
||||
// Mono library folder path
|
||||
// Mono library folder path.
|
||||
API_FIELD(ReadOnly) static String MonoPath;
|
||||
#endif
|
||||
|
||||
public:
|
||||
// State
|
||||
|
||||
// True if fatal error occurred (engine is exiting)
|
||||
static bool FatalErrorOccurred;
|
||||
// True if fatal error occurred (engine is exiting).
|
||||
// [Deprecated in v1.10]
|
||||
static DEPRECATED("Use Engine::FatalError instead.") bool FatalErrorOccurred;
|
||||
|
||||
// True if engine needs to be closed
|
||||
static bool IsRequestingExit;
|
||||
// True if engine needs to be closed.
|
||||
// [Deprecated in v1.10]
|
||||
static DEPRECATED("Use Engine::IsRequestingExit instead.") bool IsRequestingExit;
|
||||
|
||||
/// <summary>
|
||||
/// True if engine needs to be closed
|
||||
/// Flags set to true if engine needs to be closed (exit is pending).
|
||||
/// [Deprecated in v1.10]
|
||||
/// </summary>
|
||||
API_PROPERTY() FORCE_INLINE static bool GetIsRequestingExit() { return IsRequestingExit; }
|
||||
API_PROPERTY() DEPRECATED("Use Engine::IsRequestingExit instead.") FORCE_INLINE static bool GetIsRequestingExit()
|
||||
{
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS;
|
||||
return IsRequestingExit;
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// True if fatal error occurred (engine is exiting)
|
||||
/// Flags set to true if fatal error occurred (engine is exiting).
|
||||
/// [Deprecated in v1.10]
|
||||
/// </summary>
|
||||
API_PROPERTY() FORCE_INLINE static bool GetFatalErrorOccurred() { return FatalErrorOccurred; }
|
||||
API_PROPERTY() DEPRECATED("Use Engine::FatalError instead.") FORCE_INLINE static bool GetFatalErrorOccurred()
|
||||
{
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS;
|
||||
return FatalErrorOccurred;
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS;
|
||||
}
|
||||
|
||||
// Exit code
|
||||
static int32 ExitCode;
|
||||
// Process exit code (pending to return).
|
||||
// [Deprecated in v1.10]
|
||||
static DEPRECATED("Use Engine::ExitCode instead.") int32 ExitCode;
|
||||
|
||||
public:
|
||||
// Threading
|
||||
|
||||
// Main Engine thread id
|
||||
// Main Engine thread id.
|
||||
API_FIELD(ReadOnly) static uint64 MainThreadID;
|
||||
|
||||
public:
|
||||
// Config
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Graphics/GPUDevice.h"
|
||||
#include "Engine/Threading/Threading.h"
|
||||
#include "Engine/Engine/Globals.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
|
||||
#define GPU_TASKS_USE_DEDICATED_CONTEXT 0
|
||||
|
||||
@@ -37,7 +37,7 @@ GPUTasksContext::~GPUTasksContext()
|
||||
auto task = tasks[i];
|
||||
if (task->GetSyncPoint() <= _currentSyncPoint && task->GetState() != TaskState::Finished)
|
||||
{
|
||||
if (!Globals::IsRequestingExit)
|
||||
if (!Engine::IsRequestingExit)
|
||||
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
|
||||
task->CancelSync();
|
||||
}
|
||||
@@ -62,7 +62,7 @@ void GPUTasksContext::OnCancelSync(GPUTask* task)
|
||||
|
||||
_tasksDone.Remove(task);
|
||||
|
||||
if (!Globals::IsRequestingExit)
|
||||
if (!Engine::IsRequestingExit)
|
||||
LOG(Warning, "{0} has been canceled before a sync", task->ToString());
|
||||
}
|
||||
|
||||
|
||||
@@ -247,10 +247,6 @@ void NetworkStream::WriteBytes(const void* data, uint32 bytes)
|
||||
while (newLength < position + bytes)
|
||||
newLength *= 2;
|
||||
byte* newBuf = (byte*)Allocator::Allocate(newLength);
|
||||
if (newBuf == nullptr)
|
||||
{
|
||||
OUT_OF_MEMORY;
|
||||
}
|
||||
if (_buffer && _length)
|
||||
Platform::MemoryCopy(newBuf, _buffer, _length);
|
||||
if (_allocated)
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
#endif
|
||||
#define PLATFORM_TYPE PlatformType::Android
|
||||
#define PLATFORM_CACHE_LINE_SIZE 64
|
||||
#define PLATFORM_OUT_OF_MEMORY_BUFFER_SIZE (64ull * 1024) // 64 kB
|
||||
|
||||
#define USE_MONO_AOT_MODE MONO_AOT_MODE_NONE
|
||||
|
||||
|
||||
@@ -49,6 +49,7 @@ float PlatformBase::CustomDpiScale = 1.0f;
|
||||
Array<User*, FixedAllocation<8>> PlatformBase::Users;
|
||||
Delegate<User*> PlatformBase::UserAdded;
|
||||
Delegate<User*> PlatformBase::UserRemoved;
|
||||
void* OutOfMemoryBuffer = nullptr;
|
||||
|
||||
const Char* ToString(NetworkConnectionType value)
|
||||
{
|
||||
@@ -150,6 +151,12 @@ bool PlatformBase::Init()
|
||||
|
||||
srand((unsigned int)Platform::GetTimeCycles());
|
||||
|
||||
// Preallocate safety dynamic buffer to be released before Out Of Memory reporting to ensure code can properly execute
|
||||
#ifndef PLATFORM_OUT_OF_MEMORY_BUFFER_SIZE
|
||||
#define PLATFORM_OUT_OF_MEMORY_BUFFER_SIZE (1ull * 1024 * 1024) // 1 MB
|
||||
#endif
|
||||
OutOfMemoryBuffer = Allocator::Allocate(PLATFORM_OUT_OF_MEMORY_BUFFER_SIZE);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -188,6 +195,8 @@ void PlatformBase::BeforeExit()
|
||||
|
||||
void PlatformBase::Exit()
|
||||
{
|
||||
Allocator::Free(OutOfMemoryBuffer);
|
||||
OutOfMemoryBuffer = nullptr;
|
||||
}
|
||||
|
||||
#if COMPILE_WITH_PROFILER
|
||||
@@ -257,25 +266,35 @@ bool PlatformBase::Is64BitApp()
|
||||
#endif
|
||||
}
|
||||
|
||||
void PlatformBase::Fatal(const Char* msg, void* context)
|
||||
void PlatformBase::Fatal(const StringView& msg, void* context, FatalErrorType error)
|
||||
{
|
||||
// Check if is already during fatal state
|
||||
if (Globals::FatalErrorOccurred)
|
||||
if (Engine::FatalError != FatalErrorType::None)
|
||||
{
|
||||
// Just send one more error to the log and back
|
||||
LOG(Error, "Error after fatal error: {0}", msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// Free OOM safety buffer
|
||||
Allocator::Free(OutOfMemoryBuffer);
|
||||
OutOfMemoryBuffer = nullptr;
|
||||
|
||||
// Set flags
|
||||
PRAGMA_DISABLE_DEPRECATION_WARNINGS;
|
||||
Globals::FatalErrorOccurred = true;
|
||||
Globals::IsRequestingExit = true;
|
||||
Globals::ExitCode = -1;
|
||||
Globals::ExitCode = -Math::Max((int32)error, 1);
|
||||
PRAGMA_ENABLE_DEPRECATION_WARNINGS;
|
||||
Engine::IsRequestingExit = true;
|
||||
Engine::ExitCode = -Math::Max((int32)error, 1);
|
||||
Engine::FatalError = error;
|
||||
Engine::RequestingExit();
|
||||
|
||||
// Collect crash info (platform-dependant implementation that might collect stack trace and/or create memory dump)
|
||||
{
|
||||
// Log separation for crash info
|
||||
LOG_FLUSH();
|
||||
Log::Logger::WriteFloor();
|
||||
LOG(Error, "");
|
||||
LOG(Error, "Critical error! Reason: {0}", msg);
|
||||
@@ -358,11 +377,11 @@ void PlatformBase::Fatal(const Char* msg, void* context)
|
||||
// Only main thread can call exit directly
|
||||
if (IsInMainThread())
|
||||
{
|
||||
Engine::Exit(-1);
|
||||
Engine::Exit(Engine::ExitCode, error);
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformBase::Error(const Char* msg)
|
||||
void PlatformBase::Error(const StringView& msg)
|
||||
{
|
||||
#if PLATFORM_HAS_HEADLESS_MODE
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
@@ -372,7 +391,7 @@ void PlatformBase::Error(const Char* msg)
|
||||
ansi += PLATFORM_LINE_TERMINATOR;
|
||||
printf("Error: %s\n", ansi.Get());
|
||||
#else
|
||||
std::cout << "Error: " << msg << std::endl;
|
||||
std::cout << "Error: " << *msg << std::endl;
|
||||
#endif
|
||||
}
|
||||
else
|
||||
@@ -382,12 +401,12 @@ void PlatformBase::Error(const Char* msg)
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformBase::Warning(const Char* msg)
|
||||
void PlatformBase::Warning(const StringView& msg)
|
||||
{
|
||||
#if PLATFORM_HAS_HEADLESS_MODE
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
{
|
||||
std::cout << "Warning: " << msg << std::endl;
|
||||
std::cout << "Warning: " << *msg << std::endl;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
@@ -396,12 +415,12 @@ void PlatformBase::Warning(const Char* msg)
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformBase::Info(const Char* msg)
|
||||
void PlatformBase::Info(const StringView& msg)
|
||||
{
|
||||
#if PLATFORM_HAS_HEADLESS_MODE
|
||||
if (CommandLine::Options.Headless.IsTrue())
|
||||
{
|
||||
std::cout << "Info: " << msg << std::endl;
|
||||
std::cout << "Info: " << *msg << std::endl;
|
||||
}
|
||||
else
|
||||
#endif
|
||||
@@ -410,24 +429,9 @@ void PlatformBase::Info(const Char* msg)
|
||||
}
|
||||
}
|
||||
|
||||
void PlatformBase::Fatal(const StringView& msg)
|
||||
void PlatformBase::Fatal(const StringView& msg, FatalErrorType error)
|
||||
{
|
||||
Fatal(*msg);
|
||||
}
|
||||
|
||||
void PlatformBase::Error(const StringView& msg)
|
||||
{
|
||||
Error(*msg);
|
||||
}
|
||||
|
||||
void PlatformBase::Warning(const StringView& msg)
|
||||
{
|
||||
Warning(*msg);
|
||||
}
|
||||
|
||||
void PlatformBase::Info(const StringView& msg)
|
||||
{
|
||||
Info(*msg);
|
||||
Fatal(msg, nullptr, error);
|
||||
}
|
||||
|
||||
void PlatformBase::Log(const StringView& msg)
|
||||
@@ -443,28 +447,43 @@ void PlatformBase::Crash(int32 line, const char* file)
|
||||
{
|
||||
const StringAsUTF16<256> fileUTF16(file);
|
||||
const String msg = String::Format(TEXT("Fatal crash!\nFile: {0}\nLine: {1}"), fileUTF16.Get(), line);
|
||||
LOG_STR(Fatal, msg);
|
||||
LOG_STR(Error, msg);
|
||||
Fatal(msg, nullptr, FatalErrorType::Assertion);
|
||||
}
|
||||
|
||||
void PlatformBase::OutOfMemory(int32 line, const char* file)
|
||||
{
|
||||
const StringAsUTF16<256> fileUTF16(file);
|
||||
const String msg = String::Format(TEXT("Out of memory error!\nFile: {0}\nLine: {1}"), fileUTF16.Get(), line);
|
||||
LOG_STR(Fatal, msg);
|
||||
fmt_flax::allocator allocator;
|
||||
fmt_flax::memory_buffer buffer(allocator);
|
||||
static_assert(fmt::inline_buffer_size > 300, "Update stack buffer to prevent dynamic memory allocation on Out Of Memory.");
|
||||
if (file)
|
||||
{
|
||||
const StringAsUTF16<256> fileUTF16(file);
|
||||
fmt_flax::format(buffer, TEXT("Out of memory error!\nFile: {0}\nLine: {1}"), fileUTF16.Get(), line);
|
||||
}
|
||||
else
|
||||
{
|
||||
fmt_flax::format(buffer, TEXT("Out of memory error!"));
|
||||
}
|
||||
const StringView msg(buffer.data(), (int32)buffer.size());
|
||||
LOG_STR(Error, msg);
|
||||
Fatal(msg, nullptr, FatalErrorType::OutOfMemory);
|
||||
}
|
||||
|
||||
void PlatformBase::MissingCode(int32 line, const char* file, const char* info)
|
||||
{
|
||||
const StringAsUTF16<256> fileUTF16(file);
|
||||
const String msg = String::Format(TEXT("TODO: {0}\nFile: {1}\nLine: {2}"), String(info), fileUTF16.Get(), line);
|
||||
LOG_STR(Fatal, msg);
|
||||
LOG_STR(Error, msg);
|
||||
Fatal(msg, nullptr, FatalErrorType::Assertion);
|
||||
}
|
||||
|
||||
void PlatformBase::Assert(const char* message, const char* file, int line)
|
||||
{
|
||||
const StringAsUTF16<256> fileUTF16(file);
|
||||
const String msg = String::Format(TEXT("Assertion failed!\nFile: {0}\nLine: {1}\n\nExpression: {2}"), fileUTF16.Get(), line, String(message));
|
||||
LOG_STR(Fatal, msg);
|
||||
LOG_STR(Error, msg);
|
||||
Fatal(msg, nullptr, FatalErrorType::Assertion);
|
||||
}
|
||||
|
||||
void PlatformBase::CheckFailed(const char* message, const char* file, int line)
|
||||
|
||||
@@ -125,6 +125,23 @@ enum class ThreadPriority
|
||||
|
||||
extern FLAXENGINE_API const Char* ToString(ThreadPriority value);
|
||||
|
||||
/// <summary>
|
||||
/// Possible fatal error types that cause engine exit.
|
||||
/// </summary>
|
||||
API_ENUM() enum class FatalErrorType
|
||||
{
|
||||
// No fatal error set.
|
||||
None,
|
||||
// Not defined or custom error.
|
||||
Unknown,
|
||||
// Runtime exception caught by the handler (eg. stack overflow, invalid memory address access).
|
||||
Exception,
|
||||
// Data assertion failed (eg. invalid value or code usage).
|
||||
Assertion,
|
||||
// Program run out of memory to allocate.
|
||||
OutOfMemory,
|
||||
};
|
||||
|
||||
API_INJECT_CODE(cpp, "#include \"Engine/Platform/Platform.h\"");
|
||||
|
||||
/// <summary>
|
||||
@@ -457,32 +474,15 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="msg">The message content.</param>
|
||||
/// <param name="context">The platform-dependent context for the stack trace collecting (eg. platform exception info).</param>
|
||||
static void Fatal(const Char* msg, void* context = nullptr);
|
||||
/// <param name="error">The fatal error type.</param>
|
||||
API_FUNCTION() static void Fatal(const StringView& msg, void* context, FatalErrorType error = FatalErrorType::Unknown);
|
||||
|
||||
/// <summary>
|
||||
/// Shows the error message to the user.
|
||||
/// </summary>
|
||||
/// <param name="msg">The message content.</param>
|
||||
static void Error(const Char* msg);
|
||||
|
||||
/// <summary>
|
||||
/// Shows the warning message to the user.
|
||||
/// </summary>
|
||||
/// <param name="msg">The message content.</param>
|
||||
static void Warning(const Char* msg);
|
||||
|
||||
/// <summary>
|
||||
/// Shows the information message to the user.
|
||||
/// </summary>
|
||||
/// <param name="msg">The message content.</param>
|
||||
static void Info(const Char* msg);
|
||||
|
||||
public:
|
||||
/// <summary>
|
||||
/// Shows the fatal error message to the user.
|
||||
/// </summary>
|
||||
/// <param name="msg">The message content.</param>
|
||||
API_FUNCTION() static void Fatal(const StringView& msg);
|
||||
/// <param name="error">The fatal error type.</param>
|
||||
API_FUNCTION() static void Fatal(const StringView& msg, FatalErrorType error = FatalErrorType::Unknown);
|
||||
|
||||
/// <summary>
|
||||
/// Shows the error message to the user.
|
||||
@@ -527,7 +527,7 @@ public:
|
||||
/// </summary>
|
||||
/// <param name="line">The source line.</param>
|
||||
/// <param name="file">The source file.</param>
|
||||
NO_RETURN static void OutOfMemory(int32 line, const char* file);
|
||||
NO_RETURN static void OutOfMemory(int32 line = -1, const char* file = nullptr);
|
||||
|
||||
/// <summary>
|
||||
/// Performs a fatal crash due to code not being implemented.
|
||||
|
||||
@@ -30,6 +30,8 @@ void* UnixPlatform::Allocate(uint64 size, uint64 alignment)
|
||||
// Calculate the offset and store it behind aligned pointer
|
||||
*((offset_t*)ptr - 1) = (offset_t)((uintptr_t)ptr - (uintptr_t)p);
|
||||
}
|
||||
else
|
||||
OutOfMemory();
|
||||
#if COMPILE_WITH_PROFILER
|
||||
OnMemoryAlloc(ptr, size);
|
||||
#endif
|
||||
|
||||
@@ -259,6 +259,8 @@ void Win32Platform::Prefetch(void const* ptr)
|
||||
void* Win32Platform::Allocate(uint64 size, uint64 alignment)
|
||||
{
|
||||
void* ptr = _aligned_malloc((size_t)size, (size_t)alignment);
|
||||
if (!ptr)
|
||||
OutOfMemory();
|
||||
#if COMPILE_WITH_PROFILER
|
||||
OnMemoryAlloc(ptr, size);
|
||||
#endif
|
||||
|
||||
@@ -282,7 +282,7 @@ long __stdcall WindowsPlatform::SehExceptionHandler(EXCEPTION_POINTERS* ep)
|
||||
}
|
||||
|
||||
// Skip if engine already crashed
|
||||
if (Globals::FatalErrorOccurred)
|
||||
if (Engine::FatalError != FatalErrorType::None)
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
|
||||
// Get exception info
|
||||
@@ -326,7 +326,7 @@ long __stdcall WindowsPlatform::SehExceptionHandler(EXCEPTION_POINTERS* ep)
|
||||
}
|
||||
|
||||
// Crash engine
|
||||
Platform::Fatal(errorMsg.Get(), ep);
|
||||
Platform::Fatal(errorMsg.Get(), ep, FatalErrorType::Exception);
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
#define PLATFORM_ARCH ArchitectureType::ARM64
|
||||
#define PLATFORM_CACHE_LINE_SIZE 128
|
||||
#define PLATFORM_DEBUG_BREAK __builtin_trap()
|
||||
#define PLATFORM_OUT_OF_MEMORY_BUFFER_SIZE (64ull * 1024) // 64 kB
|
||||
|
||||
// Use AOT for Mono
|
||||
#define USE_MONO_AOT 1
|
||||
|
||||
@@ -14,18 +14,7 @@ MemoryWriteStream::MemoryWriteStream()
|
||||
MemoryWriteStream::MemoryWriteStream(uint32 capacity)
|
||||
: _capacity(capacity)
|
||||
{
|
||||
if (capacity > 0)
|
||||
{
|
||||
_buffer = (byte*)Allocator::Allocate(capacity);
|
||||
if (_buffer == nullptr)
|
||||
{
|
||||
OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
_buffer = nullptr;
|
||||
}
|
||||
_buffer = capacity > 0 ? (byte*)Allocator::Allocate(capacity) : nullptr;
|
||||
_position = _buffer;
|
||||
}
|
||||
|
||||
@@ -46,10 +35,6 @@ void* MemoryWriteStream::Move(uint32 bytes)
|
||||
while (newCapacity < position + bytes)
|
||||
newCapacity *= 2;
|
||||
byte* newBuf = (byte*)Allocator::Allocate(newCapacity);
|
||||
if (newBuf == nullptr)
|
||||
{
|
||||
OUT_OF_MEMORY;
|
||||
}
|
||||
Platform::MemoryCopy(newBuf, _buffer, _capacity);
|
||||
Allocator::Free(_buffer);
|
||||
|
||||
@@ -73,10 +58,6 @@ void MemoryWriteStream::Reset(uint32 capacity)
|
||||
{
|
||||
Allocator::Free(_buffer);
|
||||
_buffer = (byte*)Allocator::Allocate(capacity);
|
||||
if (_buffer == nullptr)
|
||||
{
|
||||
OUT_OF_MEMORY;
|
||||
}
|
||||
_capacity = capacity;
|
||||
}
|
||||
|
||||
@@ -142,10 +123,6 @@ void MemoryWriteStream::WriteBytes(const void* data, uint32 bytes)
|
||||
while (newCapacity < position + bytes)
|
||||
newCapacity *= 2;
|
||||
byte* newBuf = (byte*)Allocator::Allocate(newCapacity);
|
||||
if (newBuf == nullptr)
|
||||
{
|
||||
OUT_OF_MEMORY;
|
||||
}
|
||||
Platform::MemoryCopy(newBuf, _buffer, _capacity);
|
||||
Allocator::Free(_buffer);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "Engine/Core/Log.h"
|
||||
#include "Engine/Core/Types/TimeSpan.h"
|
||||
#include "Engine/Core/Math/Math.h"
|
||||
#include "Engine/Engine/Engine.h"
|
||||
#include "Engine/Level/Actors/BoxBrush.h"
|
||||
#include "Engine/Level/SceneQuery.h"
|
||||
#include "Engine/Renderer/Renderer.h"
|
||||
@@ -414,7 +415,7 @@ int32 ShadowsOfMordor::Builder::doWork()
|
||||
// Clear
|
||||
_wasBuildCalled = false;
|
||||
IsBakingLightmaps = false;
|
||||
if (!Globals::FatalErrorOccurred)
|
||||
if (Engine::FatalError == FatalErrorType::None)
|
||||
deleteState();
|
||||
|
||||
// Release scenes data
|
||||
|
||||
@@ -328,11 +328,6 @@ void VideoBackendPlayer::UpdateVideoFrame(Span<byte> data, TimeSpan time, TimeSp
|
||||
if (VideoFrameMemory.Length() < (int32)slicePitch)
|
||||
{
|
||||
VideoFrameMemory.Allocate(slicePitch);
|
||||
if (VideoFrameMemory.IsInvalid())
|
||||
{
|
||||
OUT_OF_MEMORY;
|
||||
return;
|
||||
}
|
||||
}
|
||||
Platform::MemoryCopy(VideoFrameMemory.Get(), data.Get(), slicePitch);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user