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