carb/Framework.h

File members: carb/Framework.h

// Copyright (c) 2018-2023, NVIDIA CORPORATION. All rights reserved.
//
// NVIDIA CORPORATION and its licensors retain all intellectual property
// and proprietary rights in and to this software, related documentation
// and any modifications thereto. Any use, reproduction, disclosure or
// distribution of this software and related documentation without an express
// license agreement from NVIDIA CORPORATION is strictly prohibited.
//

#pragma once

#include "Defines.h"
#include "Memory.h"
#include "Types.h"

#include <cstddef>
#include <cstdint>

// free() can be #define'd which can interfere below, so handle that here
#ifdef free
#    define CARB_FREE_UNDEFINED
#    pragma push_macro("free")
#    undef free
#endif

namespace carb
{

#define CARBONITE_MAJOR 0

#define CARBONITE_MINOR 6

constexpr struct Version kFrameworkVersion = { CARBONITE_MAJOR, CARBONITE_MINOR };

constexpr FourCC kCarb_FourCC = CARB_MAKE_FOURCC('C', 'A', 'R', 'B');

struct PluginRegistrationDesc
{
    OnPluginRegisterFn onPluginRegisterFn;
    OnPluginStartupFn onPluginStartupFn;
    OnPluginShutdownFn onPluginShutdownFn;
    GetPluginDepsFn getPluginDepsFn;
    OnReloadDependencyFn onReloadDependencyFn;
    OnPluginPreStartupFn onPluginPreStartupFn;
    OnPluginPostShutdownFn onPluginPostShutdownFn;
    OnPluginRegisterExFn onPluginRegisterExFn;
    OnPluginStartupExFn onPluginStartupExFn = nullptr;
    OnPluginRegisterEx2Fn onPluginRegisterEx2Fn = nullptr;

    FourCC const checkValue{ kCarb_FourCC };

    size_t const sizeofThis{ sizeof(PluginRegistrationDesc) };

    OnPluginQuickShutdownFn onPluginQuickShutdownFn = nullptr;

    Version frameworkVersion{ kFrameworkVersion };
};

struct PluginLoadingDesc
{
    const char* const* searchPaths;
    size_t searchPathCount;
    bool searchRecursive;

    const char* const* loadedFileWildcards;
    size_t loadedFileWildcardCount;

    const char* const* reloadableFileWildcards;
    size_t reloadableFileWildcardCount;

    bool unloadPlugins;

    const char* const* excludedFileWildcards;
    size_t excludedFileWildcardCount;

    static PluginLoadingDesc getDefault()
    {
        static constexpr const char* defaultSearchPath = "";
        static constexpr const char* defaultLoadedFileWildcard = "*.plugin";
        return { &defaultSearchPath, 1, false, &defaultLoadedFileWildcard, 1, nullptr, 0, false, nullptr, 0 };
    }
};

enum AcquireInterfaceFlags : uint64_t
{
    eAIFDefaultType = 0,

    eAIFFromInterfaceType,

    eAIFFromLibraryType,

    eAIFNumTypes,

    fAIFTypeMask = 0xf,

    fAIFOptional = (1 << 4),

    fAIFNoInitialize = (1 << 5),
};
static_assert(eAIFNumTypes <= fAIFTypeMask, "Too many types for mask");

struct AcquireInterfaceOptions
{
    size_t sizeofThis;

    const char* clientName;

    InterfaceDesc desc;

    AcquireInterfaceFlags flags;

    const void* typeParam;
};
CARB_ASSERT_INTEROP_SAFE(AcquireInterfaceOptions);

enum class LoadPluginResult : int32_t
{
    eForbiddenPath = -3,

    eInvalidArg = -2,

    eFailed = -1,

    eSucceeded = 0,

    eSucceededAsOmniverseNativeInterface = 1,

    eAlreadyLoaded = 2,
};

using ReleaseHookFn = void (*)(void* iface, void* userData);

using LoadHookFn = void (*)(const PluginDesc& plugin, void* userData);

CARB_DYNAMICLINK carb::Framework* acquireFramework(const char* appName, Version frameworkVersion = kFrameworkVersion);

CARB_DYNAMICLINK bool isFrameworkValid();

CARB_DYNAMICLINK const char* carbGetSdkVersion();

#define CARB_IS_SAME_SDK_VERSION(version) (strcmp(version, carbGetSdkVersion()) == 0)

CARB_DYNAMICLINK void releaseFramework();

CARB_DYNAMICLINK void quickReleaseFrameworkAndTerminate [[noreturn]] (int exitCode);

#if CARB_PLATFORM_WINDOWS
CARB_DYNAMICLINK void carbSignalHandler(int signal);
#endif

struct Framework
{
    void loadPlugins(const PluginLoadingDesc& desc = PluginLoadingDesc::getDefault());

    void(CARB_ABI* loadPluginsEx)(const PluginLoadingDesc& desc);

    void(CARB_ABI* unloadAllPlugins)();

    template <typename T>
    T* acquireInterface(const char* pluginName = nullptr);

    template <typename T>
    T* tryAcquireInterface(const char* pluginName = nullptr);

    template <typename T>
    T* acquireInterface(const void* pluginInterface);

    template <typename T>
    T* tryAcquireInterface(const void* pluginInterface);

    template <typename T>
    T* acquireInterfaceFromLibrary(const char* libraryPath);

    template <typename T>
    T* tryAcquireInterfaceFromLibrary(const char* libraryPath);

    template <typename T>
    T* tryAcquireExistingInterface(const char* pluginName = nullptr);

    template <typename T>
    uint32_t getInterfacesCount();

    template <typename T>
    void acquireInterfaces(T** interfaces, uint32_t interfacesSize);

    void*(CARB_ABI* acquireInterfaceWithClient)(const char* clientName, InterfaceDesc desc, const char* pluginName);
    static_assert(kFrameworkVersion.major == 0, "Remove above function in next Framework version");

    void*(CARB_ABI* tryAcquireInterfaceWithClient)(const char* clientName, InterfaceDesc desc, const char* pluginName);
    static_assert(kFrameworkVersion.major == 0, "Remove above function in next Framework version");

    void*(CARB_ABI* acquireInterfaceFromInterfaceWithClient)(const char* clientName,
                                                             InterfaceDesc desc,
                                                             const void* pluginInterface);
    static_assert(kFrameworkVersion.major == 0, "Remove above function in next Framework version");

    void*(CARB_ABI* tryAcquireInterfaceFromInterfaceWithClient)(const char* clientName,
                                                                InterfaceDesc desc,
                                                                const void* pluginInterface);
    static_assert(kFrameworkVersion.major == 0, "Remove above function in next Framework version");

    void*(CARB_ABI* acquireInterfaceFromLibraryWithClient)(const char* clientName,
                                                           InterfaceDesc desc,
                                                           const char* libraryPath);
    static_assert(kFrameworkVersion.major == 0, "Remove above function in next Framework version");

    void*(CARB_ABI* tryAcquireInterfaceFromLibraryWithClient)(const char* clientName,
                                                              InterfaceDesc desc,
                                                              const char* libraryPath);
    static_assert(kFrameworkVersion.major == 0, "Remove above function in next Framework version");

    uint32_t(CARB_ABI* getInterfacesCountEx)(InterfaceDesc interfaceDesc);

    void(CARB_ABI* acquireInterfacesWithClient)(const char* clientName,
                                                InterfaceDesc interfaceDesc,
                                                void** interfaces,
                                                uint32_t interfacesSize);

    template <typename T>
    void releaseInterface(T* pluginInterface);

    void(CARB_ABI* releaseInterfaceWithClient)(const char* clientName, void* pluginInterface);

    const PluginDesc&(CARB_ABI* getPluginDesc)(const char* pluginName);

    const PluginDesc&(CARB_ABI* getInterfacePluginDesc)(void* pluginInterface);

    void(CARB_ABI* getCompatiblePlugins)(InterfaceDesc interfaceDesc, PluginDesc* outPlugins);

    size_t(CARB_ABI* getPluginCount)();

    void(CARB_ABI* getPlugins)(PluginDesc* outPlugins);

    void(CARB_ABI* tryReloadPlugins)();

    bool(CARB_ABI* registerPlugin)(const char* clientName, const PluginRegistrationDesc& desc);

    bool(CARB_ABI* unregisterPlugin)(const char* pluginName);

    const PluginRegistrationDesc&(CARB_ABI* getBuiltinLoggingDesc)();

    const PluginRegistrationDesc&(CARB_ABI* getBuiltinFileSystemDesc)();

    template <class T>
    void setDefaultPlugin(const char* pluginName);

    void(CARB_ABI* setDefaultPluginEx)(const char* clientName, InterfaceDesc desc, const char* pluginName);

    void(CARB_ABI* setReloadableTempPath)(const char* tempPath);

    const char*(CARB_ABI* getReloadableTempPath)();

    const char*(CARB_ABI* getBuildInfo)();

    template <typename T>
    T* verifyInterface(T* interfaceCandidate);

    void*(CARB_ABI* verifyInterfaceEx)(InterfaceDesc desc, void* interfaceCandidate);

    const PluginRegistrationDesc&(CARB_ABI* getBuiltinAssertDesc)();

    const PluginRegistrationDesc&(CARB_ABI* getBuiltinThreadUtilDesc)();

    LoadPluginResult(CARB_ABI* loadPlugin)(const char* libraryPath, bool reloadable, bool unload);

    bool(CARB_ABI* unloadPlugin)(const char* libraryPath);

    bool(CARB_ABI* addReleaseHook)(void* iface, ReleaseHookFn fn, void* user);

    bool(CARB_ABI* removeReleaseHook)(void* iface, ReleaseHookFn fn, void* user);

    CARB_DEPRECATED("Use carbReallocate() instead")
    void*(CARB_ABI* internalRealloc)(void* prev, size_t newSize, size_t align);
    static_assert(kFrameworkVersion.major == 0, "Remove Framework::internalRealloc in next Framework version");

    CARB_DEPRECATED("Use carb::allocate() instead") void* allocate(size_t size, size_t align = 0)
    {
        return carb::allocate(size, align);
    }
    static_assert(kFrameworkVersion.major == 0, "Remove Framework::allocate in next Framework version");

    CARB_DEPRECATED("Use carb::deallocate() instead") void free(void* p)
    {
        return carb::deallocate(p);
    }
    static_assert(kFrameworkVersion.major == 0,
                  "Remove Framework::free and CARB_FREE_UNDEFINED in next Framework version");

    CARB_DEPRECATED("Use carb::reallocate() instead") void* reallocate(void* p, size_t size, size_t align = 0)
    {
        return carb::reallocate(p, size, align);
    }
    static_assert(kFrameworkVersion.major == 0, "Remove Framework::reallocate in next Framework version");

    const char*(CARB_ABI* getSdkVersion)();

    template <class T>
    LoadHookHandle addLoadHook(const char* pluginName, LoadHookFn func, void* userData);

    LoadHookHandle(CARB_ABI* internalAddLoadHook)(
        const InterfaceDesc& iface, const char* plugin, const char* clientName, LoadHookFn fn, void* user, bool add);

    bool(CARB_ABI* removeLoadHook)(LoadHookHandle handle);

    void(CARB_ABI* registerScriptBinding)(BindingType type, const char* clientName, const char* scriptType);

    void*(CARB_ABI* internalAcquireInterface)(const AcquireInterfaceOptions& options);
};
} // namespace carb

CARB_WEAKLINK CARB_HIDDEN const char* g_carbClientName;

CARB_WEAKLINK CARB_HIDDEN carb::Framework* g_carbFramework;

extern bool g_needToCall_CARB_GLOBALS_atGlobalScope;

#define CARB_FRAMEWORK_GLOBALS(clientName)                                                                             \
    CARB_HIDDEN bool g_needToCall_CARB_GLOBALS_atGlobalScope = carb::detail::setClientName(clientName);

namespace carb
{
namespace detail
{
inline bool setClientName(const char* clientName)
{
    g_carbClientName = clientName;
    return true;
}

} // namespace detail

inline Framework* getFramework()
{
    return g_carbFramework;
}

inline void Framework::loadPlugins(const PluginLoadingDesc& desc)
{
    return this->loadPluginsEx(desc);
}

template <typename T>
T* Framework::verifyInterface(T* interfaceCandidate)
{
    const auto desc = T::getInterfaceDesc();
    return static_cast<T*>(getFramework()->verifyInterfaceEx(desc, interfaceCandidate));
}

template <typename T>
T* Framework::acquireInterface(const char* pluginName)
{
    const char* clientName = g_needToCall_CARB_GLOBALS_atGlobalScope ? g_carbClientName : nullptr;
    if (this->internalAcquireInterface)
        return static_cast<T*>(this->internalAcquireInterface(
            { sizeof(AcquireInterfaceOptions), clientName, T::getInterfaceDesc(), eAIFDefaultType, pluginName }));
    else
        return static_cast<T*>(this->acquireInterfaceWithClient(clientName, T::getInterfaceDesc(), pluginName));
}

template <typename T>
T* Framework::tryAcquireInterface(const char* pluginName)
{
    const char* clientName = g_needToCall_CARB_GLOBALS_atGlobalScope ? g_carbClientName : nullptr;
    if (this->internalAcquireInterface)
        return static_cast<T*>(
            this->internalAcquireInterface({ sizeof(AcquireInterfaceOptions), clientName, T::getInterfaceDesc(),
                                             AcquireInterfaceFlags(eAIFDefaultType | fAIFOptional), pluginName }));
    else
        return static_cast<T*>(this->tryAcquireInterfaceWithClient(clientName, T::getInterfaceDesc(), pluginName));
}

template <typename T>
T* Framework::acquireInterface(const void* pluginInterface)
{
    const char* clientName = g_needToCall_CARB_GLOBALS_atGlobalScope ? g_carbClientName : nullptr;
    if (this->internalAcquireInterface)
        return static_cast<T*>(
            this->internalAcquireInterface({ sizeof(AcquireInterfaceOptions), clientName, T::getInterfaceDesc(),
                                             eAIFFromInterfaceType, pluginInterface }));
    else
        return static_cast<T*>(
            this->acquireInterfaceFromInterfaceWithClient(clientName, T::getInterfaceDesc(), pluginInterface));
}

template <typename T>
T* Framework::tryAcquireInterface(const void* pluginInterface)
{
    const char* clientName = g_needToCall_CARB_GLOBALS_atGlobalScope ? g_carbClientName : nullptr;
    if (this->internalAcquireInterface)
        return static_cast<T*>(this->internalAcquireInterface(
            { sizeof(AcquireInterfaceOptions), clientName, T::getInterfaceDesc(),
              AcquireInterfaceFlags(eAIFFromInterfaceType | fAIFOptional), pluginInterface }));
    else
        return static_cast<T*>(
            this->tryAcquireInterfaceFromInterfaceWithClient(clientName, T::getInterfaceDesc(), pluginInterface));
}

template <typename T>
T* Framework::acquireInterfaceFromLibrary(const char* libraryPath)
{
    const char* clientName = g_needToCall_CARB_GLOBALS_atGlobalScope ? g_carbClientName : nullptr;
    if (this->internalAcquireInterface)
        return static_cast<T*>(this->internalAcquireInterface(
            { sizeof(AcquireInterfaceOptions), clientName, T::getInterfaceDesc(), eAIFFromLibraryType, libraryPath }));
    else
        return static_cast<T*>(
            this->acquireInterfaceFromLibraryWithClient(clientName, T::getInterfaceDesc(), libraryPath));
}

template <typename T>
T* Framework::tryAcquireInterfaceFromLibrary(const char* libraryPath)
{
    const char* clientName = g_needToCall_CARB_GLOBALS_atGlobalScope ? g_carbClientName : nullptr;
    if (this->internalAcquireInterface)
        return static_cast<T*>(
            this->internalAcquireInterface({ sizeof(AcquireInterfaceOptions), clientName, T::getInterfaceDesc(),
                                             AcquireInterfaceFlags(eAIFFromLibraryType | fAIFOptional), libraryPath }));
    else
        return static_cast<T*>(
            this->tryAcquireInterfaceFromLibraryWithClient(clientName, T::getInterfaceDesc(), libraryPath));
}

template <typename T>
T* Framework::tryAcquireExistingInterface(const char* pluginName)
{
    const char* clientName = g_needToCall_CARB_GLOBALS_atGlobalScope ? g_carbClientName : nullptr;
    return this->internalAcquireInterface ?
               static_cast<T*>(this->internalAcquireInterface(
                   { sizeof(AcquireInterfaceOptions), clientName, T::getInterfaceDesc(),
                     AcquireInterfaceFlags(eAIFDefaultType | fAIFOptional | fAIFNoInitialize), pluginName })) :
               nullptr;
}

template <typename T>
uint32_t Framework::getInterfacesCount()
{
    const InterfaceDesc desc = T::getInterfaceDesc();
    return this->getInterfacesCountEx(desc);
}

template <typename T>
void Framework::acquireInterfaces(T** interfaces, uint32_t interfacesSize)
{
    const InterfaceDesc desc = T::getInterfaceDesc();
    const char* clientName = g_needToCall_CARB_GLOBALS_atGlobalScope ? g_carbClientName : nullptr;
    this->acquireInterfacesWithClient(clientName, desc, reinterpret_cast<void**>(interfaces), interfacesSize);
}

template <typename T>
void Framework::releaseInterface(T* pluginInterface)
{
    (void)(T::getInterfaceDesc()); // Compile-time check that the type is plugin interface
    const char* clientName = g_needToCall_CARB_GLOBALS_atGlobalScope ? g_carbClientName : nullptr;
    this->releaseInterfaceWithClient(clientName, pluginInterface);
}

template <typename T>
void Framework::setDefaultPlugin(const char* pluginName)
{
    const char* clientName = g_needToCall_CARB_GLOBALS_atGlobalScope ? g_carbClientName : nullptr;
    this->setDefaultPluginEx(clientName, T::getInterfaceDesc(), pluginName);
}

template <typename T>
LoadHookHandle Framework::addLoadHook(const char* pluginName, LoadHookFn func, void* user)
{
    const char* clientName = g_needToCall_CARB_GLOBALS_atGlobalScope ? g_carbClientName : nullptr;
    return this->internalAddLoadHook(T::getInterfaceDesc(), pluginName, clientName, func, user, true);
}

} // namespace carb

#ifdef CARB_FREE_UNDEFINED
#    pragma pop_macro("free")
#    undef CARB_FREE_UNDEFINED
#endif