Civ backgrounds for minimap
[0ad.git] / source / ps / Profile.cpp
blob01493815f8869ad170353cf9e3625bc61cd5da3a
1 /* Copyright (C) 2022 Wildfire Games.
2 * This file is part of 0 A.D.
4 * 0 A.D. is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 2 of the License, or
7 * (at your option) any later version.
9 * 0 A.D. is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
19 * GPG3-style hierarchical profiler
22 #include "precompiled.h"
24 #include "Profile.h"
25 #include "ProfileViewer.h"
26 #include "ThreadUtil.h"
28 #include "lib/timer.h"
30 #include <numeric>
32 ///////////////////////////////////////////////////////////////////////////////////////////////
33 // CProfileNodeTable
37 /**
38 * Class CProfileNodeTable: Implement ProfileViewer's AbstractProfileTable
39 * interface in order to display profiling data in-game.
41 class CProfileNodeTable : public AbstractProfileTable
43 public:
44 CProfileNodeTable(CProfileNode* n);
45 virtual ~CProfileNodeTable();
47 // Implementation of AbstractProfileTable interface
48 virtual CStr GetName();
49 virtual CStr GetTitle();
50 virtual size_t GetNumberRows();
51 virtual const std::vector<ProfileColumn>& GetColumns();
53 virtual CStr GetCellText(size_t row, size_t col);
54 virtual AbstractProfileTable* GetChild(size_t row);
55 virtual bool IsHighlightRow(size_t row);
57 private:
58 /**
59 * struct ColumnDescription: The only purpose of this helper structure
60 * is to provide the global constructor that sets up the column
61 * description.
63 struct ColumnDescription
65 std::vector<ProfileColumn> columns;
67 ColumnDescription()
69 columns.push_back(ProfileColumn("Name", 230));
70 columns.push_back(ProfileColumn("calls/frame", 80));
71 columns.push_back(ProfileColumn("msec/frame", 80));
72 columns.push_back(ProfileColumn("calls/turn", 80));
73 columns.push_back(ProfileColumn("msec/turn", 80));
77 /// The node represented by this table
78 CProfileNode* node;
80 /// Columns description (shared by all instances)
81 static ColumnDescription columnDescription;
84 CProfileNodeTable::ColumnDescription CProfileNodeTable::columnDescription;
87 // Constructor/Destructor
88 CProfileNodeTable::CProfileNodeTable(CProfileNode* n)
90 node = n;
93 CProfileNodeTable::~CProfileNodeTable()
97 // Short name (= name of profile node)
98 CStr CProfileNodeTable::GetName()
100 return node->GetName();
103 // Title (= explanatory text plus time totals)
104 CStr CProfileNodeTable::GetTitle()
106 char buf[512];
107 sprintf_s(buf, ARRAY_SIZE(buf), "Profiling Information for: %s (Time in node: %.3f msec/frame)", node->GetName(), node->GetFrameTime() * 1000.0f );
108 return buf;
111 // Total number of children
112 size_t CProfileNodeTable::GetNumberRows()
114 return node->GetChildren()->size() + node->GetScriptChildren()->size() + 1;
117 // Column description
118 const std::vector<ProfileColumn>& CProfileNodeTable::GetColumns()
120 return columnDescription.columns;
123 // Retrieve cell text
124 CStr CProfileNodeTable::GetCellText(size_t row, size_t col)
126 CProfileNode* child;
127 size_t nrchildren = node->GetChildren()->size();
128 size_t nrscriptchildren = node->GetScriptChildren()->size();
129 char buf[256] = "?";
131 if (row < nrchildren)
132 child = (*node->GetChildren())[row];
133 else if (row < nrchildren + nrscriptchildren)
134 child = (*node->GetScriptChildren())[row - nrchildren];
135 else if (row > nrchildren + nrscriptchildren)
136 return "!bad row!";
137 else
139 // "unlogged" row
140 if (col == 0)
141 return "unlogged";
142 else if (col == 1)
143 return "";
144 else if (col == 4)
145 return "";
147 double unlogged_time_frame = node->GetFrameTime();
148 double unlogged_time_turn = node->GetTurnTime();
149 CProfileNode::const_profile_iterator it;
151 for (it = node->GetChildren()->begin(); it != node->GetChildren()->end(); ++it)
153 unlogged_time_frame -= (*it)->GetFrameTime();
154 unlogged_time_turn -= (*it)->GetTurnTime();
156 for (it = node->GetScriptChildren()->begin(); it != node->GetScriptChildren()->end(); ++it)
158 unlogged_time_frame -= (*it)->GetFrameTime();
159 unlogged_time_turn -= (*it)->GetTurnTime();
162 // The root node can't easily count per-turn values (since Turn isn't called until
163 // halfway though a frame), so just reset them the zero to prevent weird displays
164 if (!node->GetParent())
166 unlogged_time_turn = 0.0;
169 if (col == 2)
170 sprintf_s(buf, ARRAY_SIZE(buf), "%.3f", unlogged_time_frame * 1000.0f);
171 else if (col == 4)
172 sprintf_s(buf, ARRAY_SIZE(buf), "%.3f", unlogged_time_turn * 1000.f);
174 return CStr(buf);
177 switch(col)
179 default:
180 case 0:
181 return child->GetName();
183 case 1:
184 sprintf_s(buf, ARRAY_SIZE(buf), "%.1f", child->GetFrameCalls());
185 break;
186 case 2:
187 sprintf_s(buf, ARRAY_SIZE(buf), "%.3f", child->GetFrameTime() * 1000.0f);
188 break;
189 case 3:
190 sprintf_s(buf, ARRAY_SIZE(buf), "%.1f", child->GetTurnCalls());
191 break;
192 case 4:
193 sprintf_s(buf, ARRAY_SIZE(buf), "%.3f", child->GetTurnTime() * 1000.0f);
194 break;
196 return CStr(buf);
199 // Return a pointer to the child table if the child node is expandable
200 AbstractProfileTable* CProfileNodeTable::GetChild(size_t row)
202 CProfileNode* child;
203 size_t nrchildren = node->GetChildren()->size();
204 size_t nrscriptchildren = node->GetScriptChildren()->size();
206 if (row < nrchildren)
207 child = (*node->GetChildren())[row];
208 else if (row < nrchildren + nrscriptchildren)
209 child = (*node->GetScriptChildren())[row - nrchildren];
210 else
211 return 0;
213 if (child->CanExpand())
214 return child->display_table;
216 return 0;
219 // Highlight all script nodes
220 bool CProfileNodeTable::IsHighlightRow(size_t row)
222 size_t nrchildren = node->GetChildren()->size();
223 size_t nrscriptchildren = node->GetScriptChildren()->size();
225 return (row >= nrchildren && row < (nrchildren + nrscriptchildren));
228 ///////////////////////////////////////////////////////////////////////////////////////////////
229 // CProfileNode implementation
232 // Note: As with the GPG profiler, name is assumed to be a pointer to a constant string; only pointer equality is checked.
233 CProfileNode::CProfileNode( const char* _name, CProfileNode* _parent )
235 name = _name;
236 recursion = 0;
238 Reset();
240 parent = _parent;
242 display_table = new CProfileNodeTable(this);
245 CProfileNode::~CProfileNode()
247 profile_iterator it;
248 for( it = children.begin(); it != children.end(); ++it )
249 delete( *it );
250 for( it = script_children.begin(); it != script_children.end(); ++it )
251 delete( *it );
253 delete display_table;
256 template<typename T>
257 static double average(const T& collection)
259 if (collection.empty())
260 return 0.0;
261 return std::accumulate(collection.begin(), collection.end(), 0.0) / collection.size();
264 double CProfileNode::GetFrameCalls() const
266 return average(calls_per_frame);
269 double CProfileNode::GetFrameTime() const
271 return average(time_per_frame);
274 double CProfileNode::GetTurnCalls() const
276 return average(calls_per_turn);
279 double CProfileNode::GetTurnTime() const
281 return average(time_per_turn);
284 const CProfileNode* CProfileNode::GetChild( const char* childName ) const
286 const_profile_iterator it;
287 for( it = children.begin(); it != children.end(); ++it )
288 if( (*it)->name == childName )
289 return( *it );
291 return( NULL );
294 const CProfileNode* CProfileNode::GetScriptChild( const char* childName ) const
296 const_profile_iterator it;
297 for( it = script_children.begin(); it != script_children.end(); ++it )
298 if( (*it)->name == childName )
299 return( *it );
301 return( NULL );
304 CProfileNode* CProfileNode::GetChild( const char* childName )
306 profile_iterator it;
307 for( it = children.begin(); it != children.end(); ++it )
308 if( (*it)->name == childName )
309 return( *it );
311 CProfileNode* newNode = new CProfileNode( childName, this );
312 children.push_back( newNode );
313 return( newNode );
316 CProfileNode* CProfileNode::GetScriptChild( const char* childName )
318 profile_iterator it;
319 for( it = script_children.begin(); it != script_children.end(); ++it )
320 if( (*it)->name == childName )
321 return( *it );
323 CProfileNode* newNode = new CProfileNode( childName, this );
324 script_children.push_back( newNode );
325 return( newNode );
328 bool CProfileNode::CanExpand()
330 return( !( children.empty() && script_children.empty() ) );
333 void CProfileNode::Reset()
335 calls_per_frame.clear();
336 calls_per_turn.clear();
337 calls_frame_current = 0;
338 calls_turn_current = 0;
340 time_per_frame.clear();
341 time_per_turn.clear();
342 time_frame_current = 0.0;
343 time_turn_current = 0.0;
345 profile_iterator it;
346 for (it = children.begin(); it != children.end(); ++it)
347 (*it)->Reset();
348 for (it = script_children.begin(); it != script_children.end(); ++it)
349 (*it)->Reset();
352 void CProfileNode::Frame()
354 calls_per_frame.push_back(calls_frame_current);
355 time_per_frame.push_back(time_frame_current);
357 calls_frame_current = 0;
358 time_frame_current = 0.0;
360 profile_iterator it;
361 for (it = children.begin(); it != children.end(); ++it)
362 (*it)->Frame();
363 for (it = script_children.begin(); it != script_children.end(); ++it)
364 (*it)->Frame();
367 void CProfileNode::Turn()
369 calls_per_turn.push_back(calls_turn_current);
370 time_per_turn.push_back(time_turn_current);
372 calls_turn_current = 0;
373 time_turn_current = 0.0;
375 profile_iterator it;
376 for (it = children.begin(); it != children.end(); ++it)
377 (*it)->Turn();
378 for (it = script_children.begin(); it != script_children.end(); ++it)
379 (*it)->Turn();
382 void CProfileNode::Call()
384 calls_frame_current++;
385 calls_turn_current++;
386 if (recursion++ == 0)
388 start = timer_Time();
392 bool CProfileNode::Return()
394 if (--recursion != 0)
395 return false;
397 double now = timer_Time();
398 time_frame_current += (now - start);
399 time_turn_current += (now - start);
400 return true;
403 CProfileManager::CProfileManager() :
404 root(NULL), current(NULL), needs_structural_reset(false)
406 PerformStructuralReset();
409 CProfileManager::~CProfileManager()
411 delete root;
414 void CProfileManager::Start( const char* name )
416 if( name != current->GetName() )
417 current = current->GetChild( name );
418 current->Call();
421 void CProfileManager::StartScript( const char* name )
423 if( name != current->GetName() )
424 current = current->GetScriptChild( name );
425 current->Call();
428 void CProfileManager::Stop()
430 if (current->Return())
431 current = current->GetParent();
434 void CProfileManager::Reset()
436 root->Reset();
439 void CProfileManager::Frame()
441 root->time_frame_current += (timer_Time() - root->start);
443 root->Frame();
445 if (needs_structural_reset)
447 PerformStructuralReset();
448 needs_structural_reset = false;
451 root->start = timer_Time();
454 void CProfileManager::Turn()
456 root->Turn();
459 void CProfileManager::StructuralReset()
461 // We can't immediately perform the reset, because we're probably already
462 // nested inside the profile tree and it will get very confused if we delete
463 // the tree when we're not currently at the root.
464 // So just set a flag to perform the reset at the end of the frame.
466 needs_structural_reset = true;
469 void CProfileManager::PerformStructuralReset()
471 delete root;
472 root = new CProfileNode("root", NULL);
473 root->Call();
474 current = root;
475 g_ProfileViewer.AddRootTable(root->display_table, true);
478 CProfileSample::CProfileSample(const char* name)
480 if (CProfileManager::IsInitialised())
482 // The profiler is only safe to use on the main thread
483 if(Threading::IsMainThread())
484 g_Profiler.Start(name);
488 CProfileSample::~CProfileSample()
490 if (CProfileManager::IsInitialised())
491 if(Threading::IsMainThread())
492 g_Profiler.Stop();