diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index ddff7fc8a..b6a7be5c9 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -7,6 +7,7 @@ #include "Loading/ContentLoadingManager.h" #include "Loading/Tasks/LoadAssetTask.h" #include "Engine/Core/Log.h" +#include "Engine/Core/LogContext.h" #include "Engine/Engine/Engine.h" #include "Engine/Threading/Threading.h" #include "Engine/Profiler/ProfilerCPU.h" @@ -587,9 +588,10 @@ bool Asset::IsInternalType() const bool Asset::onLoad(LoadAssetTask* task) { - if (task->Asset.Get() != this || Platform::AtomicRead(&_loadingTask) == 0) // It may fail when task is cancelled and new one was created later (don't crash but just end with an error) + if (task->Asset.Get() != this || Platform::AtomicRead(&_loadingTask) == 0) return true; + LogContextScope logContext(GetID()); Locker.Lock(); diff --git a/Source/Engine/Content/Content.cpp b/Source/Engine/Content/Content.cpp index fa4517911..92e1baf7b 100644 --- a/Source/Engine/Content/Content.cpp +++ b/Source/Engine/Content/Content.cpp @@ -9,6 +9,7 @@ #include "Storage/JsonStorageProxy.h" #include "Factories/IAssetFactory.h" #include "Engine/Core/Log.h" +#include "Engine/Core/LogContext.h" #include "Engine/Core/Types/String.h" #include "Engine/Core/ObjectsRemovalService.h" #include "Engine/Engine/EngineService.h" @@ -970,6 +971,7 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type) if (IsAssetTypeIdInvalid(type, result->GetTypeHandle()) && !result->Is(type)) { LOG(Warning, "Different loaded asset type! Asset: \'{0}\'. Expected type: {1}", result->ToString(), type.ToString()); + LogContext::Print(LogType::Warning); return nullptr; } return result; @@ -1004,6 +1006,7 @@ Asset* Content::LoadAsync(const Guid& id, const ScriptingTypeHandle& type) if (!GetAssetInfo(id, assetInfo)) { LOG(Warning, "Invalid or missing asset ({0}, {1}).", id, type.ToString()); + LogContext::Print(LogType::Warning); LOAD_FAILED(); } #if ASSETS_LOADING_EXTRA_VERIFICATION diff --git a/Source/Engine/Core/LogContext.cpp b/Source/Engine/Core/LogContext.cpp index d4516e2d1..4264d8759 100644 --- a/Source/Engine/Core/LogContext.cpp +++ b/Source/Engine/Core/LogContext.cpp @@ -1,9 +1,15 @@ // Copyright (c) 2012-2024 Wojciech Figat. All rights reserved. #include "LogContext.h" +#include "Engine/Core/Log.h" #include "Engine/Core/Types/Guid.h" #include "Engine/Core/Types/String.h" +#include "Engine/Core/Types/StringBuilder.h" #include "Engine/Core/Collections/Array.h" +#include "Engine/Scripting/Scripting.h" +#include "Engine/Scripting/Script.h" +#include "Engine/Content/Asset.h" +#include "Engine/Level/Actor.h" #include "Engine/Threading/ThreadLocal.h" struct LogContextThreadData @@ -30,7 +36,7 @@ struct LogContextThreadData Count--; } - LogContextData Peek() + LogContextData Peek() const { return Count > 0 ? Ptr[Count - 1] : LogContextData(); } @@ -38,12 +44,54 @@ struct LogContextThreadData ThreadLocal GlobalLogContexts; -String LogContext::GetInfo() +void LogContext::Print(LogType verbosity) { - LogContextData context = LogContext::Get(); - if (context.ObjectID != Guid::Empty) - return String::Format(TEXT("(Loading source was {0})"), context.ObjectID); - return String::Empty; + auto& stack = GlobalLogContexts.Get(); + if (stack.Count == 0) + return; + const StringView indentation(TEXT(" ")); + StringBuilder msg; + for (int32 index = (int32)stack.Count - 1; index >= 0; index--) + { + // Build call hierarchy via indentation + msg.Clear(); + for (uint32 i = index; i < stack.Count; i++) + msg.Append(indentation); + + LogContextData& context = stack.Ptr[index]; + if (context.ObjectID != Guid::Empty) + { + // Object reference context + msg.Append(TEXT(" Referenced by ")); + if (ScriptingObject* object = Scripting::TryFindObject(context.ObjectID)) + { + const StringAnsiView typeName(object->GetType().Fullname); + if (Asset* asset = ScriptingObject::Cast(object)) + { + msg.AppendFormat(TEXT("asset '{}' ({}, {})"), asset->GetPath(), asset->GetTypeName(), context.ObjectID); + } + else if (Actor* actor = ScriptingObject::Cast(object)) + { + msg.AppendFormat(TEXT("actor '{}' ({}, {})"), actor->GetNamePath(), String(typeName), context.ObjectID); + } + else if (Script* script = ScriptingObject::Cast