* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include <algorithm>
#include <cstdint>
#include <base/containers/array_view.h>
#include <base/containers/atomics.h>
#include <base/containers/iterator.h>
#include <base/containers/unique_ptr.h>
#include <base/containers/unordered_map.h>
#include <base/containers/vector.h>
#include <base/namespace.h>
#include <base/util/uid.h>
#include <base/util/uid_util.h>
#include <core/ecs/entity.h>
#include <core/ecs/intf_component_manager.h>
#include <core/ecs/intf_ecs.h>
#include <core/ecs/intf_system.h>
#include <core/log.h>
#include <core/namespace.h>
#include <core/perf/cpu_perf_scope.h>
#include <core/plugin/intf_plugin.h>
#include <core/plugin/intf_plugin_register.h>
#include <core/threading/intf_thread_pool.h>
#include "ecs/entity_manager.h"
CORE_BEGIN_NAMESPACE()
constexpr bool operator==(const CORE_NS::SystemTypeInfo* info, const BASE_NS::Uid& uid) noexcept
{
return info->uid == uid;
}
inline bool operator==(
const BASE_NS::unique_ptr<ISystem, SystemTypeInfo::DestroySystemFn>& ptr, const ISystem* system) noexcept
{
return ptr.get() == system;
}
namespace {
using BASE_NS::array_view;
using BASE_NS::pair;
using BASE_NS::Uid;
using BASE_NS::unique_ptr;
using BASE_NS::unordered_map;
using BASE_NS::vector;
class Ecs final : public IEcs, IPluginRegister::ITypeInfoListener {
public:
Ecs(IClassFactory&, const IThreadPool::Ptr& threadPool, uint64_t ecsId);
~Ecs() override;
Ecs(const Ecs&) = delete;
Ecs(const Ecs&&) = delete;
Ecs& operator=(const Ecs&) = delete;
Ecs& operator=(const Ecs&&) = delete;
IEntityManager& GetEntityManager() override;
const IEntityManager& GetEntityManager() const override;
void GetComponents(Entity entity, vector<IComponentManager*>& result) const override;
vector<ISystem*> GetSystems() const override;
ISystem* GetSystem(const Uid& uid) const override;
vector<IComponentManager*> GetComponentManagers() const override;
IComponentManager* GetComponentManager(const Uid& uid) const override;
Entity CloneEntity(Entity entity) override;
void ProcessEvents() override;
void Initialize() override;
bool Update(uint64_t time, uint64_t delta) override;
void Uninitialize() override;
IComponentManager* CreateComponentManager(const ComponentManagerTypeInfo& componentManagerTypeInfo) override;
ISystem* CreateSystem(const SystemTypeInfo& systemInfo) override;
void AddListener(EntityListener& listener) override;
void RemoveListener(EntityListener& listener) override;
void AddListener(ComponentListener& listener) override;
void RemoveListener(ComponentListener& listener) override;
void AddListener(IComponentManager& manager, ComponentListener& listener) override;
void RemoveListener(IComponentManager& manager, ComponentListener& listener) override;
void RequestRender() override;
void SetRenderMode(RenderMode renderMode) override;
RenderMode GetRenderMode() override;
bool NeedRender() const override;
IClassFactory& GetClassFactory() const override;
const IThreadPool::Ptr& GetThreadPool() const override;
float GetTimeScale() const override;
void SetTimeScale(float scale) override;
void Ref() noexcept override;
void Unref() noexcept override;
uint64_t GetId() const override;
using SystemPtr = unique_ptr<ISystem, SystemTypeInfo::DestroySystemFn>;
using ManagerPtr = unique_ptr<IComponentManager, ComponentManagerTypeInfo::DestroyComponentManagerFn>;
protected:
void OnTypeInfoEvent(EventType type, array_view<const ITypeInfo* const> typeInfos) override;
void ProcessComponentEvents(
IEcs::ComponentListener::EventType eventType, array_view<const Entity> removedEntities) const;
void CleanupComponentManager(IComponentManager& manager);
IThreadPool::Ptr threadPool_;
vector<SystemPtr> systemOrder_;
vector<ManagerPtr> managerOrder_;
unordered_map<Uid, ISystem*> systems_;
unordered_map<Uid, IComponentManager*> managers_;
vector<EntityListener*> entityListeners_;
vector<ComponentListener*> componentListeners_;
unordered_map<IComponentManager*, vector<ComponentListener*>> componentManagerListeners_;
bool initialized_{false};
bool needRender_{false};
bool renderRequested_{false};
bool processingEvents_{false};
RenderMode renderMode_{RENDER_ALWAYS};
IClassFactory& pluginRegistry_;
vector<pair<PluginToken, const IEcsPlugin*>> plugins_;
float timeScale_{1.f};
int32_t refcnt_{0};
uint64_t ecsId_{0xFFFFFFFFffffffff};
EntityManager entityManager_;
};
template <typename ListType, typename ValueType>
auto Find(ListType& list, const ValueType& value)
{
return std::find(list.begin(), list.end(), value);
}
void ProcessEntityListeners(const array_view<const pair<Entity, IEntityManager::EventType>> states,
const array_view<IEcs::EntityListener*> entityListeners)
{
vector<Entity> res;
res.reserve(states.size());
auto type = states[0U].second;
for (const auto& s : states) {
if (s.second != type) {
if (!res.empty()) {
for (auto* listener : entityListeners) {
if (listener) {
listener->OnEntityEvent(type, res);
}
}
res.clear();
}
type = s.second;
}
res.push_back(s.first);
}
if (!res.empty()) {
for (auto* listener : entityListeners) {
if (listener) {
listener->OnEntityEvent(type, res);
}
}
}
}
template <class TypeInfo>
const TypeInfo* FindTypeInfo(const Uid& uid, const array_view<const ITypeInfo* const>& container)
{
for (const auto& info : container) {
if (static_cast<const TypeInfo* const>(info)->uid == uid) {
return static_cast<const TypeInfo*>(info);
}
}
return nullptr;
}
bool GatherComponents(vector<const ComponentManagerTypeInfo*>& componentManagerInfos,
const array_view<const ITypeInfo* const>& componentMetadata, array_view<const Uid> componentDependencies)
{
for (const auto& dependencyUid : componentDependencies) {
if (dependencyUid == Uid{}) {
continue;
}
const auto* componentTypeInfo = FindTypeInfo<ComponentManagerTypeInfo>(dependencyUid, componentMetadata);
if (!componentTypeInfo) {
return false;
}
if (std::find(componentManagerInfos.cbegin(), componentManagerInfos.cend(), componentTypeInfo) ==
componentManagerInfos.cend()) {
componentManagerInfos.push_back(componentTypeInfo);
}
}
return true;
}
pair<vector<const SystemTypeInfo*>, vector<const ComponentManagerTypeInfo*>> GetAvailableComponentManagersAndSystems()
{
auto& pluginRegister = GetPluginRegister();
auto componentMetadata = pluginRegister.GetTypeInfos(ComponentManagerTypeInfo::UID);
auto systemMetadata = pluginRegister.GetTypeInfos(SystemTypeInfo::UID);
vector<const ComponentManagerTypeInfo*> componentManagerInfos;
componentManagerInfos.reserve(componentMetadata.size());
vector<const SystemTypeInfo*> systemInfos;
systemInfos.reserve(systemMetadata.size());
for (const auto* info : systemMetadata) {
if (!info || (info->typeUid != SystemTypeInfo::UID)) {
continue;
}
auto* systemTypeInfo = static_cast<const SystemTypeInfo*>(info);
if (!systemTypeInfo->createSystem) {
continue;
}
if (!GatherComponents(componentManagerInfos, componentMetadata, systemTypeInfo->componentDependencies) ||
!GatherComponents(
componentManagerInfos, componentMetadata, systemTypeInfo->readOnlyComponentDependencies)) {
CORE_LOG_W("Failed to resolve component dependencies: %s.", systemTypeInfo->typeName);
continue;
}
systemInfos.push_back(systemTypeInfo);
}
return {BASE_NS::move(systemInfos), BASE_NS::move(componentManagerInfos)};
}
void OrderSystems(vector<const SystemTypeInfo*>& systemInfos)
{
auto begin = systemInfos.begin();
auto end = systemInfos.end();
for (auto it = begin; it != end;) {
if ((*it)->afterSystem == Uid{}) {
++it;
continue;
}
auto pos = std::find(it, end, (*it)->afterSystem);
if (pos != end) {
std::rotate(it, it + 1, (pos + 1));
} else {
++it;
}
}
for (auto it = begin; it != end;) {
if ((*it)->beforeSystem == Uid{}) {
++it;
continue;
}
auto pos = std::find(begin, it, (*it)->beforeSystem);
if (pos != it) {
std::rotate(pos, it, (it + 1));
} else {
++it;
}
}
}
void Ecs::AddListener(EntityListener& listener)
{
if (Find(entityListeners_, &listener) != entityListeners_.end()) {
return;
}
entityListeners_.push_back(&listener);
}
void Ecs::RemoveListener(EntityListener& listener)
{
if (auto it = Find(entityListeners_, &listener); it != entityListeners_.end()) {
*it = nullptr;
return;
}
}
void Ecs::AddListener(ComponentListener& listener)
{
if (Find(componentListeners_, &listener) != componentListeners_.end()) {
return;
}
componentListeners_.push_back(&listener);
}
void Ecs::RemoveListener(ComponentListener& listener)
{
if (auto it = Find(componentListeners_, &listener); it != componentListeners_.end()) {
*it = nullptr;
return;
}
}
void Ecs::AddListener(IComponentManager& manager, ComponentListener& listener)
{
auto list = componentManagerListeners_.find(&manager);
if (list != componentManagerListeners_.end()) {
if (auto it = Find(list->second, &listener); it != list->second.end()) {
return;
}
list->second.push_back(&listener);
return;
}
componentManagerListeners_[&manager].push_back(&listener);
}
void Ecs::RemoveListener(IComponentManager& manager, ComponentListener& listener)
{
auto list = componentManagerListeners_.find(&manager);
if (list == componentManagerListeners_.end()) {
return;
}
if (auto it = Find(list->second, &listener); it != list->second.end()) {
*it = nullptr;
return;
}
}
IComponentManager* Ecs::CreateComponentManager(const ComponentManagerTypeInfo& componentManagerTypeInfo)
{
IComponentManager* manager = nullptr;
if (componentManagerTypeInfo.createManager) {
manager = GetComponentManager(componentManagerTypeInfo.uid);
if (manager) {
CORE_LOG_W("Duplicate component manager creation, returning existing instance");
} else {
manager = componentManagerTypeInfo.createManager(*this);
if (manager) {
managers_.insert({componentManagerTypeInfo.uid, manager});
managerOrder_.emplace_back(manager, componentManagerTypeInfo.destroyManager);
}
}
}
return manager;
}
ISystem* Ecs::CreateSystem(const SystemTypeInfo& systemInfo)
{
ISystem* system = nullptr;
if (!systemInfo.createSystem) {
return system;
}
system = GetSystem(systemInfo.uid);
if (system) {
CORE_LOG_W("Duplicate system creation, returning existing instance.");
return system;
}
system = systemInfo.createSystem(*this);
if (!system) {
CORE_LOG_E("Failed to create system.");
return system;
}
systems_.insert({systemInfo.uid, system});
systemOrder_.emplace_back(system, systemInfo.destroySystem);
if (initialized_) {
system->Initialize();
}
return system;
}
Ecs::Ecs(IClassFactory& registry, const IThreadPool::Ptr& threadPool, const uint64_t ecsId)
: threadPool_(threadPool), pluginRegistry_(registry), ecsId_(ecsId)
{
for (auto info : CORE_NS::GetPluginRegister().GetTypeInfos(IEcsPlugin::UID)) {
if (auto ecsPlugin = static_cast<const IEcsPlugin*>(info); ecsPlugin && ecsPlugin->createPlugin) {
auto token = ecsPlugin->createPlugin(*this);
plugins_.push_back({token, ecsPlugin});
}
}
GetPluginRegister().AddListener(*this);
}
Ecs::~Ecs()
{
GetPluginRegister().RemoveListener(*this);
Uninitialize();
managerOrder_.clear();
systemOrder_.clear();
for (auto& plugin : plugins_) {
if (plugin.second->destroyPlugin) {
plugin.second->destroyPlugin(plugin.first);
}
}
}
IClassFactory& Ecs::GetClassFactory() const
{
return pluginRegistry_;
}
IEntityManager& Ecs::GetEntityManager()
{
return entityManager_;
}
const IEntityManager& Ecs::GetEntityManager() const
{
return entityManager_;
}
void Ecs::GetComponents(Entity entity, vector<IComponentManager*>& result) const
{
result.clear();
result.reserve(managers_.size());
for (auto& m : managerOrder_) {
if (m->HasComponent(entity)) {
result.push_back(m.get());
}
}
}
vector<ISystem*> Ecs::GetSystems() const
{
vector<ISystem*> result;
result.reserve(systemOrder_.size());
for (auto& t : systemOrder_) {
result.push_back(t.get());
}
return result;
}
ISystem* Ecs::GetSystem(const Uid& uid) const
{
if (auto pos = systems_.find(uid); pos != systems_.end()) {
return pos->second;
}
return nullptr;
}
vector<IComponentManager*> Ecs::GetComponentManagers() const
{
vector<IComponentManager*> result;
result.reserve(managerOrder_.size());
for (auto& t : managerOrder_) {
result.push_back(t.get());
}
return result;
}
IComponentManager* Ecs::GetComponentManager(const Uid& uid) const
{
if (auto pos = managers_.find(uid); pos != managers_.end()) {
return pos->second;
}
return nullptr;
}
Entity Ecs::CloneEntity(const Entity entity)
{
if (!EntityUtil::IsValid(entity)) {
return {};
}
const Entity clonedEntity = entityManager_.Create();
if (entityManager_.IsAlive(entity)) {
for (auto& cm : managerOrder_) {
const auto id = cm->GetComponentId(entity);
if (id != IComponentManager::INVALID_COMPONENT_ID) {
cm->Create(clonedEntity);
if (auto dataHandle = cm->GetData(id)) {
cm->SetData(clonedEntity, *dataHandle);
}
}
}
}
return clonedEntity;
}
void Ecs::ProcessComponentEvents(
ComponentListener::EventType eventType, const array_view<const Entity> removedEntities) const
{
vector<Entity> (IComponentManager::*getter)();
switch (eventType) {
case ComponentListener::EventType::CREATED:
getter = &IComponentManager::GetAddedComponents;
break;
case ComponentListener::EventType::MODIFIED:
getter = &IComponentManager::GetUpdatedComponents;
break;
case ComponentListener::EventType::DESTROYED:
getter = &IComponentManager::GetRemovedComponents;
break;
case ComponentListener::EventType::MOVED:
getter = &IComponentManager::GetMovedComponents;
break;
default:
return;
}
for (const auto& m : managerOrder_) {
vector<Entity> affectedEntities = (*m.*getter)();
if (!removedEntities.empty()) {
affectedEntities.erase(
std::remove_if(affectedEntities.begin(),
affectedEntities.end(),
[removedEntities](const Entity& entity) {
const auto pos = std::lower_bound(removedEntities.cbegin(),
removedEntities.cend(),
entity,
[](const Entity& entity, const Entity& removed) { return entity < removed; });
return ((pos != removedEntities.cend()) && entity >= *pos);
}),
affectedEntities.cend());
}
if (!affectedEntities.empty()) {
for (auto* listener : componentListeners_) {
if (listener) {
listener->OnComponentEvent(eventType, *m, affectedEntities);
}
}
if (auto it = componentManagerListeners_.find(m.get()); it != componentManagerListeners_.cend()) {
for (auto* listener : it->second) {
if (listener) {
listener->OnComponentEvent(eventType, *m, affectedEntities);
}
}
}
}
}
}
void Ecs::ProcessEvents()
{
if (processingEvents_) {
CORE_LOG_W("Calling ProcessEvents() from an event callback is not allowed");
return;
}
processingEvents_ = true;
vector<Entity> allRemovedEntities;
bool deadEntities = false;
do {
entityManager_.UpdateDeadEntities();
if (const auto events = entityManager_.GetEvents(); !events.empty()) {
ProcessEntityListeners(events, entityListeners_);
}
const vector<Entity> removed = entityManager_.GetRemovedEntities();
deadEntities = !removed.empty();
if (deadEntities) {
allRemovedEntities.append(removed.cbegin(), removed.cend());
}
for (auto& m : managerOrder_) {
if (deadEntities) {
m->Destroy(removed);
}
m->Gc();
}
} while (deadEntities);
if (!allRemovedEntities.empty()) {
std::sort(allRemovedEntities.begin(), allRemovedEntities.end());
}
ProcessComponentEvents(ComponentListener::EventType::CREATED, allRemovedEntities);
ProcessComponentEvents(ComponentListener::EventType::MOVED, allRemovedEntities);
ProcessComponentEvents(ComponentListener::EventType::MODIFIED, allRemovedEntities);
ProcessComponentEvents(ComponentListener::EventType::DESTROYED, {});
entityListeners_.erase(
std::remove(entityListeners_.begin(), entityListeners_.end(), nullptr), entityListeners_.cend());
componentListeners_.erase(
std::remove(componentListeners_.begin(), componentListeners_.end(), nullptr), componentListeners_.cend());
for (auto it = componentManagerListeners_.begin(); it != componentManagerListeners_.cend();) {
auto& listeners = it->second;
listeners.erase(std::remove(listeners.begin(), listeners.end(), nullptr), listeners.cend());
if (listeners.empty()) {
it = componentManagerListeners_.erase(it);
} else {
++it;
}
}
processingEvents_ = false;
}
void Ecs::Initialize()
{
if (systemOrder_.empty()) {
auto [systemInfos, componentManagerInfos] = GetAvailableComponentManagersAndSystems();
for (const auto* info : componentManagerInfos) {
CreateComponentManager(*info);
}
OrderSystems(systemInfos);
for (const auto* systemInfo : systemInfos) {
ISystem* system = systemInfo->createSystem(*this);
if (!system) {
CORE_LOG_E("Failed to create system.");
continue;
}
systems_.insert({systemInfo->uid, system});
systemOrder_.emplace_back(system, systemInfo->destroySystem);
}
}
for (auto& s : systemOrder_) {
s->Initialize();
}
initialized_ = true;
}
CORE_PROFILER_SYMBOL(escUpdate, "Update");
bool Ecs::Update(uint64_t time, uint64_t delta)
{
CORE_PROFILER_MARK_FRAME_START(escUpdate);
CORE_CPU_PERF_SCOPE("CORE", "Update", "Total_Cpu", CORE_PROFILER_DEFAULT_COLOR);
bool frameRenderingQueued = false;
if (GetRenderMode() == RENDER_ALWAYS || renderRequested_) {
frameRenderingQueued = true;
}
delta = static_cast<uint64_t>(static_cast<double>(delta) * timeScale_);
for (auto& s : systemOrder_) {
CORE_CPU_PERF_SCOPE("CORE", "SystemUpdate", s->GetName(), CORE_PROFILER_DEFAULT_COLOR);
if (s->Update(frameRenderingQueued, time, delta)) {
frameRenderingQueued = true;
}
}
for (auto& componentManager : managerOrder_) {
componentManager->ClearModifiedFlags();
}
renderRequested_ = false;
needRender_ = frameRenderingQueued;
CORE_PROFILER_MARK_FRAME_END(escUpdate);
return frameRenderingQueued;
}
void Ecs::Uninitialize()
{
entityManager_.DestroyAllEntities();
ProcessEvents();
for (auto it = systemOrder_.rbegin(); it != systemOrder_.rend(); ++it) {
(*it)->Uninitialize();
}
initialized_ = false;
}
void Ecs::RequestRender()
{
renderRequested_ = true;
}
void Ecs::SetRenderMode(RenderMode renderMode)
{
renderMode_ = renderMode;
}
IEcs::RenderMode Ecs::GetRenderMode()
{
return renderMode_;
}
bool Ecs::NeedRender() const
{
return needRender_;
}
const IThreadPool::Ptr& Ecs::GetThreadPool() const
{
return threadPool_;
}
float Ecs::GetTimeScale() const
{
return timeScale_;
}
void Ecs::SetTimeScale(float scale)
{
timeScale_ = scale;
}
uint64_t Ecs::GetId() const
{
return ecsId_;
}
void Ecs::Ref() noexcept
{
BASE_NS::AtomicIncrementRelaxed(&refcnt_);
}
void Ecs::Unref() noexcept
{
if (BASE_NS::AtomicDecrementRelease(&refcnt_) == 0) {
BASE_NS::AtomicFenceAcquire();
delete this;
}
}
template <typename Container>
inline auto RemoveUid(Container& container, const Uid& uid)
{
container.erase(
std::remove_if(
container.begin(), container.end(), [&uid](const auto& thing) { return thing->GetUid() == uid; }),
container.cend());
}
void Ecs::OnTypeInfoEvent(EventType type, array_view<const ITypeInfo* const> typeInfos)
{
if (type == EventType::ADDED) {
for (const auto* info : typeInfos) {
if (info && info->typeUid == IEcsPlugin::UID) {
const auto ecsPlugin = static_cast<const IEcsPlugin*>(info);
if (ecsPlugin->createPlugin) {
auto token = ecsPlugin->createPlugin(*this);
plugins_.push_back({token, ecsPlugin});
}
}
}
} else if (type == EventType::REMOVED) {
for (const auto* info : typeInfos) {
if (!info) {
continue;
}
if (info->typeUid == SystemTypeInfo::UID) {
const auto systemInfo = static_cast<const SystemTypeInfo*>(info);
if (const auto pos = systems_.find(systemInfo->uid); pos != systems_.cend()) {
pos->second->Uninitialize();
systems_.erase(pos);
}
RemoveUid(systemOrder_, systemInfo->uid);
} else if (info->typeUid == ComponentManagerTypeInfo::UID) {
const auto managerInfo = static_cast<const ComponentManagerTypeInfo*>(info);
if (const auto pos = managers_.find(managerInfo->uid); (pos != managers_.end()) && (pos->second)) {
auto* manager = pos->second;
CleanupComponentManager(*manager);
managers_.erase(pos);
}
RemoveUid(managerOrder_, managerInfo->uid);
} else if (info->typeUid == IEcsPlugin::UID) {
const auto ecsPlugin = static_cast<const IEcsPlugin*>(info);
plugins_.erase(std::remove_if(plugins_.begin(),
plugins_.end(),
[ecsPlugin](const pair<PluginToken, const IEcsPlugin*>& plugin) {
return plugin.second == ecsPlugin;
}),
plugins_.cend());
}
}
}
}
void Ecs::CleanupComponentManager(IComponentManager& manager)
{
const auto components = static_cast<IComponentManager::ComponentId>(manager.GetComponentCount());
for (IComponentManager::ComponentId i = 0; i < components; ++i) {
manager.Destroy(manager.GetEntity(i));
}
if (const auto listenerIt = componentManagerListeners_.find(&manager);
!componentListeners_.empty() ||
((listenerIt != componentManagerListeners_.end()) && !listenerIt->second.empty())) {
if (const vector<Entity> removed = manager.GetRemovedComponents(); !removed.empty()) {
const auto removedView = array_view<const Entity>(removed);
for (auto* lister : componentListeners_) {
if (lister) {
lister->OnComponentEvent(ComponentListener::EventType::DESTROYED, manager, removedView);
}
}
if (listenerIt != componentManagerListeners_.end()) {
for (auto* lister : listenerIt->second) {
if (lister) {
lister->OnComponentEvent(ComponentListener::EventType::DESTROYED, manager, removedView);
}
}
componentManagerListeners_.erase(listenerIt);
}
}
}
manager.Gc();
}
}
IEcs* IEcsInstance(IClassFactory& registry, const IThreadPool::Ptr& threadPool, const uint64_t ecsId)
{
return new Ecs(registry, threadPool, ecsId);
}
CORE_END_NAMESPACE()