* Copyright (c) 2022 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "plugin_registry.h"
#include <algorithm>
#include <mutex>
#include <platform/common/core/os/extensions_create_info.h>
#include <base/util/errors.h>
#include <base/util/uid_util.h>
#include <core/ecs/intf_component_manager.h>
#include <core/implementation_uids.h>
#include <core/log.h>
#include "image/loaders/image_loader_astc.h"
#include "image/loaders/image_loader_ktx.h"
#include "image/loaders/image_loader_stb_image.h"
#include "io/file_manager.h"
#include "io/std_filesystem.h"
#include "os/intf_library.h"
#if (CORE_PERF_ENABLED == 1)
#include "perf/performance_data_manager.h"
#endif
#include "static_plugin_decl.h"
CORE_BEGIN_NAMESPACE()
using BASE_NS::array_view;
using BASE_NS::move;
using BASE_NS::pair;
using BASE_NS::reverse_iterator;
using BASE_NS::string;
using BASE_NS::string_view;
using BASE_NS::to_string;
using BASE_NS::Uid;
using BASE_NS::vector;
IInterface* CreateFileMonitor(CORE_NS::IClassFactory& registry, CORE_NS::PluginToken token);
IInterface* GetFileApiFactory(CORE_NS::IClassRegister& registry, CORE_NS::PluginToken token);
namespace StaticPluginRegistry {
#if defined(CORE_USE_COMPILER_GENERATED_STATIC_LIST) && (CORE_USE_COMPILER_GENERATED_STATIC_LIST == 1)
#if _MSC_VER
#pragma section("spd$b", long, read)
#pragma section("spd$d", long, read)
#pragma section("spd$e", long, read)
__declspec(allocate("spd$b")) static constexpr const IPlugin* g_staticPluginList = nullptr;
__declspec(allocate("spd$e")) static constexpr const IPlugin* g_staticPluginListEnd = nullptr;
#else
__asm__(
".pushsection " SECTION(spl.1)
" .local g_staticPluginList\n"
" g_staticPluginList:\n"
".dc.a 0x0\n"
".section " SECTION(spl.2)
" .local g_staticPluginListData\n"
"g_staticPluginListData:\n"
" .dc.a 0x0\n"
" .section " SECTION(spl.3)
" .local g_staticPluginListEnd\n"
" g_staticPluginListEnd:\n"
".dc.a 0x0\n"
" .popsection\n");
extern "C" {
extern const CORE_NS::IPlugin* const g_staticPluginList;
extern const CORE_NS::IPlugin* const g_staticPluginListData;
extern const CORE_NS::IPlugin* const g_staticPluginListEnd;
__attribute__((used)) const CORE_NS::IPlugin* const g_staticPluginListDataRef = g_staticPluginListData;
}
#endif
#else
#if _MSC_VER
static const CORE_NS::IPlugin* const* g_staticPluginList = nullptr;
static size_t g_staticPluginListCount = 0;
#else
__attribute__((visibility("hidden"))) static const CORE_NS::IPlugin* const* g_staticPluginList = nullptr;
__attribute__((visibility("hidden"))) static size_t g_staticPluginListCount = 0;
#endif
void RegisterStaticPlugin(const CORE_NS::IPlugin& plugin)
{
static BASE_NS::vector<const CORE_NS::IPlugin*> gGlobalPlugins;
gGlobalPlugins.push_back(&plugin);
g_staticPluginList = gGlobalPlugins.data();
g_staticPluginListCount = gGlobalPlugins.size();
}
#endif
}
constexpr bool operator==(const CORE_NS::IPlugin* lhs, const BASE_NS::Uid& rhs) noexcept
{
return lhs->version.uid == rhs;
}
namespace {
struct LibPlugin {
ILibrary::Ptr lib;
const IPlugin* plugin{};
const DynamicPluginInfo* dynamicInfo{nullptr};
};
Uid GetUid(const LibPlugin& libPlugin)
{
if (libPlugin.lib) {
return libPlugin.lib->GetPluginUid();
}
if (libPlugin.plugin) {
return libPlugin.plugin->version.uid;
}
if (libPlugin.dynamicInfo) {
return libPlugin.dynamicInfo->uid;
}
return {};
}
bool operator==(const CORE_NS::LibPlugin& libPlugin, const BASE_NS::Uid& rhs) noexcept
{
return GetUid(libPlugin) == rhs;
}
template <typename Container, typename Predicate>
inline typename Container::const_iterator FindIf(const Container& container, Predicate&& predicate)
{
return std::find_if(container.cbegin(), container.cend(), BASE_NS::forward<Predicate>(predicate));
}
template <typename Container, typename Predicate>
inline bool NoneOf(const Container& container, Predicate&& predicate)
{
return std::none_of(container.cbegin(), container.cend(), BASE_NS::forward<Predicate>(predicate));
}
template <typename Container, typename Predicate>
inline bool AllOf(const Container& container, Predicate&& predicate)
{
return std::all_of(container.cbegin(), container.cend(), BASE_NS::forward<Predicate>(predicate));
}
void GatherStaticPlugins(vector<LibPlugin>& plugins)
{
CORE_LOG_V("Static plugins:");
#if defined(CORE_USE_COMPILER_GENERATED_STATIC_LIST) && (CORE_USE_COMPILER_GENERATED_STATIC_LIST == 1)
const array_view<const IPlugin* const> staticPluginRegistry(
&StaticPluginRegistry::g_staticPluginList, &StaticPluginRegistry::g_staticPluginListEnd);
#else
const array_view<const IPlugin* const> staticPluginRegistry(
StaticPluginRegistry::g_staticPluginList, StaticPluginRegistry::g_staticPluginListCount);
#endif
for (const auto plugin : staticPluginRegistry) {
if (plugin && (plugin->typeUid == IPlugin::UID)) {
CORE_LOG_V("\t%s", plugin->name);
plugins.push_back({nullptr, plugin, nullptr});
}
}
}
void GatherDynamicPlugins(vector<LibPlugin>& plugins, IFileManager& fileManager, vector<DynamicPluginInfo>& pluginInfos)
{
CORE_LOG_V("Dynamic plugins:");
const auto libraryFileExtension = ILibrary::GetFileExtension();
constexpr string_view pluginRoot{"plugins://"};
IDirectory::Ptr pluginFiles = fileManager.OpenDirectory(pluginRoot);
if (!pluginFiles) {
pluginInfos.clear();
return;
}
const auto& entries = pluginFiles->GetEntries();
plugins.reserve(plugins.size() + entries.size());
vector<DynamicPluginInfo> cachedPluginInfos = move(pluginInfos);
pluginInfos.clear();
pluginInfos.reserve(entries.size());
for (const auto& file : entries) {
const string_view pluginFile = file.name;
if (!pluginFile.ends_with(libraryFileExtension)) {
continue;
}
const string pluginUri = pluginRoot + file.name;
if (file.timestamp) {
auto pos = std::find_if(cachedPluginInfos.begin(),
cachedPluginInfos.end(),
[&pluginUri, timestamp = file.timestamp](
const DynamicPluginInfo& info) { return info.timestamp == timestamp && info.uri == pluginUri; });
if (pos != cachedPluginInfos.end()) {
pluginInfos.push_back(move(*pos));
CORE_LOG_V("\tUID %s", BASE_NS::to_string(pluginInfos.back().uid).data());
plugins.push_back({{}, nullptr, &pluginInfos.back()});
continue;
}
}
auto filePtr = fileManager.OpenFile(pluginUri);
const string absoluteFile = fileManager.GetEntry(pluginUri).name;
ILibrary::Ptr lib = filePtr ? ILibrary::Load(absoluteFile, filePtr) : ILibrary::Load(absoluteFile);
if (lib) {
CORE_LOG_V("\tUID %s", BASE_NS::to_string(lib->GetPluginUid()).data());
const auto deps = lib->GetPluginDependencies();
pluginInfos.push_back(
{pluginUri, lib->GetPluginUid(), vector<Uid>(deps.cbegin().ptr(), deps.cend().ptr()), file.timestamp});
plugins.push_back({move(lib), {}, nullptr});
}
}
}
vector<int32_t> CountPluginRequests(vector<Uid>& toLoad)
{
vector<int32_t> counts;
counts.reserve(toLoad.size());
if (toLoad.size() > 1U) {
auto begin = toLoad.begin();
auto end = toLoad.end();
while ((begin + 1) != end) {
auto newEnd = std::remove_if(begin + 1, end, [current = *begin](const Uid& uid) { return uid == current; });
counts.push_back(static_cast<int32_t>(std::distance(newEnd, end)) + 1);
end = newEnd;
++begin;
}
toLoad.erase(end, toLoad.cend());
}
counts.push_back(1);
return counts;
}
bool ResolvePlugin(LibPlugin& plugin, IFileManager& fileManager)
{
if (plugin.plugin) {
return true;
}
if (plugin.lib) {
plugin.plugin = plugin.lib->GetPlugin();
return plugin.plugin != nullptr;
}
if (!plugin.dynamicInfo) {
return false;
}
const string absoluteFile = fileManager.GetEntry(plugin.dynamicInfo->uri).name;
auto filePtr = fileManager.OpenFile(plugin.dynamicInfo->uri);
plugin.lib = filePtr ? ILibrary::Load(absoluteFile, filePtr) : ILibrary::Load(absoluteFile);
if (!plugin.lib) {
return false;
}
plugin.plugin = plugin.lib->GetPlugin();
return plugin.plugin != nullptr;
}
auto FindPlugin(array_view<const LibPlugin> availablePlugins, const Uid& uid)
{
return FindIf(availablePlugins, [&uid](const LibPlugin& lp) { return lp == uid; });
}
array_view<const Uid> GetDeps(const LibPlugin& lp)
{
if (lp.lib) {
return lp.lib->GetPluginDependencies();
}
if (lp.plugin) {
return lp.plugin->pluginDependencies;
}
if (lp.dynamicInfo) {
return lp.dynamicInfo->dependencies;
}
return {};
}
bool AddDependencies(
vector<Uid>& toBeLoaded, array_view<const LibPlugin> availablePlugins, const Uid& uidToLoad, vector<Uid>& resolving)
{
if (std::find(resolving.cbegin(), resolving.cend(), uidToLoad) != resolving.cend()) {
CORE_LOG_E("Circular plugin dependency: %s", to_string(uidToLoad).data());
return false;
}
auto rootPos = FindPlugin(availablePlugins, uidToLoad);
if (rootPos == availablePlugins.end()) {
CORE_LOG_E("Plugin not found: %s", to_string(uidToLoad).data());
return false;
}
struct Frame {
Uid uid;
array_view<const Uid> deps;
size_t depIndex;
};
const size_t resolvingBase = resolving.size();
resolving.push_back(uidToLoad);
vector<Frame> stack{{uidToLoad, GetDeps(*rootPos), 0}};
bool failed = false;
while (!stack.empty() && !failed) {
Frame& frame = stack.back();
if (frame.depIndex < frame.deps.size()) {
const Uid dep = frame.deps[frame.depIndex++];
if (std::find(resolving.cbegin(), resolving.cend(), dep) != resolving.cend()) {
CORE_LOG_E("Circular plugin dependency: %s", to_string(dep).data());
failed = true;
} else if (auto pos = FindPlugin(availablePlugins, dep); pos != availablePlugins.end()) {
resolving.push_back(dep);
stack.push_back({dep, GetDeps(*pos), 0});
} else {
CORE_LOG_E("Plugin not found: %s", to_string(dep).data());
failed = true;
}
} else {
toBeLoaded.push_back(frame.uid);
resolving.pop_back();
stack.pop_back();
}
}
if (failed) {
resolving.resize(resolvingBase);
}
return !failed;
}
vector<Uid> GatherRequiredPlugins(const array_view<const Uid> pluginUids, const array_view<const LibPlugin> plugins)
{
vector<Uid> toLoad;
vector<Uid> resolving;
toLoad.reserve(plugins.size());
resolving.reserve(plugins.size());
if (pluginUids.empty()) {
for (const auto& plugin : plugins) {
vector<Uid> requiredPlugins;
requiredPlugins.reserve(plugins.size());
const Uid uid = GetUid(plugin);
if (AddDependencies(requiredPlugins, plugins, uid, resolving)) {
toLoad.insert(toLoad.cend(), requiredPlugins.cbegin(), requiredPlugins.cend());
}
resolving.clear();
}
} else if (!AllOf(pluginUids, [&](const Uid& uid) { return AddDependencies(toLoad, plugins, uid, resolving); })) {
toLoad.clear();
}
return toLoad;
}
void Notify(const array_view<IPluginRegister::ITypeInfoListener*> listeners,
IPluginRegister::ITypeInfoListener::EventType type, array_view<const ITypeInfo* const> typeInfos)
{
for (IPluginRegister::ITypeInfoListener* listener : listeners) {
if (listener) {
listener->OnTypeInfoEvent(type, typeInfos);
}
}
}
constexpr CORE_NS::IImageLoaderManager::ImageLoaderTypeInfo ASTC_LOADER{
{CORE_NS::IImageLoaderManager::ImageLoaderTypeInfo::UID},
nullptr,
BASE_NS::Uid{"5f9a6a0c-4759-4094-ac71-9192b11c93a9"},
CreateImageLoaderAstc,
ASTC_IMAGE_TYPES,
};
constexpr CORE_NS::IImageLoaderManager::ImageLoaderTypeInfo KTX_LOADER{
{CORE_NS::IImageLoaderManager::ImageLoaderTypeInfo::UID},
nullptr,
BASE_NS::Uid{"306357a4-d49c-4670-9746-5ccbba567dc9"},
CreateImageLoaderKtx,
KTX_IMAGE_TYPES,
};
#if defined(USE_STB_IMAGE) && (USE_STB_IMAGE == 1)
constexpr CORE_NS::IImageLoaderManager::ImageLoaderTypeInfo STB_LOADER{
{CORE_NS::IImageLoaderManager::ImageLoaderTypeInfo::UID},
nullptr,
BASE_NS::Uid{"a5049cb8-10bb-4047-b7f5-e9939d5bb3a5"},
CreateImageLoaderStbImage,
STB_IMAGE_TYPES,
};
#endif
}
vector<InterfaceTypeInfo> PluginRegistry::RegisterGlobalInterfaces(PluginRegistry& registry)
{
vector<InterfaceTypeInfo> interfaces = {
InterfaceTypeInfo{®istry,
UID_LOGGER,
GetName<ILogger>().data(),
nullptr,
[](IClassRegister& , PluginToken token) -> IInterface* {
return &static_cast<PluginRegistry*>(token)->logger_;
}},
InterfaceTypeInfo{®istry,
UID_FRUSTUM_UTIL,
GetName<IFrustumUtil>().data(),
nullptr,
[](IClassRegister& , PluginToken token) -> IInterface* {
return &static_cast<PluginRegistry*>(token)->frustumUtil_;
}},
InterfaceTypeInfo{®istry,
UID_ENGINE_FACTORY,
GetName<IEngineFactory>().data(),
nullptr,
[](IClassRegister& , PluginToken token) -> IInterface* {
return static_cast<PluginRegistry*>(token)->engineFactory_.GetInterface(IEngineFactory::UID);
}},
InterfaceTypeInfo{®istry,
UID_SYSTEM_GRAPH_LOADER,
GetName<ISystemGraphLoaderFactory>().data(),
nullptr,
[](IClassRegister& , PluginToken token) -> IInterface* {
return &static_cast<PluginRegistry*>(token)->systemGraphLoadeFactory;
}},
InterfaceTypeInfo{®istry,
UID_GLOBAL_FACTORY,
"Global registry factory",
nullptr,
[](IClassRegister& registry, PluginToken ) -> IInterface* {
return registry.GetInterface<IClassFactory>();
}},
InterfaceTypeInfo{®istry, UID_FILESYSTEM_API_FACTORY, "Filesystem API factory", nullptr, GetFileApiFactory},
InterfaceTypeInfo{®istry, UID_FILE_MONITOR, "Filemonitor", CreateFileMonitor, nullptr},
InterfaceTypeInfo{®istry,
UID_FILE_MANAGER,
"FileManager",
[](IClassFactory& , PluginToken ) -> IInterface* {
return new CORE_NS::FileManager();
},
nullptr},
InterfaceTypeInfo{®istry,
UID_TASK_QUEUE_FACTORY,
"Task queue factory",
nullptr,
[](IClassRegister& , PluginToken token) -> IInterface* {
return &static_cast<PluginRegistry*>(token)->taskQueueFactory_;
}},
#if (CORE_PERF_ENABLED == 1)
InterfaceTypeInfo{®istry,
UID_PERFORMANCE_FACTORY,
GetName<IPerformanceDataManagerFactory>().data(),
nullptr,
[](IClassRegister& , PluginToken token) -> IInterface* {
return &static_cast<PluginRegistry*>(token)->perfManFactory_;
}}
#endif
};
for (const auto& info : interfaces) {
registry.RegisterInterfaceType(info);
}
registry.RegisterTypeInfo(ASTC_LOADER);
registry.RegisterTypeInfo(KTX_LOADER);
#if defined(USE_STB_IMAGE) && (USE_STB_IMAGE == 1)
registry.RegisterTypeInfo(STB_LOADER);
#endif
return interfaces;
}
void PluginRegistry::UnregisterGlobalInterfaces()
{
#if defined(USE_STB_IMAGE) && (USE_STB_IMAGE == 1)
UnregisterTypeInfo(STB_LOADER);
#endif
UnregisterTypeInfo(KTX_LOADER);
UnregisterTypeInfo(ASTC_LOADER);
for (const auto& info : ownInterfaceInfos_) {
UnregisterInterfaceType(info);
}
}
WARNING_SCOPE_START(W_THIS_USED_BASE_INITIALIZER_LIST)
PluginRegistry::PluginRegistry()
#if CORE_PERF_ENABLED
: perfManFactory_(*this)
#endif
{
ownInterfaceInfos_ = RegisterGlobalInterfaces(*this);
}
WARNING_SCOPE_END()
PluginRegistry::~PluginRegistry()
{
UnloadPlugins({});
UnregisterGlobalInterfaces();
}
array_view<const IPlugin* const> PluginRegistry::GetPlugins() const
{
return plugins_;
}
bool PluginRegistry::LoadPlugins(const array_view<const Uid> pluginUids)
{
const bool topLevelLoad = loadingDepth_ == 0;
vector<LibPlugin> availablePlugins;
GatherStaticPlugins(availablePlugins);
if (topLevelLoad) {
if (dynamicPluginInfosDirty_) {
dynamicPluginInfos_.clear();
dynamicPluginInfosDirty_ = false;
}
GatherDynamicPlugins(availablePlugins, fileManager_, dynamicPluginInfos_);
} else {
availablePlugins.reserve(availablePlugins.size() + dynamicPluginInfos_.size());
for (const auto& pluginInfo : dynamicPluginInfos_) {
availablePlugins.push_back({{}, nullptr, &pluginInfo});
}
}
vector<Uid> toLoad = GatherRequiredPlugins(pluginUids, availablePlugins);
if (toLoad.empty()) {
return false;
}
vector<int32_t> counts = CountPluginRequests(toLoad);
++loadingDepth_;
auto itCounts = counts.cbegin();
for (const Uid& uid : toLoad) {
const auto currentCount = *itCounts++;
if (auto pos = std::find(plugins_.begin(), plugins_.end(), uid); pos != plugins_.end()) {
const auto index = static_cast<size_t>(std::distance(plugins_.begin(), pos));
pluginDatas_[index].refcnt += currentCount;
continue;
}
auto pos = std::find(availablePlugins.begin(), availablePlugins.end(), uid);
if (pos == availablePlugins.end() || !ResolvePlugin(*pos, fileManager_)) {
continue;
}
RegisterPlugin(BASE_NS::move(pos->lib), *(pos->plugin), currentCount);
}
--loadingDepth_;
if ((loadingDepth_ == 0) && !newTypeInfos_.empty()) {
Notify(typeInfoListeners_, ITypeInfoListener::EventType::ADDED, newTypeInfos_);
newTypeInfos_.clear();
}
return true;
}
void PluginRegistry::UnloadPlugins(const array_view<const Uid> pluginUids)
{
CORE_LOG_D("Unload plugins:");
#if defined(CORE_PERF_ENABLED) && (CORE_PERF_ENABLED)
if (perfLoggerId_) {
if (pluginUids.empty() || std::any_of(pluginUids.cbegin(),
pluginUids.cend(),
[&perfUid = perfTracePlugin_](const Uid& uid) { return uid == perfUid; })) {
logger_.RemoveOutput(perfLoggerId_);
perfLoggerId_ = 0U;
}
}
#endif
decltype(pluginDatas_) removedPluginDatas;
decltype(plugins_) removedPlugins;
if (pluginUids.empty()) {
while (!pluginDatas_.empty() && !plugins_.empty()) {
removedPlugins.push_back(BASE_NS::move(plugins_.back()));
removedPluginDatas.push_back(BASE_NS::move(pluginDatas_.back()));
plugins_.pop_back();
pluginDatas_.pop_back();
}
} else {
DecreaseRefCounts(pluginUids);
auto pdIt = pluginDatas_.rbegin();
for (auto pos = plugins_.rbegin(), last = plugins_.rend(); pos != last;) {
if (pdIt->refcnt <= 0) {
removedPlugins.push_back(BASE_NS::move(*pos));
removedPluginDatas.push_back(BASE_NS::move(*pdIt));
pos = reverse_iterator(plugins_.erase(pos.base() - 1));
pdIt = reverse_iterator(pluginDatas_.erase(pdIt.base() - 1));
} else {
++pos;
++pdIt;
}
}
}
auto dataIt = removedPluginDatas.begin();
for (auto plugin : removedPlugins) {
UnregisterPlugin(*plugin, dataIt->token);
++dataIt;
}
}
IClassRegister& PluginRegistry::GetClassRegister() const
{
return *const_cast<PluginRegistry*>(this);
}
void PluginRegistry::RegisterTypeInfo(const ITypeInfo& type)
{
if (const auto pos = typeInfos_.find(type.typeUid); pos != typeInfos_.cend()) {
pos->second.push_back(&type);
} else {
typeInfos_.insert({type.typeUid, {}}).first->second.push_back(&type);
}
if (loadingDepth_ != 0) {
newTypeInfos_.push_back(&type);
} else {
const ITypeInfo* const infos[] = {&type};
Notify(typeInfoListeners_, ITypeInfoListener::EventType::ADDED, infos);
}
}
void PluginRegistry::UnregisterTypeInfo(const ITypeInfo& type)
{
if (const auto typeInfos = typeInfos_.find(type.typeUid); typeInfos != typeInfos_.cend()) {
auto& infos = typeInfos->second;
if (const auto info = std::find(infos.cbegin(), infos.cend(), &type); info != infos.cend()) {
infos.erase(info);
}
}
const ITypeInfo* const infos[] = {&type};
Notify(typeInfoListeners_, ITypeInfoListener::EventType::REMOVED, infos);
#if defined(CORE_PERF_ENABLED) && (CORE_PERF_ENABLED)
if (type.typeUid == PerformanceTraceTypeInfo::UID) {
perfManFactory_.RemovePerformanceTrace(static_cast<const PerformanceTraceTypeInfo&>(type).uid);
}
#endif
}
array_view<const ITypeInfo* const> PluginRegistry::GetTypeInfos(const Uid& typeUid) const
{
if (const auto typeInfos = typeInfos_.find(typeUid); typeInfos != typeInfos_.cend()) {
return typeInfos->second;
}
return {};
}
void PluginRegistry::AddListener(ITypeInfoListener& listener)
{
if (std::none_of(typeInfoListeners_.begin(), typeInfoListeners_.end(), [adding = &listener](const auto& current) {
return current == adding;
})) {
typeInfoListeners_.push_back(&listener);
}
}
void PluginRegistry::RemoveListener(const ITypeInfoListener& listener)
{
if (auto pos = std::find(typeInfoListeners_.begin(), typeInfoListeners_.end(), &listener);
pos != typeInfoListeners_.end()) {
*pos = nullptr;
}
}
void PluginRegistry::RegisterInterfaceType(const InterfaceTypeInfo& interfaceInfo)
{
const auto pos = std::upper_bound(interfaceTypeInfos_.cbegin(),
interfaceTypeInfos_.cend(),
interfaceInfo.uid,
[](Uid value, const InterfaceTypeInfo* element) { return value < element->uid; });
interfaceTypeInfos_.insert(pos, &interfaceInfo);
}
void PluginRegistry::UnregisterInterfaceType(const InterfaceTypeInfo& interfaceInfo)
{
if (!interfaceTypeInfos_.empty()) {
const auto pos = std::lower_bound(interfaceTypeInfos_.cbegin(),
interfaceTypeInfos_.cend(),
interfaceInfo.uid,
[](const InterfaceTypeInfo* element, Uid value) { return element->uid < value; });
if ((pos != interfaceTypeInfos_.cend()) && (*pos)->uid == interfaceInfo.uid) {
interfaceTypeInfos_.erase(pos);
}
}
}
array_view<const InterfaceTypeInfo* const> PluginRegistry::GetInterfaceMetadata() const
{
return {interfaceTypeInfos_.data(), interfaceTypeInfos_.size()};
}
const InterfaceTypeInfo& PluginRegistry::GetInterfaceMetadata(const Uid& uid) const
{
static constexpr InterfaceTypeInfo invalidType{};
if (!interfaceTypeInfos_.empty()) {
const auto pos = std::lower_bound(interfaceTypeInfos_.cbegin(),
interfaceTypeInfos_.cend(),
uid,
[](const InterfaceTypeInfo* element, Uid value) { return element->uid < value; });
if ((pos != interfaceTypeInfos_.cend()) && (*pos)->uid == uid) {
return *(*pos);
}
}
return invalidType;
}
IInterface* PluginRegistry::GetInstance(const Uid& uid) const
{
const auto& data = GetInterfaceMetadata(uid);
if (data.getInterface) {
return data.getInterface(const_cast<PluginRegistry&>(*this), data.token);
}
return nullptr;
}
IInterface::Ptr PluginRegistry::CreateInstance(const Uid& uid)
{
const auto& data = GetInterfaceMetadata(uid);
if (data.createInterface) {
return IInterface::Ptr{data.createInterface(*this, data.token)};
}
return {};
}
const IInterface* PluginRegistry::GetInterface(const Uid& uid) const
{
return const_cast<PluginRegistry*>(this)->GetInterface(uid);
}
IInterface* PluginRegistry::GetInterface(const Uid& uid)
{
if ((uid == IInterface::UID) || (uid == IClassRegister::UID)) {
return static_cast<IClassRegister*>(this);
}
if (uid == IClassFactory::UID) {
return static_cast<IClassFactory*>(this);
}
return nullptr;
}
void PluginRegistry::Ref()
{}
void PluginRegistry::Unref()
{}
void PluginRegistry::RegisterPluginPath(const string_view path)
{
if (!fileProtocolRegistered_) {
fileProtocolRegistered_ = true;
fileManager_.RegisterFilesystem("file", IFilesystem::Ptr{new StdFilesystem("/")});
}
if (loadingDepth_ == 0) {
dynamicPluginInfos_.clear();
dynamicPluginInfosDirty_ = false;
} else {
dynamicPluginInfosDirty_ = true;
}
fileManager_.RegisterPath("plugins", path, false);
}
IFileManager& PluginRegistry::GetFileManager()
{
return fileManager_;
}
void PluginRegistry::HandlePerfTracePlugin(const PlatformCreateInfo& platformCreateInfo)
{
const PlatformCreateExtensionInfo* traceSettings = nullptr;
for (const PlatformCreateExtensionInfo* extension = Platform::Extensions(platformCreateInfo); extension;
extension = extension->next) {
switch (extension->type) {
case PLATFORM_EXTENSION_TRACE_USER:
CORE_ASSERT(traceSettings == nullptr);
traceSettings = extension;
break;
case PLATFORM_EXTENSION_TRACE_EXTENSION:
CORE_ASSERT(traceSettings == nullptr);
traceSettings = extension;
break;
}
}
#if defined(CORE_PERF_ENABLED) && (CORE_PERF_ENABLED)
if (traceSettings) {
CORE_LOG_V("Tracing is enabled and application requested it");
if (traceSettings->type == PLATFORM_EXTENSION_TRACE_USER) {
perfManFactory_.SetPerformanceTrace(
{}, IPerformanceTrace::Ptr{static_cast<const PlatformTraceInfoUsr*>(traceSettings)->tracer});
} else if (traceSettings->type == PLATFORM_EXTENSION_TRACE_EXTENSION) {
const PlatformTraceInfoExt* traceExtension = static_cast<const PlatformTraceInfoExt*>(traceSettings);
if (!LoadPlugins({&traceExtension->plugin, 1U})) {
CORE_LOG_V("Failed to load %s", to_string(traceExtension->plugin).data());
return;
}
perfTracePlugin_ = traceExtension->plugin;
auto typeInfos = GetTypeInfos(PerformanceTraceTypeInfo::UID);
auto itt = std::find_if(typeInfos.cbegin(), typeInfos.cend(), [traceExtension](const ITypeInfo* const a) {
return static_cast<const PerformanceTraceTypeInfo* const>(a)->uid == traceExtension->type;
});
if (itt != typeInfos.end()) {
auto trace = static_cast<const PerformanceTraceTypeInfo* const>(*itt);
perfManFactory_.SetPerformanceTrace(trace->uid, trace->createLoader(trace->token));
} else {
CORE_ASSERT(false && "cannot find trace plugin");
}
}
perfLoggerId_ = logger_.AddOutput(perfManFactory_.GetLogger());
} else {
CORE_LOG_V("Tracing is enabled and application didn't requested it");
}
#else
if (traceSettings) {
CORE_LOG_V("Tracing is disabled but application still requested it");
}
#endif
}
void PluginRegistry::RegisterPlugin(ILibrary::Ptr lib, const IPlugin& plugin, const int32_t refCount)
{
CORE_LOG_D("\tRegister Plugin: %s %s", plugin.name, to_string(plugin.version.uid).data());
if (plugin.version.GetVersionString) {
CORE_LOG_D("\tVersion Info: %s", plugin.version.GetVersionString());
}
PluginData pd{move(lib), {}, refCount};
if (plugin.registerInterfaces) {
pd.token = plugin.registerInterfaces(*static_cast<IPluginRegister*>(this));
}
pluginDatas_.push_back(move(pd));
plugins_.push_back(&plugin);
}
void PluginRegistry::UnregisterPlugin(const IPlugin& plugin, PluginToken token)
{
CORE_LOG_D("\tUnregister Plugin: %s %s", plugin.name, to_string(plugin.version.uid).data());
if (plugin.unregisterInterfaces) {
plugin.unregisterInterfaces(token);
}
}
void PluginRegistry::DecreaseRefCounts(const array_view<const Uid> pluginUids)
{
for (const auto& uid : pluginUids) {
if (auto pos = std::find_if(
plugins_.begin(), plugins_.end(), [uid](const IPlugin* pl) { return pl && pl->version.uid == uid; });
pos != plugins_.end()) {
const auto index = static_cast<size_t>(std::distance(plugins_.begin(), pos));
--pluginDatas_[index].refcnt;
DecreaseRefCounts((*pos)->pluginDependencies);
}
}
}
CORE_PUBLIC IPluginRegister& GetPluginRegister()
{
static PluginRegistry registry;
return registry;
}
CORE_PUBLIC void CreatePluginRegistry(const PlatformCreateInfo& platformCreateInfo)
{
static std::once_flag onceFlag;
std::call_once(onceFlag, [&platformCreateInfo]() {
auto& registry = static_cast<PluginRegistry&>(GetPluginRegister());
auto platform = Platform::Create(platformCreateInfo);
platform->RegisterPluginLocations(registry);
registry.HandlePerfTracePlugin(platformCreateInfo);
});
}
CORE_END_NAMESPACE()