2022-12-21 10:24:11 +00:00
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#if FC_PROFILER
|
|
|
|
|
|
|
|
#include "types.h"
|
|
|
|
#include <vector>
|
|
|
|
#include <string>
|
|
|
|
#include <chrono>
|
|
|
|
#include <thread>
|
|
|
|
#include <mutex>
|
|
|
|
|
|
|
|
#ifndef __PRETTY_FUNCTION__
|
|
|
|
#define __PRETTY_FUNCTION__ __FUNCSIG__
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#define FC_PROFILE_SCOPE_RESERVE_SIZE 128
|
|
|
|
#define FC_PROFILE_HISTORY_MAX_SIZE 512
|
|
|
|
|
|
|
|
namespace fc_profiler
|
|
|
|
{
|
|
|
|
struct ProfileSection
|
|
|
|
{
|
|
|
|
ProfileSection()
|
|
|
|
: function(nullptr)
|
|
|
|
, file(nullptr)
|
|
|
|
, line(0)
|
|
|
|
, scope(0)
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
ProfileSection(const char* _function, const char* _file, u32 _line, u32 _scope)
|
|
|
|
: function(_function)
|
|
|
|
, file(_file)
|
|
|
|
, line(_line)
|
|
|
|
, scope(_scope)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
const char* function;
|
|
|
|
const char* file;
|
|
|
|
u32 line;
|
|
|
|
u32 scope;
|
|
|
|
std::chrono::steady_clock::time_point start;
|
|
|
|
std::chrono::steady_clock::time_point end;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ProfileThread
|
|
|
|
{
|
|
|
|
ProfileThread()
|
|
|
|
{
|
|
|
|
startTicks = std::chrono::high_resolution_clock::now();
|
|
|
|
level = 0;
|
|
|
|
historyIdx = 0;
|
|
|
|
cachedTime = 0.0;
|
|
|
|
scopes.reserve(FC_PROFILE_SCOPE_RESERVE_SIZE);
|
|
|
|
memset(history, 0, sizeof(history));
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<ProfileSection> scopes;
|
|
|
|
std::chrono::steady_clock::time_point startTicks;
|
|
|
|
std::chrono::steady_clock::time_point endTicks;
|
|
|
|
double history[FC_PROFILE_HISTORY_MAX_SIZE];
|
|
|
|
u32 level;
|
|
|
|
u32 historyIdx;
|
|
|
|
std::thread::id threadId;
|
|
|
|
std::string threadName;
|
|
|
|
|
|
|
|
struct ResultNode
|
|
|
|
{
|
|
|
|
ResultNode() : parent(nullptr) {}
|
|
|
|
ProfileSection section;
|
|
|
|
ResultNode* parent;
|
|
|
|
std::vector<ResultNode> children;
|
|
|
|
};
|
|
|
|
|
|
|
|
double cachedTime;
|
|
|
|
std::vector<ResultNode> cachedResultTree;
|
|
|
|
static std::vector<ProfileThread*> s_allThreads;
|
|
|
|
static std::recursive_mutex s_allThreadsLock;
|
|
|
|
};
|
|
|
|
|
|
|
|
struct ProfileScope
|
|
|
|
{
|
|
|
|
ProfileScope(const char* function, const char* file, int line)
|
|
|
|
: sectionIdx(0)
|
|
|
|
{
|
|
|
|
if (s_thread)
|
|
|
|
{
|
|
|
|
ProfileSection section(function, file, line, s_thread->level++);
|
|
|
|
section.start = std::chrono::high_resolution_clock::now();
|
|
|
|
sectionIdx = s_thread->scopes.size();
|
|
|
|
s_thread->scopes.push_back(section);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
~ProfileScope()
|
|
|
|
{
|
|
|
|
if (s_thread)
|
|
|
|
{
|
|
|
|
s_thread->scopes[sectionIdx].end = std::chrono::high_resolution_clock::now();
|
|
|
|
s_thread->level--;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
size_t sectionIdx;
|
|
|
|
static thread_local ProfileThread* s_thread;
|
|
|
|
};
|
|
|
|
|
|
|
|
void startThread(const std::string& threadName);
|
|
|
|
void endThread(double warningTime = 0.0);
|
|
|
|
void drawGUI(const std::vector<ProfileThread::ResultNode>& results);
|
2022-12-23 15:07:35 +00:00
|
|
|
void drawGraph(const ProfileThread& profileThread);
|
2022-12-21 10:24:11 +00:00
|
|
|
void outputTTY(const std::vector<ProfileThread::ResultNode>& results);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define FC_PROFILE_SCOPE \
|
|
|
|
fc_profiler::ProfileScope __profile__scope(__PRETTY_FUNCTION__, __FILE__, __LINE__);
|
|
|
|
|
|
|
|
#define FC_PROFILE_SCOPE_NAMED(name) \
|
|
|
|
fc_profiler::ProfileScope __profile__scope(name, __FILE__, __LINE__);
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
namespace fc_profiler
|
|
|
|
{
|
2023-01-02 15:39:34 +00:00
|
|
|
inline static void startThread(const std::string& threadName) {}
|
|
|
|
inline static void endThread(float warningTime = 0.0) {}
|
2022-12-21 10:24:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
#define FC_PROFILE_SCOPE
|
|
|
|
#define FC_PROFILE_SCOPE_NAMED(name)
|
|
|
|
|
|
|
|
#endif
|