omni/core/ModuleExports.h

File members: omni/core/ModuleExports.h

// Copyright (c) 2020-2024, 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 "IObject.h"

#include "../../carb/Interface.h"

#include <algorithm>
#include <cstring>
#include <type_traits>

namespace omni
{

namespace log
{
class ILog;
}

namespace structuredlog
{
class IStructuredLog;

using SchemaAddFn = bool (*)(IStructuredLog* log);
} // namespace structuredlog

namespace core
{

constexpr const char* const kModuleExportEntryTypeOnModuleLoad = "omniOnModuleLoad";

constexpr const char* const kModuleExportEntryTypeOnModuleStarted = "omniOnModuleStarted";

constexpr const char* const kModuleExportEntryTypeOnModuleCanUnload = "omniOnModuleCanUnload";

constexpr const char* const kModuleExportEntryTypeOnModuleUnload = "omniOnModuleUnload";

constexpr const char* const kModuleExportEntryTypeITypeFactory = "omniITypeFactory";

constexpr const char* const kModuleExportEntryTypeILog = "omniILog";

constexpr const char* const kModuleExportEntryTypeLogChannel = "omniLogChannel";

constexpr const char* const kModuleExportEntryTypeIStructuredLog = "omniIStructuredLog";

constexpr const char* const kModuleExportEntryTypeSchema = "omniSchema";

constexpr const char* const kModuleExportEntryTypeCarbClientName = "carbClientName";

constexpr const char* const kModuleExportEntryTypeCarbFramework = "carbFramework";

constexpr const char* const kModuleExportEntryTypeCarbIAssert = "carbIAssert";

constexpr const char* const kModuleExportEntryTypeCarbILogging = "carbILogging";

constexpr const char* const kModuleExportEntryTypeCarbIProfiler = "carbIProfiler";

constexpr const char* const kModuleExportEntryTypeCarbIL10n = "carbIL10n";

constexpr const char* const kModuleExportEntryTypeGetModuleDependencies = "omniGetModuleDependecies";

using ModuleExportEntryFlag = uint32_t;
constexpr ModuleExportEntryFlag fModuleExportEntryFlagNone = 0;

constexpr ModuleExportEntryFlag fModuleExportEntryFlagRequired = (1 << 0);

#define OMNI_MODULE_EXPORT_ENTRY_BEGIN(name_)                                                                          \
    struct name_                                                                                                       \
    {                                                                                                                  \
                                                           \
        const char* type;                                                                                              \
                                                             \
        ModuleExportEntryFlag flags;                                                                                   \
                                                      \
        uint32_t byteCount;                                                                                            \
                                                                                                                       \
                                                                                             \
        name_(const char* t, ModuleExportEntryFlag f)                                                                  \
        {                                                                                                              \
            type = t;                                                                                                  \
            flags = f;                                                                                                 \
            byteCount = sizeof(*this);                                                                                 \
        };

#define OMNI_MODULE_EXPORT_ENTRY_END(name_)                                                                            \
    }                                                                                                                  \
    ;                                                                                                                  \
    CARB_ASSERT_INTEROP_SAFE(name_);

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntry)
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntry)
static_assert(sizeof(ModuleExportEntry) == (8 + sizeof(void*)), "unexpected ModuleExportEntry size");

struct InterfaceImplementation;

using OnModuleLoadFn = Result(const InterfaceImplementation** out, uint32_t* outCount);

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntryOnModuleLoad)
OnModuleLoadFn* onModuleLoad;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntryOnModuleLoad)

#define OMNI_MODULE_ON_MODULE_LOAD(exp_, fn_) OMNI_RETURN_IF_FAILED(exp_->addOnModuleLoad(fn_))

using OnModuleStartedFn = void();

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntryOnModuleStarted)
OnModuleStartedFn* onModuleStarted;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntryOnModuleStarted)

#define OMNI_MODULE_ON_MODULE_STARTED(exp_, fn_) OMNI_RETURN_IF_FAILED(exp_->addOnModuleStarted(fn_))

using OnModuleCanUnloadFn = bool();

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntryOnModuleCanUnload)
OnModuleCanUnloadFn* onModuleCanUnload;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntryOnModuleCanUnload)

#define OMNI_MODULE_ON_MODULE_CAN_UNLOAD(exp_, fn_) OMNI_RETURN_IF_FAILED(exp_->addOnModuleCanUnload(fn_))

using OnModuleUnloadFn = void();

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntryOnModuleUnload)
OnModuleUnloadFn* onModuleUnload;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntryOnModuleUnload)

#define OMNI_MODULE_ON_MODULE_UNLOAD(exp_, fn_) OMNI_RETURN_IF_FAILED(exp_->addOnModuleUnload(fn_))

OMNI_DECLARE_INTERFACE(ITypeFactory)

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntryITypeFactory)
ITypeFactory** typeFactory;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntryITypeFactory)

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntryILog)
log::ILog** log;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntryILog)

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntryLogChannel)
const char* name;
int32_t* level;
const char* description;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntryLogChannel)

#define OMNI_MODULE_ADD_LOG_CHANNEL(exp_, name_, level_, description_)                                                 \
    OMNI_RETURN_IF_FAILED(exp_->addLogChannel(name_, level_, description_))

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntryCarbClientName)
const char* clientName;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntryCarbClientName)

#define OMNI_MODULE_REQUIRE_CARB_CLIENT_NAME(exp_)                                                                     \
    OMNI_RETURN_IF_FAILED(out->requireExport(omni::core::kModuleExportEntryTypeCarbClientName))

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntryIStructuredLog)
omni::structuredlog::IStructuredLog** structuredLog;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntryIStructuredLog)

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntrySchema)
omni::structuredlog::SchemaAddFn schemaAddFn;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntrySchema)

#define OMNI_MODULE_ADD_STRUCTURED_LOG_SCHEMA(exp_, fn_) OMNI_RETURN_IF_FAILED(exp_->addStructuredLogSchema(fn_))

} // namespace core
} // namespace omni

namespace carb
{
struct Framework;

namespace assert
{
struct IAssert;
} // namespace assert

namespace logging
{
struct ILogging;
} // namespace logging

namespace profiler
{
struct IProfiler;
} // namespace profiler

namespace l10n
{
struct IL10n;
struct LanguageTable;
struct LanguageIdentifier;
} // namespace l10n

} // namespace carb

namespace omni
{
namespace core
{

#ifndef DOXYGEN_BUILD
namespace detail
{
using CarbLogFn = void (*)(const char* source,
                           int32_t level,
                           const char* fileName,
                           const char* functionName,
                           int lineNumber,
                           const char* fmt,
                           ...);

using CarbLogLevelFn = void(int32_t);

using CarbLocalizeStringFn = const char*(CARB_ABI*)(const carb::l10n::LanguageTable* table,
                                                    uint64_t id,
                                                    const carb::l10n::LanguageIdentifier* language);
} // namespace detail
#endif

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntryCarbFramework)
carb::Framework** framework;
carb::Version version;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntryCarbFramework)

#define OMNI_MODULE_REQUIRE_CARB_FRAMEWORK(exp_)                                                                       \
    OMNI_RETURN_IF_FAILED(out->requireExport(omni::core::kModuleExportEntryTypeCarbFramework))

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntryCarbIAssert)
carb::assert::IAssert** assert;
carb::InterfaceDesc interfaceDesc;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntryCarbIAssert)

#define OMNI_MODULE_REQUIRE_CARB_IASSERT(exp_)                                                                         \
    OMNI_RETURN_IF_FAILED(out->requireExport(omni::core::kModuleExportEntryTypeCarbIAssert))

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntryCarbILogging)
carb::logging::ILogging** logging;
detail::CarbLogFn* logFn;
detail::CarbLogLevelFn* logLevelFn;
int32_t* logLevel;
carb::InterfaceDesc interfaceDesc;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntryCarbILogging)

#define OMNI_MODULE_REQUIRE_CARB_ILOGGING(exp_)                                                                        \
    OMNI_RETURN_IF_FAILED(out->requireExport(omni::core::kModuleExportEntryTypeCarbILogging))

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntryCarbIProfiler)
std::atomic<carb::profiler::IProfiler*>* profiler;
carb::InterfaceDesc interfaceDesc;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntryCarbIProfiler)

#define OMNI_MODULE_REQUIRE_CARB_IPROFILER(exp_)                                                                       \
    OMNI_RETURN_IF_FAILED(out->requireExport(omni::core::kModuleExportEntryTypeCarbIProfiler))

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntryCarbIL10n)
carb::l10n::IL10n** localization;
detail::CarbLocalizeStringFn* localizationFn;
carb::InterfaceDesc interfaceDesc;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntryCarbIL10n)

using GetModuleDependenciesFn = Result(carb::InterfaceDesc** out, size_t* outCount);

OMNI_MODULE_EXPORT_ENTRY_BEGIN(ModuleExportEntryGetModuleDependencies)
GetModuleDependenciesFn* getModuleDependencies;
OMNI_MODULE_EXPORT_ENTRY_END(ModuleExportEntryGetModuleDependencies)

#define OMNI_MODULE_GET_MODULE_DEPENDENCIES(exp_, fn_) OMNI_RETURN_IF_FAILED(exp_->addGetModuleDependencies(fn_))

#define OMNI_MODULE_REQUIRE_CARB_IL10N(exp_)                                                                           \
    OMNI_RETURN_IF_FAILED(out->requireExport(omni::core::kModuleExportEntryTypeCarbIL10n))

constexpr uint16_t kModuleExportsMagic = 0x766e; // { 'n', 'v' }

constexpr uint16_t kModuleExportsVersion = 1;

struct ModuleExports
{
    uint16_t magic;

    uint16_t version;

    uint32_t byteCount;

    uint8_t* exportsBegin;

    uint8_t* exportsEnd;

    Result checkVersion(uint16_t moduleMagic, uint16_t moduleVersion)
    {
        // we can't log here, since we're to early in the module load process for logging to be available. pass back the
        // magic number and version we were expecting so omni::core::ITypeFactory can print an appropriate message if
        // the checks below fail.
        std::swap(magic, moduleMagic);
        std::swap(version, moduleVersion);

        if (magic != moduleMagic)
        {
            return kResultVersionParseError;
        }

        if (version != moduleVersion)
        {
            return kResultVersionCheckFailure;
        }

        return kResultSuccess;
    }

    Result add(const ModuleExportEntry* entry)
    {
        uint32_t neededSize = uint32_t(exportsEnd - exportsBegin) + uint32_t(sizeof(ModuleExports)) + entry->byteCount;
        if (neededSize > byteCount)
        {
            return kResultInsufficientBuffer;
        }

        std::memcpy(exportsEnd, entry, entry->byteCount);
        exportsEnd += entry->byteCount;

        return kResultSuccess;
    }

    ModuleExportEntry* find(const char* type)
    {
        if (!type)
        {
            return nullptr;
        }

        uint8_t* p = exportsBegin;
        while (p < exportsEnd)
        {
            auto entry = reinterpret_cast<ModuleExportEntry*>(p);
            if (0 == strcmp(type, entry->type))
            {
                return entry;
            }

            p += entry->byteCount;
        }

        return nullptr;
    }

    Result requireExport(const char* type)
    {
        auto entry = find(type);
        if (entry)
        {
            entry->flags |= fModuleExportEntryFlagRequired;
            return kResultSuccess;
        }

        return kResultNotFound;
    }

    Result addOnModuleLoad(OnModuleLoadFn* fn, ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntryOnModuleLoad entry{ kModuleExportEntryTypeOnModuleLoad, flags };
        entry.onModuleLoad = fn;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }

    Result addOnModuleStarted(OnModuleStartedFn* fn, ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntryOnModuleStarted entry{ kModuleExportEntryTypeOnModuleStarted, flags };
        entry.onModuleStarted = fn;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }

    Result addOnModuleCanUnload(OnModuleCanUnloadFn* fn, ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntryOnModuleCanUnload entry{ kModuleExportEntryTypeOnModuleCanUnload, flags };
        entry.onModuleCanUnload = fn;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }

    Result addOnModuleUnload(OnModuleUnloadFn* fn, ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntryOnModuleUnload entry{ kModuleExportEntryTypeOnModuleUnload, flags };
        entry.onModuleUnload = fn;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }

    Result addITypeFactory(ITypeFactory** typeFactory, ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntryITypeFactory entry{ kModuleExportEntryTypeITypeFactory, flags };
        entry.typeFactory = typeFactory;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }

    Result addILog(log::ILog** log, ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntryILog entry{ kModuleExportEntryTypeILog, flags };
        entry.log = log;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }

    Result addLogChannel(const char* channelName,
                         int32_t* level,
                         const char* description,
                         ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntryLogChannel entry{ kModuleExportEntryTypeLogChannel, flags };
        entry.name = channelName;
        entry.level = level;
        entry.description = description;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }

    Result addIStructuredLog(omni::structuredlog::IStructuredLog** strucLog,
                             ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntryIStructuredLog entry{ kModuleExportEntryTypeIStructuredLog, flags };
        entry.structuredLog = strucLog;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }

    Result addStructuredLogSchema(omni::structuredlog::SchemaAddFn fn,
                                  ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntrySchema entry{ kModuleExportEntryTypeSchema, flags };
        entry.schemaAddFn = fn;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }

    Result addCarbClientName(const char* clientName, ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntryCarbClientName entry{ kModuleExportEntryTypeCarbClientName, flags };
        entry.clientName = clientName;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }

    Result addCarbFramework(carb::Framework** carbFramework,
                            const carb::Version& ver,
                            ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntryCarbFramework entry{ kModuleExportEntryTypeCarbFramework, flags };
        entry.framework = carbFramework;
        entry.version = ver;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }

    Result addCarbIAssert(carb::assert::IAssert** assert,
                          const carb::InterfaceDesc& interfaceDesc,
                          ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntryCarbIAssert entry{ kModuleExportEntryTypeCarbIAssert, flags };
        entry.assert = assert;
        entry.interfaceDesc = interfaceDesc;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }

    Result addCarbILogging(carb::logging::ILogging** logging,
                           detail::CarbLogFn* logFn,
                           detail::CarbLogLevelFn* logLevelFn,
                           int32_t* logLevel,
                           const carb::InterfaceDesc& interfaceDesc,
                           ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntryCarbILogging entry{ kModuleExportEntryTypeCarbILogging, flags };
        entry.logging = logging;
        entry.logFn = logFn;
        entry.logLevelFn = logLevelFn;
        entry.logLevel = logLevel;
        entry.interfaceDesc = interfaceDesc;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }

    Result addCarbIProfiler(std::atomic<carb::profiler::IProfiler*>* profiler,
                            const carb::InterfaceDesc& interfaceDesc,
                            ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntryCarbIProfiler entry{ kModuleExportEntryTypeCarbIProfiler, flags };
        entry.profiler = profiler;
        entry.interfaceDesc = interfaceDesc;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }

    Result addCarbIL10n(carb::l10n::IL10n** localization,
                        detail::CarbLocalizeStringFn* localizationFn,
                        const carb::InterfaceDesc& interfaceDesc,
                        ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntryCarbIL10n entry{ kModuleExportEntryTypeCarbIL10n, flags };
        entry.localization = localization;
        entry.localizationFn = localizationFn;
        entry.interfaceDesc = interfaceDesc;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }

    Result addGetModuleDependencies(GetModuleDependenciesFn* fn, ModuleExportEntryFlag flags = fModuleExportEntryFlagNone)
    {
        ModuleExportEntryGetModuleDependencies entry{ kModuleExportEntryTypeGetModuleDependencies, flags };
        entry.getModuleDependencies = fn;
        return add(reinterpret_cast<ModuleExportEntry*>(&entry));
    }
};

CARB_ASSERT_INTEROP_SAFE(ModuleExports);
static_assert(sizeof(ModuleExports) == (8 + (2 * sizeof(void*))),
              "unexpected ModuleExports size. do not change ModuleExports?");

using ModuleGetExportsFn = Result(ModuleExports* out);

constexpr const char* const kModuleGetExportsName = "omniModuleGetExports";

} // namespace core
} // namespace omni

#ifdef DOXYGEN_BUILD
omni::core::Result omniModuleGetExports(omni::core::ModuleExports* out);
#endif