carb/profiler/Profile.h

File members: carb/profiler/Profile.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 "../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(func)                                                                           \
            [](const char* pfunc) -> const auto&                                                                       \
            {                                                                                                          \
                static auto tup = ::carb::profiler::detail::makeString2(                                               \
                    ::g_carbProfiler->registerStaticString(pfunc), ::g_carbProfiler->registerStaticString(__FILE__));  \
                return tup;                                                                                            \
            }                                                                                                          \
            (func)

#        define CARB_PROFILE_UFUNCFILESTR(func, str)                                                                   \
            [](const char* pfunc, const char* pstr) -> const auto&                                                     \
            {                                                                                                          \
                static auto tup = ::carb::profiler::detail::makeString3(                                               \
                    ::g_carbProfiler->registerStaticString(pfunc), ::g_carbProfiler->registerStaticString(__FILE__),   \
                    ::g_carbProfiler->registerStaticString(pstr));                                                     \
                return tup;                                                                                            \
            }                                                                                                          \
            (func, str)

#        define CARB_PROFILE_FUNCFILE(func)                                                                            \
            [](const char* pfunc) -> const auto&                                                                       \
            {                                                                                                          \
                if (::g_carbProfiler)                                                                                  \
                {                                                                                                      \
                    static auto tup =                                                                                  \
                        ::carb::profiler::detail::makeString2(::g_carbProfiler->registerStaticString(pfunc),           \
                                                              ::g_carbProfiler->registerStaticString(__FILE__));       \
                    return tup;                                                                                        \
                }                                                                                                      \
                return ::carb::profiler::detail::emptyTuple2();                                                        \
            }                                                                                                          \
            (func)

#        define CARB_PROFILE_FUNCFILESTR(func, str)                                                                    \
            [](const char* pfunc, const char* pstr) -> const auto&                                                     \
            {                                                                                                          \
                if (::g_carbProfiler)                                                                                  \
                {                                                                                                      \
                    static auto tup =                                                                                  \
                        ::carb::profiler::detail::makeString3(::g_carbProfiler->registerStaticString(pfunc),           \
                                                              ::g_carbProfiler->registerStaticString(__FILE__),        \
                                                              ::g_carbProfiler->registerStaticString(pstr));           \
                    return tup;                                                                                        \
                }                                                                                                      \
                return ::carb::profiler::detail::emptyTuple3();                                                        \
            }                                                                                                          \
            (func, str)

#        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(
    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 ::g_carbProfiler->beginDynamic(mask, tup.first, tup.second, line, fmt, std::forward<Args>(args)...);
}
template <class... Args>
carb::profiler::ZoneId beginDynamicHelper(const carb::profiler::Channel& channel,
                                          const ::carb::profiler::detail::String2& tup,
                                          int line,
                                          const char* fmt,
                                          Args&&... args)
{
    if (!channel.isEnabled())
        return kNoZoneId;
    return ::g_carbProfiler->beginDynamic(
        channel.getMask(), tup.first, tup.second, line, fmt, std::forward<Args>(args)...);
}
inline carb::profiler::ZoneId beginStaticHelper(const uint64_t mask, const ::carb::profiler::detail::String3& tup, int line)
{
    if (!CARB_PROFILE_CHECKMASK(mask))
        return kNoZoneId;
    return ::g_carbProfiler->beginStatic(mask, tup.first, tup.second, line, tup.third);
}
inline carb::profiler::ZoneId beginStaticHelper(const carb::profiler::Channel& channel,
                                                const ::carb::profiler::detail::String3& tup,
                                                int line)
{
    if (!channel.isEnabled())
        return kNoZoneId;
    return ::g_carbProfiler->beginStatic(channel.getMask(), tup.first, tup.second, line, tup.third);
}
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()                                                                                     \
        do                                                                                                             \
        {                                                                                                              \
            if (::g_carbProfiler)                                                                                      \
            {                                                                                                          \
                ::g_carbProfiler->startup();                                                                           \
            }                                                                                                          \
        } while (0)

#    define CARB_PROFILE_SHUTDOWN()                                                                                    \
        do                                                                                                             \
        {                                                                                                              \
            if (::g_carbProfiler)                                                                                      \
            {                                                                                                          \
                ::g_carbProfiler->shutdown();                                                                          \
            }                                                                                                          \
        } while (0)

#    define CARB_PROFILE_REGISTER_STRING(str)                                                                          \
        [](const char* pstr) {                                                                                         \
            if (::g_carbProfiler)                                                                                      \
            {                                                                                                          \
                static ::carb::profiler::StaticStringType p = ::g_carbProfiler->registerStaticString(pstr);            \
                return p;                                                                                              \
            }                                                                                                          \
            return ::carb::profiler::kInvalidStaticString;                                                             \
        }(str)

#    define CARB_PROFILE_SET_CAPTURE_MASK(mask)                                                                        \
        do                                                                                                             \
        {                                                                                                              \
            if (::g_carbProfiler)                                                                                      \
            {                                                                                                          \
                ::g_carbProfiler->setCaptureMask(mask);                                                                \
            }                                                                                                          \
        } while (0)

#    define CARB_PROFILE_BEGIN(maskOrChannel, eventName, ...)                                                          \
        ::g_carbProfiler ?                                                                                             \
            CARB_PROFILE_IF(CARB_PROFILE_HAS_VARARGS(eventName, ##__VA_ARGS__),                                        \
                            ::carb::profiler::detail::beginDynamicHelper(                                              \
                                maskOrChannel, CARB_PROFILE_UFUNCFILE(__func__), __LINE__, eventName, ##__VA_ARGS__),  \
                            ::carb::profiler::detail::beginStaticHelper(                                               \
                                maskOrChannel, CARB_PROFILE_UFUNCFILESTR(__func__, eventName), __LINE__)) :            \
            (0 ? /*compiler validate*/ printf(eventName, ##__VA_ARGS__) : 0)

#    define CARB_PROFILE_END(maskOrChannel, ...)                                                                       \
        do                                                                                                             \
        {                                                                                                              \
            if (::g_carbProfiler)                                                                                      \
            {                                                                                                          \
                ::g_carbProfiler->CARB_PROFILE_IF(                                                                     \
                    CARB_PROFILE_HAS_VARARGS(maskOrChannel, ##__VA_ARGS__),                                            \
                    endEx(::carb::profiler::detail::maskHelper(maskOrChannel), ##__VA_ARGS__),                         \
                    end(::carb::profiler::detail::maskHelper(maskOrChannel)));                                         \
            }                                                                                                          \
        } while (0)

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

#    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, ...)                                                   \
        do                                                                                                             \
        {                                                                                                              \
            /* Use printf to validate the format string */                                                             \
            if (0)                                                                                                     \
            {                                                                                                          \
                printf(valueName, ##__VA_ARGS__);                                                                      \
            }                                                                                                          \
            if (::g_carbProfiler && ::carb::profiler::detail::enabled(maskOrChannel))                                  \
            {                                                                                                          \
                CARB_PROFILE_IF(                                                                                       \
                    CARB_PROFILE_HAS_VARARGS(valueName, ##__VA_ARGS__),                                                \
                    ::g_carbProfiler->valueDynamic(                                                                    \
                        ::carb::profiler::detail::maskHelper(maskOrChannel), value, valueName, ##__VA_ARGS__),         \
                    ::g_carbProfiler->valueStatic(::carb::profiler::detail::maskHelper(maskOrChannel), value, []() {   \
                        static ::carb::profiler::StaticStringType p =                                                  \
                            ::g_carbProfiler->registerStaticString("" valueName);                                      \
                        return p;                                                                                      \
                    }()));                                                                                             \
            }                                                                                                          \
        } while (0)

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

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

#    define CARB_PROFILE_ALLOC(maskOrChannel, ptr, size)                                                               \
        do                                                                                                             \
        {                                                                                                              \
            if (::g_carbProfiler && ::carb::profiler::detail::enabled(maskOrChannel))                                  \
            {                                                                                                          \
                ::g_carbProfiler->allocStatic(::carb::profiler::detail::maskHelper(maskOrChannel), ptr, size);         \
            }                                                                                                          \
        } while (0)

#    define CARB_PROFILE_FREE(maskOrChannel, ptr)                                                                      \
        do                                                                                                             \
        {                                                                                                              \
            if (::g_carbProfiler && ::carb::profiler::detail::enabled(maskOrChannel))                                  \
            {                                                                                                          \
                ::g_carbProfiler->freeStatic(::carb::profiler::detail::maskHelper(maskOrChannel), ptr);                \
            }                                                                                                          \
        } while (0)

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

#    define CARB_PROFILE_EVENT(maskOrChannel, type, name, ...)                                                           \
        do                                                                                                               \
        {                                                                                                                \
            if (0)                                                                                                       \
                printf((name), ##__VA_ARGS__);                                                                           \
            if (::g_carbProfiler && ::carb::profiler::detail::enabled(maskOrChannel))                                    \
            {                                                                                                            \
                CARB_PROFILE_IF(                                                                                         \
                    CARB_PROFILE_HAS_VARARGS(name, ##__VA_ARGS__),                                                       \
                    static auto tup = ::carb::profiler::detail::makeString2(                                             \
                        ::g_carbProfiler->registerStaticString(CARB_PRETTY_FUNCTION),                                    \
                        ::g_carbProfiler->registerStaticString(__FILE__));                                               \
                    ::g_carbProfiler->emitInstantDynamic(::carb::profiler::detail::maskHelper(maskOrChannel), tup.first, \
                                                         tup.second, __LINE__, (type), (name), ##__VA_ARGS__),           \
                    static auto tup = ::carb::profiler::detail::makeString3(                                             \
                        ::g_carbProfiler->registerStaticString(CARB_PRETTY_FUNCTION),                                    \
                        ::g_carbProfiler->registerStaticString(__FILE__), ::g_carbProfiler->registerStaticString(name)); \
                    ::g_carbProfiler->emitInstantStatic(::carb::profiler::detail::maskHelper(maskOrChannel),             \
                                                        tup.first, tup.second, __LINE__, (type), tup.third));            \
            }                                                                                                            \
        } while (0)

#    define CARB_PROFILE_FLOW_BEGIN(maskOrChannel, id, name, ...)                                                      \
        do                                                                                                             \
        {                                                                                                              \
            if (0)                                                                                                     \
                printf((name), ##__VA_ARGS__);                                                                         \
            if (::g_carbProfiler && ::carb::profiler::detail::enabled(maskOrChannel))                                  \
            {                                                                                                          \
                CARB_PROFILE_IF(CARB_PROFILE_HAS_VARARGS(name, ##__VA_ARGS__),                                         \
                                static auto tup = ::carb::profiler::detail::makeString2(                               \
                                    ::g_carbProfiler->registerStaticString(CARB_PRETTY_FUNCTION),                      \
                                    ::g_carbProfiler->registerStaticString(__FILE__));                                 \
                                ::g_carbProfiler->emitFlowDynamic(                                                     \
                                    ::carb::profiler::detail::maskHelper(maskOrChannel), tup.first, tup.second,        \
                                    __LINE__, ::carb::profiler::FlowType::Begin, (id), (name), ##__VA_ARGS__),         \
                                static auto tup = ::carb::profiler::detail::makeString3(                               \
                                    ::g_carbProfiler->registerStaticString(CARB_PRETTY_FUNCTION),                      \
                                    ::g_carbProfiler->registerStaticString(__FILE__),                                  \
                                    ::g_carbProfiler->registerStaticString("" name));                                  \
                                ::g_carbProfiler->emitFlowStatic(::carb::profiler::detail::maskHelper(maskOrChannel),  \
                                                                 tup.first, tup.second, __LINE__,                      \
                                                                 ::carb::profiler::FlowType::Begin, (id), tup.third)); \
            }                                                                                                          \
        } while (0)

#    define CARB_PROFILE_FLOW_END(maskOrChannel, id)                                                                    \
        do                                                                                                              \
        {                                                                                                               \
            if (::g_carbProfiler && ::carb::profiler::detail::enabled(maskOrChannel))                                   \
            {                                                                                                           \
                static auto tup =                                                                                       \
                    ::carb::profiler::detail::makeString2(::g_carbProfiler->registerStaticString(CARB_PRETTY_FUNCTION), \
                                                          ::g_carbProfiler->registerStaticString(__FILE__));            \
                ::g_carbProfiler->emitFlowStatic(::carb::profiler::detail::maskHelper(maskOrChannel), tup.first,        \
                                                 tup.second, __LINE__, ::carb::profiler::FlowType::End, (id),           \
                                                 ::carb::profiler::kInvalidStaticString);                               \
            }                                                                                                           \
        } while (0)

#    define CARB_PROFILE_CREATE_GPU_CONTEXT(                                                                           \
        name, correlatedCpuTimestampNs, correlatedGpuTimestamp, gpuTimestampPeriodNs, graphicApi)                      \
        (::g_carbProfiler ? ::g_carbProfiler->createGpuContext(name, correlatedCpuTimestampNs, correlatedGpuTimestamp, \
                                                               gpuTimestampPeriodNs, graphicApi) :                     \
                            carb::profiler::kInvalidGpuContextId)

#    define CARB_PROFILE_DESTROY_GPU_CONTEXT(contextId)                                                                \
        do                                                                                                             \
        {                                                                                                              \
            if (::g_carbProfiler)                                                                                      \
            {                                                                                                          \
                ::g_carbProfiler->destroyGpuContext(contextId);                                                        \
            }                                                                                                          \
        } while (0)

#    define CARB_PROFILE_CALIBRATE_GPU_CONTEXT(                                                                        \
        contextId, correlatedCpuTimestampNs, previousCorrelatedCpuTimestampNs, correlatedGpuTimestamp)                 \
        ((::g_carbProfiler) ?                                                                                          \
             (::g_carbProfiler->calibrateGpuContext(                                                                   \
                 contextId, correlatedCpuTimestampNs, previousCorrelatedCpuTimestampNs, correlatedGpuTimestamp)) :     \
             false)

#    define CARB_PROFILE_GPU_QUERY_BEGIN(maskOrChannel, contextId, queryId, eventName, ...)                            \
        do                                                                                                             \
        {                                                                                                              \
            if (0)                                                                                                     \
                printf((eventName), ##__VA_ARGS__);                                                                    \
            if (::g_carbProfiler && ::carb::profiler::detail::enabled(maskOrChannel))                                  \
            {                                                                                                          \
                CARB_PROFILE_IF(CARB_PROFILE_HAS_VARARGS(eventName, ##__VA_ARGS__),                                    \
                                static auto tup = ::carb::profiler::detail::makeString2(                               \
                                    ::g_carbProfiler->registerStaticString(CARB_PRETTY_FUNCTION),                      \
                                    ::g_carbProfiler->registerStaticString(__FILE__));                                 \
                                ::g_carbProfiler->beginGpuQueryDynamic(                                                \
                                    ::carb::profiler::detail::maskHelper(maskOrChannel), tup.first, tup.second,        \
                                    __LINE__, contextId, queryId, eventName, ##__VA_ARGS__),                           \
                                static auto tup = ::carb::profiler::detail::makeString3(                               \
                                    ::g_carbProfiler->registerStaticString(CARB_PRETTY_FUNCTION),                      \
                                    ::g_carbProfiler->registerStaticString(__FILE__),                                  \
                                    ::g_carbProfiler->registerStaticString("" eventName));                             \
                                ::g_carbProfiler->beginGpuQueryStatic(                                                 \
                                    ::carb::profiler::detail::maskHelper(maskOrChannel), tup.first, tup.second,        \
                                    __LINE__, contextId, queryId, tup.third));                                         \
            }                                                                                                          \
        } while (0)

#    define CARB_PROFILE_GPU_QUERY_END(maskOrChannel, contextId, queryId)                                               \
        do                                                                                                              \
        {                                                                                                               \
            if (::g_carbProfiler && ::carb::profiler::detail::enabled(maskOrChannel))                                   \
            {                                                                                                           \
                ::g_carbProfiler->endGpuQuery(::carb::profiler::detail::maskHelper(maskOrChannel), contextId, queryId); \
            }                                                                                                           \
        } while (0)

#    define CARB_PROFILE_GPU_SET_QUERY_VALUE(maskOrChannel, contextId, queryId, gpuTimestamp)                          \
        do                                                                                                             \
        {                                                                                                              \
            if (::g_carbProfiler && ::carb::profiler::detail::enabled(maskOrChannel))                                  \
            {                                                                                                          \
                ::g_carbProfiler->setGpuQueryValue(                                                                    \
                    ::carb::profiler::detail::maskHelper(maskOrChannel), contextId, queryId, gpuTimestamp);            \
            }                                                                                                          \
        } while (0)

#    define CARB_PROFILE_LOCKABLE_CREATE(maskOrChannel, isSharedLock, name)                                              \
        [](bool enabled, const uint64_t maskParam, const bool isSharedLockParam, const char* nameParam,                  \
           const char* function) {                                                                                       \
            if (::g_carbProfiler && enabled)                                                                             \
            {                                                                                                            \
                static auto tup = ::carb::profiler::detail::makeString2(                                                 \
                    ::g_carbProfiler->registerStaticString(function), ::g_carbProfiler->registerStaticString(__FILE__)); \
                return ::g_carbProfiler->createLockable(                                                                 \
                    maskParam, nameParam, isSharedLockParam, tup.first, tup.second, __LINE__);                           \
            }                                                                                                            \
            return ::carb::profiler::kInvalidLockableId;                                                                 \
        }(::carb::profiler::detail::enabled(maskOrChannel), ::carb::profiler::detail::maskHelper(maskOrChannel),         \
          (isSharedLock), (name), CARB_PRETTY_FUNCTION)

#    define CARB_PROFILE_LOCKABLE_DESTROY(lockableId)                                                                  \
        do                                                                                                             \
        {                                                                                                              \
            if (::g_carbProfiler && lockableId != carb::profiler::kInvalidLockableId)                                  \
            {                                                                                                          \
                ::g_carbProfiler->destroyLockable((lockableId));                                                       \
            }                                                                                                          \
        } while (0)

#    define CARB_PROFILE_LOCKABLE_OPERATION(lockableId, operation)                                                     \
        do                                                                                                             \
        {                                                                                                              \
            if (::g_carbProfiler && lockableId != carb::profiler::kInvalidLockableId)                                  \
            {                                                                                                          \
                ::g_carbProfiler->lockableOperation((lockableId), (operation));                                        \
            }                                                                                                          \
        } while (0)

#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*)
{
    cpp::atomic_ref<IProfiler*>(g_carbProfiler).store(nullptr); // sequentially consistent
    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);
            cpp::atomic_ref<IProfiler*>(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()
{
    return g_carbProfiler;
}

inline void deregisterProfilerForClient() noexcept
{
    if (std::exchange(detail::registered(), false))
    {
        auto fw = getFramework();
        auto handle = std::exchange(detail::loadHookHandle(), kInvalidLoadHook);
        IProfiler* profiler = cpp::atomic_ref<IProfiler*>(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);
        }
        cpp::atomic_ref<IProfiler*>(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