forked from 0ad/0ad
prefect
5087732fe3
data model. Profile.cpp implements AbstractProfileTable so the original functionality of the profiling display remains. CRenderer also contains an AbstractProfileTable implementation that displays the rendering stats. Switch through different profile views using F11. This was SVN commit r3154.
481 lines
12 KiB
C++
481 lines
12 KiB
C++
/**
|
|
* =========================================================================
|
|
* File : Profile.cpp
|
|
* Project : Pyrogeneses
|
|
* Description : GPG3-style hierarchical profiler
|
|
*
|
|
* @author Mark Thompson (mark@wildfiregames.com / mot20@cam.ac.uk)
|
|
* @author Nicolai Haehnle <nicolai@wildfiregames.com>
|
|
* =========================================================================
|
|
*/
|
|
|
|
#include "precompiled.h"
|
|
|
|
#include "Profile.h"
|
|
#include "ProfileViewer.h"
|
|
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
// CProfileNodeTable
|
|
|
|
|
|
|
|
/**
|
|
* Class CProfileNodeTable: Implement ProfileViewer's AbstractProfileTable
|
|
* interface in order to display profiling data in-game.
|
|
*/
|
|
class CProfileNodeTable : public AbstractProfileTable
|
|
{
|
|
public:
|
|
CProfileNodeTable(CProfileNode* n);
|
|
virtual ~CProfileNodeTable();
|
|
|
|
// Implementation of AbstractProfileTable interface
|
|
virtual CStr GetName();
|
|
virtual CStr GetTitle();
|
|
virtual uint GetNumberRows();
|
|
virtual const std::vector<ProfileColumn>& GetColumns();
|
|
|
|
virtual CStr GetCellText(uint row, uint col);
|
|
virtual AbstractProfileTable* GetChild(uint row);
|
|
virtual bool IsHighlightRow(uint row);
|
|
|
|
private:
|
|
/**
|
|
* struct ColumnDescription: The only purpose of this helper structure
|
|
* is to provide the global constructor that sets up the column
|
|
* description.
|
|
*/
|
|
struct ColumnDescription
|
|
{
|
|
std::vector<ProfileColumn> columns;
|
|
|
|
ColumnDescription()
|
|
{
|
|
columns.push_back(ProfileColumn("Name", 230));
|
|
columns.push_back(ProfileColumn("calls/frame", 100));
|
|
columns.push_back(ProfileColumn("msec/frame", 100));
|
|
columns.push_back(ProfileColumn("%/frame", 100));
|
|
columns.push_back(ProfileColumn("%/parent", 100));
|
|
}
|
|
};
|
|
|
|
/// The node represented by this table
|
|
CProfileNode* node;
|
|
|
|
/// Columns description (shared by all instances)
|
|
static ColumnDescription columnDescription;
|
|
};
|
|
|
|
CProfileNodeTable::ColumnDescription CProfileNodeTable::columnDescription;
|
|
|
|
|
|
// Constructor/Destructor
|
|
CProfileNodeTable::CProfileNodeTable(CProfileNode* n)
|
|
{
|
|
node = n;
|
|
}
|
|
|
|
CProfileNodeTable::~CProfileNodeTable()
|
|
{
|
|
}
|
|
|
|
// Short name (= name of profile node)
|
|
CStr CProfileNodeTable::GetName()
|
|
{
|
|
return node->GetName();
|
|
}
|
|
|
|
// Title (= explanatory text plus time totals)
|
|
CStr CProfileNodeTable::GetTitle()
|
|
{
|
|
char buf[512];
|
|
|
|
snprintf(buf, sizeof(buf), "Profiling Information for: %s (Time in node: %.3f msec/frame)", node->GetName(), node->GetFrameTime() * 1000.0f );
|
|
|
|
return buf;
|
|
}
|
|
|
|
// Total number of children
|
|
uint CProfileNodeTable::GetNumberRows()
|
|
{
|
|
return node->GetChildren()->size() + node->GetScriptChildren()->size() + 1;
|
|
}
|
|
|
|
// Column description
|
|
const std::vector<ProfileColumn>& CProfileNodeTable::GetColumns()
|
|
{
|
|
return columnDescription.columns;
|
|
}
|
|
|
|
// Retrieve cell text
|
|
CStr CProfileNodeTable::GetCellText(uint row, uint col)
|
|
{
|
|
CProfileNode* child;
|
|
uint nrchildren = node->GetChildren()->size();
|
|
uint nrscriptchildren = node->GetScriptChildren()->size();
|
|
char buf[256];
|
|
|
|
if (row < nrchildren)
|
|
child = (*node->GetChildren())[row];
|
|
else if (row < nrchildren + nrscriptchildren)
|
|
child = (*node->GetScriptChildren())[row - nrchildren];
|
|
else if (row > nrchildren + nrscriptchildren)
|
|
return "!bad row!";
|
|
else
|
|
{
|
|
// "unlogged" row
|
|
if (col == 0)
|
|
return "unlogged";
|
|
else if (col == 1)
|
|
return "";
|
|
|
|
float unlogged = node->GetFrameTime();
|
|
CProfileNode::const_profile_iterator it;
|
|
|
|
for(it = node->GetChildren()->begin(); it != node->GetChildren()->end(); ++it)
|
|
unlogged -= (*it)->GetFrameTime();
|
|
for(it = node->GetScriptChildren()->begin(); it != node->GetScriptChildren()->end(); ++it)
|
|
unlogged -= (*it)->GetFrameTime();
|
|
|
|
if (col == 2)
|
|
snprintf(buf, sizeof(buf), "%.3f", unlogged * 1000.0f);
|
|
else if (col == 3)
|
|
snprintf(buf, sizeof(buf), "%.1f", unlogged / g_Profiler.GetRoot()->GetFrameTime());
|
|
else
|
|
snprintf(buf, sizeof(buf), "%.1f", unlogged * 100.0f / g_Profiler.GetRoot()->GetFrameTime());
|
|
|
|
return CStr(buf);
|
|
}
|
|
|
|
switch(col)
|
|
{
|
|
default:
|
|
case 0:
|
|
return child->GetName();
|
|
|
|
case 1:
|
|
#ifdef PROFILE_AMORTIZE
|
|
snprintf(buf, sizeof(buf), "%.3f", child->GetFrameCalls());
|
|
#else
|
|
snprintf(buf, sizeof(buf), "%d", child->GetFrameCalls());
|
|
#endif
|
|
return CStr(buf);
|
|
|
|
case 2:
|
|
snprintf(buf, sizeof(buf), "%.3f", child->GetFrameTime() * 1000.0f);
|
|
return CStr(buf);
|
|
|
|
case 3:
|
|
snprintf(buf, sizeof(buf), "%.1f", child->GetFrameTime() * 100.0 / g_Profiler.GetRoot()->GetFrameTime());
|
|
return CStr(buf);
|
|
|
|
case 4:
|
|
snprintf(buf, sizeof(buf), "%.1f", child->GetFrameTime() * 100.0 / node->GetFrameTime());
|
|
return CStr(buf);
|
|
}
|
|
}
|
|
|
|
// Return a pointer to the child table if the child node is expandable
|
|
AbstractProfileTable* CProfileNodeTable::GetChild(uint row)
|
|
{
|
|
CProfileNode* child;
|
|
uint nrchildren = node->GetChildren()->size();
|
|
uint nrscriptchildren = node->GetScriptChildren()->size();
|
|
|
|
if (row < nrchildren)
|
|
child = (*node->GetChildren())[row];
|
|
else if (row < nrchildren + nrscriptchildren)
|
|
child = (*node->GetScriptChildren())[row - nrchildren];
|
|
else
|
|
return 0;
|
|
|
|
if (child->CanExpand())
|
|
return child->display_table;
|
|
|
|
return 0;
|
|
}
|
|
|
|
// Highlight all script nodes
|
|
bool CProfileNodeTable::IsHighlightRow(uint row)
|
|
{
|
|
uint nrchildren = node->GetChildren()->size();
|
|
uint nrscriptchildren = node->GetScriptChildren()->size();
|
|
|
|
return (row >= nrchildren && row < (nrchildren + nrscriptchildren));
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////
|
|
// CProfileNode implementation
|
|
|
|
|
|
// Note: As with the GPG profiler, name is assumed to be a pointer to a constant string; only pointer equality is checked.
|
|
CProfileNode::CProfileNode( const char* _name, CProfileNode* _parent )
|
|
{
|
|
name = _name;
|
|
recursion = 0;
|
|
calls_total = 0;
|
|
calls_frame_current = 0;
|
|
#ifdef PROFILE_AMORTIZE
|
|
int i;
|
|
for( i = 0; i < PROFILE_AMORTIZE_FRAMES; i++ )
|
|
{
|
|
calls_frame_buffer[i] = 0;
|
|
time_frame_buffer[i] = 0.0;
|
|
}
|
|
calls_frame_last = calls_frame_buffer;
|
|
calls_frame_amortized = 0.0f;
|
|
#else
|
|
calls_frame_last = 0;
|
|
#endif
|
|
time_total = 0.0;
|
|
time_frame_current = 0.0;
|
|
#ifdef PROFILE_AMORTIZE
|
|
time_frame_last = time_frame_buffer;
|
|
time_frame_amortized = 0.0;
|
|
#else
|
|
time_frame_last = 0.0;
|
|
#endif
|
|
parent = _parent;
|
|
|
|
display_table = new CProfileNodeTable(this);
|
|
|
|
Reset();
|
|
}
|
|
|
|
CProfileNode::~CProfileNode()
|
|
{
|
|
profile_iterator it;
|
|
for( it = children.begin(); it != children.end(); it++ )
|
|
delete( *it );
|
|
for( it = script_children.begin(); it != script_children.end(); it++ )
|
|
delete( *it );
|
|
|
|
delete display_table;
|
|
}
|
|
|
|
const CProfileNode* CProfileNode::GetChild( const char* childName ) const
|
|
{
|
|
const_profile_iterator it;
|
|
for( it = children.begin(); it != children.end(); it++ )
|
|
if( (*it)->name == childName )
|
|
return( *it );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
const CProfileNode* CProfileNode::GetScriptChild( const char* childName ) const
|
|
{
|
|
const_profile_iterator it;
|
|
for( it = script_children.begin(); it != script_children.end(); it++ )
|
|
if( (*it)->name == childName )
|
|
return( *it );
|
|
|
|
return( NULL );
|
|
}
|
|
|
|
CProfileNode* CProfileNode::GetChild( const char* childName )
|
|
{
|
|
profile_iterator it;
|
|
for( it = children.begin(); it != children.end(); it++ )
|
|
if( (*it)->name == childName )
|
|
return( *it );
|
|
|
|
CProfileNode* newNode = new CProfileNode( childName, this );
|
|
children.push_back( newNode );
|
|
return( newNode );
|
|
}
|
|
|
|
CProfileNode* CProfileNode::GetScriptChild( const char* childName )
|
|
{
|
|
profile_iterator it;
|
|
for( it = script_children.begin(); it != script_children.end(); it++ )
|
|
if( (*it)->name == childName )
|
|
return( *it );
|
|
|
|
CProfileNode* newNode = new CProfileNode( childName, this );
|
|
script_children.push_back( newNode );
|
|
return( newNode );
|
|
}
|
|
|
|
bool CProfileNode::CanExpand()
|
|
{
|
|
return( !( children.empty() && script_children.empty() ) );
|
|
}
|
|
|
|
void CProfileNode::Reset()
|
|
{
|
|
calls_total = 0;
|
|
calls_frame_current = 0;
|
|
#ifdef PROFILE_AMORTIZE
|
|
int i;
|
|
for( i = 0; i < PROFILE_AMORTIZE_FRAMES; i++ )
|
|
{
|
|
calls_frame_buffer[i] = 0;
|
|
time_frame_buffer[i] = 0.0;
|
|
}
|
|
calls_frame_last = calls_frame_buffer;
|
|
calls_frame_amortized = 0.0f;
|
|
#else
|
|
calls_frame_last = 0;
|
|
#endif
|
|
time_total = 0.0;
|
|
time_frame_current = 0.0;
|
|
#ifdef PROFILE_AMORTIZE
|
|
time_frame_last = time_frame_buffer;
|
|
time_frame_amortized = 0.0;
|
|
#else
|
|
time_frame_last = 0.0;
|
|
#endif
|
|
|
|
profile_iterator it;
|
|
for( it = children.begin(); it != children.end(); it++ )
|
|
(*it)->Reset();
|
|
for( it = script_children.begin(); it != script_children.end(); it++ )
|
|
(*it)->Reset();
|
|
}
|
|
|
|
void CProfileNode::Frame()
|
|
{
|
|
calls_total += calls_frame_current;
|
|
time_total += time_frame_current;
|
|
|
|
#ifdef PROFILE_AMORTIZE
|
|
calls_frame_amortized -= *calls_frame_last;
|
|
*calls_frame_last = calls_frame_current;
|
|
calls_frame_amortized += calls_frame_current;
|
|
time_frame_amortized -= *time_frame_last;
|
|
*time_frame_last = time_frame_current;
|
|
time_frame_amortized += time_frame_current;
|
|
if( ++calls_frame_last == ( calls_frame_buffer + PROFILE_AMORTIZE_FRAMES ) )
|
|
calls_frame_last = calls_frame_buffer;
|
|
if( ++time_frame_last == ( time_frame_buffer + PROFILE_AMORTIZE_FRAMES ) )
|
|
time_frame_last = time_frame_buffer;
|
|
#else
|
|
calls_frame_last = calls_frame_current;
|
|
time_frame_last = time_frame_current;
|
|
#endif
|
|
|
|
calls_frame_current = 0;
|
|
time_frame_current = 0.0;
|
|
|
|
profile_iterator it;
|
|
for( it = children.begin(); it != children.end(); it++ )
|
|
(*it)->Frame();
|
|
for( it = script_children.begin(); it != script_children.end(); it++ )
|
|
(*it)->Frame();
|
|
}
|
|
|
|
void CProfileNode::Call()
|
|
{
|
|
calls_frame_current++;
|
|
if( recursion++ == 0 )
|
|
start = get_time();
|
|
}
|
|
|
|
bool CProfileNode::Return()
|
|
{
|
|
if( !parent ) return( false );
|
|
|
|
if( ( --recursion == 0 ) && ( calls_frame_current != 0 ) )
|
|
time_frame_current += ( get_time() - start );
|
|
return( recursion == 0 );
|
|
}
|
|
|
|
void CProfileNode::ScriptingInit()
|
|
{
|
|
AddProperty( L"name", (IJSObject::GetFn)&CProfileNode::JS_GetName );
|
|
/*
|
|
AddReadOnlyClassProperty( L"callsTotal", &CProfileNode::calls_total );
|
|
AddReadOnlyClassProperty( L"callsPerFrame", &CProfileNode::calls_frame_last );
|
|
AddReadOnlyClassProperty( L"timeTotal", &CProfileNode::time_total );
|
|
AddReadOnlyClassProperty( L"timePerFrame", &CProfileNode::time_frame_last );
|
|
*/
|
|
CJSObject<CProfileNode, true>::ScriptingInit( "ProfilerNode" );
|
|
}
|
|
|
|
CProfileManager::CProfileManager()
|
|
{
|
|
root = new CProfileNode( "root", NULL );
|
|
current = root;
|
|
frame_start = 0.0;
|
|
g_ProfileViewer.AddRootTable(root->display_table);
|
|
}
|
|
|
|
CProfileManager::~CProfileManager()
|
|
{
|
|
std::map<CStr8, const char*>::iterator it;
|
|
for( it = m_internedStrings.begin(); it != m_internedStrings.end(); it++ )
|
|
delete[]( it->second );
|
|
|
|
delete( root );
|
|
}
|
|
|
|
void CProfileManager::Start( const char* name )
|
|
{
|
|
if( name != current->GetName() )
|
|
current = current->GetChild( name );
|
|
current->Call();
|
|
}
|
|
|
|
void CProfileManager::StartScript( const char* name )
|
|
{
|
|
if( name != current->GetName() )
|
|
current = current->GetScriptChild( name );
|
|
current->Call();
|
|
}
|
|
|
|
const char* CProfileManager::InternString( CStr8 intern )
|
|
{
|
|
std::map<CStr8, const char*>::iterator it = m_internedStrings.find( intern );
|
|
if( it != m_internedStrings.end() )
|
|
return( it->second );
|
|
|
|
size_t length = intern.length();
|
|
char* data = new char[length + 1];
|
|
strcpy( data, intern.c_str() );
|
|
data[length] = 0;
|
|
m_internedStrings.insert( std::pair<CStr8, const char*>( intern, data ) );
|
|
return( data );
|
|
}
|
|
|
|
void CProfileManager::Stop()
|
|
{
|
|
if( current->Return() )
|
|
current = current->GetParent();
|
|
}
|
|
|
|
void CProfileManager::Reset()
|
|
{
|
|
root->Reset();
|
|
start = get_time();
|
|
frame_start = get_time();
|
|
}
|
|
|
|
void CProfileManager::Frame()
|
|
{
|
|
root->time_frame_current = ( get_time() - frame_start );
|
|
root->Frame();
|
|
|
|
frame_start = get_time();
|
|
}
|
|
|
|
void CProfileManager::StructuralReset()
|
|
{
|
|
delete( root );
|
|
root = new CProfileNode( "root", NULL );
|
|
current = root;
|
|
g_ProfileViewer.AddRootTable(root->display_table);
|
|
}
|
|
|
|
double CProfileManager::GetTime()
|
|
{
|
|
return( get_time() - start );
|
|
}
|
|
|
|
double CProfileManager::GetFrameTime()
|
|
{
|
|
return( get_time() - frame_start );
|
|
}
|
|
|