1 /*
2 * Copyright (c) 2024 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include <algorithm>
17 #include <atomic>
18 #include <cstdint>
19
20 #include <base/containers/array_view.h>
21 #include <base/containers/iterator.h>
22 #include <base/containers/string_view.h>
23 #include <base/containers/type_traits.h>
24 #include <base/containers/unique_ptr.h>
25 #include <base/containers/unordered_map.h>
26 #include <base/containers/vector.h>
27 #include <base/namespace.h>
28 #include <base/util/uid.h>
29 #include <core/ecs/entity.h>
30 #include <core/ecs/intf_component_manager.h>
31 #include <core/ecs/intf_ecs.h>
32 #include <core/ecs/intf_system.h>
33 #include <core/log.h>
34 #include <core/namespace.h>
35 #include <core/perf/cpu_perf_scope.h>
36 #include <core/plugin/intf_plugin.h>
37 #include <core/plugin/intf_plugin_register.h>
38 #include <core/threading/intf_thread_pool.h>
39
40 #include "ecs/entity_manager.h"
41
42 CORE_BEGIN_NAMESPACE()
43 namespace {
44 using BASE_NS::array_view;
45 using BASE_NS::pair;
46 using BASE_NS::Uid;
47 using BASE_NS::unique_ptr;
48 using BASE_NS::unordered_map;
49 using BASE_NS::vector;
50
51 class Ecs final : public IEcs, IPluginRegister::ITypeInfoListener {
52 public:
53 Ecs(IClassFactory&, const IThreadPool::Ptr& threadPool);
54 ~Ecs() override;
55
56 Ecs(const Ecs&) = delete;
57 Ecs(const Ecs&&) = delete;
58 Ecs& operator=(const Ecs&) = delete;
59 Ecs& operator=(const Ecs&&) = delete;
60
61 IEntityManager& GetEntityManager() override;
62 void GetComponents(Entity entity, vector<IComponentManager*>& result) override;
63 vector<ISystem*> GetSystems() const override;
64 ISystem* GetSystem(const Uid& uid) const override;
65 vector<IComponentManager*> GetComponentManagers() const override;
66 IComponentManager* GetComponentManager(const Uid& uid) const override;
67 Entity CloneEntity(const Entity entity) override;
68 void ProcessEvents() override;
69
70 void Initialize() override;
71 bool Update(uint64_t time, uint64_t delta) override;
72 void Uninitialize() override;
73
74 IComponentManager* CreateComponentManager(const ComponentManagerTypeInfo& componentManagerTypeInfo) override;
75 ISystem* CreateSystem(const SystemTypeInfo& systemInfo) override;
76
77 void AddListener(EntityListener& listener) override;
78 void RemoveListener(EntityListener& listener) override;
79 void AddListener(ComponentListener& listener) override;
80 void RemoveListener(ComponentListener& listener) override;
81 void AddListener(IComponentManager& manager, ComponentListener& listener) override;
82 void RemoveListener(IComponentManager& manager, ComponentListener& listener) override;
83
84 void RequestRender() override;
85 void SetRenderMode(RenderMode renderMode) override;
86 RenderMode GetRenderMode() override;
87
88 bool NeedRender() const override;
89
90 IClassFactory& GetClassFactory() const override;
91
92 const IThreadPool::Ptr& GetThreadPool() const override;
93
94 float GetTimeScale() const override;
95 void SetTimeScale(float scale) override;
96
97 void Ref() noexcept override;
98 void Unref() noexcept override;
99
100 protected:
101 using SystemPtr = unique_ptr<ISystem, SystemTypeInfo::DestroySystemFn>;
102 using ManagerPtr = unique_ptr<IComponentManager, ComponentManagerTypeInfo::DestroyComponentManagerFn>;
103
104 // IPluginRegister::ITypeInfoListener
105 void OnTypeInfoEvent(EventType type, array_view<const ITypeInfo* const> typeInfos) override;
106
107 void ProcessComponentEvents(
108 IEcs::ComponentListener::EventType eventType, array_view<const Entity> removedEntities) const;
109
110 IThreadPool::Ptr threadPool_;
111
112 // for storing systems and component managers in creation order
113 vector<SystemPtr> systemOrder_;
114 vector<ManagerPtr> managerOrder_;
115 // for finding systems and component managers with UID
116 unordered_map<Uid, ISystem*> systems_;
117 unordered_map<Uid, IComponentManager*> managers_;
118
119 vector<EntityListener*> entityListeners_;
120 vector<ComponentListener*> componentListeners_;
121 unordered_map<IComponentManager*, vector<ComponentListener*>> componentManagerListeners_;
122
123 bool needRender_ { false };
124 bool renderRequested_ { false };
125 RenderMode renderMode_ { RENDER_ALWAYS };
126
127 IClassFactory& pluginRegistry_;
128 EntityManager entityManager_;
129
130 vector<pair<PluginToken, const IEcsPlugin*>> plugins_;
131 float timeScale_ { 1.f };
132 std::atomic<int32_t> refcnt_ { 0 };
133
134 bool processingEvents_ { false };
135 };
136
137 template<typename ListType, typename ValueType>
Find(ListType & list,const ValueType & value)138 auto Find(ListType& list, const ValueType& value)
139 {
140 return std::find(list.begin(), list.end(), value);
141 }
142
ProcessEntityListeners(const array_view<const pair<Entity,IEntityManager::EventType>> states,const array_view<IEcs::EntityListener * > entityListeners)143 void ProcessEntityListeners(const array_view<const pair<Entity, IEntityManager::EventType>> states,
144 const array_view<IEcs::EntityListener*> entityListeners)
145 {
146 // handle state changes (collect to groups of same kind of events)
147 vector<Entity> res;
148 res.reserve(states.size());
149 auto type = states[0U].second;
150 for (const auto& s : states) {
151 if (s.second != type) {
152 if (!res.empty()) {
153 // Let listeners know that entity state has changed.
154 for (auto* listener : entityListeners) {
155 if (listener) {
156 listener->OnEntityEvent(type, res);
157 }
158 }
159 // start collecting new events.
160 res.clear();
161 }
162 type = s.second;
163 }
164 // add to event list.
165 res.push_back(s.first);
166 }
167 if (!res.empty()) {
168 // Send the final events.
169 for (auto* listener : entityListeners) {
170 if (listener) {
171 listener->OnEntityEvent(type, res);
172 }
173 }
174 }
175 }
176
AddListener(EntityListener & listener)177 void Ecs::AddListener(EntityListener& listener)
178 {
179 if (Find(entityListeners_, &listener) != entityListeners_.end()) {
180 // already added.
181 return;
182 }
183 entityListeners_.push_back(&listener);
184 }
185
RemoveListener(EntityListener & listener)186 void Ecs::RemoveListener(EntityListener& listener)
187 {
188 if (auto it = Find(entityListeners_, &listener); it != entityListeners_.end()) {
189 // Setting the listener to null instead of removing. This allows removing listeners from a listener callback.
190 *it = nullptr;
191 return;
192 }
193 }
194
AddListener(ComponentListener & listener)195 void Ecs::AddListener(ComponentListener& listener)
196 {
197 if (Find(componentListeners_, &listener) != componentListeners_.end()) {
198 // already added.
199 return;
200 }
201 componentListeners_.push_back(&listener);
202 }
203
RemoveListener(ComponentListener & listener)204 void Ecs::RemoveListener(ComponentListener& listener)
205 {
206 if (auto it = Find(componentListeners_, &listener); it != componentListeners_.end()) {
207 *it = nullptr;
208 return;
209 }
210 }
211
AddListener(IComponentManager & manager,ComponentListener & listener)212 void Ecs::AddListener(IComponentManager& manager, ComponentListener& listener)
213 {
214 auto list = componentManagerListeners_.find(&manager);
215 if (list != componentManagerListeners_.end()) {
216 if (auto it = Find(list->second, &listener); it != list->second.end()) {
217 return;
218 }
219 list->second.push_back(&listener);
220 return;
221 }
222 componentManagerListeners_[&manager].push_back(&listener);
223 }
224
RemoveListener(IComponentManager & manager,ComponentListener & listener)225 void Ecs::RemoveListener(IComponentManager& manager, ComponentListener& listener)
226 {
227 auto list = componentManagerListeners_.find(&manager);
228 if (list == componentManagerListeners_.end()) {
229 return;
230 }
231 if (auto it = Find(list->second, &listener); it != list->second.end()) {
232 *it = nullptr;
233 return;
234 }
235 }
236
CreateComponentManager(const ComponentManagerTypeInfo & componentManagerTypeInfo)237 IComponentManager* Ecs::CreateComponentManager(const ComponentManagerTypeInfo& componentManagerTypeInfo)
238 {
239 IComponentManager* manager = nullptr;
240 if (componentManagerTypeInfo.createManager) {
241 manager = GetComponentManager(componentManagerTypeInfo.UID);
242 if (manager) {
243 CORE_LOG_W("Duplicate component manager creation, returning existing instance");
244 } else {
245 manager = componentManagerTypeInfo.createManager(*this);
246 if (manager) {
247 managers_.insert({ componentManagerTypeInfo.uid, manager });
248 managerOrder_.emplace_back(manager, componentManagerTypeInfo.destroyManager);
249 }
250 }
251 }
252 return manager;
253 }
254
CreateSystem(const SystemTypeInfo & systemInfo)255 ISystem* Ecs::CreateSystem(const SystemTypeInfo& systemInfo)
256 {
257 ISystem* system = nullptr;
258 if (systemInfo.createSystem) {
259 system = GetSystem(systemInfo.UID);
260 if (system) {
261 CORE_LOG_W("Duplicate system creation, returning existing instance");
262 } else {
263 system = systemInfo.createSystem(*this);
264 if (system) {
265 systems_.insert({ systemInfo.uid, system });
266 systemOrder_.emplace_back(system, systemInfo.destroySystem);
267 }
268 }
269 }
270 return system;
271 }
272
Ecs(IClassFactory & registry,const IThreadPool::Ptr & threadPool)273 Ecs::Ecs(IClassFactory& registry, const IThreadPool::Ptr& threadPool)
274 : threadPool_(threadPool), pluginRegistry_(registry)
275 {
276 for (auto info : CORE_NS::GetPluginRegister().GetTypeInfos(IEcsPlugin::UID)) {
277 if (auto ecsPlugin = static_cast<const IEcsPlugin*>(info); ecsPlugin && ecsPlugin->createPlugin) {
278 auto token = ecsPlugin->createPlugin(*this);
279 plugins_.push_back({ token, ecsPlugin });
280 }
281 }
282 GetPluginRegister().AddListener(*this);
283 }
284
~Ecs()285 Ecs::~Ecs()
286 {
287 GetPluginRegister().RemoveListener(*this);
288
289 Uninitialize();
290 managerOrder_.clear();
291 systemOrder_.clear();
292
293 for (auto& plugin : plugins_) {
294 if (plugin.second->destroyPlugin) {
295 plugin.second->destroyPlugin(plugin.first);
296 }
297 }
298 }
299
GetClassFactory() const300 IClassFactory& Ecs::GetClassFactory() const
301 {
302 return pluginRegistry_;
303 }
304
GetEntityManager()305 IEntityManager& Ecs::GetEntityManager()
306 {
307 return entityManager_;
308 }
309
GetComponents(Entity entity,vector<IComponentManager * > & result)310 void Ecs::GetComponents(Entity entity, vector<IComponentManager*>& result)
311 {
312 result.clear();
313 result.reserve(managers_.size());
314 for (auto& m : managerOrder_) {
315 if (m->HasComponent(entity)) {
316 result.push_back(m.get());
317 }
318 }
319 }
320
GetSystems() const321 vector<ISystem*> Ecs::GetSystems() const
322 {
323 vector<ISystem*> result;
324 result.reserve(systemOrder_.size());
325 for (auto& t : systemOrder_) {
326 result.push_back(t.get());
327 }
328 return result;
329 }
330
GetSystem(const Uid & uid) const331 ISystem* Ecs::GetSystem(const Uid& uid) const
332 {
333 if (auto pos = systems_.find(uid); pos != systems_.end()) {
334 return pos->second;
335 }
336 return nullptr;
337 }
338
GetComponentManagers() const339 vector<IComponentManager*> Ecs::GetComponentManagers() const
340 {
341 vector<IComponentManager*> result;
342 result.reserve(managerOrder_.size());
343 for (auto& t : managerOrder_) {
344 result.push_back(t.get());
345 }
346 return result;
347 }
348
GetComponentManager(const Uid & uid) const349 IComponentManager* Ecs::GetComponentManager(const Uid& uid) const
350 {
351 if (auto pos = managers_.find(uid); pos != managers_.end()) {
352 return pos->second;
353 }
354 return nullptr;
355 }
356
CloneEntity(const Entity entity)357 Entity Ecs::CloneEntity(const Entity entity)
358 {
359 if (!EntityUtil::IsValid(entity)) {
360 return Entity();
361 }
362
363 const Entity clonedEntity = entityManager_.Create();
364 if (entityManager_.IsAlive(entity)) {
365 for (auto& cm : managerOrder_) {
366 const auto id = cm->GetComponentId(entity);
367 if (id != IComponentManager::INVALID_COMPONENT_ID) {
368 cm->Create(clonedEntity);
369 cm->SetData(clonedEntity, *cm->GetData(id));
370 }
371 }
372 }
373 return clonedEntity;
374 }
375
ProcessComponentEvents(ComponentListener::EventType eventType,const array_view<const Entity> removedEntities) const376 void Ecs::ProcessComponentEvents(
377 ComponentListener::EventType eventType, const array_view<const Entity> removedEntities) const
378 {
379 vector<Entity> (IComponentManager::*getter)();
380 switch (eventType) {
381 case ComponentListener::EventType::CREATED:
382 getter = &IComponentManager::GetAddedComponents;
383 break;
384 case ComponentListener::EventType::MODIFIED:
385 getter = &IComponentManager::GetUpdatedComponents;
386 break;
387 case ComponentListener::EventType::DESTROYED:
388 getter = &IComponentManager::GetRemovedComponents;
389 break;
390 default:
391 return;
392 }
393 for (const auto& m : managerOrder_) {
394 vector<Entity> affectedEntities = (*m.*getter)();
395 if (!removedEntities.empty()) {
396 affectedEntities.erase(
397 std::remove_if(affectedEntities.begin(), affectedEntities.end(),
398 [removedEntities](const Entity& entity) {
399 const auto pos = std::lower_bound(removedEntities.cbegin(), removedEntities.cend(), entity,
400 [](const Entity& entity, const Entity& removed) { return entity.id < removed.id; });
401 return ((pos != removedEntities.cend()) && !(entity.id < pos->id));
402 }),
403 affectedEntities.cend());
404 }
405 if (!affectedEntities.empty()) {
406 // global listeners
407 for (auto* listener : componentListeners_) {
408 if (listener) {
409 listener->OnComponentEvent(eventType, *m, affectedEntities);
410 }
411 }
412 // per manager listeners
413 if (auto it = componentManagerListeners_.find(m.get()); it != componentManagerListeners_.cend()) {
414 for (auto* listener : it->second) {
415 if (listener) {
416 listener->OnComponentEvent(eventType, *m, affectedEntities);
417 }
418 }
419 }
420 }
421 }
422 }
423
ProcessEvents()424 void Ecs::ProcessEvents()
425 {
426 if (processingEvents_) {
427 CORE_ASSERT_MSG(false, "Calling ProcessEvents() from an event callback is not allowed");
428 return;
429 }
430 processingEvents_ = true;
431
432 vector<Entity> allRemovedEntities;
433 bool deadEntities = false;
434 do {
435 // Let entity manager check entity reference counts
436 entityManager_.UpdateDeadEntities();
437
438 // Send entity related events
439 if (const auto events = entityManager_.GetEvents(); !events.empty()) {
440 ProcessEntityListeners(events, entityListeners_);
441 }
442
443 // Remove components for removed entities.
444 const vector<Entity> removed = entityManager_.GetRemovedEntities();
445 deadEntities = !removed.empty();
446 if (deadEntities) {
447 allRemovedEntities.append(removed.cbegin(), removed.cend());
448 }
449 for (auto& m : managerOrder_) {
450 // Destroy all components related to these entities.
451 if (deadEntities) {
452 m->Destroy(removed);
453 }
454 m->Gc();
455 }
456 // Destroying components may release the last reference for some entity so we loop until there are no new
457 // deaths reported.
458 } while (deadEntities);
459
460 if (!allRemovedEntities.empty()) {
461 std::sort(allRemovedEntities.begin(), allRemovedEntities.end(),
462 [](const Entity& lhs, const Entity& rhs) { return lhs.id < rhs.id; });
463 }
464
465 // Send component related events
466 ProcessComponentEvents(ComponentListener::EventType::CREATED, allRemovedEntities);
467 ProcessComponentEvents(ComponentListener::EventType::MODIFIED, allRemovedEntities);
468 ProcessComponentEvents(ComponentListener::EventType::DESTROYED, {});
469
470 // Clean-up removed listeners.
471 entityListeners_.erase(
472 std::remove(entityListeners_.begin(), entityListeners_.end(), nullptr), entityListeners_.cend());
473 componentListeners_.erase(
474 std::remove(componentListeners_.begin(), componentListeners_.end(), nullptr), componentListeners_.cend());
475
476 for (auto it = componentManagerListeners_.begin(); it != componentManagerListeners_.cend();) {
477 auto& listeners = it->second;
478 listeners.erase(std::remove(listeners.begin(), listeners.end(), nullptr), listeners.cend());
479 if (listeners.empty()) {
480 it = componentManagerListeners_.erase(it);
481 } else {
482 ++it;
483 }
484 }
485
486 processingEvents_ = false;
487 }
488
Initialize()489 void Ecs::Initialize()
490 {
491 for (auto& s : systemOrder_) {
492 s->Initialize();
493 }
494 }
495
Update(uint64_t time,uint64_t delta)496 bool Ecs::Update(uint64_t time, uint64_t delta)
497 {
498 CORE_CPU_PERF_SCOPE("ECS", "Update", "Total_Cpu");
499
500 bool frameRenderingQueued = false;
501 if (GetRenderMode() == RENDER_ALWAYS || renderRequested_) {
502 frameRenderingQueued = true;
503 }
504
505 // Update all systems.
506 delta = static_cast<uint64_t>(static_cast<float>(delta) * timeScale_);
507 for (auto& s : systemOrder_) {
508 CORE_CPU_PERF_SCOPE("ECS", "SystemUpdate_Cpu", s->GetName());
509 if (s->Update(frameRenderingQueued, time, delta)) {
510 frameRenderingQueued = true;
511 }
512 }
513
514 // Clear modification flags from component managers.
515 for (auto& componentManager : managerOrder_) {
516 componentManager->ClearModifiedFlags();
517 }
518
519 renderRequested_ = false;
520 needRender_ = frameRenderingQueued;
521
522 return frameRenderingQueued;
523 }
524
Uninitialize()525 void Ecs::Uninitialize()
526 {
527 // Destroy all entities from scene.
528 entityManager_.DestroyAllEntities();
529
530 // Garbage-collect.
531 ProcessEvents();
532
533 // Uninitialize systems.
534 for (auto it = systemOrder_.rbegin(); it != systemOrder_.rend(); ++it) {
535 (*it)->Uninitialize();
536 }
537 }
538
RequestRender()539 void Ecs::RequestRender()
540 {
541 renderRequested_ = true;
542 }
543
SetRenderMode(RenderMode renderMode)544 void Ecs::SetRenderMode(RenderMode renderMode)
545 {
546 renderMode_ = renderMode;
547 }
548
GetRenderMode()549 IEcs::RenderMode Ecs::GetRenderMode()
550 {
551 return renderMode_;
552 }
553
NeedRender() const554 bool Ecs::NeedRender() const
555 {
556 return needRender_;
557 }
558
GetThreadPool() const559 const IThreadPool::Ptr& Ecs::GetThreadPool() const
560 {
561 return threadPool_;
562 }
563
GetTimeScale() const564 float Ecs::GetTimeScale() const
565 {
566 return timeScale_;
567 }
568
SetTimeScale(float scale)569 void Ecs::SetTimeScale(float scale)
570 {
571 timeScale_ = scale;
572 }
573
Ref()574 void Ecs::Ref() noexcept
575 {
576 refcnt_.fetch_add(1, std::memory_order_relaxed);
577 }
578
Unref()579 void Ecs::Unref() noexcept
580 {
581 if (std::atomic_fetch_sub_explicit(&refcnt_, 1, std::memory_order_release) == 1) {
582 std::atomic_thread_fence(std::memory_order_acquire);
583 delete this;
584 }
585 }
586
587 template<typename Container>
RemoveUid(Container & container,const Uid & uid)588 inline auto RemoveUid(Container& container, const Uid& uid)
589 {
590 container.erase(std::remove_if(container.begin(), container.end(),
591 [&uid](const auto& thing) { return thing->GetUid() == uid; }),
592 container.cend());
593 }
594
OnTypeInfoEvent(EventType type,array_view<const ITypeInfo * const> typeInfos)595 void Ecs::OnTypeInfoEvent(EventType type, array_view<const ITypeInfo* const> typeInfos)
596 {
597 if (type == EventType::ADDED) {
598 // not really interesed in these events. systems and component managers are added when SystemGraphLoader parses
599 // a configuration. we could store them in systems_ and managers_ and only define the order based on the graph.
600 } else if (type == EventType::REMOVED) {
601 for (const auto* info : typeInfos) {
602 if (info && info->typeUid == SystemTypeInfo::UID) {
603 const auto systemInfo = static_cast<const SystemTypeInfo*>(info);
604 // for systems Untinitialize should be called before destroying the instance
605 if (const auto pos = systems_.find(systemInfo->uid); pos != systems_.cend()) {
606 pos->second->Uninitialize();
607 systems_.erase(pos);
608 }
609 RemoveUid(systemOrder_, systemInfo->uid);
610 } else if (info && info->typeUid == ComponentManagerTypeInfo::UID) {
611 const auto managerInfo = static_cast<const ComponentManagerTypeInfo*>(info);
612 // BaseManager expects that the component list is empty when it's destroyed. might be also
613 // nice to notify all the listeners that the components are being destroyed.
614 if (const auto pos = managers_.find(managerInfo->uid); pos != managers_.end()) {
615 auto* manager = pos->second;
616
617 // remove all the components.
618 const auto components = static_cast<IComponentManager::ComponentId>(manager->GetComponentCount());
619 for (IComponentManager::ComponentId i = 0; i < components; ++i) {
620 manager->Destroy(manager->GetEntity(i));
621 }
622
623 // check are there generic or specific component listeners to inform.
624 if (const auto listenerIt = componentManagerListeners_.find(manager);
625 !componentListeners_.empty() ||
626 ((listenerIt != componentManagerListeners_.end()) && !listenerIt->second.empty())) {
627 if (const vector<Entity> removed = manager->GetRemovedComponents(); !removed.empty()) {
628 const auto removedView = array_view<const Entity>(removed);
629 for (auto* lister : componentListeners_) {
630 if (lister) {
631 lister->OnComponentEvent(
632 ComponentListener::EventType::DESTROYED, *manager, removedView);
633 }
634 }
635 if (listenerIt != componentManagerListeners_.end()) {
636 for (auto* lister : listenerIt->second) {
637 if (lister) {
638 lister->OnComponentEvent(
639 ComponentListener::EventType::DESTROYED, *manager, removedView);
640 }
641 }
642 // remove all the listeners for this manager. RemoveListener won't do anything. this
643 // isn't neccessary, but rather not leave invalid manager pointer even if it's just used
644 // as the key.
645 componentManagerListeners_.erase(listenerIt);
646 }
647 }
648 }
649 // garbage collection will remove dead entries from the list and BaseManager is happy.
650 manager->Gc();
651 managers_.erase(pos);
652 }
653 RemoveUid(managerOrder_, managerInfo->uid);
654 }
655 }
656 }
657 }
658 } // namespace
659
IEcsInstance(IClassFactory & registry,const IThreadPool::Ptr & threadPool)660 IEcs* IEcsInstance(IClassFactory& registry, const IThreadPool::Ptr& threadPool)
661 {
662 return new Ecs(registry, threadPool);
663 }
664 CORE_END_NAMESPACE()
665