carb/profiler/Profile.h

File members: carb/profiler/Profile.h

// Copyright (c) 2018-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 "../Defines.h"

#include "../Framework.h"
#include "../cpp/Atomic.h"
#include "IProfiler.h"

#include <cstdarg>
#include <cstdio>

#include "ProfilerUtils.h"

#define CARB_PROFILE_DECLARE_CHANNEL(name_, defaultMask_, defaultEnabled_, symbol_)                                    \
    ::carb::profiler::Channel symbol_((defaultMask_), (defaultEnabled_), "" name_)

#define CARB_PROFILE_EXTERN_CHANNEL(symbol_) extern ::carb::profiler::Channel symbol_

#if CARB_PROFILING || defined(DOXYGEN_BUILD)

#    ifndef DOXYGEN_BUILD
// The following are helper macros for the profiler.
#        define CARB_PROFILE_IF(cond, true_case, false_case) CARB_PROFILE_IF_HELPER(cond, true_case, false_case)

// Note: CARB_PROFILE_HAS_VARARGS only supports up to 10 args now. If more are desired, increase the sequences below
// and add test cases to TestProfiler.cpp
// This trick is from https://stackoverflow.com/a/36015150/1450686
#        if CARB_COMPILER_MSC
#            define CARB_PROFILE_HAS_VARARGS(x, ...) CARB_PROFILE_EXPAND_ARGS(CARB_PROFILE_AUGMENT_ARGS(__VA_ARGS__))
#        elif CARB_COMPILER_GNUC
#            define CARB_PROFILE_HAS_VARARGS(...)                                                                      \
                CARB_PROFILE_ARGCHK_PRIVATE2(0, ##__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0)
#        else
#            error Unsupported Compiler!
#        endif

// The following are implementation helpers not intended to be used
#        define CARB_PROFILE_IF_HELPER(cond, true_case, false_case)                                                    \
            CARB_JOIN(CARB_PROFILE_IF_HELPER_, cond)(true_case, false_case)
#        define CARB_PROFILE_IF_HELPER_0(true_case, false_case) false_case
#        define CARB_PROFILE_IF_HELPER_1(true_case, false_case) true_case

#        define CARB_PROFILE_AUGMENT_ARGS(...) unused, __VA_ARGS__
#        define CARB_PROFILE_EXPAND_ARGS(...)                                                                          \
            CARB_PROFILE_EXPAND(CARB_PROFILE_ARGCHK_PRIVATE(__VA_ARGS__, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0))
#        define CARB_PROFILE_EXPAND(x) x
#        define CARB_PROFILE_ARGCHK_PRIVATE(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10, count, ...) count
#        define CARB_PROFILE_ARGCHK_PRIVATE2(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, count, ...) count

#        define CARB_PROFILE_UFUNCFILE(prof, func)                                                                     \
            [p = (prof), f = (func)] {                                                                                 \
                static auto tup = ::carb::profiler::detail::makeString2(                                               \
                    p->registerStaticString(f), p->registerStaticString(__FILE__));                                    \
                return tup;                                                                                            \
            }()

#        define CARB_PROFILE_UFUNCFILESTR(prof, func, str)                                                             \
            [p = (prof), f = (func), s = (str)] {                                                                      \
                static auto tup = ::carb::profiler::detail::makeString3(                                               \
                    p->registerStaticString(f), p->registerStaticString(__FILE__), p->registerStaticString(s));        \
                return tup;                                                                                            \
            }()

#        define CARB_PROFILE_FUNCFILE(func)                                                                            \
            [f = (func)] {                                                                                             \
                if (auto prof = ::g_carbProfiler.load(std::memory_order_acquire))                                      \
                {                                                                                                      \
                    static auto tup = ::carb::profiler::detail::makeString2(                                           \
                        prof->registerStaticString(f), prof->registerStaticString(__FILE__));                          \
                    return tup;                                                                                        \
                }                                                                                                      \
                return ::carb::profiler::detail::emptyTuple2();                                                        \
            }()

#        define CARB_PROFILE_FUNCFILESTR(func, str)                                                                    \
            [f = (func), s = (str)] {                                                                                  \
                if (auto prof = ::g_carbProfiler.load(std::memory_order_acquire))                                      \
                {                                                                                                      \
                    static auto tup = ::carb::profiler::detail::makeString3(prof->registerStaticString(f),             \
                                                                            prof->registerStaticString(__FILE__),      \
                                                                            prof->registerStaticString(s));            \
                    return tup;                                                                                        \
                }                                                                                                      \
                return ::carb::profiler::detail::emptyTuple3();                                                        \
            }()

#        define CARB_PROFILE_CHECKMASK(mask)                                                                           \
            (((mask) ? (mask) : carb::profiler::kCaptureMaskDefault) &                                                 \
             g_carbProfilerMask.load(std::memory_order_acquire))

namespace carb
{
namespace profiler
{
namespace detail
{

// Helper functions for begin that take the tuples created by CARB_PROFILE_UFUNCFILE and CARB_PROFILE_UFUNCFILESTR
template <class... Args>
carb::profiler::ZoneId beginDynamicHelper(::carb::profiler::IProfiler* prof,
                                          const uint64_t mask,
                                          const ::carb::profiler::detail::String2& tup,
                                          int line,
                                          const char* fmt,
                                          Args&&... args)
{
    if (!CARB_PROFILE_CHECKMASK(mask))
        return kNoZoneId;
    return prof->beginDynamic(mask, tup.first, tup.second, line, fmt, std::forward<Args>(args)...);
}
template <class... Args>
carb::profiler::ZoneId beginDynamicHelper(::carb::profiler::IProfiler* prof,
                                          const carb::profiler::Channel& channel,
                                          const ::carb::profiler::detail::String2& tup,
                                          int line,
                                          const char* fmt,
                                          Args&&... args)
{
    if (!channel.isEnabled())
        return kNoZoneId;
    return prof->beginDynamic(channel.getMask(), tup.first, tup.second, line, fmt, std::forward<Args>(args)...);
}
inline carb::profiler::ZoneId beginStaticHelper(::carb::profiler::IProfiler* prof,
                                                const uint64_t mask,
                                                const ::carb::profiler::detail::String3& tup,
                                                int line)
{
    if (!CARB_PROFILE_CHECKMASK(mask))
        return kNoZoneId;
    return prof->beginStatic(mask, tup.first, tup.second, line, tup.third);
}
inline carb::profiler::ZoneId beginStaticHelper(::carb::profiler::IProfiler* prof,
                                                const carb::profiler::Channel& channel,
                                                const ::carb::profiler::detail::String3& tup,
                                                int line)
{
    if (!channel.isEnabled())
        return kNoZoneId;
    return prof->beginStatic(channel.getMask(), tup.first, tup.second, line, tup.third);
}
inline void endHelper(const uint64_t mask, ::carb::profiler::ZoneId id = ::carb::profiler::kUnknownZoneId)
{
    auto prof = ::g_carbProfiler.load(std::memory_order_acquire);
    if (prof && id != ::carb::profiler::kNoZoneId && CARB_PROFILE_CHECKMASK(mask))
        prof->endEx(mask, id);
}
inline void endHelper(const carb::profiler::Channel& channel,
                      ::carb::profiler::ZoneId id = ::carb::profiler::kUnknownZoneId)
{
    auto prof = ::g_carbProfiler.load(std::memory_order_acquire);
    if (prof && id != ::carb::profiler::kNoZoneId && channel.isEnabled())
        prof->endEx(channel.getMask(), id);
}
inline uint64_t maskHelper(uint64_t mask)
{
    return mask;
}
inline uint64_t maskHelper(const carb::profiler::Channel& channel)
{
    return channel.getMask();
}
inline bool enabled(uint64_t mask)
{
    return CARB_PROFILE_CHECKMASK(mask);
}
inline bool enabled(const carb::profiler::Channel& channel)
{
    return channel.isEnabled();
}

inline const ::carb::profiler::detail::String2& emptyTuple2()
{
    static constexpr auto tup = ::carb::profiler::detail::makeString2(kInvalidStaticString, kInvalidStaticString);
    return tup;
}

inline const ::carb::profiler::detail::String3& emptyTuple3()
{
    static constexpr auto tup =
        ::carb::profiler::detail::makeString3(kInvalidStaticString, kInvalidStaticString, kInvalidStaticString);
    return tup;
}

} // namespace detail
} // namespace profiler
} // namespace carb

#    endif

#    define CARB_PROFILE_STARTUP()                                                                                     \
        [] {                                                                                                           \
            if (auto prof = ::g_carbProfiler.load(std::memory_order_acquire))                                          \
            {                                                                                                          \
                prof->startup();                                                                                       \
            }                                                                                                          \
        }()

#    define CARB_PROFILE_SHUTDOWN()                                                                                    \
        [] {                                                                                                           \
            if (auto prof = ::g_carbProfiler.load(std::memory_order_acquire))                                          \
            {                                                                                                          \
                prof->shutdown();                                                                                      \
            }                                                                                                          \
        }()

#    define CARB_PROFILE_REGISTER_STRING(str)                                                                          \
        [&] {                                                                                                          \
            if (auto __prof = ::g_carbProfiler.load(std::memory_order_acquire))                                        \
            {                                                                                                          \
                static auto __p = __prof->registerStaticString(str);                                                   \
                return __p;                                                                                            \
            }                                                                                                          \
            return ::carb::profiler::kInvalidStaticString;                                                             \
        }()

#    define CARB_PROFILE_SET_CAPTURE_MASK(mask)                                                                        \
        [&] {                                                                                                          \
            if (auto __prof = ::g_carbProfiler.load(std::memory_order_acquire))                                        \
            {                                                                                                          \
                __prof->setCaptureMask(mask);                                                                          \
            }                                                                                                          \
        }()

#    define CARB_PROFILE_BEGIN(maskOrChannel, eventName, ...)                                                          \
        [&, __prof = g_carbProfiler.load(std::memory_order_acquire), __func = __func__] {                              \
            return __prof ? CARB_PROFILE_IF(CARB_PROFILE_HAS_VARARGS((eventName), ##__VA_ARGS__),                      \
                                            ::carb::profiler::detail::beginDynamicHelper(                              \
                                                __prof, (maskOrChannel), CARB_PROFILE_UFUNCFILE(__prof, __func),       \
                                                __LINE__, (eventName), ##__VA_ARGS__),                                 \
                                            ::carb::profiler::detail::beginStaticHelper(                               \
                                                __prof, (maskOrChannel),                                               \
                                                CARB_PROFILE_UFUNCFILESTR(__prof, __func, (eventName)), __LINE__)) :   \
                            (0 ? /*compiler validate*/ printf((eventName), ##__VA_ARGS__) : 0);                        \
        }()

#    define CARB_PROFILE_END(maskOrChannel, ...) ::carb::profiler::detail::endHelper((maskOrChannel), ##__VA_ARGS__)

#    define CARB_PROFILE_FRAME(maskOrChannel, frameName, ...)                                                          \
        [&] {                                                                                                          \
            /* Use printf to validate the format string */                                                             \
            if (0)                                                                                                     \
            {                                                                                                          \
                printf((frameName), ##__VA_ARGS__);                                                                    \
            }                                                                                                          \
            if (auto __prof = ::g_carbProfiler.load(std::memory_order_acquire))                                        \
            {                                                                                                          \
                CARB_PROFILE_IF(                                                                                       \
                    CARB_PROFILE_HAS_VARARGS((frameName), ##__VA_ARGS__),                                              \
                    __prof->frameDynamic(0, (frameName), ##__VA_ARGS__), __prof->frameStatic(0, [__prof]() {           \
                        static ::carb::profiler::StaticStringType p = __prof->registerStaticString("" frameName);      \
                        return p;                                                                                      \
                    }()));                                                                                             \
            }                                                                                                          \
        }()

#    define CARB_PROFILE_ZONE(maskOrChannel, zoneName, ...)                                                            \
        CARB_PROFILE_IF(CARB_PROFILE_HAS_VARARGS((zoneName), ##__VA_ARGS__),                                           \
                        ::carb::profiler::ProfileZoneDynamic CARB_JOIN(_carbZone, __LINE__)(                           \
                            (maskOrChannel), CARB_PROFILE_FUNCFILE(__func__), __LINE__, (zoneName), ##__VA_ARGS__),    \
                        ::carb::profiler::ProfileZoneStatic CARB_JOIN(_carbZone, __LINE__)(                            \
                            (maskOrChannel), CARB_PROFILE_FUNCFILESTR(__func__, (zoneName)), __LINE__))

#    define CARB_PROFILE_FUNCTION(maskOrChannel)                                                                       \
        ::carb::profiler::ProfileZoneStatic CARB_JOIN(_carbZoneFunction, __LINE__)(                                    \
            (maskOrChannel), CARB_PROFILE_FUNCFILESTR(__func__, CARB_PRETTY_FUNCTION), __LINE__)

#    define CARB_PROFILE_VALUE(value, maskOrChannel, valueName, ...)                                                   \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire)] {                                               \
            /* Use printf to validate the format string */                                                             \
            if (0)                                                                                                     \
            {                                                                                                          \
                printf((valueName), ##__VA_ARGS__);                                                                    \
            }                                                                                                          \
            auto&& __moc = (maskOrChannel);                                                                            \
            if (__prof && ::carb::profiler::detail::enabled(__moc))                                                    \
            {                                                                                                          \
                CARB_PROFILE_IF(CARB_PROFILE_HAS_VARARGS((valueName), ##__VA_ARGS__),                                  \
                                __prof->valueDynamic(                                                                  \
                                    ::carb::profiler::detail::maskHelper(__moc), (value), (valueName), ##__VA_ARGS__), \
                                __prof->valueStatic(::carb::profiler::detail::maskHelper(__moc), (value), [__prof]() { \
                                    static ::carb::profiler::StaticStringType p =                                      \
                                        __prof->registerStaticString("" valueName);                                    \
                                    return p;                                                                          \
                                }()));                                                                                 \
            }                                                                                                          \
        }()

#    define CARB_PROFILE_ALLOC_NAMED(maskOrChannel, ptr, size, poolName, ...)                                          \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire)] {                                               \
            /* Use printf to validate the format string */                                                             \
            if (0)                                                                                                     \
            {                                                                                                          \
                printf((poolName), ##__VA_ARGS__);                                                                     \
            }                                                                                                          \
            auto&& __moc = (maskOrChannel);                                                                            \
            if (__prof && ::carb::profiler::detail::enabled(__moc))                                                    \
            {                                                                                                          \
                CARB_PROFILE_IF(                                                                                       \
                    CARB_PROFILE_HAS_VARARGS((poolName), ##__VA_ARGS__),                                               \
                    __prof->allocNamedDynamic(                                                                         \
                        ::carb::profiler::detail::maskHelper(__moc), (ptr), (size), (poolName), ##__VA_ARGS__),        \
                    __prof->allocNamedStatic(::carb::profiler::detail::maskHelper(__moc), (ptr), (size), [__prof]() {  \
                        static ::carb::profiler::StaticStringType p = __prof->registerStaticString("" poolName);       \
                        return p;                                                                                      \
                    }()));                                                                                             \
            }                                                                                                          \
        }()

#    define CARB_PROFILE_FREE_NAMED(maskOrChannel, ptr, poolName, ...)                                                 \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire)] {                                               \
            /* Use printf to validate the format string */                                                             \
            if (0)                                                                                                     \
            {                                                                                                          \
                printf((poolName), ##__VA_ARGS__);                                                                     \
            }                                                                                                          \
            auto&& __moc = (maskOrChannel);                                                                            \
            if (__prof && ::carb::profiler::detail::enabled(__moc))                                                    \
            {                                                                                                          \
                CARB_PROFILE_IF(                                                                                       \
                    CARB_PROFILE_HAS_VARARGS((poolName), ##__VA_ARGS__),                                               \
                    __prof->freeNamedDynamic(                                                                          \
                        ::carb::profiler::detail::maskHelper(__moc), (ptr), (poolName), ##__VA_ARGS__),                \
                    __prof->freeNamedStatic(::carb::profiler::detail::maskHelper(__moc), (ptr), [__prof]() {           \
                        static ::carb::profiler::StaticStringType p = __prof->registerStaticString("" poolName);       \
                        return p;                                                                                      \
                    }()));                                                                                             \
            }                                                                                                          \
        }()

#    define CARB_PROFILE_ALLOC(maskOrChannel, ptr, size)                                                               \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire)] {                                               \
            auto&& __moc = (maskOrChannel);                                                                            \
            if (__prof && ::carb::profiler::detail::enabled(__moc))                                                    \
            {                                                                                                          \
                __prof->allocStatic(::carb::profiler::detail::maskHelper(__moc), (ptr), (size));                       \
            }                                                                                                          \
        }()

#    define CARB_PROFILE_FREE(maskOrChannel, ptr)                                                                      \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire)] {                                               \
            auto&& __moc = (maskOrChannel);                                                                            \
            if (__prof && ::carb::profiler::detail::enabled(__moc))                                                    \
            {                                                                                                          \
                __prof->freeStatic(::carb::profiler::detail::maskHelper(__moc), (ptr));                                \
            }                                                                                                          \
        }()

#    define CARB_NAME_THREAD(tidOrZero, threadName, ...)                                                               \
        [&] {                                                                                                          \
            /* Use printf to validate the format string */                                                             \
            if (0)                                                                                                     \
            {                                                                                                          \
                printf((threadName), ##__VA_ARGS__);                                                                   \
            }                                                                                                          \
            if (auto __prof = ::g_carbProfiler.load(std::memory_order_acquire))                                        \
            {                                                                                                          \
                CARB_PROFILE_IF(CARB_PROFILE_HAS_VARARGS((threadName), ##__VA_ARGS__),                                 \
                                __prof->nameThreadDynamic((tidOrZero), (threadName), ##__VA_ARGS__),                   \
                                __prof->nameThreadStatic((tidOrZero), [__prof]() {                                     \
                                    static ::carb::profiler::StaticStringType p =                                      \
                                        __prof->registerStaticString("" threadName);                                   \
                                    return p;                                                                          \
                                }()));                                                                                 \
            }                                                                                                          \
        }()

#    define CARB_PROFILE_EVENT(maskOrChannel, type, name, ...)                                                         \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire), __func = CARB_PRETTY_FUNCTION] {                \
            if (0)                                                                                                     \
                printf((name), ##__VA_ARGS__);                                                                         \
            auto&& __moc = (maskOrChannel);                                                                            \
            if (__prof && ::carb::profiler::detail::enabled(__moc))                                                    \
            {                                                                                                          \
                CARB_PROFILE_IF(CARB_PROFILE_HAS_VARARGS((name), ##__VA_ARGS__),                                       \
                                static auto __tup = ::carb::profiler::detail::makeString2(                             \
                                    __prof->registerStaticString(__func), __prof->registerStaticString(__FILE__));     \
                                __prof->emitInstantDynamic(::carb::profiler::detail::maskHelper(__moc), __tup.first,   \
                                                           __tup.second, __LINE__, (type), (name), ##__VA_ARGS__),     \
                                static auto __tup = ::carb::profiler::detail::makeString3(                             \
                                    __prof->registerStaticString(__func), __prof->registerStaticString(__FILE__),      \
                                    __prof->registerStaticString(name));                                               \
                                __prof->emitInstantStatic(::carb::profiler::detail::maskHelper(__moc), __tup.first,    \
                                                          __tup.second, __LINE__, (type), __tup.third));               \
            }                                                                                                          \
        }()

#    define CARB_PROFILE_FLOW_BEGIN(maskOrChannel, id, name, ...)                                                      \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire), __func = CARB_PRETTY_FUNCTION] {                \
            if (0)                                                                                                     \
                printf((name), ##__VA_ARGS__);                                                                         \
            auto&& __moc = (maskOrChannel);                                                                            \
            if (__prof && ::carb::profiler::detail::enabled(__moc))                                                    \
            {                                                                                                          \
                CARB_PROFILE_IF(                                                                                       \
                    CARB_PROFILE_HAS_VARARGS(name, ##__VA_ARGS__),                                                     \
                    static auto __tup = ::carb::profiler::detail::makeString2(                                         \
                        __prof->registerStaticString(__func), __prof->registerStaticString(__FILE__));                 \
                    __prof->emitFlowDynamic(::carb::profiler::detail::maskHelper(__moc), __tup.first, __tup.second,    \
                                            __LINE__, ::carb::profiler::FlowType::Begin, (id), (name), ##__VA_ARGS__), \
                    static auto __tup = ::carb::profiler::detail::makeString3(__prof->registerStaticString(__func),    \
                                                                              __prof->registerStaticString(__FILE__),  \
                                                                              __prof->registerStaticString("" name));  \
                    __prof->emitFlowStatic(::carb::profiler::detail::maskHelper(__moc), __tup.first, __tup.second,     \
                                           __LINE__, ::carb::profiler::FlowType::Begin, (id), __tup.third));           \
            }                                                                                                          \
        }()

#    define CARB_PROFILE_FLOW_END(maskOrChannel, id)                                                                     \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire), __func = CARB_PRETTY_FUNCTION] {                  \
            auto&& __moc = (maskOrChannel);                                                                              \
            if (__prof && ::carb::profiler::detail::enabled(__moc))                                                      \
            {                                                                                                            \
                static auto __tup = ::carb::profiler::detail::makeString2(                                               \
                    __prof->registerStaticString(__func), __prof->registerStaticString(__FILE__));                       \
                __prof->emitFlowStatic(::carb::profiler::detail::maskHelper(__moc), __tup.first, __tup.second, __LINE__, \
                                       ::carb::profiler::FlowType::End, (id), ::carb::profiler::kInvalidStaticString);   \
            }                                                                                                            \
        }()

#    define CARB_PROFILE_CREATE_GPU_CONTEXT(                                                                           \
        name, correlatedCpuTimestampNs, correlatedGpuTimestamp, gpuTimestampPeriodNs, graphicsApi)                     \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire)]() {                                             \
            return __prof ? __prof->createGpuContext((name), (correlatedCpuTimestampNs), (correlatedGpuTimestamp),     \
                                                     (gpuTimestampPeriodNs), (graphicsApi)) :                          \
                            ::carb::profiler::kInvalidGpuContextId;                                                    \
        }()

#    define CARB_PROFILE_DESTROY_GPU_CONTEXT(contextId)                                                                \
        [&] {                                                                                                          \
            if (auto __prof = ::g_carbProfiler.load(std::memory_order_acquire))                                        \
            {                                                                                                          \
                __prof->destroyGpuContext(contextId);                                                                  \
            }                                                                                                          \
        }()

#    define CARB_PROFILE_CALIBRATE_GPU_CONTEXT(                                                                         \
        contextId, correlatedCpuTimestampNs, previousCorrelatedCpuTimestampNs, correlatedGpuTimestamp)                  \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire)]() {                                              \
            return __prof ? __prof->calibrateGpuContext((contextId), (correlatedCpuTimestampNs),                        \
                                                        (previousCorrelatedCpuTimestampNs), (correlatedGpuTimestamp)) : \
                            false;                                                                                      \
        }()

#    define CARB_PROFILE_GPU_QUERY_BEGIN(maskOrChannel, contextId, queryId, eventName, ...)                              \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire), __func = CARB_PRETTY_FUNCTION] {                  \
            if (0)                                                                                                       \
                printf((eventName), ##__VA_ARGS__);                                                                      \
            auto&& __moc = (maskOrChannel);                                                                              \
            if (__prof && ::carb::profiler::detail::enabled(__moc))                                                      \
            {                                                                                                            \
                CARB_PROFILE_IF(                                                                                         \
                    CARB_PROFILE_HAS_VARARGS((eventName), ##__VA_ARGS__),                                                \
                    static auto __tup = ::carb::profiler::detail::makeString2(                                           \
                        __prof->registerStaticString(__func), __prof->registerStaticString(__FILE__));                   \
                    __prof->beginGpuQueryDynamic(::carb::profiler::detail::maskHelper(__moc), __tup.first, __tup.second, \
                                                 __LINE__, (contextId), (queryId), (eventName), ##__VA_ARGS__),          \
                    static auto __tup = ::carb::profiler::detail::makeString3(                                           \
                        __prof->registerStaticString(__func), __prof->registerStaticString(__FILE__),                    \
                        __prof->registerStaticString("" eventName));                                                     \
                    __prof->beginGpuQueryStatic(::carb::profiler::detail::maskHelper(__moc), __tup.first,                \
                                                __tup.second, __LINE__, (contextId), (queryId), __tup.third));           \
            }                                                                                                            \
        }()

#    define CARB_PROFILE_GPU_QUERY_END(maskOrChannel, contextId, queryId)                                              \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire)] {                                               \
            auto&& __moc = (maskOrChannel);                                                                            \
            if (__prof && ::carb::profiler::detail::enabled(__moc))                                                    \
            {                                                                                                          \
                __prof->endGpuQuery(::carb::profiler::detail::maskHelper(__moc), (contextId), (queryId));              \
            }                                                                                                          \
        }()

#    define CARB_PROFILE_GPU_SET_QUERY_VALUE(maskOrChannel, contextId, queryId, gpuTimestamp)                          \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire)] {                                               \
            auto&& __moc = (maskOrChannel);                                                                            \
            if (__prof && ::carb::profiler::detail::enabled(__moc))                                                    \
            {                                                                                                          \
                __prof->setGpuQueryValue(                                                                              \
                    ::carb::profiler::detail::maskHelper(__moc), (contextId), (queryId), (gpuTimestamp));              \
            }                                                                                                          \
        }()

#    define CARB_PROFILE_LOCKABLE_CREATE(maskOrChannel, isSharedLock, name)                                            \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire), __func = CARB_PRETTY_FUNCTION] {                \
            auto&& __moc = (maskOrChannel);                                                                            \
            if (__prof && ::carb::profiler::detail::enabled(__moc))                                                    \
            {                                                                                                          \
                static auto __tup = ::carb::profiler::detail::makeString2(                                             \
                    __prof->registerStaticString(__func), __prof->registerStaticString(__FILE__));                     \
                return __prof->createLockable(::carb::profiler::detail::maskHelper(__moc), (name), (isSharedLock),     \
                                              __tup.first, __tup.second, __LINE__);                                    \
            }                                                                                                          \
            return ::carb::profiler::kInvalidLockableId;                                                               \
        }()

#    define CARB_PROFILE_LOCKABLE_DESTROY(lockableId)                                                                  \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire)] {                                               \
            auto&& __id = (lockableId);                                                                                \
            if (__prof && __id != carb::profiler::kInvalidLockableId)                                                  \
            {                                                                                                          \
                __prof->destroyLockable(__id);                                                                         \
            }                                                                                                          \
        }()

#    define CARB_PROFILE_LOCKABLE_OPERATION(lockableId, operation)                                                     \
        [&, __prof = ::g_carbProfiler.load(std::memory_order_acquire)] {                                               \
            auto&& __id = (lockableId);                                                                                \
            if (__prof && __id != carb::profiler::kInvalidLockableId)                                                  \
            {                                                                                                          \
                __prof->lockableOperation(__id, (operation));                                                          \
            }                                                                                                          \
        }()

#else

#    define CARB_PROFILE_STARTUP() (void(0))
#    define CARB_PROFILE_SHUTDOWN() (void(0))
#    define CARB_PROFILE_REGISTER_STRING(str) (CARB_UNUSED(str), ::carb::profiler::kInvalidStaticString)
#    define CARB_PROFILE_SET_CAPTURE_MASK(mask) CARB_UNUSED(mask)
#    define CARB_PROFILE_BEGIN(maskOrChannel, eventName, ...)                                                          \
        (CARB_UNUSED((maskOrChannel), (eventName), ##__VA_ARGS__), ::carb::profiler::kNoZoneId)
#    define CARB_PROFILE_END(maskOrChannel, ...) CARB_UNUSED((maskOrChannel), ##__VA_ARGS__)
#    define CARB_PROFILE_FRAME(mask, frameName, ...) CARB_UNUSED((mask), (frameName), ##__VA_ARGS__)
#    define CARB_PROFILE_ZONE(maskOrChannel, zoneName, ...) CARB_UNUSED((maskOrChannel), (zoneName), ##__VA_ARGS__)
#    define CARB_PROFILE_FUNCTION(maskOrChannel) CARB_UNUSED(maskOrChannel)
#    define CARB_PROFILE_VALUE(value, maskOrChannel, valueName, ...)                                                   \
        CARB_UNUSED((value), (maskOrChannel), (valueName), ##__VA_ARGS__)
#    define CARB_PROFILE_ALLOC_NAMED(maskOrChannel, ptr, size, poolName, ...)                                          \
        CARB_UNUSED((maskOrChannel), (ptr), (size), (poolName), ##__VA_ARGS__)
#    define CARB_PROFILE_FREE_NAMED(maskOrChannel, ptr, poolName, ...)                                                 \
        CARB_UNUSED((maskOrChannel), (ptr), (poolName), ##__VA_ARGS__)
#    define CARB_PROFILE_ALLOC(maskOrChannel, ptr, size) CARB_UNUSED((maskOrChannel), (ptr), (size))
#    define CARB_PROFILE_FREE(maskOrChannel, ptr) CARB_UNUSED((maskOrChannel), (ptr))
#    define CARB_NAME_THREAD(tidOrZero, threadName, ...) CARB_UNUSED((tidOrZero), (threadName), ##__VA_ARGS__)
#    define CARB_PROFILE_EVENT(maskOrChannel, type, name, ...)                                                         \
        CARB_UNUSED((maskOrChannel), (type), (name), ##__VA_ARGS__)
#    define CARB_PROFILE_FLOW_BEGIN(maskOrChannel, id, name, ...)                                                      \
        CARB_UNUSED((maskOrChannel), (id), (name), ##__VA_ARGS__)
#    define CARB_PROFILE_FLOW_END(maskOrChannel, id) CARB_UNUSED((maskOrChannel), (id))
#    define CARB_PROFILE_CREATE_GPU_CONTEXT(                                                                           \
        name, correlatedCpuTimestampNs, correlatedGpuTimestamp, gpuTimestampPeriodNs, graphicApi)                      \
        (CARB_UNUSED(                                                                                                  \
             (name), (correlatedCpuTimestampNs), (correlatedGpuTimestamp), (gpuTimestampPeriodNs), (graphicsApi)),     \
         carb::profiler::kInvalidGpuContextId)
#    define CARB_PROFILE_DESTROY_GPU_CONTEXT(contextId) CARB_UNUSED(contextId)
#    define CARB_PROFILE_CALIBRATE_GPU_CONTEXT(                                                                        \
        contextId, correlatedCpuTimestampNs, previousCorrelatedCpuTimestampNs, correlatedGpuTimestamp)                 \
        CARB_UNUSED(                                                                                                   \
            (contextId), (correlatedCpuTimestampNs), (previousCorrelatedCpuTimestampNs), (correlatedGpuTimestamp))
#    define CARB_PROFILE_GPU_QUERY_BEGIN(maskOrChannel, contextId, queryId, eventName, ...)                            \
        CARB_UNUSED((maskOrChannel), (contextId), (queryId), (eventName), ##__VA_ARGS__)
#    define CARB_PROFILE_GPU_QUERY_END(maskOrChannel, contextId, queryId)                                              \
        (CARB_UNUSED((maskOrChannel), (contextId), (queryId)))
#    define CARB_PROFILE_GPU_SET_QUERY_VALUE(maskOrChannel, contextId, queryId, gpuTimestamp)                          \
        CARB_UNUSED((maskOrChannel), (contextId), (queryId), (gpuTimestamp))
#    define CARB_PROFILE_LOCKABLE_CREATE(maskOrChannel, isSharedLock, name)                                            \
        (CARB_UNUSED((maskOrChannel), (isSharedLock), (name)), ::carb::profiler::kInvalidLockableId)
#    define CARB_PROFILE_LOCKABLE_DESTROY(lockableId) CARB_UNUSED(lockableId)
#    define CARB_PROFILE_LOCKABLE_OPERATION(lockableId, operation) CARB_UNUSED((lockableId), (operation))

#endif

#define CARB_PROFILER_GLOBALS()

namespace carb
{
namespace profiler
{

template <class Mutex>
class ProfiledMutex
{
public:
    ProfiledMutex(const uint64_t profileMask, const char* name) : ProfiledMutex(profileMask, false, name)
    {
    }

    ProfiledMutex(const carb::profiler::Channel& channel, const char* name) : ProfiledMutex(channel, false, name)
    {
    }

    ~ProfiledMutex()
    {
        CARB_PROFILE_LOCKABLE_DESTROY(m_lockableId);
    }

    void lock()
    {
        CARB_PROFILE_LOCKABLE_OPERATION(m_lockableId, LockableOperationType::BeforeLock);
        m_mutex.lock();
        CARB_PROFILE_LOCKABLE_OPERATION(m_lockableId, LockableOperationType::AfterLock);
    }

    bool try_lock()
    {
        bool acquired = m_mutex.try_lock();
        if (acquired)
        {
            CARB_PROFILE_LOCKABLE_OPERATION(m_lockableId, LockableOperationType::AfterSuccessfulTryLock);
        }
        return acquired;
    }

    void unlock()
    {
        m_mutex.unlock();
        CARB_PROFILE_LOCKABLE_OPERATION(m_lockableId, LockableOperationType::AfterUnlock);
    }

    Mutex& getMutex()
    {
        return m_mutex;
    }

    const Mutex& getMutex() const
    {
        return m_mutex;
    }

protected:
    ProfiledMutex(const uint64_t profileMask, bool isSharedMutex, const char* name)
    {
        m_lockableId = CARB_PROFILE_LOCKABLE_CREATE(profileMask, isSharedMutex, name);
    }
    ProfiledMutex(const carb::profiler::Channel& channel, bool isSharedMutex, const char* name)
    {
        m_lockableId = CARB_PROFILE_LOCKABLE_CREATE(channel, isSharedMutex, name);
    }

    Mutex m_mutex;
    LockableId m_lockableId;
};

template <class Mutex>
class ProfiledSharedMutex : public ProfiledMutex<Mutex>
{
    using Base = ProfiledMutex<Mutex>;

public:
    ProfiledSharedMutex(const uint64_t profileMask, const char* name) : Base(profileMask, true, name)
    {
    }

    ProfiledSharedMutex(const carb::profiler::Channel& channel, const char* name) : Base(channel, true, name)
    {
    }

    ~ProfiledSharedMutex()
    {
    }

    void lock_shared()
    {
        CARB_PROFILE_LOCKABLE_OPERATION(this->m_lockableId, LockableOperationType::BeforeLockShared);
        this->m_mutex.lock_shared();
        CARB_PROFILE_LOCKABLE_OPERATION(this->m_lockableId, LockableOperationType::AfterLockShared);
    }

    bool try_lock_shared()
    {
        bool acquired = this->m_mutex.try_lock_shared();
        if (acquired)
        {
            CARB_PROFILE_LOCKABLE_OPERATION(this->m_lockableId, LockableOperationType::AfterSuccessfulTryLockShared);
        }
        return acquired;
    }

    void unlock_shared()
    {
        this->m_mutex.unlock_shared();
        CARB_PROFILE_LOCKABLE_OPERATION(this->m_lockableId, LockableOperationType::AfterUnlockShared);
    }
};

void deregisterProfilerForClient() noexcept;

#ifndef DOXYGEN_SHOULD_SKIP_THIS
namespace detail
{

inline void updateMask(uint64_t mask)
{
    g_carbProfilerMask.store(mask, std::memory_order_release);
}

inline void releaseHook(void* iface, void*)
{
    g_carbProfiler.store(nullptr, std::memory_order_seq_cst);
    getFramework()->removeReleaseHook(iface, &releaseHook, nullptr);
}

inline void frameworkReleaseHook(void*, void*)
{
    // Framework is going away, so make sure we get fully deregistered.
    deregisterProfilerForClient();
}

inline void loadHook(const PluginDesc&, void*)
{
    if (!g_carbProfiler)
    {
        IProfiler* profiler = getFramework()->tryAcquireInterface<IProfiler>();
        if (profiler)
        {
            if (profiler->setMaskCallback)
            {
                // Relaxed semantics since we will shortly be synchronizing on g_carbProfiler.
                g_carbProfilerMask.store(profiler->setMaskCallback(updateMask, true), std::memory_order_relaxed);
            }
            else
            {
                g_carbProfilerMask.store(uint64_t(-1), std::memory_order_relaxed); // not supported; let everything
                                                                                   // through
            }
            getFramework()->addReleaseHook(profiler, &detail::releaseHook, nullptr);
            g_carbProfiler.store(profiler, std::memory_order_seq_cst); // sequentially consistent
        }
    }
}

inline bool& registered()
{
    static bool r{ false };
    return r;
}

inline LoadHookHandle& loadHookHandle()
{
    static carb::LoadHookHandle handle{};
    return handle;
}

} // namespace detail
#endif

inline IProfiler* getProfiler(std::memory_order order = std::memory_order_seq_cst)
{
    return g_carbProfiler.load(order);
}

inline void deregisterProfilerForClient() noexcept
{
    if (std::exchange(detail::registered(), false))
    {
        auto fw = getFramework();
        auto handle = std::exchange(detail::loadHookHandle(), kInvalidLoadHook);
        IProfiler* profiler = g_carbProfiler.exchange(nullptr, std::memory_order_seq_cst);
        if (fw)
        {
            if (profiler && fw->verifyInterface(profiler) && profiler->setMaskCallback)
            {
                profiler->setMaskCallback(detail::updateMask, false);
            }
            if (handle)
            {
                fw->removeLoadHook(handle);
            }
            fw->removeReleaseHook(nullptr, &detail::frameworkReleaseHook, nullptr);
            if (profiler)
            {
                fw->removeReleaseHook(profiler, &detail::releaseHook, nullptr);
            }

            // Unregister channels
            Channel::onProfilerUnregistered();
        }
    }
}

inline void registerProfilerForClient() noexcept
{
    if (!std::exchange(detail::registered(), true))
    {
        auto fw = getFramework();
        fw->addReleaseHook(nullptr, &detail::frameworkReleaseHook, nullptr);
        IProfiler* profiler = fw->tryAcquireInterface<IProfiler>();
        if (profiler)
        {
            if (profiler->setMaskCallback)
            {
                // Relaxed semantics since we will shortly be synchronizing on g_carbProfiler.
                g_carbProfilerMask.store(profiler->setMaskCallback(detail::updateMask, true), std::memory_order_relaxed);
            }
            else
            {
                g_carbProfilerMask.store(uint64_t(-1), std::memory_order_relaxed); // let everything through
            }
            bool b = fw->addReleaseHook(profiler, &detail::releaseHook, nullptr);
            CARB_ASSERT(b);
            CARB_UNUSED(b);
        }
        g_carbProfiler.store(profiler, std::memory_order_seq_cst); // sequentially consistent
        detail::loadHookHandle() = fw->addLoadHook<IProfiler>(nullptr, &detail::loadHook, nullptr);

        // Register channels
        Channel::onProfilerRegistered();

        // Make sure this only happens once even if re-registered.
        static bool ensureDeregister = (atexit(&deregisterProfilerForClient), true);
        CARB_UNUSED(ensureDeregister);
    }
}

} // namespace profiler
} // namespace carb