From a48ce78733ec1b286d72f8c61ba6e30ff3e39d09 Mon Sep 17 00:00:00 2001 From: Wojtek Figat Date: Thu, 26 Mar 2026 12:28:31 +0100 Subject: [PATCH] Fix crash when loading `CollisionData` before physics init #3971 --- Source/Engine/Content/Asset.cpp | 34 +++++++++++++++++++++++ Source/Engine/Content/Asset.h | 4 +++ Source/Engine/Content/Assets/Material.cpp | 13 ++------- Source/Engine/Physics/CollisionData.cpp | 2 ++ 4 files changed, 42 insertions(+), 11 deletions(-) diff --git a/Source/Engine/Content/Asset.cpp b/Source/Engine/Content/Asset.cpp index 7a35d3b28..a9b1a7fc9 100644 --- a/Source/Engine/Content/Asset.cpp +++ b/Source/Engine/Content/Asset.cpp @@ -8,6 +8,8 @@ #include "Loading/Tasks/LoadAssetTask.h" #include "Engine/Core/Log.h" #include "Engine/Core/LogContext.h" +#include "Engine/Graphics/GPUDevice.h" +#include "Engine/Physics/Physics.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerMemory.h" #include "Engine/Scripting/ManagedCLR/MCore.h" @@ -703,6 +705,38 @@ void Asset::onUnload_MainThread() OnUnloaded(this); } +bool Asset::WaitForInitGraphics() +{ +#define IS_GPU_NOT_READY() (GPUDevice::Instance == nullptr || GPUDevice::Instance->GetState() != GPUDevice::DeviceState::Ready) + if (!IsInMainThread() && IS_GPU_NOT_READY()) + { + PROFILE_CPU(); + ZoneColor(TracyWaitZoneColor); + int32 timeout = 1000; + while (IS_GPU_NOT_READY() && timeout-- > 0) + Platform::Sleep(1); + if (IS_GPU_NOT_READY()) + return true; + } +#undef IS_GPU_NOT_READY + return false; +} + +bool Asset::WaitForInitPhysics() +{ + if (!IsInMainThread() && !Physics::DefaultScene) + { + PROFILE_CPU(); + ZoneColor(TracyWaitZoneColor); + int32 timeout = 1000; + while (!Physics::DefaultScene && timeout-- > 0) + Platform::Sleep(1); + if (!Physics::DefaultScene) + return true; + } + return false; +} + #if USE_EDITOR bool Asset::OnCheckSave(const StringView& path) const diff --git a/Source/Engine/Content/Asset.h b/Source/Engine/Content/Asset.h index e6607e62c..a7913a5a5 100644 --- a/Source/Engine/Content/Asset.h +++ b/Source/Engine/Content/Asset.h @@ -285,6 +285,10 @@ protected: virtual void onRename(const StringView& newPath) = 0; #endif + // Utilities to ensure specific engine systems are initialized before loading asset (eg. assets can be loaded during engine startup). + static bool WaitForInitGraphics(); + static bool WaitForInitPhysics(); + public: // [ManagedScriptingObject] String ToString() const override; diff --git a/Source/Engine/Content/Assets/Material.cpp b/Source/Engine/Content/Assets/Material.cpp index b4cf55d4d..e46060397 100644 --- a/Source/Engine/Content/Assets/Material.cpp +++ b/Source/Engine/Content/Assets/Material.cpp @@ -5,7 +5,6 @@ #include "Engine/Core/Types/DataContainer.h" #include "Engine/Content/Upgraders/ShaderAssetUpgrader.h" #include "Engine/Content/Factories/BinaryAssetFactory.h" -#include "Engine/Graphics/GPUDevice.h" #include "Engine/Graphics/RenderTools.h" #include "Engine/Graphics/Materials/MaterialShader.h" #include "Engine/Graphics/Shaders/Cache/ShaderCacheManager.h" @@ -156,16 +155,8 @@ Asset::LoadResult Material::load() FlaxChunk* materialParamsChunk; // Wait for the GPU Device to be ready (eg. case when loading material before GPU init) -#define IS_GPU_NOT_READY() (GPUDevice::Instance == nullptr || GPUDevice::Instance->GetState() != GPUDevice::DeviceState::Ready) - if (!IsInMainThread() && IS_GPU_NOT_READY()) - { - int32 timeout = 1000; - while (IS_GPU_NOT_READY() && timeout-- > 0) - Platform::Sleep(1); - if (IS_GPU_NOT_READY()) - return LoadResult::InvalidData; - } -#undef IS_GPU_NOT_READY + if (WaitForInitGraphics()) + return LoadResult::CannotLoadData; // If engine was compiled with shaders compiling service: // - Material should be changed in need to convert it to the newer version (via Visject Surface) diff --git a/Source/Engine/Physics/CollisionData.cpp b/Source/Engine/Physics/CollisionData.cpp index c65ea8a6b..94cef111e 100644 --- a/Source/Engine/Physics/CollisionData.cpp +++ b/Source/Engine/Physics/CollisionData.cpp @@ -257,6 +257,8 @@ Asset::LoadResult CollisionData::load() CollisionData::LoadResult CollisionData::load(const SerializedOptions* options, byte* dataPtr, int32 dataSize) { + if (WaitForInitPhysics()) + return LoadResult::CannotLoadData; PROFILE_MEM(Physics); // Load options