• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 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 "plugin_registry.h"
17 
18 #include <algorithm>
19 #include <platform/common/core/os/extensions_create_info.h>
20 
21 #include <base/util/errors.h>
22 #include <base/util/uid_util.h>
23 #include <core/ecs/intf_component_manager.h>
24 #include <core/implementation_uids.h>
25 #include <core/log.h>
26 
27 #include "image/loaders/image_loader_ktx.h"
28 #include "image/loaders/image_loader_stb_image.h"
29 #include "io/file_manager.h"
30 #include "io/std_filesystem.h"
31 #include "os/intf_library.h"
32 #if (CORE_PERF_ENABLED == 1)
33 #include "perf/performance_data_manager.h"
34 #endif
35 #include "static_plugin_decl.h"
36 
37 CORE_BEGIN_NAMESPACE()
38 using BASE_NS::array_view;
39 using BASE_NS::move;
40 using BASE_NS::pair;
41 using BASE_NS::reverse_iterator;
42 using BASE_NS::string;
43 using BASE_NS::string_view;
44 using BASE_NS::to_string;
45 using BASE_NS::Uid;
46 using BASE_NS::vector;
47 
48 IInterface* CreateFileMonitor(CORE_NS::IClassFactory& registry, CORE_NS::PluginToken token);
49 IInterface* GetFileApiFactory(CORE_NS::IClassRegister& registry, CORE_NS::PluginToken token);
50 
51 namespace StaticPluginRegistry {
52 // static_plugin_registry magic begin
53 #if defined(CORE_USE_COMPILER_GENERATED_STATIC_LIST) && (CORE_USE_COMPILER_GENERATED_STATIC_LIST == 1)
54 
55 #if _MSC_VER
56 #pragma section("spd$b", long, read)
57 #pragma section("spd$d", long, read)
58 #pragma section("spd$e", long, read)
59 __declspec(allocate("spd$b")) static constexpr const IPlugin* g_staticPluginList = nullptr;
60 __declspec(allocate("spd$e")) static constexpr const IPlugin* g_staticPluginListEnd = nullptr;
61 #else
62 // clang-format off
63 __asm__(
64     ".pushsection " SECTION(spl.1)
65     " .local g_staticPluginList\n"
66     " g_staticPluginList:\n"
67     ".dc.a 0x0\n"
68     ".section " SECTION(spl.2)
69     " .local g_staticPluginListData\n"
70     "g_staticPluginListData:\n"
71     " .dc.a 0x0\n"
72     " .section " SECTION(spl.3)
73     " .local g_staticPluginListEnd\n"
74     " g_staticPluginListEnd:\n"
75     ".dc.a 0x0\n"
76     " .popsection\n");
77 // clang-format on
78 extern "C" {
79 extern const CORE_NS::IPlugin* const g_staticPluginList;
80 extern const CORE_NS::IPlugin* const g_staticPluginListData;
81 extern const CORE_NS::IPlugin* const g_staticPluginListEnd;
82 __attribute__((used)) const CORE_NS::IPlugin* const g_staticPluginListDataRef = g_staticPluginListData;
83 }
84 #endif
85 
86 #else
87 
88 #if _MSC_VER
89 static const CORE_NS::IPlugin* const *g_staticPluginList = nullptr;
90 static size_t g_staticPluginListCount = 0;
91 #else
92 __attribute__((visibility("hidden"))) static const CORE_NS::IPlugin* const *g_staticPluginList = nullptr;
93 __attribute__((visibility("hidden"))) static size_t g_staticPluginListCount = 0;
94 #endif
95 
96 void RegisterStaticPlugin(const CORE_NS::IPlugin& plugin)
97 {
98     static BASE_NS::vector<const CORE_NS::IPlugin*> gGlobalPlugins;
99     gGlobalPlugins.push_back(&plugin);
100     g_staticPluginList = gGlobalPlugins.data();
101     g_staticPluginListCount = gGlobalPlugins.size();
102 }
103 #endif
104 } // namespace StaticPluginRegistry
105 
operator ==(const CORE_NS::IPlugin * lhs,const BASE_NS::Uid & rhs)106 constexpr bool operator==(const CORE_NS::IPlugin* lhs, const BASE_NS::Uid& rhs) noexcept
107 {
108     return lhs->version.uid == rhs;
109 }
110 
111 namespace {
112 struct LibPlugin {
113     ILibrary::Ptr lib;
114     const IPlugin* plugin;
115 };
116 
operator ==(const CORE_NS::LibPlugin & libPlugin,const BASE_NS::Uid & rhs)117 constexpr bool operator==(const CORE_NS::LibPlugin& libPlugin, const BASE_NS::Uid& rhs) noexcept
118 {
119     return libPlugin.plugin->version.uid == rhs;
120 }
121 
122 template<typename Container, typename Predicate>
FindIf(const Container & container,Predicate && predicate)123 inline typename Container::const_iterator FindIf(const Container& container, Predicate&& predicate)
124 {
125     return std::find_if(container.cbegin(), container.cend(), BASE_NS::forward<Predicate>(predicate));
126 }
127 
128 template<typename Container, typename Predicate>
NoneOf(const Container & container,Predicate && predicate)129 inline bool NoneOf(const Container& container, Predicate&& predicate)
130 {
131     return std::none_of(container.cbegin(), container.cend(), BASE_NS::forward<Predicate>(predicate));
132 }
133 
134 template<typename Container, typename Predicate>
AllOf(const Container & container,Predicate && predicate)135 inline bool AllOf(const Container& container, Predicate&& predicate)
136 {
137     return std::all_of(container.cbegin(), container.cend(), BASE_NS::forward<Predicate>(predicate));
138 }
139 
GatherStaticPlugins(vector<LibPlugin> & plugins)140 void GatherStaticPlugins(vector<LibPlugin>& plugins)
141 {
142     CORE_LOG_V("Static plugins:");
143 #if defined(CORE_USE_COMPILER_GENERATED_STATIC_LIST) && (CORE_USE_COMPILER_GENERATED_STATIC_LIST == 1)
144     const array_view<const IPlugin* const> staticPluginRegistry(
145         &StaticPluginRegistry::g_staticPluginList, &StaticPluginRegistry::g_staticPluginListEnd);
146 #else
147     const array_view<const IPlugin* const> staticPluginRegistry(
148         StaticPluginRegistry::g_staticPluginList, StaticPluginRegistry::g_staticPluginListCount);
149 #endif
150 
151     for (const auto plugin : staticPluginRegistry) {
152         if (plugin && (plugin->typeUid == IPlugin::UID)) {
153             CORE_LOG_V("\t%s", plugin->name);
154             plugins.push_back({ nullptr, plugin });
155         }
156     }
157 }
158 
GatherDynamicPlugins(vector<LibPlugin> & plugins,IFileManager & fileManager)159 void GatherDynamicPlugins(vector<LibPlugin>& plugins, IFileManager& fileManager)
160 {
161     CORE_LOG_V("Dynamic plugins:");
162     const auto libraryFileExtension = ILibrary::GetFileExtension();
163     constexpr string_view pluginRoot { "plugins://" };
164     IDirectory::Ptr pluginFiles = fileManager.OpenDirectory(pluginRoot);
165     if (!pluginFiles) {
166         return;
167     }
168     for (const auto& file : pluginFiles->GetEntries()) {
169         const string_view pluginFile = file.name;
170         if (!pluginFile.ends_with(libraryFileExtension)) {
171             continue;
172         }
173         const string absoluteFile = fileManager.GetEntry(pluginRoot + file.name).name;
174         if (ILibrary::Ptr lib = ILibrary::Load(absoluteFile); lib) {
175             const IPlugin* plugin = lib->GetPlugin();
176             if (plugin && (plugin->typeUid == IPlugin::UID)) {
177                 CORE_LOG_V("\t%s", plugin->name);
178                 plugins.push_back({ move(lib), plugin });
179             }
180         }
181     }
182 }
183 
AddDependencies(vector<Uid> & toBeLoaded,array_view<const LibPlugin> availablePlugins,const Uid & uidToLoad)184 bool AddDependencies(vector<Uid>& toBeLoaded, array_view<const LibPlugin> availablePlugins, const Uid& uidToLoad)
185 {
186     bool found = true;
187     if (auto pos =
188             FindIf(availablePlugins,
189                    [&uidToLoad](const LibPlugin &libPlugin) { return libPlugin.plugin->version.uid == uidToLoad; });
190         pos != availablePlugins.end()) {
191         found = AllOf(pos->plugin->pluginDependencies,
192             [&](const Uid& dependency) { return AddDependencies(toBeLoaded, availablePlugins, dependency); });
193         if (found) {
194             toBeLoaded.push_back(uidToLoad);
195         } else {
196             CORE_LOG_E("Missing dependencies for: %s", to_string(uidToLoad).data());
197         }
198     } else {
199         CORE_LOG_E("Plugin not found: %s", to_string(uidToLoad).data());
200         found = false;
201     }
202     return found;
203 }
204 
GatherRequiredPlugins(const array_view<const Uid> pluginUids,const array_view<const LibPlugin> plugins)205 vector<Uid> GatherRequiredPlugins(const array_view<const Uid> pluginUids, const array_view<const LibPlugin> plugins)
206 {
207     vector<Uid> loadAll(pluginUids.cbegin(), pluginUids.cend());
208     if (pluginUids.empty()) {
209         loadAll.reserve(plugins.size());
210         for (auto& plugin : plugins) {
211             loadAll.push_back(plugin.plugin->version.uid);
212         }
213     }
214 
215     if (loadAll.empty()) {
216         return {};
217     }
218 
219     // Gather dependencies of each plugin. toLoad will contain all the dependencies in depth first order.
220     vector<Uid> toLoad;
221     toLoad.reserve(plugins.size());
222     if (!AllOf(loadAll, [&](const Uid& uid) { return AddDependencies(toLoad, plugins, uid); })) {
223         toLoad.clear();
224     }
225     return toLoad;
226 }
227 
Notify(const array_view<IPluginRegister::ITypeInfoListener * > listeners,IPluginRegister::ITypeInfoListener::EventType type,array_view<const ITypeInfo * const> typeInfos)228 void Notify(const array_view<IPluginRegister::ITypeInfoListener*> listeners,
229     IPluginRegister::ITypeInfoListener::EventType type, array_view<const ITypeInfo* const> typeInfos)
230 {
231     for (IPluginRegister::ITypeInfoListener* listener : listeners) {
232         if (listener) {
233             listener->OnTypeInfoEvent(type, typeInfos);
234         }
235     }
236 }
237 
238 constexpr CORE_NS::IImageLoaderManager::ImageLoaderTypeInfo KTX_LOADER {
239     { CORE_NS::IImageLoaderManager::ImageLoaderTypeInfo::UID },
240     nullptr,
241     BASE_NS::Uid { "306357a4-d49c-4670-9746-5ccbba567dc9" },
242     CreateImageLoaderKtx,
243     KTX_IMAGE_TYPES,
244 };
245 
246 constexpr CORE_NS::IImageLoaderManager::ImageLoaderTypeInfo STB_LOADER {
247     { CORE_NS::IImageLoaderManager::ImageLoaderTypeInfo::UID },
248     nullptr,
249     BASE_NS::Uid { "a5049cb8-10bb-4047-b7f5-e9939d5bb3a5" },
250     CreateImageLoaderStbImage,
251     STB_IMAGE_TYPES,
252 };
253 } // namespace
254 
RegisterGlobalInterfaces(PluginRegistry & registry)255 vector<InterfaceTypeInfo> PluginRegistry::RegisterGlobalInterfaces(PluginRegistry& registry)
256 {
257     vector<InterfaceTypeInfo> interfaces = {
258         InterfaceTypeInfo { &registry, UID_LOGGER, GetName<ILogger>().data(), nullptr,
259             [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
260                 return &static_cast<PluginRegistry*>(token)->logger_;
261             } },
262         InterfaceTypeInfo { &registry, UID_FRUSTUM_UTIL, GetName<IFrustumUtil>().data(), nullptr,
263             [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
264                 return &static_cast<PluginRegistry*>(token)->frustumUtil_;
265             } },
266         InterfaceTypeInfo { &registry, UID_ENGINE_FACTORY, GetName<IEngineFactory>().data(), nullptr,
267             [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
268                 return static_cast<PluginRegistry*>(token)->engineFactory_.GetInterface(IEngineFactory::UID);
269             } },
270         InterfaceTypeInfo { &registry, UID_SYSTEM_GRAPH_LOADER, GetName<ISystemGraphLoaderFactory>().data(), nullptr,
271             [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
272                 return &static_cast<PluginRegistry*>(token)->systemGraphLoadeFactory;
273             } },
274         InterfaceTypeInfo { &registry, UID_GLOBAL_FACTORY, "Global registry factory", nullptr,
275             [](IClassRegister& registry, PluginToken /* token */) -> IInterface* {
276                 return registry.GetInterface<IClassFactory>();
277             } },
278         InterfaceTypeInfo {
279             &registry, UID_FILESYSTEM_API_FACTORY, "Filesystem API factory", nullptr, GetFileApiFactory },
280         InterfaceTypeInfo { &registry, UID_FILE_MONITOR, "Filemonitor", CreateFileMonitor, nullptr },
281         InterfaceTypeInfo { &registry, UID_FILE_MANAGER, "FileManager",
282             [](IClassFactory& /* factory */, PluginToken /* token */) -> IInterface* {
283                 return new CORE_NS::FileManager();
284             },
285             nullptr },
286         InterfaceTypeInfo { &registry, UID_TASK_QUEUE_FACTORY, "Task queue factory", nullptr,
287             [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
288                 return &static_cast<PluginRegistry*>(token)->taskQueueFactory_;
289             } },
290 #if (CORE_PERF_ENABLED == 1)
291         InterfaceTypeInfo { &registry, UID_PERFORMANCE_FACTORY, GetName<IPerformanceDataManagerFactory>().data(),
292             nullptr,
293             [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
294                 return &static_cast<PluginRegistry*>(token)->perfManFactory_;
295             } }
296 #endif
297     };
298 
299     for (const auto& info : interfaces) {
300         registry.RegisterInterfaceType(info);
301     }
302 
303     registry.RegisterTypeInfo(KTX_LOADER);
304     registry.RegisterTypeInfo(STB_LOADER);
305 
306     return interfaces;
307 }
308 
UnregisterGlobalInterfaces()309 void PluginRegistry::UnregisterGlobalInterfaces()
310 {
311     UnregisterTypeInfo(STB_LOADER);
312     UnregisterTypeInfo(KTX_LOADER);
313 
314     for (const auto& info : ownInterfaceInfos_) {
315         UnregisterInterfaceType(info);
316     }
317 }
318 
WARNING_SCOPE_START(W_THIS_USED_BASE_INITIALIZER_LIST)319 WARNING_SCOPE_START(W_THIS_USED_BASE_INITIALIZER_LIST)
320 PluginRegistry::PluginRegistry()
321 #if CORE_PERF_ENABLED
322     : perfManFactory_(*this)
323 #endif // CORE_PERF_ENABLED
324 {
325     ownInterfaceInfos_ = RegisterGlobalInterfaces(*this);
326 }
WARNING_SCOPE_END()327 WARNING_SCOPE_END()
328 
329 PluginRegistry::~PluginRegistry()
330 {
331     UnloadPlugins({});
332     UnregisterGlobalInterfaces();
333 }
334 
335 // IPluginRegister
GetPlugins() const336 array_view<const IPlugin* const> PluginRegistry::GetPlugins() const
337 {
338     return plugins_;
339 }
340 
LoadPlugins(const array_view<const Uid> pluginUids)341 bool PluginRegistry::LoadPlugins(const array_view<const Uid> pluginUids)
342 {
343     // Gather all the available static and dynamic libraries.
344     vector<LibPlugin> availablePlugins;
345     GatherStaticPlugins(availablePlugins);
346     GatherDynamicPlugins(availablePlugins, fileManager_);
347 
348     vector<Uid> toLoad = GatherRequiredPlugins(pluginUids, availablePlugins);
349     if (toLoad.empty()) {
350         return false;
351     }
352 
353     // Remove duplicates while counting how many times plugins were requested due to dependencies.
354     vector<int32_t> counts;
355     counts.reserve(toLoad.size());
356 
357     if (toLoad.size() > 1U) {
358         auto begin = toLoad.begin();
359         auto end = toLoad.end();
360         while ((begin + 1) != end) {
361             auto newEnd = std::remove_if(begin + 1, end, [current = *begin](const Uid& uid) { return uid == current; });
362             auto count = static_cast<int32_t>(std::distance(newEnd, end)) + 1;
363             counts.push_back(count);
364             end = newEnd;
365             ++begin;
366         }
367         toLoad.erase(end, toLoad.cend());
368     }
369     counts.push_back(1);
370 
371     loading_ = true;
372     auto itCounts = counts.cbegin();
373     for (const Uid& uid : toLoad) {
374         const auto currentCount = *itCounts++;
375         // If the plugin was already loaded just increase the reference count.
376         if (auto pos = std::find(plugins_.begin(), plugins_.end(), uid); pos != plugins_.end()) {
377             const auto index = static_cast<size_t>(std::distance(plugins_.begin(), pos));
378             pluginDatas_[index].refcnt += currentCount;
379             continue;
380         }
381 
382         auto pos = std::find(availablePlugins.begin(), availablePlugins.end(), uid);
383         if (pos == availablePlugins.end()) {
384             // This shouldn't be possible as toLoad should contain UIDs which are found in plugins.
385             continue;
386         }
387         if (!pos->plugin) {
388             // This shouldn't be possible as GatherStatic/DynamicPlugins should add only valid plugin pointers, lib can
389             // be null.
390             continue;
391         }
392         RegisterPlugin(BASE_NS::move(pos->lib), *(pos->plugin), currentCount);
393     }
394     loading_ = false;
395 
396     if (!newTypeInfos_.empty()) {
397         Notify(typeInfoListeners_, ITypeInfoListener::EventType::ADDED, newTypeInfos_);
398         newTypeInfos_.clear();
399     }
400 
401     return true;
402 }
403 
UnloadPlugins(const array_view<const Uid> pluginUids)404 void PluginRegistry::UnloadPlugins(const array_view<const Uid> pluginUids)
405 {
406     CORE_LOG_D("Unload plugins:");
407 #if defined(CORE_PERF_ENABLED) && (CORE_PERF_ENABLED)
408     if (perfLoggerId_) {
409         if (pluginUids.empty() ||
410             std::any_of(pluginUids.cbegin(), pluginUids.cend(),
411                         [&perfUid = perfTracePlugin_](const Uid &uid) { return uid == perfUid; })) {
412             logger_.RemoveOutput(perfLoggerId_);
413             perfLoggerId_ = 0U;
414         }
415     }
416 #endif
417     // to allow plugins to call UnloadPlugins from unregisterInterfaces, gather unloadable entries into separate
418     // containers to avoid modifying member containers during iteration.
419     decltype(pluginDatas_) removedPluginDatas;
420     decltype(plugins_) removedPlugins;
421     if (pluginUids.empty()) {
422         while (!pluginDatas_.empty() && !plugins_.empty()) {
423             removedPlugins.push_back(BASE_NS::move(plugins_.back()));
424             removedPluginDatas.push_back(BASE_NS::move(pluginDatas_.back()));
425             plugins_.pop_back();
426             pluginDatas_.pop_back();
427         }
428     } else {
429         DecreaseRefCounts(pluginUids);
430 
431         auto pdIt = pluginDatas_.rbegin();
432         for (auto pos = plugins_.rbegin(), last = plugins_.rend(); pos != last;) {
433             if (pdIt->refcnt <= 0) {
434                 removedPlugins.push_back(BASE_NS::move(*pos));
435                 removedPluginDatas.push_back(BASE_NS::move(*pdIt));
436                 plugins_.erase(pos.base() - 1);
437                 pluginDatas_.erase(pdIt.base() - 1);
438             }
439             ++pos;
440             ++pdIt;
441         }
442     }
443     // now it's safe to call unregisterInterfaces without messing up the iteration of pluginDatas_ and plugins_.
444     auto dataIt = removedPluginDatas.begin();
445     for (auto plugin : removedPlugins) {
446         UnregisterPlugin(*plugin, dataIt->token);
447         ++dataIt;
448     }
449 }
450 
GetClassRegister() const451 IClassRegister& PluginRegistry::GetClassRegister() const
452 {
453     return *const_cast<PluginRegistry*>(this);
454 }
455 
RegisterTypeInfo(const ITypeInfo & type)456 void PluginRegistry::RegisterTypeInfo(const ITypeInfo& type)
457 {
458     if (const auto pos = typeInfos_.find(type.typeUid); pos != typeInfos_.cend()) {
459         pos->second.push_back(&type);
460     } else {
461         typeInfos_.insert({ type.typeUid, {} }).first->second.push_back(&type);
462     }
463 
464     // During plugin loading gather all the infos and send events once.
465     if (loading_) {
466         newTypeInfos_.push_back(&type);
467     } else {
468         const ITypeInfo* const infos[] = { &type };
469         Notify(typeInfoListeners_, ITypeInfoListener::EventType::ADDED, infos);
470     }
471 }
472 
UnregisterTypeInfo(const ITypeInfo & type)473 void PluginRegistry::UnregisterTypeInfo(const ITypeInfo& type)
474 {
475     if (const auto typeInfos = typeInfos_.find(type.typeUid); typeInfos != typeInfos_.cend()) {
476         auto& infos = typeInfos->second;
477         if (const auto info = std::find(infos.cbegin(), infos.cend(), &type); info != infos.cend()) {
478             infos.erase(info);
479         }
480     }
481 
482     const ITypeInfo* const infos[] = { &type };
483     Notify(typeInfoListeners_, ITypeInfoListener::EventType::REMOVED, infos);
484 
485 #if defined(CORE_PERF_ENABLED) && (CORE_PERF_ENABLED)
486     if (type.typeUid == PerformanceTraceTypeInfo::UID) {
487         perfManFactory_.RemovePerformanceTrace(static_cast<const PerformanceTraceTypeInfo&>(type).uid);
488     }
489 #endif
490 }
491 
GetTypeInfos(const Uid & typeUid) const492 array_view<const ITypeInfo* const> PluginRegistry::GetTypeInfos(const Uid& typeUid) const
493 {
494     if (const auto typeInfos = typeInfos_.find(typeUid); typeInfos != typeInfos_.cend()) {
495         return typeInfos->second;
496     }
497     return {};
498 }
499 
AddListener(ITypeInfoListener & listener)500 void PluginRegistry::AddListener(ITypeInfoListener& listener)
501 {
502     if (std::none_of(typeInfoListeners_.begin(), typeInfoListeners_.end(),
503                      [adding = &listener](const auto &current) { return current == adding; })) {
504         typeInfoListeners_.push_back(&listener);
505     }
506 }
507 
RemoveListener(const ITypeInfoListener & listener)508 void PluginRegistry::RemoveListener(const ITypeInfoListener& listener)
509 {
510     if (auto pos = std::find(typeInfoListeners_.begin(), typeInfoListeners_.end(), &listener);
511         pos != typeInfoListeners_.end()) {
512         *pos = nullptr;
513     }
514 }
515 
516 // IClassRegister
RegisterInterfaceType(const InterfaceTypeInfo & interfaceInfo)517 void PluginRegistry::RegisterInterfaceType(const InterfaceTypeInfo& interfaceInfo)
518 {
519     // keep interfaceTypeInfos_ sorted according to UIDs
520     const auto pos = std::upper_bound(interfaceTypeInfos_.cbegin(), interfaceTypeInfos_.cend(), interfaceInfo.uid,
521         [](Uid value, const InterfaceTypeInfo* element) { return value < element->uid; });
522     interfaceTypeInfos_.insert(pos, &interfaceInfo);
523 }
524 
UnregisterInterfaceType(const InterfaceTypeInfo & interfaceInfo)525 void PluginRegistry::UnregisterInterfaceType(const InterfaceTypeInfo& interfaceInfo)
526 {
527     if (!interfaceTypeInfos_.empty()) {
528         const auto pos = std::lower_bound(interfaceTypeInfos_.cbegin(), interfaceTypeInfos_.cend(), interfaceInfo.uid,
529             [](const InterfaceTypeInfo* element, Uid value) { return element->uid < value; });
530         if ((pos != interfaceTypeInfos_.cend()) && (*pos)->uid == interfaceInfo.uid) {
531             interfaceTypeInfos_.erase(pos);
532         }
533     }
534 }
535 
GetInterfaceMetadata() const536 array_view<const InterfaceTypeInfo* const> PluginRegistry::GetInterfaceMetadata() const
537 {
538     return { interfaceTypeInfos_.data(), interfaceTypeInfos_.size() };
539 }
540 
GetInterfaceMetadata(const Uid & uid) const541 const InterfaceTypeInfo& PluginRegistry::GetInterfaceMetadata(const Uid& uid) const
542 {
543     static constexpr InterfaceTypeInfo invalidType {};
544 
545     if (!interfaceTypeInfos_.empty()) {
546         const auto pos = std::lower_bound(interfaceTypeInfos_.cbegin(), interfaceTypeInfos_.cend(), uid,
547             [](const InterfaceTypeInfo* element, Uid value) { return element->uid < value; });
548         if ((pos != interfaceTypeInfos_.cend()) && (*pos)->uid == uid) {
549             return *(*pos);
550         }
551     }
552     return invalidType;
553 }
554 
GetInstance(const Uid & uid) const555 IInterface* PluginRegistry::GetInstance(const Uid& uid) const
556 {
557     const auto& data = GetInterfaceMetadata(uid);
558     if (data.getInterface) {
559         return data.getInterface(const_cast<PluginRegistry&>(*this), data.token);
560     }
561     return nullptr;
562 }
563 
564 // IClassFactory
CreateInstance(const Uid & uid)565 IInterface::Ptr PluginRegistry::CreateInstance(const Uid& uid)
566 {
567     const auto& data = GetInterfaceMetadata(uid);
568     if (data.createInterface) {
569         return IInterface::Ptr { data.createInterface(*this, data.token) };
570     }
571     return {};
572 }
573 
574 // IInterface
GetInterface(const Uid & uid) const575 const IInterface* PluginRegistry::GetInterface(const Uid& uid) const
576 {
577     return const_cast<PluginRegistry*>(this)->GetInterface(uid);
578 }
579 
GetInterface(const Uid & uid)580 IInterface* PluginRegistry::GetInterface(const Uid& uid)
581 {
582     if ((uid == IInterface::UID) || (uid == IClassRegister::UID)) {
583         return static_cast<IClassRegister*>(this);
584     }
585     if (uid == IClassFactory::UID) {
586         return static_cast<IClassFactory*>(this);
587     }
588     return nullptr;
589 }
590 
Ref()591 void PluginRegistry::Ref() {}
592 
Unref()593 void PluginRegistry::Unref() {}
594 
595 // Public members
RegisterPluginPath(const string_view path)596 void PluginRegistry::RegisterPluginPath(const string_view path)
597 {
598     if (!fileProtocolRegistered_) {
599         fileProtocolRegistered_ = true;
600         fileManager_.RegisterFilesystem("file", IFilesystem::Ptr { new StdFilesystem("/") });
601     }
602     fileManager_.RegisterPath("plugins", path, false);
603 }
604 
GetFileManager()605 IFileManager& PluginRegistry::GetFileManager()
606 {
607     return fileManager_;
608 }
609 
610 // Internal members
HandlePerfTracePlugin(const PlatformCreateInfo & platformCreateInfo)611 void PluginRegistry::HandlePerfTracePlugin(const PlatformCreateInfo& platformCreateInfo)
612 {
613     const PlatformCreateExtensionInfo* traceSettings = nullptr;
614     for (const PlatformCreateExtensionInfo* extension = Platform::Extensions(platformCreateInfo); extension;
615          extension = extension->next) {
616         switch (extension->type) {
617             case PLATFORM_EXTENSION_TRACE_USER:
618                 CORE_ASSERT(traceSettings == nullptr);
619                 traceSettings = extension;
620                 break;
621             case PLATFORM_EXTENSION_TRACE_EXTENSION:
622                 CORE_ASSERT(traceSettings == nullptr);
623                 traceSettings = extension;
624                 break;
625         }
626     }
627 
628     // attempt to initialize performance tracing before any other subsystems, performance tracing should come from
629     // an dynamic plugin so ideally the dynamic module is loaded automatically during loading of LumeEngine but
630     // before any other plugins are loaded so that other plugins.
631 #if defined(CORE_PERF_ENABLED) && (CORE_PERF_ENABLED)
632     if (traceSettings) {
633         CORE_LOG_V("Tracing is enabled and application requested it");
634         if (traceSettings->type == PLATFORM_EXTENSION_TRACE_USER) {
635             // Provide a user applied performance tracer from the application
636             perfManFactory_.SetPerformanceTrace(
637                 {}, IPerformanceTrace::Ptr { static_cast<const PlatformTraceInfoUsr*>(traceSettings)->tracer });
638         } else if (traceSettings->type == PLATFORM_EXTENSION_TRACE_EXTENSION) {
639             // This load plugin call isn't really that ideal, basically dlopen/LoadLibrary is called all plugins
640             // found. The order is undefined, so apotentional risks is that the modules could attempt to use
641             // tracing (i.e. memory allocation tracking) prior to tracing plugin being effectively loaded.
642             //
643             // others problem with loading plugins like this is that it might simply be undesireable to map
644             // /all/ plugins into memory space for example when two versions of same plugins exists,
645             // (hot-)reloading of modules
646             const PlatformTraceInfoExt* traceExtension = static_cast<const PlatformTraceInfoExt*>(traceSettings);
647             if (!LoadPlugins({ &traceExtension->plugin, 1U })) {
648                 CORE_LOG_V("Failed to load %s", to_string(traceExtension->plugin).data());
649                 return;
650             }
651             perfTracePlugin_ = traceExtension->plugin;
652             auto typeInfos = GetTypeInfos(PerformanceTraceTypeInfo::UID);
653             auto itt = std::find_if(typeInfos.cbegin(), typeInfos.cend(), [traceExtension](const ITypeInfo* const a) {
654                 return static_cast<const PerformanceTraceTypeInfo* const>(a)->uid == traceExtension->type;
655             });
656             if (itt != typeInfos.end()) {
657                 auto trace = static_cast<const PerformanceTraceTypeInfo* const>(*itt);
658                 perfManFactory_.SetPerformanceTrace(trace->uid, trace->createLoader(trace->token));
659             } else {
660                 CORE_ASSERT(false && "cannot find trace plugin");
661             }
662         }
663 
664         // Capture console message and forward them to performance tracer
665         perfLoggerId_ = logger_.AddOutput(perfManFactory_.GetLogger());
666     } else {
667         CORE_LOG_V("Tracing is enabled and application didn't requested it");
668     }
669 #else
670     if (traceSettings) {
671         CORE_LOG_V("Tracing is disabled but application still requested it");
672     }
673 #endif
674 }
675 
676 // Private members
RegisterPlugin(ILibrary::Ptr lib,const IPlugin & plugin,const int32_t refCount)677 void PluginRegistry::RegisterPlugin(ILibrary::Ptr lib, const IPlugin& plugin, const int32_t refCount)
678 {
679     CORE_LOG_D("\tRegister Plugin: %s %s", plugin.name, to_string(plugin.version.uid).data());
680     if (plugin.version.GetVersionString) {
681         CORE_LOG_D("\tVersion Info: %s", plugin.version.GetVersionString());
682     }
683     // when a plugin is loaded/ registered due a requested plugin depends on it ref count starts from zero and it's
684     // expected that some following plugin will place a reference. once that plugin releases its reference the
685     // dependency is known to be a candidate for unloading.
686     PluginData pd { move(lib), {}, refCount };
687     if (plugin.registerInterfaces) {
688         pd.token = plugin.registerInterfaces(*static_cast<IPluginRegister*>(this));
689     }
690 
691     pluginDatas_.push_back(move(pd));
692     plugins_.push_back(&plugin);
693 }
694 
UnregisterPlugin(const IPlugin & plugin,PluginToken token)695 void PluginRegistry::UnregisterPlugin(const IPlugin& plugin, PluginToken token)
696 {
697     CORE_LOG_D("\tUnregister Plugin: %s %s", plugin.name, to_string(plugin.version.uid).data());
698 
699     if (plugin.unregisterInterfaces) {
700         plugin.unregisterInterfaces(token);
701     }
702 }
703 
DecreaseRefCounts(const array_view<const Uid> pluginUids)704 void PluginRegistry::DecreaseRefCounts(const array_view<const Uid> pluginUids)
705 {
706     for (const auto& uid : pluginUids) {
707         if (auto pos = std::find_if(plugins_.begin(), plugins_.end(),
708                                     [uid](const IPlugin *pl) { return pl && pl->version.uid == uid; });
709             pos != plugins_.end()) {
710             const auto index = static_cast<size_t>(std::distance(plugins_.begin(), pos));
711             --pluginDatas_[index].refcnt;
712             DecreaseRefCounts((*pos)->pluginDependencies);
713         }
714     }
715 }
716 
717 // Library exports
GetPluginRegister()718 CORE_PUBLIC IPluginRegister& GetPluginRegister()
719 {
720     static PluginRegistry registry;
721     return registry;
722 }
723 
CreatePluginRegistry(const PlatformCreateInfo & platformCreateInfo)724 CORE_PUBLIC void CreatePluginRegistry(const PlatformCreateInfo& platformCreateInfo)
725 {
726     static bool once = false;
727     if (!once) {
728         once = true;
729         auto& registry = static_cast<PluginRegistry&>(GetPluginRegister());
730 
731         auto platform = Platform::Create(platformCreateInfo);
732         platform->RegisterPluginLocations(registry);
733 
734         registry.HandlePerfTracePlugin(platformCreateInfo);
735     }
736 }
737 CORE_END_NAMESPACE()
738