1
0
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:
prefect 2005-11-19 16:15:34 +00:00
parent 560e4d5077
commit 5087732fe3
7 changed files with 783 additions and 162 deletions

View File

@ -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");

View File

@ -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()

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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]);
}

View File

@ -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