diff --git a/Source/Engine/Level/Scene/SceneRendering.cpp b/Source/Engine/Level/Scene/SceneRendering.cpp index e55dbd43f..d7225036c 100644 --- a/Source/Engine/Level/Scene/SceneRendering.cpp +++ b/Source/Engine/Level/Scene/SceneRendering.cpp @@ -7,9 +7,23 @@ #include "Engine/Graphics/RenderView.h" #include "Engine/Renderer/RenderList.h" #include "Engine/Threading/JobSystem.h" -#include "Engine/Threading/Threading.h" #include "Engine/Profiler/ProfilerCPU.h" #include "Engine/Profiler/ProfilerMemory.h" +#if !BUILD_RELEASE +#include "Engine/Graphics/GPUDevice.h" +#include "Engine/Core/Log.h" +#endif + +#if BUILD_RELEASE +#define CHECK_SCENE_EDIT_ACCESS() +#else +#define CHECK_SCENE_EDIT_ACCESS() \ + if (Locker.HasLock(false) && IsInMainThread() && GPUDevice::Instance && GPUDevice::Instance->IsRendering()) \ + { \ + LOG(Error, "Adding/removing actors during rendering is not supported ({}, '{}').", a->ToString(), a->GetNamePath()); \ + return; \ + } +#endif ISceneRenderingListener::~ISceneRenderingListener() { @@ -148,6 +162,7 @@ void SceneRendering::AddActor(Actor* a, int32& key) if (key != -1) return; PROFILE_MEM(Graphics); + CHECK_SCENE_EDIT_ACCESS(); const int32 category = a->_drawCategory; ConcurrentSystemLocker::WriteScope lock(Locker, true); auto& list = Actors[category]; @@ -192,6 +207,7 @@ void SceneRendering::UpdateActor(Actor* a, int32& key, ISceneRenderingListener:: void SceneRendering::RemoveActor(Actor* a, int32& key) { + CHECK_SCENE_EDIT_ACCESS(); const int32 category = a->_drawCategory; ConcurrentSystemLocker::WriteScope lock(Locker, true); auto& list = Actors[category]; diff --git a/Source/Engine/Threading/ConcurrentSystemLocker.cpp b/Source/Engine/Threading/ConcurrentSystemLocker.cpp index d936f8307..cafc3dadb 100644 --- a/Source/Engine/Threading/ConcurrentSystemLocker.cpp +++ b/Source/Engine/Threading/ConcurrentSystemLocker.cpp @@ -2,6 +2,9 @@ #include "ConcurrentSystemLocker.h" #include "Engine/Platform/Platform.h" +#if !BUILD_RELEASE +#include "Engine/Core/Log.h" +#endif ConcurrentSystemLocker::ConcurrentSystemLocker() { @@ -12,7 +15,25 @@ void ConcurrentSystemLocker::Begin(bool write, bool exclusively) { volatile int64* thisCounter = &_counters[write]; volatile int64* otherCounter = &_counters[!write]; + +#if !BUILD_RELEASE + int32 retries = 0; + double startTime = Platform::GetTimeSeconds(); +#endif RETRY: +#if !BUILD_RELEASE + retries++; + if (retries > 1000) + { + double endTime = Platform::GetTimeSeconds(); + if (endTime - startTime > 0.5f) + { + LOG(Error, "Deadlock detected in ConcurrentSystemLocker! Thread 0x{0:x} waits for {1} ms...", Platform::GetCurrentThreadID(), (int32)((endTime - startTime) * 1000.0)); + retries = 0; + } + } +#endif + // Check if we can enter (cannot read while someone else is writing and vice versa) if (Platform::AtomicRead(otherCounter) != 0) { @@ -47,3 +68,8 @@ void ConcurrentSystemLocker::End(bool write) // Mark that we left this section Platform::InterlockedDecrement(&_counters[write]); } + +bool ConcurrentSystemLocker::HasLock(bool write) const +{ + return Platform::AtomicRead(&_counters[write]) != 0; +} diff --git a/Source/Engine/Threading/ConcurrentSystemLocker.h b/Source/Engine/Threading/ConcurrentSystemLocker.h index 031b7e685..0b46a64f5 100644 --- a/Source/Engine/Threading/ConcurrentSystemLocker.h +++ b/Source/Engine/Threading/ConcurrentSystemLocker.h @@ -19,6 +19,7 @@ public: void Begin(bool write, bool exclusively = false); void End(bool write); + bool HasLock(bool write) const; public: template