Files
FlaxEngine/Source/Engine/Core/ObjectsRemovalService.cpp
2021-01-17 10:18:30 +01:00

210 lines
5.2 KiB
C++

// Copyright (c) 2012-2021 Wojciech Figat. All rights reserved.
#include "ObjectsRemovalService.h"
#include "Collections/Dictionary.h"
#include "Engine/Engine/EngineService.h"
#include "Engine/Threading/Threading.h"
#include "Engine/Engine/Time.h"
#include "Engine/Profiler/ProfilerCPU.h"
#include "Engine/Scripting/ScriptingObject.h"
#include "Log.h"
namespace ObjectsRemovalServiceImpl
{
bool IsReady = false;
CriticalSection PoolLocker;
CriticalSection NewItemsLocker;
DateTime LastUpdate;
float LastUpdateGameTime;
Dictionary<RemovableObject*, float> Pool(8192);
Dictionary<RemovableObject*, float> NewItemsPool(2048);
}
using namespace ObjectsRemovalServiceImpl;
class ObjectsRemovalServiceService : public EngineService
{
public:
ObjectsRemovalServiceService()
: EngineService(TEXT("Objects Removal Service"), -1000)
{
LastUpdate = DateTime::NowUTC();
LastUpdateGameTime = 0;
}
void LateUpdate() override;
void Dispose() override;
};
ObjectsRemovalServiceService ObjectsRemovalServiceServiceInstance;
bool ObjectsRemovalService::IsInPool(RemovableObject* obj)
{
if (!IsReady)
return false;
{
ScopeLock lock(NewItemsLocker);
if (NewItemsPool.ContainsKey(obj))
return true;
}
{
ScopeLock lock(PoolLocker);
if (Pool.ContainsKey(obj))
return true;
}
return false;
}
bool ObjectsRemovalService::HasNewItemsForFlush()
{
NewItemsLocker.Lock();
const bool result = NewItemsPool.HasItems();
NewItemsLocker.Unlock();
return result;
}
void ObjectsRemovalService::Dereference(RemovableObject* obj)
{
if (!IsReady)
return;
NewItemsLocker.Lock();
NewItemsPool.Remove(obj);
NewItemsLocker.Unlock();
PoolLocker.Lock();
Pool.Remove(obj);
PoolLocker.Unlock();
}
void ObjectsRemovalService::Add(RemovableObject* obj, float timeToLive, bool useGameTime)
{
ScopeLock lock(NewItemsLocker);
obj->Flags |= ObjectFlags::WasMarkedToDelete;
if (useGameTime)
obj->Flags |= ObjectFlags::UseGameTimeForDelete;
else
obj->Flags &= ~ObjectFlags::UseGameTimeForDelete;
NewItemsPool[obj] = timeToLive;
}
void ObjectsRemovalService::Flush(float dt, float gameDelta)
{
// Add new items
{
ScopeLock lock(NewItemsLocker);
for (auto i = NewItemsPool.Begin(); i.IsNotEnd(); ++i)
{
Pool[i->Key] = i->Value;
}
NewItemsPool.Clear();
}
// Update timeouts and delete objects that timed out
{
ScopeLock lock(PoolLocker);
for (auto i = Pool.Begin(); i.IsNotEnd(); ++i)
{
auto obj = i->Key;
const float ttl = i->Value - (obj->Flags & ObjectFlags::UseGameTimeForDelete ? gameDelta : dt);
if (ttl <= ZeroTolerance)
{
Pool.Remove(i);
#if BUILD_DEBUG || BUILD_DEVELOPMENT
if (NewItemsPool.ContainsKey(obj))
{
const auto asScriptingObj = dynamic_cast<ScriptingObject*>(obj);
if (asScriptingObj)
{
LOG(Warning, "Object {0} was marked to delete after delete timeout", asScriptingObj->GetID());
}
}
#endif
NewItemsPool.Remove(obj);
//ASSERT(!NewItemsPool.ContainsKey(obj));
obj->OnDeleteObject();
}
else
{
i->Value = ttl;
}
}
}
// Perform removing in loop
// Note: objects during OnDeleteObject call can register new objects to remove with timeout=0, for example Actors do that to remove children and scripts
while (HasNewItemsForFlush())
{
// Add new items
{
ScopeLock lock(NewItemsLocker);
for (auto i = NewItemsPool.Begin(); i.IsNotEnd(); ++i)
{
Pool[i->Key] = i->Value;
}
NewItemsPool.Clear();
}
// Delete objects that timed out
{
ScopeLock lock(PoolLocker);
for (auto i = Pool.Begin(); i.IsNotEnd(); ++i)
{
if (i->Value <= ZeroTolerance)
{
auto obj = i->Key;
Pool.Remove(i);
ASSERT(!NewItemsPool.ContainsKey(obj));
obj->OnDeleteObject();
}
}
}
}
}
void ObjectsRemovalServiceService::LateUpdate()
{
PROFILE_CPU();
// Delete all objects
const auto now = DateTime::NowUTC();
const float dt = (now - LastUpdate).GetTotalSeconds();
float gameDelta = Time::Update.DeltaTime.GetTotalSeconds();
if (Time::GetGamePaused())
gameDelta = 0;
ObjectsRemovalService::Flush(dt, gameDelta);
LastUpdate = now;
}
void ObjectsRemovalServiceService::Dispose()
{
// Collect new objects
ObjectsRemovalService::Flush();
// Delete all remaining objects
{
ScopeLock lock(PoolLocker);
for (auto i = Pool.Begin(); i.IsNotEnd(); ++i)
{
auto obj = i->Key;
Pool.Remove(i);
obj->OnDeleteObject();
}
Pool.Clear();
}
IsReady = false;
}