Add a statistical profiler. Also applied a bugfix b3Profiler.
- This is a compact hierarchical profiler which also stores node statistics - Might need to use a hash-table to lookup statistic for node because each frame trees are build
This commit is contained in:
parent
ff535f9f7b
commit
1ef6d46d33
@ -105,8 +105,12 @@ static void Run()
|
|||||||
{
|
{
|
||||||
g_profiler->Begin();
|
g_profiler->Begin();
|
||||||
|
|
||||||
|
g_profilerSt->Begin();
|
||||||
|
|
||||||
g_profiler->BeginScope("Frame");
|
g_profiler->BeginScope("Frame");
|
||||||
|
|
||||||
|
g_profilerSt->BeginScope("Frame");
|
||||||
|
|
||||||
g_view->BeginInterface();
|
g_view->BeginInterface();
|
||||||
|
|
||||||
if (g_model->IsPaused())
|
if (g_model->IsPaused())
|
||||||
@ -122,6 +126,8 @@ static void Run()
|
|||||||
|
|
||||||
g_model->Update();
|
g_model->Update();
|
||||||
|
|
||||||
|
g_profilerSt->EndScope();
|
||||||
|
|
||||||
g_profiler->EndScope();
|
g_profiler->EndScope();
|
||||||
|
|
||||||
if (g_settings->drawProfileTree)
|
if (g_settings->drawProfileTree)
|
||||||
@ -129,20 +135,13 @@ static void Run()
|
|||||||
g_view->InterfaceProfileTree();
|
g_view->InterfaceProfileTree();
|
||||||
}
|
}
|
||||||
|
|
||||||
g_profilerRecorder->BuildRecords();
|
if (g_settings->drawProfileTreeStats)
|
||||||
|
|
||||||
if (g_settings->drawProfile)
|
|
||||||
{
|
{
|
||||||
b3StackArray<ProfilerRecord*, 256> records;
|
g_view->InterfaceProfileTreeStats();
|
||||||
g_profilerRecorder->BuildSortedRecords(records);
|
|
||||||
|
|
||||||
for (u32 i = 0; i < records.Count(); ++i)
|
|
||||||
{
|
|
||||||
ProfilerRecord* r = records[i];
|
|
||||||
g_draw->DrawString(b3Color_white, "%s %.4f (min = %.4f) (max = %.4f) (calls = %d) [ms]", r->name, r->elapsed, r->minElapsed, r->maxElapsed, r->callCount);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
g_profilerSt->End();
|
||||||
|
|
||||||
g_profiler->End();
|
g_profiler->End();
|
||||||
|
|
||||||
g_view->EndInterface();
|
g_view->EndInterface();
|
||||||
|
@ -26,7 +26,7 @@ Model::Model()
|
|||||||
g_draw = &m_draw;
|
g_draw = &m_draw;
|
||||||
g_camera = &m_camera;
|
g_camera = &m_camera;
|
||||||
g_profiler = &m_profiler;
|
g_profiler = &m_profiler;
|
||||||
g_profilerRecorder = &m_profilerRecorder;
|
g_profilerSt = &m_profilerSt;
|
||||||
|
|
||||||
#if (PROFILE_JSON == 1)
|
#if (PROFILE_JSON == 1)
|
||||||
g_profilerListener = &m_jsonListener;
|
g_profilerListener = &m_jsonListener;
|
||||||
@ -56,7 +56,7 @@ Model::~Model()
|
|||||||
g_draw = nullptr;
|
g_draw = nullptr;
|
||||||
g_camera = nullptr;
|
g_camera = nullptr;
|
||||||
g_profiler = nullptr;
|
g_profiler = nullptr;
|
||||||
g_profilerRecorder = nullptr;
|
g_profilerSt = nullptr;
|
||||||
|
|
||||||
#if (PROFILE_JSON == 1)
|
#if (PROFILE_JSON == 1)
|
||||||
g_profilerListener = nullptr;
|
g_profilerListener = nullptr;
|
||||||
|
@ -21,7 +21,7 @@
|
|||||||
|
|
||||||
#include <testbed/framework/draw.h>
|
#include <testbed/framework/draw.h>
|
||||||
#include <testbed/framework/profiler.h>
|
#include <testbed/framework/profiler.h>
|
||||||
#include <testbed/framework/profiler_recorder.h>
|
#include <testbed/framework/profiler_st.h>
|
||||||
|
|
||||||
// Set to 1 to write profile events into a .json file. Set to 0 otherwise.
|
// Set to 1 to write profile events into a .json file. Set to 0 otherwise.
|
||||||
#define PROFILE_JSON 0
|
#define PROFILE_JSON 0
|
||||||
@ -71,7 +71,7 @@ private:
|
|||||||
Draw m_draw;
|
Draw m_draw;
|
||||||
Camera m_camera;
|
Camera m_camera;
|
||||||
Profiler m_profiler;
|
Profiler m_profiler;
|
||||||
ProfilerRecorder m_profilerRecorder;
|
ProfilerSt m_profilerSt;
|
||||||
|
|
||||||
#if (PROFILE_JSON == 1)
|
#if (PROFILE_JSON == 1)
|
||||||
JsonProfiler m_jsonListener;
|
JsonProfiler m_jsonListener;
|
||||||
|
@ -127,12 +127,13 @@ void Profiler::End()
|
|||||||
listener->BeginEvents();
|
listener->BeginEvents();
|
||||||
}
|
}
|
||||||
|
|
||||||
RecurseEvents(m_root);
|
if (m_root)
|
||||||
|
{
|
||||||
|
RecurseEvents(m_root);
|
||||||
|
|
||||||
RecurseDestroyNode(m_root);
|
RecurseDestroyNode(m_root);
|
||||||
m_root = nullptr;
|
m_root = nullptr;
|
||||||
|
}
|
||||||
assert(m_root == nullptr);
|
|
||||||
|
|
||||||
if (listener)
|
if (listener)
|
||||||
{
|
{
|
||||||
|
@ -62,8 +62,6 @@ public:
|
|||||||
// Get the root profiler node.
|
// Get the root profiler node.
|
||||||
ProfilerNode* GetRoot() { return m_root; }
|
ProfilerNode* GetRoot() { return m_root; }
|
||||||
private:
|
private:
|
||||||
friend class ProfilerRecorder;
|
|
||||||
|
|
||||||
ProfilerNode* CreateNode();
|
ProfilerNode* CreateNode();
|
||||||
void DestroyNode(ProfilerNode* node);
|
void DestroyNode(ProfilerNode* node);
|
||||||
|
|
||||||
|
@ -1,120 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io
|
|
||||||
*
|
|
||||||
* This software is provided 'as-is', without any express or implied
|
|
||||||
* warranty. In no event will the authors be held liable for any damages
|
|
||||||
* arising from the use of this software.
|
|
||||||
* Permission is granted to anyone to use this software for any purpose,
|
|
||||||
* including commercial applications, and to alter it and redistribute it
|
|
||||||
* freely, subject to the following restrictions:
|
|
||||||
* 1. The origin of this software must not be misrepresented; you must not
|
|
||||||
* claim that you wrote the original software. If you use this software
|
|
||||||
* in a product, an acknowledgment in the product documentation would be
|
|
||||||
* appreciated but is not required.
|
|
||||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
||||||
* misrepresented as being the original software.
|
|
||||||
* 3. This notice may not be removed or altered from any source distribution.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <testbed/framework/profiler_recorder.h>
|
|
||||||
#include <testbed/framework/profiler.h>
|
|
||||||
|
|
||||||
ProfilerRecorder* g_profilerRecorder = nullptr;
|
|
||||||
|
|
||||||
ProfilerRecord* ProfilerRecorder::FindRecord(const char* name)
|
|
||||||
{
|
|
||||||
for (u32 i = 0; i < m_records.Count(); ++i)
|
|
||||||
{
|
|
||||||
ProfilerRecord& r = m_records[i];
|
|
||||||
if (r.name == name)
|
|
||||||
{
|
|
||||||
return &r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProfilerRecorder::RecurseBuildRecords(ProfilerNode* node)
|
|
||||||
{
|
|
||||||
ProfilerRecord* fr = FindRecord(node->name);
|
|
||||||
if (fr)
|
|
||||||
{
|
|
||||||
float64 elapsedTime = node->t1 - node->t0;
|
|
||||||
|
|
||||||
fr->elapsed += elapsedTime;
|
|
||||||
fr->minElapsed = b3Min(fr->minElapsed, elapsedTime);
|
|
||||||
fr->maxElapsed = b3Max(fr->maxElapsed, elapsedTime);
|
|
||||||
++fr->callCount;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
float64 elapsedTime = node->t1 - node->t0;
|
|
||||||
|
|
||||||
ProfilerRecord r;
|
|
||||||
r.name = node->name;
|
|
||||||
r.elapsed = elapsedTime;
|
|
||||||
r.minElapsed = elapsedTime;
|
|
||||||
r.maxElapsed = elapsedTime;
|
|
||||||
r.callCount = 1;
|
|
||||||
|
|
||||||
m_records.PushBack(r);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (u32 i = 0; i < node->children.Count(); ++i)
|
|
||||||
{
|
|
||||||
RecurseBuildRecords(node->children[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProfilerRecorder::BuildRecords()
|
|
||||||
{
|
|
||||||
for (u32 i = 0; i < m_records.Count(); ++i)
|
|
||||||
{
|
|
||||||
m_records[i].elapsed = 0.0;
|
|
||||||
m_records[i].callCount = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
RecurseBuildRecords(g_profiler->m_root);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ProfilerRecord* FindSortedRecord(b3Array<ProfilerRecord*>& records, const char* name)
|
|
||||||
{
|
|
||||||
for (u32 i = 0; i < records.Count(); ++i)
|
|
||||||
{
|
|
||||||
ProfilerRecord* r = records[i];
|
|
||||||
if (r->name == name)
|
|
||||||
{
|
|
||||||
return r;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProfilerRecorder::RecurseBuildSortedRecords(ProfilerNode* node, b3Array<ProfilerRecord*>& output)
|
|
||||||
{
|
|
||||||
ProfilerRecord* fsr = FindSortedRecord(output, node->name);
|
|
||||||
|
|
||||||
if (fsr == nullptr)
|
|
||||||
{
|
|
||||||
ProfilerRecord* fr = FindRecord(node->name);
|
|
||||||
|
|
||||||
assert(fr != nullptr);
|
|
||||||
|
|
||||||
// Push back the first ocurrence of call in calling order
|
|
||||||
output.PushBack(fr);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (u32 i = 0; i < node->children.Count(); ++i)
|
|
||||||
{
|
|
||||||
RecurseBuildSortedRecords(node->children[i], output);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void ProfilerRecorder::BuildSortedRecords(b3Array<ProfilerRecord*>& output)
|
|
||||||
{
|
|
||||||
assert(output.Count() == 0);
|
|
||||||
|
|
||||||
output.Reserve(m_records.Count());
|
|
||||||
|
|
||||||
RecurseBuildSortedRecords(g_profiler->m_root, output);
|
|
||||||
}
|
|
@ -1,58 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io
|
|
||||||
*
|
|
||||||
* This software is provided 'as-is', without any express or implied
|
|
||||||
* warranty. In no event will the authors be held liable for any damages
|
|
||||||
* arising from the use of this software.
|
|
||||||
* Permission is granted to anyone to use this software for any purpose,
|
|
||||||
* including commercial applications, and to alter it and redistribute it
|
|
||||||
* freely, subject to the following restrictions:
|
|
||||||
* 1. The origin of this software must not be misrepresented; you must not
|
|
||||||
* claim that you wrote the original software. If you use this software
|
|
||||||
* in a product, an acknowledgment in the product documentation would be
|
|
||||||
* appreciated but is not required.
|
|
||||||
* 2. Altered source versions must be plainly marked as such, and must not be
|
|
||||||
* misrepresented as being the original software.
|
|
||||||
* 3. This notice may not be removed or altered from any source distribution.
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef PROFILER_RECORDER_H
|
|
||||||
#define PROFILER_RECORDER_H
|
|
||||||
|
|
||||||
#include <bounce/common/math/math.h>
|
|
||||||
#include <bounce/common/template/array.h>
|
|
||||||
|
|
||||||
struct ProfilerNode;
|
|
||||||
|
|
||||||
// A persistent profiler record
|
|
||||||
struct ProfilerRecord
|
|
||||||
{
|
|
||||||
const char* name;
|
|
||||||
float64 elapsed;
|
|
||||||
float64 minElapsed;
|
|
||||||
float64 maxElapsed;
|
|
||||||
u32 callCount;
|
|
||||||
};
|
|
||||||
|
|
||||||
// This class maintains persistent profiler records
|
|
||||||
class ProfilerRecorder
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
void BuildRecords();
|
|
||||||
|
|
||||||
void BuildSortedRecords(b3Array<ProfilerRecord*>& output);
|
|
||||||
|
|
||||||
const b3Array<ProfilerRecord>& GetRecords() const { return m_records; }
|
|
||||||
private:
|
|
||||||
void RecurseBuildRecords(ProfilerNode* node);
|
|
||||||
|
|
||||||
void RecurseBuildSortedRecords(ProfilerNode* node, b3Array<ProfilerRecord*>& output);
|
|
||||||
|
|
||||||
ProfilerRecord* FindRecord(const char* name);
|
|
||||||
|
|
||||||
b3StackArray<ProfilerRecord, 256> m_records; // persistent profiler records
|
|
||||||
};
|
|
||||||
|
|
||||||
extern ProfilerRecorder* g_profilerRecorder;
|
|
||||||
|
|
||||||
#endif
|
|
199
examples/testbed/framework/profiler_st.cpp
Normal file
199
examples/testbed/framework/profiler_st.cpp
Normal file
@ -0,0 +1,199 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <testbed/framework/profiler_st.h>
|
||||||
|
|
||||||
|
ProfilerSt* g_profilerSt = nullptr;
|
||||||
|
|
||||||
|
ProfilerSt::ProfilerSt() : m_pool(sizeof(ProfilerStNode))
|
||||||
|
{
|
||||||
|
m_root = nullptr;
|
||||||
|
m_top = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfilerSt::~ProfilerSt()
|
||||||
|
{
|
||||||
|
if (m_root)
|
||||||
|
{
|
||||||
|
RecurseDestroyNode(m_root);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfilerStNodeStat* ProfilerSt::FindStat(const char* name)
|
||||||
|
{
|
||||||
|
for (u32 i = 0; i < m_stats.Count(); ++i)
|
||||||
|
{
|
||||||
|
if (m_stats[i].name == name)
|
||||||
|
{
|
||||||
|
return &m_stats[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfilerStNodeStat* ProfilerSt::CreateStat()
|
||||||
|
{
|
||||||
|
m_stats.PushBack(ProfilerStNodeStat());
|
||||||
|
return &m_stats.Back();
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfilerStNode* ProfilerSt::CreateNode()
|
||||||
|
{
|
||||||
|
void* block = m_pool.Allocate();
|
||||||
|
return new (block) ProfilerStNode();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfilerSt::DestroyNode(ProfilerStNode* node)
|
||||||
|
{
|
||||||
|
node->~ProfilerStNode();
|
||||||
|
m_pool.Free(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfilerSt::RecurseDestroyNode(ProfilerStNode* node)
|
||||||
|
{
|
||||||
|
for (u32 i = 0; i < node->children.Count(); ++i)
|
||||||
|
{
|
||||||
|
return RecurseDestroyNode(node->children[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
DestroyNode(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
static ProfilerStNode* RecurseFindNode(ProfilerStNode* node, const char* name)
|
||||||
|
{
|
||||||
|
if (node->name == name)
|
||||||
|
{
|
||||||
|
return node;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfilerStNode* result = nullptr;
|
||||||
|
for (u32 i = 0; result == nullptr && i < node->children.Count(); ++i)
|
||||||
|
{
|
||||||
|
result = RecurseFindNode(node->children[i], name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
ProfilerStNode* ProfilerSt::FindNode(const char* name)
|
||||||
|
{
|
||||||
|
if (m_top)
|
||||||
|
{
|
||||||
|
return RecurseFindNode(m_top, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_root)
|
||||||
|
{
|
||||||
|
return RecurseFindNode(m_root, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfilerSt::BeginScope(const char* name)
|
||||||
|
{
|
||||||
|
ProfilerStNode* fn = FindNode(name);
|
||||||
|
|
||||||
|
if (fn)
|
||||||
|
{
|
||||||
|
m_time.Update();
|
||||||
|
fn->t0 = m_time.GetCurrentMilis();
|
||||||
|
++fn->callCount;
|
||||||
|
m_top = fn;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_time.Update();
|
||||||
|
|
||||||
|
ProfilerStNode* n = CreateNode();
|
||||||
|
n->name = name;
|
||||||
|
n->t0 = m_time.GetCurrentMilis();
|
||||||
|
n->elapsed = 0.0f;
|
||||||
|
n->callCount = 1;
|
||||||
|
n->parent = m_top;
|
||||||
|
n->stat = nullptr;
|
||||||
|
|
||||||
|
if (m_root == nullptr)
|
||||||
|
{
|
||||||
|
m_root = n;
|
||||||
|
m_top = n;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_top)
|
||||||
|
{
|
||||||
|
m_top->children.PushBack(n);
|
||||||
|
}
|
||||||
|
|
||||||
|
m_top = n;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfilerSt::EndScope()
|
||||||
|
{
|
||||||
|
assert(m_top != nullptr);
|
||||||
|
|
||||||
|
m_time.Update();
|
||||||
|
|
||||||
|
m_top->t1 = m_time.GetCurrentMilis();
|
||||||
|
|
||||||
|
float64 elapsedTime = m_top->t1 - m_top->t0;
|
||||||
|
|
||||||
|
m_top->elapsed += elapsedTime;
|
||||||
|
|
||||||
|
ProfilerStNodeStat* stat = FindStat(m_top->name);
|
||||||
|
|
||||||
|
if (stat == nullptr)
|
||||||
|
{
|
||||||
|
stat = CreateStat();
|
||||||
|
stat->name = m_top->name;
|
||||||
|
stat->minElapsed = elapsedTime;
|
||||||
|
stat->maxElapsed = elapsedTime;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
stat->minElapsed = b3Min(stat->minElapsed, elapsedTime);
|
||||||
|
stat->maxElapsed = b3Max(stat->maxElapsed, elapsedTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_top->stat == nullptr)
|
||||||
|
{
|
||||||
|
m_top->stat = stat;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert(m_top->stat == stat);
|
||||||
|
|
||||||
|
m_top = m_top->parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfilerSt::Begin()
|
||||||
|
{
|
||||||
|
assert(m_top == nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ProfilerSt::End()
|
||||||
|
{
|
||||||
|
assert(m_top == nullptr);
|
||||||
|
|
||||||
|
if (m_root)
|
||||||
|
{
|
||||||
|
RecurseDestroyNode(m_root);
|
||||||
|
m_root = nullptr;
|
||||||
|
}
|
||||||
|
}
|
94
examples/testbed/framework/profiler_st.h
Normal file
94
examples/testbed/framework/profiler_st.h
Normal file
@ -0,0 +1,94 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2016-2019 Irlan Robson https://irlanrobson.github.io
|
||||||
|
*
|
||||||
|
* This software is provided 'as-is', without any express or implied
|
||||||
|
* warranty. In no event will the authors be held liable for any damages
|
||||||
|
* arising from the use of this software.
|
||||||
|
* Permission is granted to anyone to use this software for any purpose,
|
||||||
|
* including commercial applications, and to alter it and redistribute it
|
||||||
|
* freely, subject to the following restrictions:
|
||||||
|
* 1. The origin of this software must not be misrepresented; you must not
|
||||||
|
* claim that you wrote the original software. If you use this software
|
||||||
|
* in a product, an acknowledgment in the product documentation would be
|
||||||
|
* appreciated but is not required.
|
||||||
|
* 2. Altered source versions must be plainly marked as such, and must not be
|
||||||
|
* misrepresented as being the original software.
|
||||||
|
* 3. This notice may not be removed or altered from any source distribution.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef PROFILER_ST_H
|
||||||
|
#define PROFILER_ST_H
|
||||||
|
|
||||||
|
#include <bounce/common/math/math.h>
|
||||||
|
#include <bounce/common/memory/block_pool.h>
|
||||||
|
#include <bounce/common/template/array.h>
|
||||||
|
#include <bounce/common/time.h>
|
||||||
|
|
||||||
|
// Profiler tree node statistics
|
||||||
|
struct ProfilerStNodeStat
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
float64 minElapsed;
|
||||||
|
float64 maxElapsed;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A profiler tree node
|
||||||
|
struct ProfilerStNode
|
||||||
|
{
|
||||||
|
const char* name;
|
||||||
|
float64 t0;
|
||||||
|
float64 t1;
|
||||||
|
|
||||||
|
float64 elapsed;
|
||||||
|
u32 callCount;
|
||||||
|
|
||||||
|
ProfilerStNode* parent;
|
||||||
|
b3StackArray<ProfilerStNode*, 32> children;
|
||||||
|
|
||||||
|
ProfilerStNodeStat* stat;
|
||||||
|
};
|
||||||
|
|
||||||
|
// A profiler tree
|
||||||
|
class ProfilerSt
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
ProfilerSt();
|
||||||
|
|
||||||
|
~ProfilerSt();
|
||||||
|
|
||||||
|
// Must be called before profiling.
|
||||||
|
void Begin();
|
||||||
|
|
||||||
|
// Must be called after profiling.
|
||||||
|
void End();
|
||||||
|
|
||||||
|
// Begin a new scope.
|
||||||
|
void BeginScope(const char* name);
|
||||||
|
|
||||||
|
// End the top scope.
|
||||||
|
void EndScope();
|
||||||
|
|
||||||
|
ProfilerStNode* GetRoot() { return m_root; }
|
||||||
|
private:
|
||||||
|
ProfilerStNode* CreateNode();
|
||||||
|
void DestroyNode(ProfilerStNode* node);
|
||||||
|
|
||||||
|
void RecurseDestroyNode(ProfilerStNode* node);
|
||||||
|
|
||||||
|
ProfilerStNode* FindNode(const char* name);
|
||||||
|
|
||||||
|
ProfilerStNodeStat* CreateStat();
|
||||||
|
|
||||||
|
ProfilerStNodeStat* FindStat(const char* name);
|
||||||
|
|
||||||
|
b3BlockPool m_pool; // pool of nodes
|
||||||
|
b3Time m_time; // timer
|
||||||
|
ProfilerStNode* m_root; // tree root node
|
||||||
|
ProfilerStNode* m_top; // top node
|
||||||
|
|
||||||
|
b3StackArray<ProfilerStNodeStat, 256> m_stats; // node statistics
|
||||||
|
};
|
||||||
|
|
||||||
|
extern ProfilerSt* g_profilerSt;
|
||||||
|
|
||||||
|
#endif
|
@ -18,6 +18,7 @@
|
|||||||
|
|
||||||
#include <testbed/framework/test.h>
|
#include <testbed/framework/test.h>
|
||||||
#include <testbed/framework/profiler.h>
|
#include <testbed/framework/profiler.h>
|
||||||
|
#include <testbed/framework/profiler_st.h>
|
||||||
|
|
||||||
extern u32 b3_allocCalls, b3_maxAllocCalls;
|
extern u32 b3_allocCalls, b3_maxAllocCalls;
|
||||||
extern u32 b3_convexCalls, b3_convexCacheHits;
|
extern u32 b3_convexCalls, b3_convexCacheHits;
|
||||||
@ -27,11 +28,13 @@ extern bool b3_convexCache;
|
|||||||
void b3BeginProfileScope(const char* name)
|
void b3BeginProfileScope(const char* name)
|
||||||
{
|
{
|
||||||
g_profiler->BeginScope(name);
|
g_profiler->BeginScope(name);
|
||||||
|
g_profilerSt->BeginScope(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
void b3EndProfileScope()
|
void b3EndProfileScope()
|
||||||
{
|
{
|
||||||
g_profiler->EndScope();
|
g_profiler->EndScope();
|
||||||
|
g_profilerSt->EndScope();
|
||||||
}
|
}
|
||||||
|
|
||||||
Test::Test() :
|
Test::Test() :
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
#include <testbed/framework/view_model.h>
|
#include <testbed/framework/view_model.h>
|
||||||
#include <testbed/framework/test.h>
|
#include <testbed/framework/test.h>
|
||||||
#include <testbed/framework/profiler.h>
|
#include <testbed/framework/profiler.h>
|
||||||
|
#include <testbed/framework/profiler_st.h>
|
||||||
|
|
||||||
#include <imgui/imgui.h>
|
#include <imgui/imgui.h>
|
||||||
#if defined (U_OPENGL_2)
|
#if defined (U_OPENGL_2)
|
||||||
@ -218,8 +219,8 @@ void View::Interface()
|
|||||||
|
|
||||||
if (ImGui::BeginMenu("View"))
|
if (ImGui::BeginMenu("View"))
|
||||||
{
|
{
|
||||||
ImGui::MenuItem("Profile", "", &settings.drawProfile);
|
|
||||||
ImGui::MenuItem("Profile Tree", "", &settings.drawProfileTree);
|
ImGui::MenuItem("Profile Tree", "", &settings.drawProfileTree);
|
||||||
|
ImGui::MenuItem("Profile Tree Statistics", "", &settings.drawProfileTreeStats);
|
||||||
ImGui::MenuItem("Statistics", "", &settings.drawStats);
|
ImGui::MenuItem("Statistics", "", &settings.drawStats);
|
||||||
|
|
||||||
ImGui::Separator();
|
ImGui::Separator();
|
||||||
@ -435,7 +436,7 @@ void View::InterfaceProfileTree()
|
|||||||
ImGui::SetNextWindowPos(ImVec2(0.0f, wp.y + ws.y));
|
ImGui::SetNextWindowPos(ImVec2(0.0f, wp.y + ws.y));
|
||||||
ImGui::SetNextWindowSize(ImVec2(g_camera->m_width - 250.0f, 0.0f));
|
ImGui::SetNextWindowSize(ImVec2(g_camera->m_width - 250.0f, 0.0f));
|
||||||
|
|
||||||
ImGui::Begin("##ProfileTree", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize);
|
ImGui::Begin("Profile Tree", NULL, ImGuiWindowFlags_AlwaysAutoResize);
|
||||||
|
|
||||||
ProfilerNode* root = g_profiler->GetRoot();
|
ProfilerNode* root = g_profiler->GetRoot();
|
||||||
if (root)
|
if (root)
|
||||||
@ -449,6 +450,64 @@ void View::InterfaceProfileTree()
|
|||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void TreeNode(ProfilerStNode* node, u32& index)
|
||||||
|
{
|
||||||
|
ImGui::PushID(index);
|
||||||
|
++index;
|
||||||
|
|
||||||
|
if (ImGui::TreeNode(node->name))
|
||||||
|
{
|
||||||
|
ImGui::Text("%.4f (min = %.4f) (max = %.4f) (calls = %d) [ms]", node->elapsed, node->stat->minElapsed, node->stat->maxElapsed, node->callCount);
|
||||||
|
|
||||||
|
for (u32 i = 0; i < node->children.Count(); ++i)
|
||||||
|
{
|
||||||
|
TreeNode(node->children[i], index);
|
||||||
|
}
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
|
||||||
|
void View::InterfaceProfileTreeStats()
|
||||||
|
{
|
||||||
|
ImGui::Begin("Overlay", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar);
|
||||||
|
ImVec2 wp = ImGui::GetWindowPos();
|
||||||
|
ImVec2 ws = ImGui::GetWindowSize();
|
||||||
|
ImGui::End();
|
||||||
|
|
||||||
|
wp.y = wp.y + ws.y;
|
||||||
|
|
||||||
|
if (g_settings->drawProfileTree)
|
||||||
|
{
|
||||||
|
ImGui::Begin("Profile Tree", NULL, ImGuiWindowFlags_AlwaysAutoResize);
|
||||||
|
ImVec2 ptwp = ImGui::GetWindowPos();
|
||||||
|
ImVec2 ptws = ImGui::GetWindowSize();
|
||||||
|
ImGui::End();
|
||||||
|
|
||||||
|
wp.y = ptwp.y + ptws.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::PushStyleVar(ImGuiStyleVar_WindowBorderSize, 0.0f);
|
||||||
|
|
||||||
|
ImGui::SetNextWindowBgAlpha(0.0f);
|
||||||
|
ImGui::SetNextWindowPos(ImVec2(0.0f, wp.y));
|
||||||
|
ImGui::SetNextWindowSize(ImVec2(g_camera->m_width - 250.0f, 0.0f));
|
||||||
|
|
||||||
|
ImGui::Begin("Profile Tree Statistics", NULL, ImGuiWindowFlags_AlwaysAutoResize);
|
||||||
|
|
||||||
|
ProfilerStNode* root = g_profilerSt->GetRoot();
|
||||||
|
if (root)
|
||||||
|
{
|
||||||
|
u32 index = 0;
|
||||||
|
TreeNode(root, index);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
|
||||||
|
ImGui::PopStyleVar();
|
||||||
|
}
|
||||||
|
|
||||||
void View::EndInterface()
|
void View::EndInterface()
|
||||||
{
|
{
|
||||||
ImGui::PopStyleVar();
|
ImGui::PopStyleVar();
|
||||||
|
@ -42,6 +42,7 @@ public:
|
|||||||
void BeginInterface();
|
void BeginInterface();
|
||||||
void Interface();
|
void Interface();
|
||||||
void InterfaceProfileTree();
|
void InterfaceProfileTree();
|
||||||
|
void InterfaceProfileTreeStats();
|
||||||
void EndInterface();
|
void EndInterface();
|
||||||
private:
|
private:
|
||||||
friend class ViewModel;
|
friend class ViewModel;
|
||||||
|
@ -32,8 +32,8 @@ struct Settings
|
|||||||
drawLines = true;
|
drawLines = true;
|
||||||
drawTriangles = true;
|
drawTriangles = true;
|
||||||
drawGrid = true;
|
drawGrid = true;
|
||||||
drawProfile = false;
|
|
||||||
drawProfileTree = false;
|
drawProfileTree = false;
|
||||||
|
drawProfileTreeStats = false;
|
||||||
drawStats = false;
|
drawStats = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -43,8 +43,8 @@ struct Settings
|
|||||||
bool drawLines;
|
bool drawLines;
|
||||||
bool drawTriangles;
|
bool drawTriangles;
|
||||||
bool drawGrid;
|
bool drawGrid;
|
||||||
bool drawProfile;
|
|
||||||
bool drawProfileTree;
|
bool drawProfileTree;
|
||||||
|
bool drawProfileTreeStats;
|
||||||
bool drawStats;
|
bool drawStats;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user