• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "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 
106 namespace {
107 struct LibPlugin {
108     ILibrary::Ptr lib;
109     const IPlugin* plugin;
110 };
111 
112 template<typename Container, typename Predicate>
FindIf(const Container & container,Predicate && predicate)113 inline typename Container::const_iterator FindIf(const Container& container, Predicate&& predicate)
114 {
115     return std::find_if(container.cbegin(), container.cend(), BASE_NS::forward<Predicate>(predicate));
116 }
117 
118 template<typename Container, typename Predicate>
NoneOf(const Container & container,Predicate && predicate)119 inline bool NoneOf(const Container& container, Predicate&& predicate)
120 {
121     return std::none_of(container.cbegin(), container.cend(), BASE_NS::forward<Predicate>(predicate));
122 }
123 
124 template<typename Container, typename Predicate>
AllOf(const Container & container,Predicate && predicate)125 inline bool AllOf(const Container& container, Predicate&& predicate)
126 {
127     return std::all_of(container.cbegin(), container.cend(), BASE_NS::forward<Predicate>(predicate));
128 }
129 
GatherStaticPlugins(vector<LibPlugin> & plugins)130 void GatherStaticPlugins(vector<LibPlugin>& plugins)
131 {
132     CORE_LOG_V("Static plugins:");
133 #if defined(CORE_USE_COMPILER_GENERATED_STATIC_LIST) && (CORE_USE_COMPILER_GENERATED_STATIC_LIST == 1)
134     const array_view<const IPlugin* const> staticPluginRegistry(
135         &StaticPluginRegistry::g_staticPluginList, &StaticPluginRegistry::g_staticPluginListEnd);
136 #else
137     const array_view<const IPlugin* const> staticPluginRegistry(
138         StaticPluginRegistry::g_staticPluginList, StaticPluginRegistry::g_staticPluginListCount);
139 #endif
140 
141     for (const auto plugin : staticPluginRegistry) {
142         if (plugin && (plugin->typeUid == IPlugin::UID)) {
143             CORE_LOG_V("\t%s", plugin->name);
144             plugins.push_back({ nullptr, plugin });
145         }
146     }
147 }
148 
GatherDynamicPlugins(vector<LibPlugin> & plugins,IFileManager & fileManager)149 void GatherDynamicPlugins(vector<LibPlugin>& plugins, IFileManager& fileManager)
150 {
151     CORE_LOG_V("Dynamic plugins:");
152     const auto libraryFileExtension = ILibrary::GetFileExtension();
153     constexpr string_view pluginRoot { "plugins://" };
154     if (IDirectory::Ptr pluginFiles = fileManager.OpenDirectory(pluginRoot); pluginFiles) {
155         for (const auto& file : pluginFiles->GetEntries()) {
156             const string_view pluginFile = file.name;
157             if (pluginFile.ends_with(libraryFileExtension)) {
158                 const string absoluteFile = fileManager.GetEntry(pluginRoot + file.name).name;
159                 if (ILibrary::Ptr lib = ILibrary::Load(absoluteFile); lib) {
160                     const IPlugin* plugin = lib->GetPlugin();
161                     if (plugin && (plugin->typeUid == IPlugin::UID)) {
162                         CORE_LOG_V("\t%s", plugin->name);
163                         plugins.push_back({ move(lib), plugin });
164                     }
165                 }
166             }
167         }
168     }
169 }
170 
AddDependencies(vector<Uid> & toBeLoaded,array_view<const LibPlugin> availablePlugins,array_view<const IPlugin * const> loadedPlugins,const Uid & uidToLoad)171 bool AddDependencies(vector<Uid>& toBeLoaded, array_view<const LibPlugin> availablePlugins,
172     array_view<const IPlugin* const> loadedPlugins, const Uid& uidToLoad)
173 {
174     bool found = true;
175     // Only consider plugins which are not already loaded, and not yet in the loading list.
176     if (NoneOf(loadedPlugins, [&uidToLoad](const IPlugin* loaded) { return loaded->version.uid == uidToLoad; }) &&
177         NoneOf(toBeLoaded, [&uidToLoad](const Uid& willLoad) { return willLoad == uidToLoad; })) {
178         if (auto pos = FindIf(availablePlugins,
179             [&uidToLoad](const LibPlugin& libPlugin) { return libPlugin.plugin->version.uid == uidToLoad; });
180             pos != availablePlugins.end()) {
181             found = AllOf(pos->plugin->pluginDependencies, [&](const Uid& dependency) {
182                 return AddDependencies(toBeLoaded, availablePlugins, loadedPlugins, dependency);
183             });
184             if (found) {
185                 toBeLoaded.push_back(uidToLoad);
186             } else {
187                 CORE_LOG_E("Missing dependencies for: %s", to_string(uidToLoad).data());
188             }
189         } else {
190             CORE_LOG_E("Plugin not found: %s", to_string(uidToLoad).data());
191             found = false;
192         }
193     }
194     return found;
195 }
LogMissingPlugins(const array_view<const Uid> requestedPlugins,const array_view<const LibPlugin> availablePlugins)196 void LogMissingPlugins(const array_view<const Uid> requestedPlugins, const array_view<const LibPlugin> availablePlugins)
197 {
198     CORE_LOG_E("Unable to load plugins:");
199     for (const auto& uid : requestedPlugins) {
200         if (NoneOf(availablePlugins, [uid](const auto& available) { return available.plugin->version.uid == uid; })) {
201             CORE_LOG_E("\t%s", to_string(uid).data());
202         }
203     }
204 }
Notify(const array_view<IPluginRegister::ITypeInfoListener * > listeners,IPluginRegister::ITypeInfoListener::EventType type,array_view<const ITypeInfo * const> typeInfos)205 void Notify(const array_view<IPluginRegister::ITypeInfoListener*> listeners,
206     IPluginRegister::ITypeInfoListener::EventType type, array_view<const ITypeInfo* const> typeInfos)
207 {
208     for (IPluginRegister::ITypeInfoListener* listener : listeners) {
209         if (listener) {
210             listener->OnTypeInfoEvent(type, typeInfos);
211         }
212     }
213 }
214 
215 constexpr CORE_NS::IImageLoaderManager::ImageLoaderTypeInfo KTX_LOADER {
216     { CORE_NS::IImageLoaderManager::ImageLoaderTypeInfo::UID },
217     nullptr,
218     BASE_NS::Uid { "306357a4-d49c-4670-9746-5ccbba567dc9" },
219     CreateImageLoaderKtx,
220     KTX_IMAGE_TYPES,
221 };
222 
223 constexpr CORE_NS::IImageLoaderManager::ImageLoaderTypeInfo STB_LOADER {
224     { CORE_NS::IImageLoaderManager::ImageLoaderTypeInfo::UID },
225     nullptr,
226     BASE_NS::Uid { "a5049cb8-10bb-4047-b7f5-e9939d5bb3a5" },
227     CreateImageLoaderStbImage,
228     STB_IMAGE_TYPES,
229 };
230 } // namespace
231 
RegisterGlobalInterfaces(PluginRegistry & registry)232 vector<InterfaceTypeInfo> PluginRegistry::RegisterGlobalInterfaces(PluginRegistry& registry)
233 {
234     vector<InterfaceTypeInfo> interfaces = {
235         InterfaceTypeInfo { &registry, UID_LOGGER, GetName<ILogger>().data(), nullptr,
236             [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
237                 return &static_cast<PluginRegistry*>(token)->logger_;
238             } },
239         InterfaceTypeInfo { &registry, UID_FRUSTUM_UTIL, GetName<IFrustumUtil>().data(), nullptr,
240             [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
241                 return &static_cast<PluginRegistry*>(token)->frustumUtil_;
242             } },
243         InterfaceTypeInfo { &registry, UID_ENGINE_FACTORY, GetName<IEngineFactory>().data(), nullptr,
244             [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
245                 return static_cast<PluginRegistry*>(token)->engineFactory_.GetInterface(IEngineFactory::UID);
246             } },
247         InterfaceTypeInfo { &registry, UID_SYSTEM_GRAPH_LOADER, GetName<ISystemGraphLoaderFactory>().data(), nullptr,
248             [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
249                 return &static_cast<PluginRegistry*>(token)->systemGraphLoadeFactory;
250             } },
251         InterfaceTypeInfo { &registry, UID_GLOBAL_FACTORY, "Global registry factory", nullptr,
252             [](IClassRegister& registry, PluginToken /* token */) -> IInterface* {
253                 return registry.GetInterface<IClassFactory>();
254             } },
255         InterfaceTypeInfo {
256             &registry, UID_FILESYSTEM_API_FACTORY, "Filesystem API factory", nullptr, GetFileApiFactory },
257         InterfaceTypeInfo { &registry, UID_FILE_MONITOR, "Filemonitor", CreateFileMonitor, nullptr },
258         InterfaceTypeInfo { &registry, UID_FILE_MANAGER, "FileManager",
259             [](IClassFactory& /* factory */, PluginToken /* token */) -> IInterface* {
260                 return new CORE_NS::FileManager();
261             },
262             nullptr },
263         InterfaceTypeInfo { &registry, UID_TASK_QUEUE_FACTORY, "Task queue factory", nullptr,
264             [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
265                 return &static_cast<PluginRegistry*>(token)->taskQueueFactory_;
266             } },
267 #if (CORE_PERF_ENABLED == 1)
268         InterfaceTypeInfo { &registry, UID_PERFORMANCE_FACTORY, GetName<IPerformanceDataManagerFactory>().data(),
269             nullptr,
270             [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
271                 return &static_cast<PluginRegistry*>(token)->perfManFactory_;
272             } }
273 #endif
274     };
275 
276     for (const auto& info : interfaces) {
277         registry.RegisterInterfaceType(info);
278     }
279 
280     registry.RegisterTypeInfo(KTX_LOADER);
281     registry.RegisterTypeInfo(STB_LOADER);
282 
283     return interfaces;
284 }
285 
UnregisterGlobalInterfaces()286 void PluginRegistry::UnregisterGlobalInterfaces()
287 {
288     UnregisterTypeInfo(STB_LOADER);
289     UnregisterTypeInfo(KTX_LOADER);
290 
291     for (const auto& info : ownInterfaceInfos_) {
292         UnregisterInterfaceType(info);
293     }
294 }
295 
WARNING_SCOPE_START(W_THIS_USED_BASE_INITIALIZER_LIST)296 WARNING_SCOPE_START(W_THIS_USED_BASE_INITIALIZER_LIST)
297 PluginRegistry::PluginRegistry()
298 #if CORE_PERF_ENABLED
299     : perfManFactory_(*this)
300 #endif // CORE_PERF_ENABLED
301 {
302     ownInterfaceInfos_ = RegisterGlobalInterfaces(*this);
303 }
WARNING_SCOPE_END()304 WARNING_SCOPE_END()
305 
306 PluginRegistry::~PluginRegistry()
307 {
308     UnloadPlugins({});
309     UnregisterGlobalInterfaces();
310 }
311 
312 // IPluginRegister
GetPlugins() const313 array_view<const IPlugin* const> PluginRegistry::GetPlugins() const
314 {
315     return plugins_;
316 }
317 
LoadPlugins(const array_view<const Uid> pluginUids)318 bool PluginRegistry::LoadPlugins(const array_view<const Uid> pluginUids)
319 {
320     // Gather all the available static and dynamic libraries.
321     vector<LibPlugin> plugins;
322     GatherStaticPlugins(plugins);
323     GatherDynamicPlugins(plugins, fileManager_);
324 
325     // If a list of UIDs was given remove all except the requires plugins.
326     if (!pluginUids.empty()) {
327         // Gather dependencies of each plugin.
328         vector<Uid> toLoad;
329         toLoad.reserve(plugins.size());
330 
331         const bool found =
332             AllOf(pluginUids, [&](const Uid& uid) { return AddDependencies(toLoad, plugins, plugins_, uid); });
333 
334         // Order the available plugins to match the to-be-loaded list and remove extras.
335         auto begin = plugins.begin();
336         auto end = plugins.end();
337         for (const Uid& uid : toLoad) {
338             if (auto pos = std::find_if(
339                 begin, end, [uid](const LibPlugin& libPlugin) { return libPlugin.plugin->version.uid == uid; });
340                 pos != end) {
341                 std::rotate(begin, pos, end);
342                 ++begin;
343             }
344         }
345         plugins.erase(begin, end);
346 
347         if (!found) {
348             LogMissingPlugins(pluginUids, plugins);
349             return false;
350         }
351     }
352 
353     // Now we should have only the desired plugins and can load all of them.
354     CORE_LOG_D("Load plugins:");
355     loading_ = true;
356     for (auto& plugin : plugins) {
357         RegisterPlugin(move(plugin.lib), *plugin.plugin,
358             NoneOf(pluginUids,
359                 [&loading = (plugin.plugin->version.uid)](const Uid& userRequest) { return userRequest == loading; }));
360     }
361     loading_ = false;
362 
363     if (!newTypeInfos_.empty()) {
364         Notify(typeInfoListeners_, ITypeInfoListener::EventType::ADDED, newTypeInfos_);
365         newTypeInfos_.clear();
366     }
367 
368     return true;
369 }
370 
UnloadPlugins(const array_view<const Uid> pluginUids)371 void PluginRegistry::UnloadPlugins(const array_view<const Uid> pluginUids)
372 {
373     CORE_LOG_D("Unload plugins:");
374 #if defined(CORE_PERF_ENABLED) && (CORE_PERF_ENABLED)
375     if (perfLoggerId_) {
376         if (pluginUids.empty() || std::any_of(pluginUids.cbegin(), pluginUids.cend(),
377             [&perfUid = perfTracePlugin_](const Uid& uid) { return uid == perfUid; })) {
378             logger_.RemoveOutput(perfLoggerId_);
379             perfLoggerId_ = 0U;
380         }
381     }
382 #endif
383     if (pluginUids.empty()) {
384         while (!pluginDatas_.empty() && !plugins_.empty()) {
385             UnregisterPlugin(*plugins_.back(), pluginDatas_.back().token);
386             plugins_.pop_back();
387             pluginDatas_.pop_back();
388         }
389         plugins_.clear();
390         pluginDatas_.clear();
391     } else {
392         [](array_view<const IPlugin*> plugins, array_view<PluginData> pluginDatas,
393             const array_view<const Uid>& pluginUids) {
394             auto recurse = [](const array_view<const IPlugin*>& plugins, array_view<PluginData>& pluginDatas,
395                                const array_view<const Uid>& pluginUids, auto& recurseRef) -> void {
396                 for (const auto& uid : pluginUids) {
397                     if (auto pos = std::find_if(plugins.begin(), plugins.end(),
398                         [uid](const IPlugin* pl) { return pl && pl->version.uid == uid; });
399                         pos != plugins.end()) {
400                         const auto index = static_cast<size_t>(std::distance(plugins.begin(), pos));
401                         if (--pluginDatas[index].refcnt <= 0) {
402                             recurseRef(plugins, pluginDatas, (*pos)->pluginDependencies, recurseRef);
403                         }
404                     }
405                 }
406             };
407             recurse(plugins, pluginDatas, pluginUids, recurse);
408         }(plugins_, pluginDatas_, pluginUids);
409 
410         auto pdIt = pluginDatas_.crbegin();
411         for (auto pos = plugins_.crbegin(), last = plugins_.crend(); pos != last;) {
412             if (pdIt->refcnt <= 0) {
413                 UnregisterPlugin(*(*pos), pdIt->token);
414                 plugins_.erase(pos.base() - 1);
415                 pluginDatas_.erase(pdIt.base() - 1);
416             }
417             ++pos;
418             ++pdIt;
419         }
420     }
421 }
422 
GetClassRegister() const423 IClassRegister& PluginRegistry::GetClassRegister() const
424 {
425     return *const_cast<PluginRegistry*>(this);
426 }
427 
RegisterTypeInfo(const ITypeInfo & type)428 void PluginRegistry::RegisterTypeInfo(const ITypeInfo& type)
429 {
430     if (const auto pos = typeInfos_.find(type.typeUid); pos != typeInfos_.cend()) {
431         pos->second.push_back(&type);
432     } else {
433         typeInfos_.insert({ type.typeUid, {} }).first->second.push_back(&type);
434     }
435 
436     // During plugin loading gather all the infos and send events once.
437     if (loading_) {
438         newTypeInfos_.push_back(&type);
439     } else {
440         const ITypeInfo* const infos[] = { &type };
441         Notify(typeInfoListeners_, ITypeInfoListener::EventType::ADDED, infos);
442     }
443 }
444 
UnregisterTypeInfo(const ITypeInfo & type)445 void PluginRegistry::UnregisterTypeInfo(const ITypeInfo& type)
446 {
447     if (const auto typeInfos = typeInfos_.find(type.typeUid); typeInfos != typeInfos_.cend()) {
448         auto& infos = typeInfos->second;
449         if (const auto info = std::find(infos.cbegin(), infos.cend(), &type); info != infos.cend()) {
450             infos.erase(info);
451         }
452     }
453 
454     const ITypeInfo* const infos[] = { &type };
455     Notify(typeInfoListeners_, ITypeInfoListener::EventType::REMOVED, infos);
456 
457 #if defined(CORE_PERF_ENABLED) && (CORE_PERF_ENABLED)
458     if (type.typeUid == PerformanceTraceTypeInfo::UID) {
459         perfManFactory_.RemovePerformanceTrace(static_cast<const PerformanceTraceTypeInfo&>(type).uid);
460     }
461 #endif
462 }
463 
GetTypeInfos(const Uid & typeUid) const464 array_view<const ITypeInfo* const> PluginRegistry::GetTypeInfos(const Uid& typeUid) const
465 {
466     if (const auto typeInfos = typeInfos_.find(typeUid); typeInfos != typeInfos_.cend()) {
467         return typeInfos->second;
468     }
469     return {};
470 }
471 
AddListener(ITypeInfoListener & listener)472 void PluginRegistry::AddListener(ITypeInfoListener& listener)
473 {
474     if (std::none_of(typeInfoListeners_.begin(), typeInfoListeners_.end(),
475         [adding = &listener](const auto& current) { return current == adding; })) {
476         typeInfoListeners_.push_back(&listener);
477     }
478 }
479 
RemoveListener(const ITypeInfoListener & listener)480 void PluginRegistry::RemoveListener(const ITypeInfoListener& listener)
481 {
482     if (auto pos = std::find(typeInfoListeners_.begin(), typeInfoListeners_.end(), &listener);
483         pos != typeInfoListeners_.end()) {
484         *pos = nullptr;
485     }
486 }
487 
488 // IClassRegister
RegisterInterfaceType(const InterfaceTypeInfo & interfaceInfo)489 void PluginRegistry::RegisterInterfaceType(const InterfaceTypeInfo& interfaceInfo)
490 {
491     // keep interfaceTypeInfos_ sorted according to UIDs
492     const auto pos = std::upper_bound(interfaceTypeInfos_.cbegin(), interfaceTypeInfos_.cend(), interfaceInfo.uid,
493         [](Uid value, const InterfaceTypeInfo* element) { return value < element->uid; });
494     interfaceTypeInfos_.insert(pos, &interfaceInfo);
495 }
496 
UnregisterInterfaceType(const InterfaceTypeInfo & interfaceInfo)497 void PluginRegistry::UnregisterInterfaceType(const InterfaceTypeInfo& interfaceInfo)
498 {
499     if (!interfaceTypeInfos_.empty()) {
500         const auto pos = std::lower_bound(interfaceTypeInfos_.cbegin(), interfaceTypeInfos_.cend(), interfaceInfo.uid,
501             [](const InterfaceTypeInfo* element, Uid value) { return element->uid < value; });
502         if ((pos != interfaceTypeInfos_.cend()) && (*pos)->uid == interfaceInfo.uid) {
503             interfaceTypeInfos_.erase(pos);
504         }
505     }
506 }
507 
GetInterfaceMetadata() const508 array_view<const InterfaceTypeInfo* const> PluginRegistry::GetInterfaceMetadata() const
509 {
510     return { interfaceTypeInfos_.data(), interfaceTypeInfos_.size() };
511 }
512 
GetInterfaceMetadata(const Uid & uid) const513 const InterfaceTypeInfo& PluginRegistry::GetInterfaceMetadata(const Uid& uid) const
514 {
515     static constexpr InterfaceTypeInfo invalidType {};
516 
517     if (!interfaceTypeInfos_.empty()) {
518         const auto pos = std::lower_bound(interfaceTypeInfos_.cbegin(), interfaceTypeInfos_.cend(), uid,
519             [](const InterfaceTypeInfo* element, Uid value) { return element->uid < value; });
520         if ((pos != interfaceTypeInfos_.cend()) && (*pos)->uid == uid) {
521             return *(*pos);
522         }
523     }
524     return invalidType;
525 }
526 
GetInstance(const Uid & uid) const527 IInterface* PluginRegistry::GetInstance(const Uid& uid) const
528 {
529     const auto& data = GetInterfaceMetadata(uid);
530     if (data.getInterface) {
531         return data.getInterface(const_cast<PluginRegistry&>(*this), data.token);
532     }
533     return nullptr;
534 }
535 
536 // IClassFactory
CreateInstance(const Uid & uid)537 IInterface::Ptr PluginRegistry::CreateInstance(const Uid& uid)
538 {
539     const auto& data = GetInterfaceMetadata(uid);
540     if (data.createInterface) {
541         return IInterface::Ptr { data.createInterface(*this, data.token) };
542     }
543     return {};
544 }
545 
546 // IInterface
GetInterface(const Uid & uid) const547 const IInterface* PluginRegistry::GetInterface(const Uid& uid) const
548 {
549     return const_cast<PluginRegistry*>(this)->GetInterface(uid);
550 }
551 
GetInterface(const Uid & uid)552 IInterface* PluginRegistry::GetInterface(const Uid& uid)
553 {
554     if ((uid == IInterface::UID) || (uid == IClassRegister::UID)) {
555         return static_cast<IClassRegister*>(this);
556     }
557     if (uid == IClassFactory::UID) {
558         return static_cast<IClassFactory*>(this);
559     }
560     return nullptr;
561 }
562 
Ref()563 void PluginRegistry::Ref() {}
564 
Unref()565 void PluginRegistry::Unref() {}
566 
567 // Public members
RegisterPluginPath(const string_view path)568 void PluginRegistry::RegisterPluginPath(const string_view path)
569 {
570     if (!fileProtocolRegistered_) {
571         fileProtocolRegistered_ = true;
572         fileManager_.RegisterFilesystem("file", IFilesystem::Ptr { new StdFilesystem("/") });
573     }
574     fileManager_.RegisterPath("plugins", path, false);
575 }
576 
GetFileManager()577 IFileManager& PluginRegistry::GetFileManager()
578 {
579     return fileManager_;
580 }
581 
582 // Internal members
HandlePerfTracePlugin(const PlatformCreateInfo & platformCreateInfo)583 void PluginRegistry::HandlePerfTracePlugin(const PlatformCreateInfo& platformCreateInfo)
584 {
585     const PlatformCreateExtensionInfo* traceSettings = nullptr;
586     for (const PlatformCreateExtensionInfo* extension = Platform::Extensions(platformCreateInfo); extension;
587          extension = extension->next) {
588         switch (extension->type) {
589             case PLATFORM_EXTENSION_TRACE_USER:
590                 CORE_ASSERT(traceSettings == nullptr);
591                 traceSettings = extension;
592                 break;
593             case PLATFORM_EXTENSION_TRACE_EXTENSION:
594                 CORE_ASSERT(traceSettings == nullptr);
595                 traceSettings = extension;
596                 break;
597         }
598     }
599 
600     // attempt to initialize performance tracing before any other subsystems, performance tracing should come from
601     // an dynamic plugin so ideally the dynamic module is loaded automatically during loading of LumeEngine but
602     // before any other plugins are loaded so that other plugins.
603 #if defined(CORE_PERF_ENABLED) && (CORE_PERF_ENABLED)
604     if (traceSettings) {
605         CORE_LOG_V("Tracing is enabled and application requested it");
606         if (traceSettings->type == PLATFORM_EXTENSION_TRACE_USER) {
607             // Provide a user applied performance tracer from the application
608             perfManFactory_.SetPerformanceTrace(
609                 {},
610                 IPerformanceTrace::Ptr { static_cast<const PlatformTraceInfoUsr*>(traceSettings)->tracer }
611             );
612         } else if (traceSettings->type == PLATFORM_EXTENSION_TRACE_EXTENSION) {
613             // This load plugin call isn't really that ideal, basically dlopen/LoadLibrary is called all plugins
614             // found. The order is undefined, so apotentional risks is that the modules could attempt to use
615             // tracing (i.e. memory allocation tracking) prior to tracing plugin being effectively loaded.
616             //
617             // others problem with loading plugins like this is that it might simply be undesireable to map
618             // /all/ plugins into memory space for example when two versions of same plugins exists,
619             // (hot-)reloading of modules
620             const PlatformTraceInfoExt* traceExtension = static_cast<const PlatformTraceInfoExt*>(traceSettings);
621             if (!LoadPlugins({ &traceExtension->plugin, 1U })) {
622                 CORE_LOG_V("Failed to load %s", to_string(traceExtension->plugin).data());
623                 return;
624             }
625             perfTracePlugin_ = traceExtension->plugin;
626             auto typeInfos = GetTypeInfos(PerformanceTraceTypeInfo::UID);
627             auto itt = std::find_if(typeInfos.cbegin(), typeInfos.cend(), [traceExtension](const ITypeInfo* const a) {
628                 return static_cast<const PerformanceTraceTypeInfo* const>(a)->uid == traceExtension->type;
629             });
630             if (itt != typeInfos.end()) {
631                 auto trace = static_cast<const PerformanceTraceTypeInfo* const>(*itt);
632                 perfManFactory_.SetPerformanceTrace(trace->uid, trace->createLoader(trace->token));
633             } else {
634                 CORE_ASSERT(false && "cannot find trace plugin");
635             }
636         }
637 
638         // Capture console message and forward them to performance tracer
639         perfLoggerId_ = logger_.AddOutput(perfManFactory_.GetLogger());
640     } else {
641         CORE_LOG_V("Tracing is enabled and application didn't requested it");
642     }
643 #else
644     if (traceSettings) {
645         CORE_LOG_V("Tracing is disabled but application still requested it");
646     }
647 #endif
648 }
649 
650 // Private members
RegisterPlugin(ILibrary::Ptr lib,const IPlugin & plugin,bool asDependency)651 void PluginRegistry::RegisterPlugin(ILibrary::Ptr lib, const IPlugin& plugin, bool asDependency)
652 {
653     CORE_LOG_D("\tRegister Plugin: %s %s", plugin.name, to_string(plugin.version.uid).data());
654     if (plugin.version.GetVersionString) {
655         CORE_LOG_D("\tVersion Info: %s", plugin.version.GetVersionString());
656     }
657     if (std::any_of(plugins_.begin(), plugins_.end(),
658         [&plugin](const IPlugin* pl) { return strcmp(plugin.name, pl->name) == 0; })) {
659         CORE_LOG_W("\tSkipping duplicate plugin: %s!", plugin.name);
660         return;
661     }
662     // when a plugin is loaded/ registered due a requested plugin depends on it ref count starts from zero and it's
663     // expected that some following plugin will place a reference. once that plugin releases its reference the
664     // dependency is known to be a candidate for unloading.
665     PluginData pd { move(lib), {}, asDependency ? 0 : 1 };
666     if (plugin.registerInterfaces) {
667         pd.token = plugin.registerInterfaces(*static_cast<IPluginRegister*>(this));
668     }
669 
670     pluginDatas_.push_back(move(pd));
671     plugins_.push_back(&plugin);
672     for (const auto& dependency : plugin.pluginDependencies) {
673         if (auto pos = std::find_if(plugins_.begin(), plugins_.end(),
674             [dependency](const IPlugin* plugin) { return plugin->version.uid == dependency; });
675             pos != plugins_.end()) {
676             const auto index = static_cast<size_t>(std::distance(plugins_.begin(), pos));
677             ++pluginDatas_[index].refcnt;
678         }
679     }
680 }
681 
UnregisterPlugin(const IPlugin & plugin,PluginToken token)682 void PluginRegistry::UnregisterPlugin(const IPlugin& plugin, PluginToken token)
683 {
684     CORE_LOG_D("\tUnregister Plugin: %s %s", plugin.name, to_string(plugin.version.uid).data());
685 
686     if (plugin.unregisterInterfaces) {
687         plugin.unregisterInterfaces(token);
688     }
689 }
690 
691 // Library exports
GetPluginRegister()692 CORE_PUBLIC IPluginRegister& GetPluginRegister()
693 {
694     static PluginRegistry registry;
695     return registry;
696 }
697 
CreatePluginRegistry(const PlatformCreateInfo & platformCreateInfo)698 CORE_PUBLIC void CreatePluginRegistry(const PlatformCreateInfo& platformCreateInfo)
699 {
700     static bool once = false;
701     if (!once) {
702         once = true;
703         auto& registry = static_cast<PluginRegistry&>(GetPluginRegister());
704 
705         auto platform = Platform::Create(platformCreateInfo);
706         platform->RegisterPluginLocations(registry);
707 
708         registry.HandlePerfTracePlugin(platformCreateInfo);
709     }
710 }
711 CORE_END_NAMESPACE()
712