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:
		| @@ -105,8 +105,12 @@ static void Run() | ||||
| 	{ | ||||
| 		g_profiler->Begin(); | ||||
| 		 | ||||
| 		g_profilerSt->Begin(); | ||||
|  | ||||
| 		g_profiler->BeginScope("Frame"); | ||||
|  | ||||
| 		g_profilerSt->BeginScope("Frame"); | ||||
|  | ||||
| 		g_view->BeginInterface(); | ||||
|  | ||||
| 		if (g_model->IsPaused()) | ||||
| @@ -122,6 +126,8 @@ static void Run() | ||||
|  | ||||
| 		g_model->Update(); | ||||
|  | ||||
| 		g_profilerSt->EndScope(); | ||||
| 		 | ||||
| 		g_profiler->EndScope(); | ||||
| 		 | ||||
| 		if (g_settings->drawProfileTree) | ||||
| @@ -129,20 +135,13 @@ static void Run() | ||||
| 			g_view->InterfaceProfileTree(); | ||||
| 		} | ||||
|  | ||||
| 		g_profilerRecorder->BuildRecords(); | ||||
|  | ||||
| 		if (g_settings->drawProfile) | ||||
| 		if (g_settings->drawProfileTreeStats) | ||||
| 		{ | ||||
| 			b3StackArray<ProfilerRecord*, 256> records; | ||||
| 			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_view->InterfaceProfileTreeStats(); | ||||
| 		} | ||||
|  | ||||
| 		g_profilerSt->End(); | ||||
|  | ||||
| 		g_profiler->End(); | ||||
| 		 | ||||
| 		g_view->EndInterface(); | ||||
|   | ||||
| @@ -26,7 +26,7 @@ Model::Model() | ||||
| 	g_draw = &m_draw; | ||||
| 	g_camera = &m_camera; | ||||
| 	g_profiler = &m_profiler; | ||||
| 	g_profilerRecorder = &m_profilerRecorder; | ||||
| 	g_profilerSt = &m_profilerSt; | ||||
| 	 | ||||
| #if (PROFILE_JSON == 1) | ||||
| 	g_profilerListener = &m_jsonListener; | ||||
| @@ -56,7 +56,7 @@ Model::~Model() | ||||
| 	g_draw = nullptr; | ||||
| 	g_camera = nullptr; | ||||
| 	g_profiler = nullptr; | ||||
| 	g_profilerRecorder = nullptr; | ||||
| 	g_profilerSt = nullptr; | ||||
|  | ||||
| #if (PROFILE_JSON == 1) | ||||
| 	g_profilerListener = nullptr; | ||||
|   | ||||
| @@ -21,7 +21,7 @@ | ||||
|  | ||||
| #include <testbed/framework/draw.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. | ||||
| #define PROFILE_JSON 0 | ||||
| @@ -71,7 +71,7 @@ private: | ||||
| 	Draw m_draw; | ||||
| 	Camera m_camera; | ||||
| 	Profiler m_profiler; | ||||
| 	ProfilerRecorder m_profilerRecorder; | ||||
| 	ProfilerSt m_profilerSt; | ||||
|  | ||||
| #if (PROFILE_JSON == 1) | ||||
| 	JsonProfiler m_jsonListener; | ||||
|   | ||||
| @@ -127,12 +127,13 @@ void Profiler::End() | ||||
| 		listener->BeginEvents(); | ||||
| 	} | ||||
|  | ||||
| 	RecurseEvents(m_root); | ||||
| 	if (m_root) | ||||
| 	{ | ||||
| 		RecurseEvents(m_root); | ||||
|  | ||||
| 	RecurseDestroyNode(m_root); | ||||
| 	m_root = nullptr; | ||||
|  | ||||
| 	assert(m_root == nullptr); | ||||
| 		RecurseDestroyNode(m_root); | ||||
| 		m_root = nullptr; | ||||
| 	} | ||||
|  | ||||
| 	if (listener) | ||||
| 	{ | ||||
|   | ||||
| @@ -62,8 +62,6 @@ public: | ||||
| 	// Get the root profiler node. | ||||
| 	ProfilerNode* GetRoot() { return m_root; } | ||||
| private: | ||||
| 	friend class ProfilerRecorder; | ||||
| 	 | ||||
| 	ProfilerNode* CreateNode(); | ||||
| 	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/profiler.h> | ||||
| #include <testbed/framework/profiler_st.h> | ||||
|  | ||||
| extern u32 b3_allocCalls, b3_maxAllocCalls; | ||||
| extern u32 b3_convexCalls, b3_convexCacheHits; | ||||
| @@ -27,11 +28,13 @@ extern bool b3_convexCache; | ||||
| void b3BeginProfileScope(const char* name) | ||||
| { | ||||
| 	g_profiler->BeginScope(name); | ||||
| 	g_profilerSt->BeginScope(name); | ||||
| } | ||||
|  | ||||
| void b3EndProfileScope() | ||||
| { | ||||
| 	g_profiler->EndScope(); | ||||
| 	g_profilerSt->EndScope(); | ||||
| } | ||||
|  | ||||
| Test::Test() :  | ||||
|   | ||||
| @@ -20,6 +20,7 @@ | ||||
| #include <testbed/framework/view_model.h> | ||||
| #include <testbed/framework/test.h> | ||||
| #include <testbed/framework/profiler.h> | ||||
| #include <testbed/framework/profiler_st.h> | ||||
|  | ||||
| #include <imgui/imgui.h> | ||||
| #if defined (U_OPENGL_2) | ||||
| @@ -218,8 +219,8 @@ void View::Interface() | ||||
|  | ||||
| 		if (ImGui::BeginMenu("View")) | ||||
| 		{ | ||||
| 			ImGui::MenuItem("Profile", "", &settings.drawProfile); | ||||
| 			ImGui::MenuItem("Profile Tree", "", &settings.drawProfileTree); | ||||
| 			ImGui::MenuItem("Profile Tree Statistics", "", &settings.drawProfileTreeStats); | ||||
| 			ImGui::MenuItem("Statistics", "", &settings.drawStats); | ||||
|  | ||||
| 			ImGui::Separator(); | ||||
| @@ -435,7 +436,7 @@ void View::InterfaceProfileTree() | ||||
| 	ImGui::SetNextWindowPos(ImVec2(0.0f, wp.y + ws.y)); | ||||
| 	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(); | ||||
| 	if (root) | ||||
| @@ -449,6 +450,64 @@ void View::InterfaceProfileTree() | ||||
| 	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() | ||||
| { | ||||
| 	ImGui::PopStyleVar(); | ||||
|   | ||||
| @@ -42,6 +42,7 @@ public: | ||||
| 	void BeginInterface(); | ||||
| 	void Interface(); | ||||
| 	void InterfaceProfileTree(); | ||||
| 	void InterfaceProfileTreeStats(); | ||||
| 	void EndInterface(); | ||||
| private: | ||||
| 	friend class ViewModel; | ||||
|   | ||||
| @@ -32,8 +32,8 @@ struct Settings | ||||
| 		drawLines = true; | ||||
| 		drawTriangles = true; | ||||
| 		drawGrid = true; | ||||
| 		drawProfile = false; | ||||
| 		drawProfileTree = false; | ||||
| 		drawProfileTreeStats = false; | ||||
| 		drawStats = false; | ||||
| 	} | ||||
|  | ||||
| @@ -43,8 +43,8 @@ struct Settings | ||||
| 	bool drawLines; | ||||
| 	bool drawTriangles; | ||||
| 	bool drawGrid; | ||||
| 	bool drawProfile; | ||||
| 	bool drawProfileTree; | ||||
| 	bool drawProfileTreeStats; | ||||
| 	bool drawStats; | ||||
| }; | ||||
|  | ||||
|   | ||||
		Reference in New Issue
	
	Block a user