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 { ®istry, UID_LOGGER, GetName<ILogger>().data(), nullptr,
236 [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
237 return &static_cast<PluginRegistry*>(token)->logger_;
238 } },
239 InterfaceTypeInfo { ®istry, UID_FRUSTUM_UTIL, GetName<IFrustumUtil>().data(), nullptr,
240 [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
241 return &static_cast<PluginRegistry*>(token)->frustumUtil_;
242 } },
243 InterfaceTypeInfo { ®istry, 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 { ®istry, UID_SYSTEM_GRAPH_LOADER, GetName<ISystemGraphLoaderFactory>().data(), nullptr,
248 [](IClassRegister& /* registry */, PluginToken token) -> IInterface* {
249 return &static_cast<PluginRegistry*>(token)->systemGraphLoadeFactory;
250 } },
251 InterfaceTypeInfo { ®istry, UID_GLOBAL_FACTORY, "Global registry factory", nullptr,
252 [](IClassRegister& registry, PluginToken /* token */) -> IInterface* {
253 return registry.GetInterface<IClassFactory>();
254 } },
255 InterfaceTypeInfo {
256 ®istry, UID_FILESYSTEM_API_FACTORY, "Filesystem API factory", nullptr, GetFileApiFactory },
257 InterfaceTypeInfo { ®istry, UID_FILE_MONITOR, "Filemonitor", CreateFileMonitor, nullptr },
258 InterfaceTypeInfo { ®istry, UID_FILE_MANAGER, "FileManager",
259 [](IClassFactory& /* factory */, PluginToken /* token */) -> IInterface* {
260 return new CORE_NS::FileManager();
261 },
262 nullptr },
263 InterfaceTypeInfo { ®istry, 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 { ®istry, 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