Backlog/lib/zphysics/libs/Jolt/Core/Profiler.h

267 lines
8.9 KiB
C++

// Jolt Physics Library (https://github.com/jrouwe/JoltPhysics)
// SPDX-FileCopyrightText: 2021 Jorrit Rouwe
// SPDX-License-Identifier: MIT
#pragma once
JPH_SUPPRESS_WARNINGS_STD_BEGIN
#include <mutex>
JPH_SUPPRESS_WARNINGS_STD_END
#include <Jolt/Core/NonCopyable.h>
#include <Jolt/Core/TickCounter.h>
#include <Jolt/Core/UnorderedMap.h>
#if defined(JPH_EXTERNAL_PROFILE)
JPH_NAMESPACE_BEGIN
/// Create this class on the stack to start sampling timing information of a particular scope.
///
/// Left unimplemented intentionally. Needs to be implemented by the user of the library.
/// On construction a measurement should start, on destruction it should be stopped.
class alignas(16) ExternalProfileMeasurement : public NonCopyable
{
public:
/// Constructor
ExternalProfileMeasurement(const char *inName, uint32 inColor = 0);
~ExternalProfileMeasurement();
private:
uint8 mUserData[64];
};
JPH_NAMESPACE_END
//////////////////////////////////////////////////////////////////////////////////////////
// Macros to do the actual profiling
//////////////////////////////////////////////////////////////////////////////////////////
JPH_SUPPRESS_WARNING_PUSH
JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
// Dummy implementations
#define JPH_PROFILE_THREAD_START(name)
#define JPH_PROFILE_THREAD_END()
#define JPH_PROFILE_NEXTFRAME()
#define JPH_PROFILE_DUMP(...)
// Scope profiling measurement
#define JPH_PROFILE_TAG2(line) profile##line
#define JPH_PROFILE_TAG(line) JPH_PROFILE_TAG2(line)
/// Macro to collect profiling information.
///
/// Usage:
///
/// {
/// JPH_PROFILE("Operation");
/// do operation;
/// }
///
#define JPH_PROFILE(...) ExternalProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__)
// Scope profiling for function
#define JPH_PROFILE_FUNCTION() JPH_PROFILE(JPH_FUNCTION_NAME)
JPH_SUPPRESS_WARNING_POP
#elif defined(JPH_PROFILE_ENABLED)
JPH_NAMESPACE_BEGIN
class ProfileSample;
class ProfileThread;
/// Singleton class for managing profiling information
class Profiler : public NonCopyable
{
public:
JPH_OVERRIDE_NEW_DELETE
/// Increments the frame counter to provide statistics per frame
void NextFrame();
/// Dump profiling statistics at the start of the next frame
/// @param inTag If not empty, this overrides the auto incrementing number in the filename of the dump file
void Dump(const string_view &inTag = string_view());
/// Add a thread to be instrumented
void AddThread(ProfileThread *inThread);
/// Remove a thread from being instrumented
void RemoveThread(ProfileThread *inThread);
/// Singleton instance
static Profiler * sInstance;
private:
/// Helper class to freeze ProfileSamples per thread while processing them
struct ThreadSamples
{
String mThreadName;
ProfileSample * mSamplesBegin;
ProfileSample * mSamplesEnd;
};
/// Helper class to aggregate ProfileSamples
class Aggregator
{
public:
/// Constructor
Aggregator(const char *inName) : mName(inName) { }
/// Accumulate results for a measurement
void AccumulateMeasurement(uint64 inCyclesInCallWithChildren, uint64 inCyclesInChildren)
{
mCallCounter++;
mTotalCyclesInCallWithChildren += inCyclesInCallWithChildren;
mTotalCyclesInChildren += inCyclesInChildren;
mMinCyclesInCallWithChildren = min(inCyclesInCallWithChildren, mMinCyclesInCallWithChildren);
mMaxCyclesInCallWithChildren = max(inCyclesInCallWithChildren, mMaxCyclesInCallWithChildren);
}
/// Sort descending by total cycles
bool operator < (const Aggregator &inRHS) const
{
return mTotalCyclesInCallWithChildren > inRHS.mTotalCyclesInCallWithChildren;
}
/// Identification
const char * mName; ///< User defined name of this item
/// Statistics
uint32 mCallCounter = 0; ///< Number of times AccumulateMeasurement was called
uint64 mTotalCyclesInCallWithChildren = 0; ///< Total amount of cycles spent in this scope
uint64 mTotalCyclesInChildren = 0; ///< Total amount of cycles spent in children of this scope
uint64 mMinCyclesInCallWithChildren = 0xffffffffffffffffUL; ///< Minimum amount of cycles spent per call
uint64 mMaxCyclesInCallWithChildren = 0; ///< Maximum amount of cycles spent per call
};
using Threads = Array<ThreadSamples>;
using Aggregators = Array<Aggregator>;
using KeyToAggregator = UnorderedMap<const char *, size_t>;
/// Helper function to aggregate profile sample data
static void sAggregate(int inDepth, uint32 inColor, ProfileSample *&ioSample, const ProfileSample *inEnd, Aggregators &ioAggregators, KeyToAggregator &ioKeyToAggregator);
/// Dump profiling statistics
void DumpInternal();
void DumpList(const char *inTag, const Aggregators &inAggregators);
void DumpChart(const char *inTag, const Threads &inThreads, const KeyToAggregator &inKeyToAggregators, const Aggregators &inAggregators);
std::mutex mLock; ///< Lock that protects mThreads
Array<ProfileThread *> mThreads; ///< List of all active threads
bool mDump = false; ///< When true, the samples are dumped next frame
String mDumpTag; ///< When not empty, this overrides the auto incrementing number of the dump filename
};
// Class that contains the information of a single scoped measurement
class alignas(16) ProfileSample : public NonCopyable
{
public:
JPH_OVERRIDE_NEW_DELETE
const char * mName; ///< User defined name of this item
uint32 mColor; ///< Color to use for this sample
uint8 mDepth; ///< Calculated depth
uint8 mUnused[3];
uint64 mStartCycle; ///< Cycle counter at start of measurement
uint64 mEndCycle; ///< Cycle counter at end of measurement
};
/// Collects all samples of a single thread
class ProfileThread : public NonCopyable
{
public:
JPH_OVERRIDE_NEW_DELETE
/// Constructor
inline ProfileThread(const string_view &inThreadName);
inline ~ProfileThread();
static const uint cMaxSamples = 65536;
String mThreadName; ///< Name of the thread that we're collecting information for
ProfileSample mSamples[cMaxSamples]; ///< Buffer of samples
uint mCurrentSample = 0; ///< Next position to write a sample to
static thread_local ProfileThread *sInstance;
};
/// Create this class on the stack to start sampling timing information of a particular scope
class ProfileMeasurement : public NonCopyable
{
public:
/// Constructor
inline ProfileMeasurement(const char *inName, uint32 inColor = 0);
inline ~ProfileMeasurement();
private:
ProfileSample * mSample;
ProfileSample mTemp;
static bool sOutOfSamplesReported;
};
JPH_NAMESPACE_END
#include "Profiler.inl"
//////////////////////////////////////////////////////////////////////////////////////////
// Macros to do the actual profiling
//////////////////////////////////////////////////////////////////////////////////////////
JPH_SUPPRESS_WARNING_PUSH
JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
/// Start instrumenting program
#define JPH_PROFILE_START(name) do { Profiler::sInstance = new Profiler; JPH_PROFILE_THREAD_START(name); } while (false)
/// End instrumenting program
#define JPH_PROFILE_END() do { JPH_PROFILE_THREAD_END(); delete Profiler::sInstance; Profiler::sInstance = nullptr; } while (false)
/// Start instrumenting a thread
#define JPH_PROFILE_THREAD_START(name) do { if (Profiler::sInstance) ProfileThread::sInstance = new ProfileThread(name); } while (false)
/// End instrumenting a thread
#define JPH_PROFILE_THREAD_END() do { delete ProfileThread::sInstance; ProfileThread::sInstance = nullptr; } while (false)
/// Scope profiling measurement
#define JPH_PROFILE_TAG2(line) profile##line
#define JPH_PROFILE_TAG(line) JPH_PROFILE_TAG2(line)
#define JPH_PROFILE(...) ProfileMeasurement JPH_PROFILE_TAG(__LINE__)(__VA_ARGS__)
/// Scope profiling for function
#define JPH_PROFILE_FUNCTION() JPH_PROFILE(JPH_FUNCTION_NAME)
/// Update frame counter
#define JPH_PROFILE_NEXTFRAME() Profiler::sInstance->NextFrame()
/// Dump profiling info
#define JPH_PROFILE_DUMP(...) Profiler::sInstance->Dump(__VA_ARGS__)
JPH_SUPPRESS_WARNING_POP
#else
//////////////////////////////////////////////////////////////////////////////////////////
// Dummy profiling instructions
//////////////////////////////////////////////////////////////////////////////////////////
JPH_SUPPRESS_WARNING_PUSH
JPH_CLANG_SUPPRESS_WARNING("-Wc++98-compat-pedantic")
#define JPH_PROFILE_START(name)
#define JPH_PROFILE_END()
#define JPH_PROFILE_THREAD_START(name)
#define JPH_PROFILE_THREAD_END()
#define JPH_PROFILE(...)
#define JPH_PROFILE_FUNCTION()
#define JPH_PROFILE_NEXTFRAME()
#define JPH_PROFILE_DUMP(...)
JPH_SUPPRESS_WARNING_POP
#endif