forked from 0ad/0ad
Refactor ProfileViewer into a singleton that uses AbstractProfileTable as
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.
This commit is contained in:
parent
560e4d5077
commit
5087732fe3
@ -421,7 +421,7 @@ void Render()
|
||||
// Profile information
|
||||
|
||||
PROFILE_START( "render profiling" );
|
||||
RenderProfile();
|
||||
g_ProfileViewer.RenderProfile();
|
||||
PROFILE_END( "render profiling" );
|
||||
|
||||
oglCheck();
|
||||
@ -582,7 +582,7 @@ static void InitInput()
|
||||
|
||||
in_add_handler(conInputHandler);
|
||||
|
||||
in_add_handler(profilehandler);
|
||||
in_add_handler(CProfileViewer::InputThunk);
|
||||
|
||||
in_add_handler(hotkeyInputHandler);
|
||||
|
||||
@ -790,6 +790,7 @@ void Shutdown()
|
||||
debug_shutdown();
|
||||
delete &g_Logger;
|
||||
delete &g_Profiler;
|
||||
delete &g_ProfileViewer;
|
||||
TIMER_END("shutdown misc");
|
||||
}
|
||||
|
||||
@ -853,6 +854,7 @@ void Init(int argc, char* argv[], uint flags)
|
||||
// (command line params may override these)
|
||||
get_cur_vmode(&g_xres, &g_yres, &g_bpp, &g_freq);
|
||||
|
||||
new CProfileViewer;
|
||||
new CProfileManager; // before any script code
|
||||
|
||||
MICROLOG(L"init scripting");
|
||||
|
@ -1,6 +1,213 @@
|
||||
/**
|
||||
* =========================================================================
|
||||
* 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 )
|
||||
@ -31,6 +238,8 @@ CProfileNode::CProfileNode( const char* _name, CProfileNode* _parent )
|
||||
#endif
|
||||
parent = _parent;
|
||||
|
||||
display_table = new CProfileNodeTable(this);
|
||||
|
||||
Reset();
|
||||
}
|
||||
|
||||
@ -41,6 +250,8 @@ CProfileNode::~CProfileNode()
|
||||
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
|
||||
@ -188,6 +399,7 @@ CProfileManager::CProfileManager()
|
||||
root = new CProfileNode( "root", NULL );
|
||||
current = root;
|
||||
frame_start = 0.0;
|
||||
g_ProfileViewer.AddRootTable(root->display_table);
|
||||
}
|
||||
|
||||
CProfileManager::~CProfileManager()
|
||||
@ -253,7 +465,7 @@ void CProfileManager::StructuralReset()
|
||||
delete( root );
|
||||
root = new CProfileNode( "root", NULL );
|
||||
current = root;
|
||||
ResetProfileViewer();
|
||||
g_ProfileViewer.AddRootTable(root->display_table);
|
||||
}
|
||||
|
||||
double CProfileManager::GetTime()
|
||||
|
@ -1,26 +1,33 @@
|
||||
// Profile.h
|
||||
//
|
||||
// GPG3-style hierarchical profiler
|
||||
//
|
||||
// Mark Thompson (mark@wildfiregames.com / mot20@cam.ac.uk)
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : Profile.h
|
||||
* Project : Pyrogenesis
|
||||
* Description : GPG3-style hierarchical profiler
|
||||
*
|
||||
* @author Mark Thompson (mark@wildfiregames.com / mot20@cam.ac.uk)
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
#ifndef PROFILE_H_INCLUDED
|
||||
#define PROFILE_H_INCLUDED
|
||||
|
||||
#include <vector>
|
||||
#include "Singleton.h"
|
||||
#include "scripting/ScriptableObject.h"
|
||||
#include "timer.h"
|
||||
|
||||
// TODO: Shouldn't depend on this.
|
||||
#include "ProfileViewer.h"
|
||||
|
||||
#define PROFILE_AMORTIZE
|
||||
#define PROFILE_AMORTIZE_FRAMES 50
|
||||
|
||||
class CProfileManager;
|
||||
class CProfileNodeTable;
|
||||
|
||||
class CProfileNode : public CJSObject<CProfileNode, true>
|
||||
{
|
||||
friend class CProfileManager;
|
||||
|
||||
friend class CProfileNodeTable;
|
||||
|
||||
const char* name;
|
||||
int calls_total;
|
||||
int calls_frame_current;
|
||||
@ -47,6 +54,7 @@ class CProfileNode : public CJSObject<CProfileNode, true>
|
||||
CProfileNode* parent;
|
||||
std::vector<CProfileNode*> children;
|
||||
std::vector<CProfileNode*> script_children;
|
||||
CProfileNodeTable* display_table;
|
||||
|
||||
public:
|
||||
typedef std::vector<CProfileNode*>::iterator profile_iterator;
|
||||
@ -149,3 +157,5 @@ public:
|
||||
// Cheat a bit to make things slightly easier on the user
|
||||
#define PROFILE_START( name ) { CProfileSample __profile( name )
|
||||
#define PROFILE_END( name ) }
|
||||
|
||||
#endif // PROFILE_H_INCLUDED
|
||||
|
@ -1,3 +1,15 @@
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : ProfileViewer.cpp
|
||||
* Project : Pyrogenesis
|
||||
* Description : Implementation of profile display (containing only display
|
||||
* : routines, the data model(s) are implemented elsewhere).
|
||||
*
|
||||
* @author Mark Thompson <mark@wildfiregames.com>
|
||||
* @author Nicolai Haehnle <nicolai@wildfiregames.com>
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
#include "precompiled.h"
|
||||
#include "ProfileViewer.h"
|
||||
#include "Profile.h"
|
||||
@ -5,151 +17,203 @@
|
||||
#include "lib/res/graphics/unifont.h"
|
||||
#include "Hotkey.h"
|
||||
|
||||
bool profileVisible = false;
|
||||
|
||||
extern int g_xres, g_yres;
|
||||
|
||||
const CProfileNode* currentNode = NULL;
|
||||
|
||||
void ResetProfileViewer()
|
||||
struct CProfileViewerInternals
|
||||
{
|
||||
currentNode = NULL;
|
||||
/// Whether the profiling display is currently visible
|
||||
bool profileVisible;
|
||||
|
||||
/// List of root tables
|
||||
std::vector<AbstractProfileTable*> rootTables;
|
||||
|
||||
/// Path from a root table (path[0]) to the currently visible table (path[size-1])
|
||||
std::vector<AbstractProfileTable*> path;
|
||||
|
||||
/// Helper functions
|
||||
void TableIsDeleted(AbstractProfileTable* table);
|
||||
void NavigateTree(int id);
|
||||
};
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// AbstractProfileTable implementation
|
||||
|
||||
AbstractProfileTable::~AbstractProfileTable()
|
||||
{
|
||||
if (CProfileViewer::IsInitialised())
|
||||
{
|
||||
g_ProfileViewer.m->TableIsDeleted(this);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderProfileNode( CProfileNode* node, int currentNodeid )
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////
|
||||
// CProfileViewer implementation
|
||||
|
||||
|
||||
// AbstractProfileTable got deleted, make sure we have no dangling pointers
|
||||
void CProfileViewerInternals::TableIsDeleted(AbstractProfileTable* table)
|
||||
{
|
||||
glPushMatrix();
|
||||
if( node->CanExpand() )
|
||||
for(int idx = rootTables.size()-1; idx >= 0; --idx)
|
||||
{
|
||||
glPushMatrix();
|
||||
glTranslatef( -15.0f, 0.0f, 0.0f );
|
||||
glwprintf( L"%d", currentNodeid );
|
||||
glPopMatrix();
|
||||
if (rootTables[idx] == table)
|
||||
rootTables.erase(rootTables.begin() + idx);
|
||||
}
|
||||
|
||||
for(int idx = 0; (uint)idx < path.size(); ++idx)
|
||||
{
|
||||
if (path[idx] != table)
|
||||
continue;
|
||||
|
||||
path.erase(path.begin() + idx, path.end());
|
||||
if (path.size() == 0)
|
||||
profileVisible = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Move into child tables or return to parent tables based on the given number
|
||||
void CProfileViewerInternals::NavigateTree(int id)
|
||||
{
|
||||
if (id == 0)
|
||||
{
|
||||
if (path.size() > 1)
|
||||
path.pop_back();
|
||||
}
|
||||
else
|
||||
{
|
||||
AbstractProfileTable* table = path[path.size() - 1];
|
||||
int numrows = table->GetNumberRows();
|
||||
|
||||
for(int row = 0; row < numrows; ++row)
|
||||
{
|
||||
AbstractProfileTable* child = table->GetChild(row);
|
||||
|
||||
if (!child)
|
||||
continue;
|
||||
|
||||
--id;
|
||||
if (id == 0)
|
||||
{
|
||||
path.push_back(child);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Construction/Destruction
|
||||
CProfileViewer::CProfileViewer()
|
||||
{
|
||||
m = new CProfileViewerInternals;
|
||||
m->profileVisible = false;
|
||||
}
|
||||
|
||||
CProfileViewer::~CProfileViewer()
|
||||
{
|
||||
delete m;
|
||||
}
|
||||
|
||||
|
||||
// Render
|
||||
void CProfileViewer::RenderProfile()
|
||||
{
|
||||
if (!m->profileVisible)
|
||||
return;
|
||||
|
||||
if (!m->path.size())
|
||||
{
|
||||
m->profileVisible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
glPushMatrix();
|
||||
glwprintf( L"%hs", node->GetName() );
|
||||
glPopMatrix();
|
||||
glTranslatef( 230.0f, 0.0f, 0.0f );
|
||||
glPushMatrix();
|
||||
#ifdef PROFILE_AMORTIZE
|
||||
glwprintf( L"%.3f", node->GetFrameCalls() );
|
||||
#else
|
||||
glwprintf( L"%d", node->GetFrameCalls() );
|
||||
#endif
|
||||
glPopMatrix();
|
||||
glTranslatef( 100.0f, 0.0f, 0.0f );
|
||||
glPushMatrix();
|
||||
glwprintf( L"%.3f", node->GetFrameTime() * 1000.0f );
|
||||
glPopMatrix();
|
||||
glTranslatef( 100.0f, 0.0f, 0.0f );
|
||||
glPushMatrix();
|
||||
AbstractProfileTable* table = m->path[m->path.size() - 1];
|
||||
const std::vector<ProfileColumn>& columns = table->GetColumns();
|
||||
uint numrows = table->GetNumberRows();
|
||||
|
||||
glwprintf( L"%.1f", node->GetFrameTime() * 100.0 / g_Profiler.GetRoot()->GetFrameTime() );
|
||||
glPopMatrix();
|
||||
glTranslatef( 100.0f, 0.0f, 0.0f );
|
||||
glPushMatrix();
|
||||
glwprintf( L"%.1f", node->GetFrameTime() * 100.0 / currentNode->GetFrameTime() );
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
|
||||
glTranslatef( 0.0f, 20.0f, 0.0f );
|
||||
}
|
||||
|
||||
void RenderProfile()
|
||||
{
|
||||
if( !profileVisible ) return;
|
||||
if( !currentNode ) currentNode = g_Profiler.GetRoot();
|
||||
|
||||
// Render background
|
||||
int estimate_height;
|
||||
|
||||
estimate_height = 6 + (int)currentNode->GetChildren()->size() + (int)currentNode->GetScriptChildren()->size();
|
||||
estimate_height = 20*estimate_height;
|
||||
int estimate_width;
|
||||
|
||||
estimate_width = 50;
|
||||
for(uint i = 0; i < columns.size(); ++i)
|
||||
estimate_width += columns[i].width;
|
||||
|
||||
estimate_height = 3 + numrows;
|
||||
if (m->path.size() > 1)
|
||||
estimate_height += 2;
|
||||
estimate_height = 20*estimate_height;
|
||||
|
||||
glDisable(GL_TEXTURE_2D);
|
||||
glColor4ub(0,0,0,128);
|
||||
glBegin(GL_QUADS);
|
||||
glVertex2i(0, g_yres);
|
||||
glVertex2i(660, g_yres);
|
||||
glVertex2i(660, g_yres-estimate_height);
|
||||
glVertex2i(0, g_yres-estimate_height);
|
||||
glVertex2i(0, g_yres);
|
||||
glVertex2i(estimate_width, g_yres);
|
||||
glVertex2i(estimate_width, g_yres-estimate_height);
|
||||
glVertex2i(0, g_yres-estimate_height);
|
||||
glEnd();
|
||||
glEnable(GL_TEXTURE_2D);
|
||||
|
||||
glPushMatrix();
|
||||
glColor3f(1.0f, 1.0f, 1.0f);
|
||||
|
||||
// Print table and column titles
|
||||
glPushMatrix();
|
||||
glTranslatef(2.0f, g_yres - 20.0f, 0.0f );
|
||||
glScalef(1.0f, -1.0f, 1.0f);
|
||||
|
||||
glColor3ub(255, 255, 255);
|
||||
|
||||
glPushMatrix();
|
||||
glwprintf( L"Profiling Information for: %hs (Time in node: %.3f msec/frame)", currentNode->GetName(), currentNode->GetFrameTime() * 1000.0f );
|
||||
glwprintf(L"%s", table->GetTitle().c_str());
|
||||
glPopMatrix();
|
||||
glTranslatef( 20.0f, 20.0f, 0.0f );
|
||||
|
||||
glPushMatrix();
|
||||
glPushMatrix();
|
||||
glwprintf( L"Name" );
|
||||
glPopMatrix();
|
||||
glTranslatef( 230.0f, 0.0f, 0.0f );
|
||||
glPushMatrix();
|
||||
glwprintf( L"calls/frame" );
|
||||
glPopMatrix();
|
||||
glTranslatef( 100.0f, 0.0f, 0.0f );
|
||||
glPushMatrix();
|
||||
glwprintf( L"msec/frame" );
|
||||
glPopMatrix();
|
||||
glTranslatef( 100.0f, 0.0f, 0.0f );
|
||||
glPushMatrix();
|
||||
glwprintf( L"%%/frame" );
|
||||
glPopMatrix();
|
||||
glTranslatef( 100.0f, 0.0f, 0.0f );
|
||||
glPushMatrix();
|
||||
glwprintf( L"%%/parent" );
|
||||
glPopMatrix();
|
||||
for(uint col = 0; col < columns.size(); ++col)
|
||||
{
|
||||
glPushMatrix();
|
||||
glwprintf(L"%s", columns[col].title.c_str());
|
||||
glPopMatrix();
|
||||
glTranslatef(columns[col].width, 0, 0);
|
||||
}
|
||||
glPopMatrix();
|
||||
|
||||
|
||||
CProfileNode::const_profile_iterator it;
|
||||
|
||||
glTranslatef( 0.0f, 20.0f, 0.0f );
|
||||
|
||||
float unlogged = (float)currentNode->GetFrameTime();
|
||||
|
||||
int currentNodeid = 1;
|
||||
|
||||
for( it = currentNode->GetChildren()->begin(); it != currentNode->GetChildren()->end(); it++, currentNodeid++ )
|
||||
// Print rows
|
||||
int currentExpandId = 1;
|
||||
|
||||
for(uint row = 0; row < numrows; ++row)
|
||||
{
|
||||
unlogged -= (float)(*it)->GetFrameTime();
|
||||
RenderProfileNode( *it, currentNodeid );
|
||||
}
|
||||
glColor3f( 1.0f, 0.5f, 0.5f );
|
||||
for( it = currentNode->GetScriptChildren()->begin(); it != currentNode->GetScriptChildren()->end(); it++, currentNodeid++ )
|
||||
{
|
||||
unlogged -= (float)(*it)->GetFrameTime();
|
||||
RenderProfileNode( *it, currentNodeid );
|
||||
}
|
||||
glColor3f( 1.0f, 1.0f, 1.0f );
|
||||
glPushMatrix();
|
||||
|
||||
if (table->IsHighlightRow(row))
|
||||
glColor3ub(255, 128, 128);
|
||||
else
|
||||
glColor3ub(255, 255, 255);
|
||||
|
||||
if (table->GetChild(row))
|
||||
{
|
||||
glPushMatrix();
|
||||
glTranslatef( -15.0f, 0.0f, 0.0f );
|
||||
glwprintf(L"%d", currentExpandId);
|
||||
glPopMatrix();
|
||||
currentExpandId++;
|
||||
}
|
||||
|
||||
glTranslatef( 0.0f, 20.0f, 0.0f );
|
||||
glPushMatrix();
|
||||
|
||||
glPushMatrix();
|
||||
glwprintf( L"unlogged" );
|
||||
glPopMatrix();
|
||||
glTranslatef( 330.0f, 0.0f, 0.0f );
|
||||
glPushMatrix();
|
||||
glwprintf( L"%.3f", unlogged * 1000.0f );
|
||||
glPopMatrix();
|
||||
glTranslatef( 100.0f, 0.0f, 0.0f );
|
||||
glPushMatrix();
|
||||
glwprintf( L"%.1f", ( unlogged / g_Profiler.GetRoot()->GetFrameTime() ) * 100.0f );
|
||||
glPopMatrix();
|
||||
glTranslatef( 100.0f, 0.0f, 0.0f );
|
||||
glPushMatrix();
|
||||
glwprintf( L"%.1f", ( unlogged / currentNode->GetFrameTime() ) * 100.0f );
|
||||
glPopMatrix();
|
||||
glPopMatrix();
|
||||
for(uint col = 0; col < columns.size(); ++col)
|
||||
{
|
||||
glPushMatrix();
|
||||
glwprintf(L"%s", table->GetCellText(row, col).c_str());
|
||||
glPopMatrix();
|
||||
glTranslatef(columns[col].width, 0, 0);
|
||||
}
|
||||
|
||||
glPopMatrix();
|
||||
glTranslatef( 0.0f, 20.0f, 0.0f );
|
||||
}
|
||||
glColor3ub(255, 255, 255);
|
||||
|
||||
if( currentNode->GetParent() )
|
||||
if (m->path.size() > 1)
|
||||
{
|
||||
glTranslatef( 0.0f, 20.0f, 0.0f );
|
||||
glPushMatrix();
|
||||
@ -164,48 +228,76 @@ void RenderProfile()
|
||||
glPopMatrix();
|
||||
}
|
||||
|
||||
InReaction profilehandler( const SDL_Event* ev )
|
||||
|
||||
// Handle input
|
||||
InReaction CProfileViewer::Input(const SDL_Event* ev)
|
||||
{
|
||||
switch( ev->type )
|
||||
switch(ev->type)
|
||||
{
|
||||
case SDL_KEYDOWN:
|
||||
{
|
||||
if( profileVisible )
|
||||
if (!m->profileVisible)
|
||||
break;
|
||||
|
||||
int k = ev->key.keysym.sym - SDLK_0;
|
||||
if (k >= 0 && k <= 9)
|
||||
{
|
||||
int k = ev->key.keysym.sym - SDLK_0;
|
||||
if( k == 0 )
|
||||
{
|
||||
if( currentNode->GetParent() )
|
||||
currentNode = currentNode->GetParent();
|
||||
}
|
||||
else if( ( k >= 1 ) && ( k <= 9 ) )
|
||||
{
|
||||
k--;
|
||||
CProfileNode::const_profile_iterator it;
|
||||
for( it = currentNode->GetChildren()->begin(); it != currentNode->GetChildren()->end(); it++, k-- )
|
||||
if( (*it)->CanExpand() && !k )
|
||||
{
|
||||
currentNode = (*it);
|
||||
return( IN_HANDLED );
|
||||
}
|
||||
for( it = currentNode->GetScriptChildren()->begin(); it != currentNode->GetScriptChildren()->end(); it++, k-- )
|
||||
if( (*it)->CanExpand() && !k )
|
||||
{
|
||||
currentNode = (*it);
|
||||
return( IN_HANDLED );
|
||||
}
|
||||
return( IN_HANDLED );
|
||||
}
|
||||
m->NavigateTree(k);
|
||||
return IN_HANDLED;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SDL_HOTKEYDOWN:
|
||||
if( ev->user.code == HOTKEY_PROFILE_TOGGLE )
|
||||
{
|
||||
profileVisible = !profileVisible;
|
||||
if (!m->profileVisible)
|
||||
{
|
||||
if (m->rootTables.size())
|
||||
{
|
||||
m->profileVisible = true;
|
||||
m->path.push_back(m->rootTables[0]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
uint i;
|
||||
|
||||
for(i = 0; i < m->rootTables.size(); ++i)
|
||||
{
|
||||
if (m->rootTables[i] == m->path[0])
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
|
||||
m->path.clear();
|
||||
if (i < m->rootTables.size())
|
||||
{
|
||||
m->path.push_back(m->rootTables[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
m->profileVisible = false;
|
||||
}
|
||||
}
|
||||
return( IN_HANDLED );
|
||||
}
|
||||
break;
|
||||
}
|
||||
return( IN_PASS );
|
||||
}
|
||||
|
||||
InReaction CProfileViewer::InputThunk(const SDL_Event* ev)
|
||||
{
|
||||
if (CProfileViewer::IsInitialised())
|
||||
return g_ProfileViewer.Input(ev);
|
||||
|
||||
return IN_PASS;
|
||||
}
|
||||
|
||||
|
||||
// Add a table to the list of roots
|
||||
void CProfileViewer::AddRootTable(AbstractProfileTable* table)
|
||||
{
|
||||
m->rootTables.push_back(table);
|
||||
}
|
||||
|
||||
|
@ -1,14 +1,172 @@
|
||||
// ProfileViewer.h
|
||||
//
|
||||
// A temporary interface for viewing profile information
|
||||
/**
|
||||
* =========================================================================
|
||||
* File : ProfileViewer.h
|
||||
* Project : Pyrogenesis
|
||||
* Description : Viewing profiling information (timing and other statistics)
|
||||
*
|
||||
* @author Mark Thompson <mark@wildfiregames.com>
|
||||
* @author Nicolai Haehnle <nicolai@wildfiregames.com>
|
||||
* =========================================================================
|
||||
*/
|
||||
|
||||
#ifndef PROFILE_VIEWER_INCLUDED
|
||||
#define PROFILE_VIEWER_INCLUDED
|
||||
|
||||
#include "input.h"
|
||||
#include "ps/CStr.h"
|
||||
#include "ps/Singleton.h"
|
||||
|
||||
void ResetProfileViewer();
|
||||
void RenderProfile();
|
||||
InReaction profilehandler( const SDL_Event* ev );
|
||||
|
||||
#endif
|
||||
/**
|
||||
* Struct ProfileColumn: Describes one column of an AbstractProfileTable.
|
||||
*/
|
||||
struct ProfileColumn
|
||||
{
|
||||
/// Title of the column
|
||||
CStr title;
|
||||
|
||||
/// Recommended width of the column, in pixels.
|
||||
uint width;
|
||||
|
||||
ProfileColumn(const CStr& t, uint w) : title(t), width(w) { }
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Class AbstractProfileTable: Profile table data model.
|
||||
*
|
||||
* Clients that wish to display debug information in the profile viewer
|
||||
* have to implement this class and hook it into CProfileViewer.
|
||||
*
|
||||
* Note that the profiling system is robust against deletion of
|
||||
* object instances in the sense that it will automatically remove
|
||||
* an AbstractProfileTable instance from its internal records when
|
||||
* you delete it.
|
||||
* Conversely, deleting an AbstractProfileTable instance is the responsibility
|
||||
* of its creator.
|
||||
*/
|
||||
class AbstractProfileTable
|
||||
{
|
||||
public:
|
||||
virtual ~AbstractProfileTable();
|
||||
|
||||
/**
|
||||
* GetName: Short descriptive name of this table (should be static).
|
||||
*
|
||||
* @return Descriptive name of this table.
|
||||
*/
|
||||
virtual CStr GetName() = 0;
|
||||
|
||||
/**
|
||||
* GetTitle: Longer, explanatory text (can be dynamic).
|
||||
*
|
||||
* @return Title for the table.
|
||||
*/
|
||||
virtual CStr GetTitle() = 0;
|
||||
|
||||
|
||||
/**
|
||||
* GetNumberRows
|
||||
*
|
||||
* @return Number of rows in this table.
|
||||
*/
|
||||
virtual uint GetNumberRows() = 0;
|
||||
|
||||
/**
|
||||
* GetColumnDescriptions
|
||||
*
|
||||
* @return A vector describing all columns of the table.
|
||||
*/
|
||||
virtual const std::vector<ProfileColumn>& GetColumns() = 0;
|
||||
|
||||
/**
|
||||
* GetCellText
|
||||
*
|
||||
* @param row Row index (the first row has index 0).
|
||||
* @param col Column index (the first column has index 0).
|
||||
*
|
||||
* @return Text to be displayed in the given cell.
|
||||
*/
|
||||
virtual CStr GetCellText(uint row, uint col) = 0;
|
||||
|
||||
/**
|
||||
* GetChild: Return a row's child table if the child is expandable.
|
||||
*
|
||||
* @param row Row index (the first row has index 0).
|
||||
*
|
||||
* @return Pointer to the child table if the given row has one.
|
||||
* Otherwise, return 0.
|
||||
*/
|
||||
virtual AbstractProfileTable* GetChild(uint row) = 0;
|
||||
|
||||
/**
|
||||
* IsHighlightRow
|
||||
*
|
||||
* @param row Row index (the first row has index 0).
|
||||
*
|
||||
* @return true if the row should be highlighted in a special color.
|
||||
*/
|
||||
virtual bool IsHighlightRow(uint row) { return false; }
|
||||
};
|
||||
|
||||
|
||||
struct CProfileViewerInternals;
|
||||
|
||||
/**
|
||||
* Class CProfileViewer: Manage and display profiling tables.
|
||||
*/
|
||||
class CProfileViewer : public Singleton<CProfileViewer>
|
||||
{
|
||||
friend class AbstractProfileTable;
|
||||
|
||||
public:
|
||||
CProfileViewer();
|
||||
~CProfileViewer();
|
||||
|
||||
/**
|
||||
* RenderProfile: Render the profile display using OpenGL if the user
|
||||
* has enabled it.
|
||||
*/
|
||||
void RenderProfile();
|
||||
|
||||
/**
|
||||
* Input: Filter and handle any input events that the profile display
|
||||
* is interested in.
|
||||
*
|
||||
* In particular, this function handles enable/disable of the profile
|
||||
* display as well as navigating the information tree.
|
||||
*
|
||||
* @param ev The incoming event.
|
||||
*
|
||||
* @return IN_PASS or IN_HANDLED depending on whether the event relates
|
||||
* to the profiling display.
|
||||
*/
|
||||
InReaction Input(const SDL_Event* ev);
|
||||
|
||||
/**
|
||||
* AddRootTable: Add a new profile table as a root table (i.e. the
|
||||
* tables that you cycle through via the profile hotkey).
|
||||
*
|
||||
* @note Tables added via this function are automatically removed from
|
||||
* the list of root tables when they are deleted.
|
||||
*
|
||||
* @param table This table is added as a root table.
|
||||
*/
|
||||
void AddRootTable(AbstractProfileTable* table);
|
||||
|
||||
/**
|
||||
* InputThunk: Delegate to the singleton's Input() member function
|
||||
* if the singleton has been initialized.
|
||||
*
|
||||
* This allows our input handler to be installed via in_add_handler
|
||||
* like a normal, global function input handler.
|
||||
*/
|
||||
static InReaction InputThunk(const SDL_Event* ev);
|
||||
|
||||
private:
|
||||
CProfileViewerInternals* m;
|
||||
};
|
||||
|
||||
#define g_ProfileViewer CProfileViewer::GetSingleton()
|
||||
|
||||
#endif
|
||||
|
@ -43,6 +43,7 @@
|
||||
#include "lib/res/graphics/tex.h"
|
||||
#include "lib/res/graphics/ogl_tex.h"
|
||||
#include "ps/Loader.h"
|
||||
#include "ps/ProfileViewer.h"
|
||||
|
||||
#include "renderer/FixedFunctionModelRenderer.h"
|
||||
#include "renderer/HWLightingModelRenderer.h"
|
||||
@ -55,10 +56,150 @@
|
||||
|
||||
#define LOG_CATEGORY "graphics"
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// CRendererStatsTable - Profile display of rendering stats
|
||||
|
||||
/**
|
||||
* Class CRendererStatsTable: Implementation of AbstractProfileTable to
|
||||
* display the renderer stats in-game.
|
||||
*
|
||||
* Accesses CRenderer::m_Stats by keeping the reference passed to the
|
||||
* constructor.
|
||||
*/
|
||||
class CRendererStatsTable : public AbstractProfileTable
|
||||
{
|
||||
public:
|
||||
CRendererStatsTable(const CRenderer::Stats& st);
|
||||
|
||||
// Implementation of AbstractProfileTable interface
|
||||
CStr GetName();
|
||||
CStr GetTitle();
|
||||
uint GetNumberRows();
|
||||
const std::vector<ProfileColumn>& GetColumns();
|
||||
CStr GetCellText(uint row, uint col);
|
||||
AbstractProfileTable* GetChild(uint row);
|
||||
|
||||
private:
|
||||
/// Reference to the renderer singleton's stats
|
||||
const CRenderer::Stats& Stats;
|
||||
|
||||
/// Column descriptions
|
||||
std::vector<ProfileColumn> columnDescriptions;
|
||||
|
||||
enum {
|
||||
Row_Counter = 0,
|
||||
Row_DrawCalls,
|
||||
Row_TerrainTris,
|
||||
Row_ModelTris,
|
||||
Row_BlendSplats,
|
||||
|
||||
// Must be last to count number of rows
|
||||
NumberRows
|
||||
};
|
||||
};
|
||||
|
||||
// Construction
|
||||
CRendererStatsTable::CRendererStatsTable(const CRenderer::Stats& st)
|
||||
: Stats(st)
|
||||
{
|
||||
columnDescriptions.push_back(ProfileColumn("Name", 230));
|
||||
columnDescriptions.push_back(ProfileColumn("Value", 100));
|
||||
}
|
||||
|
||||
// Implementation of AbstractProfileTable interface
|
||||
CStr CRendererStatsTable::GetName()
|
||||
{
|
||||
return "renderer";
|
||||
}
|
||||
|
||||
CStr CRendererStatsTable::GetTitle()
|
||||
{
|
||||
return "Renderer statistics";
|
||||
}
|
||||
|
||||
uint CRendererStatsTable::GetNumberRows()
|
||||
{
|
||||
return NumberRows;
|
||||
}
|
||||
|
||||
const std::vector<ProfileColumn>& CRendererStatsTable::GetColumns()
|
||||
{
|
||||
return columnDescriptions;
|
||||
}
|
||||
|
||||
CStr CRendererStatsTable::GetCellText(uint row, uint col)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
switch(row)
|
||||
{
|
||||
case Row_Counter:
|
||||
if (col == 0)
|
||||
return "counter";
|
||||
snprintf(buf, sizeof(buf), "%d", Stats.m_Counter);
|
||||
return buf;
|
||||
|
||||
case Row_DrawCalls:
|
||||
if (col == 0)
|
||||
return "# draw calls";
|
||||
snprintf(buf, sizeof(buf), "%d", Stats.m_DrawCalls);
|
||||
return buf;
|
||||
|
||||
case Row_TerrainTris:
|
||||
if (col == 0)
|
||||
return "# terrain tris";
|
||||
snprintf(buf, sizeof(buf), "%d", Stats.m_TerrainTris);
|
||||
return buf;
|
||||
|
||||
case Row_ModelTris:
|
||||
if (col == 0)
|
||||
return "# model tris";
|
||||
snprintf(buf, sizeof(buf), "%d", Stats.m_ModelTris);
|
||||
return buf;
|
||||
|
||||
case Row_BlendSplats:
|
||||
if (col == 0)
|
||||
return "# blend splats";
|
||||
snprintf(buf, sizeof(buf), "%d", Stats.m_BlendSplats);
|
||||
return buf;
|
||||
|
||||
default:
|
||||
return "???";
|
||||
}
|
||||
}
|
||||
|
||||
AbstractProfileTable* CRendererStatsTable::GetChild(uint row)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// CRenderer implementation
|
||||
|
||||
/**
|
||||
* Struct CRendererInternals: Truly hide data that is supposed to be hidden
|
||||
* in this structure so it won't even appear in header files.
|
||||
*/
|
||||
struct CRendererInternals
|
||||
{
|
||||
/// Table to display renderer stats in-game via profile system
|
||||
CRendererStatsTable profileTable;
|
||||
|
||||
CRendererInternals()
|
||||
: profileTable(g_Renderer.m_Stats)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////
|
||||
// CRenderer destructor
|
||||
CRenderer::CRenderer()
|
||||
{
|
||||
m = new CRendererInternals;
|
||||
g_ProfileViewer.AddRootTable(&m->profileTable);
|
||||
|
||||
m_Width=0;
|
||||
m_Height=0;
|
||||
m_Depth=0;
|
||||
@ -177,6 +318,7 @@ CRenderer::~CRenderer()
|
||||
// we no longer UnloadAlphaMaps / UnloadWaterTextures here -
|
||||
// that is the responsibility of the module that asked for
|
||||
// them to be loaded (i.e. CGameView).
|
||||
delete m;
|
||||
}
|
||||
|
||||
|
||||
@ -1618,7 +1760,7 @@ int CRenderer::LoadWaterTextures()
|
||||
|
||||
void CRenderer::UnloadWaterTextures()
|
||||
{
|
||||
for (int i = 0; i < ARRAY_SIZE(m_WaterTexture); i++)
|
||||
for (uint i = 0; i < ARRAY_SIZE(m_WaterTexture); i++)
|
||||
ogl_tex_free(m_WaterTexture[i]);
|
||||
}
|
||||
|
||||
|
@ -78,6 +78,8 @@ struct SVertex2D
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////
|
||||
// CRenderer: base renderer class - primary interface to the rendering engine
|
||||
struct CRendererInternals;
|
||||
|
||||
class CRenderer : public Singleton<CRenderer>, public CJSObject<CRenderer>
|
||||
{
|
||||
private:
|
||||
@ -291,6 +293,7 @@ public:
|
||||
void SetFastPlayerColor(bool fast);
|
||||
|
||||
protected:
|
||||
friend class CRendererInternals;
|
||||
friend class CVertexBuffer;
|
||||
friend class CPatchRData;
|
||||
friend class FixedFunctionModelRenderer;
|
||||
@ -328,6 +331,8 @@ protected:
|
||||
void CalcShadowBounds(CBound& bounds);
|
||||
|
||||
// RENDERER DATA:
|
||||
/// Private data that is not needed by inline functions
|
||||
CRendererInternals* m;
|
||||
// view width
|
||||
int m_Width;
|
||||
// view height
|
||||
|
Loading…
Reference in New Issue
Block a user