Program Listing for carb/profiler/ProfilerUtils.h

↰ Return to documentation for carb/profiler/ProfilerUtils.h

// Copyright (c) 2018-2022, 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 "IProfiler.h"
#include "../cpp20/Atomic.h"
#include "../settings/ISettings.h"
#include "../InterfaceUtils.h"

namespace carb
{
namespace profiler
{

class Channel final
{
    uint64_t m_mask;
    bool m_enabled;
    const char* m_name;
    Channel* m_next;

    struct ModuleData
    {
        Channel* head{ nullptr };
        LoadHookHandle onSettingsLoadHandle{ kInvalidLoadHook };
        dictionary::SubscriptionId* changeSubscription{ nullptr };

#if CARB_ASSERT_ENABLED
        ~ModuleData()
        {
            // If these weren't unregistered we could crash later
            CARB_ASSERT(onSettingsLoadHandle == kInvalidLoadHook);
            CARB_ASSERT(changeSubscription == nullptr);
        }
#endif
    };

    static ModuleData& moduleData()
    {
        static ModuleData s_moduleData;
        return s_moduleData;
    }

    static void onSettingsLoad(const PluginDesc&, void*)
    {
        // DO NOT USE getCachedInterface here! This is called by a load hook, which can be triggered by
        // getCachedInterface in this module. This means if we were to recursively call getCachedInterface() here, we
        // could hang indefinitely as this thread is the thread responsible for loading the cached interface.
        if (loadSettings(getFramework()->tryAcquireInterface<settings::ISettings>(), true))
        {
            g_carbFramework->removeLoadHook(moduleData().onSettingsLoadHandle);
            moduleData().onSettingsLoadHandle = kInvalidLoadHook;
        }
    }

    static void onSettingsUnload(void*, void*)
    {
        // Settings was unloaded. Make sure we no longer have a subscription callback.
        moduleData().changeSubscription = nullptr;
    }

    static void onSettingsChange(const dictionary::Item*,
                                 const dictionary::Item* changedItem,
                                 dictionary::ChangeEventType eventType,
                                 void*)
    {
        if (eventType == dictionary::ChangeEventType::eDestroyed)
            return;

        auto dict = getCachedInterface<dictionary::IDictionary>();

        // Only care about elements that can change at runtime.
        const char* name = dict->getItemName(changedItem);
        if (strcmp(name, "enabled") != 0 && strcmp(name, "mask") != 0)
            return;

        loadSettings(
            getCachedInterface<settings::ISettings>(), false, dict->getItemName(dict->getItemParent(changedItem)));
    }

    static bool loadSettings(settings::ISettings* settings, bool initial, const char* channelName = nullptr)
    {
        // Only proceed if settings is already initialized
        if (!settings)
            return false;

        auto dict = carb::getCachedInterface<dictionary::IDictionary>();
        if (!dict)
            return false;

        auto root = settings->getSettingsDictionary("/profiler/channels");
        if (root)
        {
            for (Channel* c = moduleData().head; c; c = c->m_next)
            {
                if (channelName && strcmp(c->m_name, channelName) != 0)
                    continue;

                auto channelRoot = dict->getItem(root, c->m_name);
                if (!channelRoot)
                    continue;

                auto enabled = dict->getItem(channelRoot, "enabled");
                if (enabled)
                {
                    c->setEnabled(dict->getAsBool(enabled));
                }
                auto mask = dict->getItem(channelRoot, "mask");
                if (mask)
                {
                    c->setMask(uint64_t(dict->getAsInt64(mask)));
                }
            }
        }

        // Register a change subscription on initial setup if we have any channels.
        if (initial && !moduleData().changeSubscription && moduleData().head)
        {
            moduleData().changeSubscription =
                settings->subscribeToTreeChangeEvents("/profiler/channels", onSettingsChange, nullptr);

            ::g_carbFramework->addReleaseHook(settings, onSettingsUnload, nullptr);
        }
        return true;
    }

public:
    Channel(uint64_t mask, bool enabled, const char* name) : m_mask(mask), m_enabled(enabled), m_name(name)
    {
        // Add ourselves to the list of channels for this module
        auto& head = moduleData().head;
        m_next = head;
        head = this;
    }

    const char* getName() const noexcept
    {
        return m_name;
    }

    uint64_t getMask() const noexcept
    {
        return m_mask;
    }

    void setMask(uint64_t mask) noexcept
    {
        cpp20::atomic_ref<uint64_t>(m_mask).store(mask, std::memory_order_release);
    }

    bool isEnabled() const noexcept
    {
        return m_enabled;
    }

    void setEnabled(bool enabled) noexcept
    {
        cpp20::atomic_ref<bool>(m_enabled).store(enabled, std::memory_order_release);
    }

    static void onProfilerRegistered()
    {
        // example-begin acquire-without-init
        // Don't try to load settings, but if it's already available we will load settings from it.
        auto settings = g_carbFramework->tryAcquireExistingInterface<settings::ISettings>();
        // example-end acquire-without-init
        if (!loadSettings(settings, true))
        {
            // If settings isn't available, wait for it to load.
            moduleData().onSettingsLoadHandle =
                g_carbFramework->addLoadHook<settings::ISettings>(nullptr, onSettingsLoad, nullptr);
        }
    }

    static void onProfilerUnregistered()
    {
        if (moduleData().onSettingsLoadHandle != kInvalidLoadHook)
        {
            g_carbFramework->removeLoadHook(moduleData().onSettingsLoadHandle);
            moduleData().onSettingsLoadHandle = kInvalidLoadHook;
        }
        if (moduleData().changeSubscription)
        {
            // Don't re-initialize settings if it's already been unloaded (though in this case we should've gotten a
            // callback)
            auto settings = g_carbFramework->tryAcquireExistingInterface<settings::ISettings>();
            CARB_ASSERT(settings);
            if (settings)
            {
                settings->unsubscribeToChangeEvents(moduleData().changeSubscription);
                g_carbFramework->removeReleaseHook(settings, onSettingsUnload, nullptr);
            }
            moduleData().changeSubscription = nullptr;
        }
    }
};

class ProfileZoneStatic final
{
    const uint64_t m_mask;
    ZoneId m_zoneId;

public:
    ProfileZoneStatic(const uint64_t mask, std::tuple<StaticStringType, StaticStringType, StaticStringType> tup, int line)
        : m_mask(mask)
    {
        if (g_carbProfiler && ((mask ? mask : kCaptureMaskDefault) & g_carbProfilerMask.load(std::memory_order_acquire)))
            m_zoneId = g_carbProfiler->beginStatic(m_mask, std::get<0>(tup), std::get<1>(tup), line, std::get<2>(tup));
        else
            m_zoneId = kNoZoneId;
    }

    ProfileZoneStatic(const Channel& channel, std::tuple<StaticStringType, StaticStringType, StaticStringType> tup, int line)
        : m_mask(channel.getMask())
    {
        if (g_carbProfiler && channel.isEnabled())
            m_zoneId = g_carbProfiler->beginStatic(m_mask, std::get<0>(tup), std::get<1>(tup), line, std::get<2>(tup));
        else
            m_zoneId = kNoZoneId;
    }

    ~ProfileZoneStatic()
    {
        if (g_carbProfiler && m_zoneId != kNoZoneId)
            g_carbProfiler->endEx(m_mask, m_zoneId);
    }
};

class ProfileZoneDynamic final
{
    const uint64_t m_mask;
    ZoneId m_zoneId;

public:
    template <typename... Args>
    ProfileZoneDynamic(const uint64_t mask,
                       std::tuple<StaticStringType, StaticStringType> tup,
                       int line,
                       const char* nameFmt,
                       Args&&... args)
        : m_mask(mask)
    {
        if (g_carbProfiler && ((mask ? mask : kCaptureMaskDefault) & g_carbProfilerMask.load(std::memory_order_acquire)))
            m_zoneId = g_carbProfiler->beginDynamic(
                m_mask, std::get<0>(tup), std::get<1>(tup), line, nameFmt, std::forward<Args>(args)...);
        else
            m_zoneId = kNoZoneId;
    }

    template <typename... Args>
    ProfileZoneDynamic(const Channel& channel,
                       std::tuple<StaticStringType, StaticStringType> tup,
                       int line,
                       const char* nameFmt,
                       Args&&... args)
        : m_mask(channel.getMask())
    {
        if (g_carbProfiler && channel.isEnabled())
            m_zoneId = g_carbProfiler->beginDynamic(
                m_mask, std::get<0>(tup), std::get<1>(tup), line, nameFmt, std::forward<Args>(args)...);
        else
            m_zoneId = kNoZoneId;
    }

    ~ProfileZoneDynamic()
    {
        if (g_carbProfiler && m_zoneId != kNoZoneId)
            g_carbProfiler->endEx(m_mask, m_zoneId);
    }
};

} // namespace profiler
} // namespace carb