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 { ®istry, UID_LOGGER, GetName<ILogger>().data(), nullptr,
259 [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
260 return &static_cast<PluginRegistry*>(token)->logger_;
261 } },
262 InterfaceTypeInfo { ®istry, UID_FRUSTUM_UTIL, GetName<IFrustumUtil>().data(), nullptr,
263 [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
264 return &static_cast<PluginRegistry*>(token)->frustumUtil_;
265 } },
266 InterfaceTypeInfo { ®istry, 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 { ®istry, UID_SYSTEM_GRAPH_LOADER, GetName<ISystemGraphLoaderFactory>().data(), nullptr,
271 [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
272 return &static_cast<PluginRegistry*>(token)->systemGraphLoadeFactory;
273 } },
274 InterfaceTypeInfo { ®istry, UID_GLOBAL_FACTORY, "Global registry factory", nullptr,
275 [](IClassRegister& registry, PluginToken /* token */) -> IInterface* {
276 return registry.GetInterface<IClassFactory>();
277 } },
278 InterfaceTypeInfo {
279 ®istry, UID_FILESYSTEM_API_FACTORY, "Filesystem API factory", nullptr, GetFileApiFactory },
280 InterfaceTypeInfo { ®istry, UID_FILE_MONITOR, "Filemonitor", CreateFileMonitor, nullptr },
281 InterfaceTypeInfo { ®istry, UID_FILE_MANAGER, "FileManager",
282 [](IClassFactory& /* factory */, PluginToken /* token */) -> IInterface* {
283 return new CORE_NS::FileManager();
284 },
285 nullptr },
286 InterfaceTypeInfo { ®istry, 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 { ®istry, 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 ¤t) { 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