source-engine/engine/vgui_vprofpanel.cpp
FluorescentCIAAfricanAmerican 3bf9df6b27 1
2020-04-22 12:56:21 -04:00

1156 lines
29 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: VGUI scoreboard
//
// $Workfile: $
// $Date: $
// $NoKeywords: $
//===========================================================================//
#include "client_pch.h"
#include "vgui_vprofpanel.h"
#include <KeyValues.h>
#include "vgui_budgetpanel.h"
#include "vprof_engine.h"
#include "vprof_record.h"
#include "ivideomode.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
using namespace vgui;
// positions
#define VPROF_INDENT_X XRES(20)
#define VPROF_INDENT_Y YRES(10)
// Scoreboard dimensions
#define VPROF_TITLE_SIZE_Y YRES(22)
#define X_BORDER XRES(4)
#define Y_BORDER YRES(4)
static ConVar vprof_verbose( "vprof_verbose", "1", FCVAR_ARCHIVE, "Set to one to show average and peak times" );
static ConVar vprof_unaccounted_limit( "vprof_unaccounted_limit", "0.3", FCVAR_ARCHIVE,
"number of milliseconds that a node must exceed to turn red in the vprof panel" );
static ConVar vprof_warningmsec( "vprof_warningmsec", "10", FCVAR_ARCHIVE, "Above this many milliseconds render the label red to indicate slow code." );
#ifdef VPROF_ENABLED
//-----------------------------------------------------------------------------
// Singleton accessor
//-----------------------------------------------------------------------------
static CVProfPanel *g_pVProfPanel = NULL;
CVProfPanel *GetVProfPanel( void )
{
return g_pVProfPanel;
}
static void ClearNodeClientData( CVProfNode *pNode )
{
pNode->SetClientData( -1 );
if( pNode->GetSibling() )
{
ClearNodeClientData( pNode->GetSibling() );
}
if( pNode->GetChild() )
{
ClearNodeClientData( pNode->GetChild() );
}
}
void CVProfPanel::Reset()
{
m_pHierarchy->RemoveAll();
m_RootItem = -1;
ClearNodeClientData( m_pVProfile->GetRoot() );
}
CON_COMMAND( vprof_expand_all, "Expand the whole vprof tree" )
{
Msg("VProf expand all.\n");
GetVProfPanel()->ExpandAll();
}
CON_COMMAND( vprof_collapse_all, "Collapse the whole vprof tree" )
{
Msg("VProf collapse all.\n");
GetVProfPanel()->CollapseAll();
}
CON_COMMAND( vprof_expand_group, "Expand a budget group in the vprof tree by name" )
{
Msg("VProf expand group.\n");
if ( args.ArgC() >= 2 )
{
GetVProfPanel()->ExpandGroup( args[ 1 ] );
}
}
void IN_VProfDown(void)
{
GetVProfPanel()->UserCmd_ShowVProf();
}
void IN_VProfUp(void)
{
GetVProfPanel()->UserCmd_HideVProf();
}
static ConCommand startshowvprof("+showvprof", IN_VProfDown);
static ConCommand endshowvprof("-showvprof", IN_VProfUp);
void ChangeVProfScopeCallback( IConVar *pConVar, const char *pOldString, float flOldValue )
{
ConVarRef var( pConVar );
Msg( "VProf setting scope to %s\n", var.GetString( ) );
if ( g_pVProfPanel )
{
g_pVProfPanel->Reset();
}
}
ConVar vprof_scope(
"vprof_scope",
"",
0,
"Set a specific scope to start showing vprof tree",
0, 0, 0, 0,
ChangeVProfScopeCallback );
#define PROF_FONT "DefaultFixed"
class CProfileTree : public TreeView
{
DECLARE_CLASS_SIMPLE( CProfileTree, TreeView );
public:
CProfileTree( CVProfPanel *parent, const char *panelName );
~CProfileTree();
virtual void OnCommand( const char *cmd );
virtual void InvalidateLayout( bool layoutNow = false, bool reloadScheme = false );
virtual void ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
SetFont( pScheme->GetFont( PROF_FONT ) );
}
virtual void SetBgColor( Color color )
{
BaseClass::SetBgColor( color );
}
private:
Menu *m_pEditMenu;
CVProfPanel *m_pParent;
};
CProfileTree::CProfileTree( CVProfPanel *parent, const char *panelName ) :
BaseClass( (Panel *)parent, panelName ),
m_pEditMenu( 0 ),
m_pParent( parent )
{
}
CProfileTree::~CProfileTree()
{
delete m_pEditMenu;
}
void CProfileTree::InvalidateLayout( bool layoutNow, bool reloadScheme )
{
BaseClass::InvalidateLayout( layoutNow, reloadScheme );
if ( GetParent() )
{
GetParent()->InvalidateLayout( layoutNow, reloadScheme );
}
}
void CProfileTree::OnCommand( const char *cmd )
{
// Relay to parent
GetParent()->OnCommand( cmd );
}
CProfileHierarchyPanel::ColumnPanels_t::ColumnPanels_t() :
treeViewItem( -1 )
{
}
CProfileHierarchyPanel::ColumnPanels_t::ColumnPanels_t( const ColumnPanels_t& src )
{
treeViewItem = src.treeViewItem;
int c = src.m_Columns.Count();
for ( int i = 0; i < c; ++i )
{
PanelEntry_t pe;
pe.dataname = src.m_Columns[ i ].dataname;
pe.label = src.m_Columns[ i ].label;
m_Columns.AddToTail( pe );
}
}
void CProfileHierarchyPanel::ColumnPanels_t::AddColumn( int index, const char *name, vgui::Label *label )
{
m_Columns.EnsureCount( index + 1 );
m_Columns[ index ].label = label;
m_Columns[ index ].dataname = name;
}
void CProfileHierarchyPanel::ColumnPanels_t::Refresh( KeyValues *kv )
{
VPROF( "CProfileHierarchyPanel::ColumnPanels_t" );
int c = m_Columns.Count();
for ( int i = 0; i < c; ++i )
{
vgui::Label *label = m_Columns[ i ].label;
if ( !label )
continue;
const char *name = m_Columns[ i ].dataname.String();
if ( name && name[ 0 ] )
{
const char *value = kv->GetString( name, "" );
if ( value )
{
if ( !value[ 0 ] )
{
label->SetText( "" );
label->SetVisible( false );
}
else
{
label->SetVisible( true );
label->SetText( value );
}
}
else
{
label->SetVisible( false );
}
}
}
}
CProfileHierarchyPanel::CProfileHierarchyPanel(vgui::Panel *parent, const char *panelName)
: BaseClass(parent,panelName),
m_Panels( 0, 0, PanelsLessFunc )
{
}
CProfileHierarchyPanel::~CProfileHierarchyPanel()
{
}
void CProfileHierarchyPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
{
//SetProportional( true );
BaseClass::ApplySchemeSettings( pScheme );
m_itemFont = pScheme->GetFont( PROF_FONT );
SetTitleBarInfo( m_itemFont, 20 );
SetBgColor( Color(0, 0, 0, 176) );
(( CProfileTree *)GetTree())->SetBgColor( Color( 0, 0, 0, 176 ) );
}
void CProfileHierarchyPanel::SetItemColors( int id, const Color& fg, const Color& bg )
{
VPROF( "CProfileHierarchyPanel::SetItemColors" );
GetTree()->SetItemFgColor( id, fg );
GetTree()->SetItemBgColor( id, bg );
ColumnPanels_t search;
search.treeViewItem = id;
int idx = m_Panels.Find( search );
if ( idx == m_Panels.InvalidIndex() )
{
return;
}
ColumnPanels_t& info = m_Panels[ idx ];
int c = info.m_Columns.Count();
for ( int i = 0; i < c; ++i )
{
Label *label = info.m_Columns[ i ].label;
if ( !label )
continue;
label->SetFgColor( fg );
label->SetBgColor( bg );
}
}
void CProfileHierarchyPanel::SetItemColumnColors( int id, int col, const Color& fg, const Color& bg )
{
VPROF( "CProfileHierarchyPanel::SetItemColumnColors" );
ColumnPanels_t search;
search.treeViewItem = id;
int idx = m_Panels.Find( search );
if ( idx == m_Panels.InvalidIndex() )
{
return;
}
ColumnPanels_t& info = m_Panels[ idx ];
int c = info.m_Columns.Count();
if ( col < 0 || col >= c )
return;
Label *label = info.m_Columns[ col ].label;
if ( !label )
return;
label->SetFgColor( fg );
label->SetBgColor( bg );
}
void CProfileHierarchyPanel::ModifyItem( KeyValues *data, int itemIndex )
{
GetTree()->SetItemFgColor( itemIndex, GetFgColor() );
GetTree()->SetItemBgColor( itemIndex, GetBgColor() );
ColumnPanels_t search;
search.treeViewItem = itemIndex;
int idx = m_Panels.Find( search );
if ( idx == m_Panels.InvalidIndex() )
{
Assert( 0 );
return;
}
ColumnPanels_t& info = m_Panels[ idx ];
info.Refresh( data );
}
int CProfileHierarchyPanel::AddItem( KeyValues *data, int parentItemIndex, ColumnPanels_t& columnPanels )
{
int itemIndex = GetTree()->AddItem( data, parentItemIndex );
columnPanels.treeViewItem = itemIndex;
ColumnPanels_t search;
search.treeViewItem = itemIndex;
int idx = m_Panels.Find( search );
if ( idx == m_Panels.InvalidIndex() )
{
m_Panels.Insert( columnPanels );
int c = columnPanels.m_Columns.Count();
for ( int i = 0; i < c; ++i )
{
if ( columnPanels.m_Columns[ i ].label )
{
columnPanels.m_Columns[ i ].label->SetParent( this );
}
}
}
ModifyItem( data, itemIndex );
return itemIndex;
}
void CProfileHierarchyPanel::PostChildPaint()
{
}
void CProfileHierarchyPanel::PerformLayout()
{
VPROF( "CProfileHierarchyPanel::PerformLayout" );
BaseClass::PerformLayout();
// Assume all invisible at first
HideAll();
int tall = GetTall();
int rowheight = GetTree()->GetRowHeight();
int top, visitems;
bool hbarVisible = false;
GetTree()->GetVBarInfo( top, visitems, hbarVisible );
int offset = -top * rowheight;
int numColumns = GetNumColumns();
// Now position column panels into the correct spot
int rows = GetNumRows();
for ( int row = 0; row < rows; ++row )
{
int tvi = GetTreeItemAtRow( row );
for ( int col = 0; col < numColumns; ++col )
{
int left, right, bottom;
GetGridElementBounds( col, row, left, top, right, bottom );
ColumnPanels_t search;
search.treeViewItem = tvi;
int idx = m_Panels.Find( search );
if ( idx != m_Panels.InvalidIndex() )
{
ColumnPanels_t& info = m_Panels[ idx ];
if ( col >= info.m_Columns.Count() )
continue;
vgui::Label *p = info.m_Columns[ col ].label;
if ( !p )
{
continue;
}
bool vis = ( top + offset - 20 ) >= 0 && ( bottom + offset ) < tall;
p->SetParent( vis ? this : NULL );
p->SetVisible( vis );
p->SetBounds( left, top + offset, right - left, bottom - top );
p->InvalidateLayout();
}
else
{
Assert( 0 );
}
}
}
}
void CProfileHierarchyPanel::HideAll()
{
for ( int i = m_Panels.FirstInorder(); i != m_Panels.InvalidIndex(); i = m_Panels.NextInorder( i ) )
{
ColumnPanels_t& info = m_Panels[ i ];
int c = info.m_Columns.Count();
for ( int j = 0 ; j < c; ++j )
{
Label *panel = info.m_Columns[ j ].label;
if ( !panel )
{
continue;
}
panel->SetVisible( false );
}
}
}
void CProfileHierarchyPanel::RemoveAll()
{
GetTree()->RemoveAll();
for ( int i = m_Panels.FirstInorder(); i != m_Panels.InvalidIndex(); i = m_Panels.NextInorder( i ) )
{
ColumnPanels_t& info = m_Panels[ i ];
int c = info.m_Columns.Count();
for ( int j = 0 ; j < c; ++j )
{
delete info.m_Columns[ j ].label;
}
info.m_Columns.RemoveAll();
}
m_Panels.RemoveAll();
InvalidateLayout();
}
void CProfileHierarchyPanel::ExpandItem(int itemIndex, bool bExpand)
{
GetTree()->ExpandItem( itemIndex, bExpand );
}
bool CProfileHierarchyPanel::IsItemExpanded( int itemIndex )
{
return GetTree()->IsItemExpanded( itemIndex );
}
KeyValues *CProfileHierarchyPanel::GetItemData(int itemIndex)
{
return GetTree()->GetItemData( itemIndex );
}
//-----------------------------------------------------------------------------
// Purpose: Create the VProf panel
//-----------------------------------------------------------------------------
CVProfPanel::CVProfPanel( vgui::Panel *pParent, const char *pElementName )
: vgui::Frame( pParent, pElementName )
{
m_pVProfile = g_pVProfileForDisplay;
Assert( g_pVProfPanel == NULL );
g_pVProfPanel = this;
m_RootItem = -1;
m_fShowVprofHeld = false;
int x = VPROF_INDENT_X;
int y = VPROF_INDENT_Y;
int wide = videomode->GetModeStereoWidth() - x * 2;
int tall = videomode->GetModeStereoHeight() - y * 2;
SetBgColor(Color(0, 0, 0, 175));
// Initialize the top title.
#ifdef VPROF_ENABLED
SetTitle("VProf", false);
#else
SetTitle("** VProf is not enabled **", false);
#endif
SetZPos( 1002 );
CProfileTree *profileTree = new CProfileTree( this, "ProfileTree" );
m_pHierarchy = new CProfileHierarchyPanel( this, "Hierarchy" );
m_pHierarchy->SetTreeView( profileTree );
m_pHierarchy->SetNumColumns( 3 );
int treewide = wide - 780;
m_pHierarchy->SetColumnInfo( 0, "Tree", treewide );
m_pHierarchy->SetColumnInfo( 1, "Group", 120 );
m_pHierarchy->SetColumnInfo( 2, "Data", 180 );
// Treeview of the hierarchical calls
m_pHierarchy->SetBounds(X_BORDER, VPROF_TITLE_SIZE_Y, wide - X_BORDER*2, tall - Y_BORDER*2 - VPROF_TITLE_SIZE_Y);
m_pHierarchy->SetParent(this);
m_pHierarchy->SetPaintBorderEnabled( false );
m_pHierarchy->SetPaintBackgroundEnabled( false );
SETUP_PANEL( m_pHierarchy );
SETUP_PANEL( profileTree );
// Mode selection used to populate the tree view + lists
m_pVProfCategory = new ComboBox(this, "CategoryCombo", 5, false);
m_pVProfCategory->AddItem( "All Categories", NULL );
m_pVProfCategory->AddActionSignalTarget( this );
m_pVProfCategory->ActivateItem( 0 );
m_pVProfSort = new ComboBox( this, "SortCombo", 5, false );
m_pVProfSort->AddItem( "By Time", NULL );
m_pVProfSort->AddItem( "By Name", NULL );
m_pVProfSort->AddItem( "By Budget Group", NULL );
m_pVProfSort->AddActionSignalTarget( this );
m_pVProfSort->ActivateItem( 0 );
m_nLastBudgetGroupCount = 0;
m_nCurrentBudgetGroup = -1;
m_pHierarchicalView = new vgui::CheckButton( this, "HierarchicalViewSelection", "" );
m_pHierarchicalView->AddActionSignalTarget( this );
m_pHierarchicalView->SetSelected( true );
m_bHierarchicalView = true;
m_pRedoSort = new vgui::Button( this, "RedoSorting", "", this, "redosort" );
m_pVerbose = new vgui::CheckButton( this, "VerboseCheckbox", "" );
m_pVerbose->AddActionSignalTarget( this );
m_pVerbose->SetSelected( vprof_verbose.GetBool() );
// Setup the playback controls.
m_pPlaybackLabel = new vgui::Label( this, "PlaybackLabel", "" );
m_pPlaybackLabel->SetBgColor( Color( 0, 0, 0, 200 ) );
m_pPlaybackLabel->SetPaintBackgroundEnabled( true );
m_pStepForward = new vgui::Button( this, "StepForward", "", this, "StepForward" );
m_pStepBack = new vgui::Button( this, "StepBack", "", this, "StepBack" );
m_pGotoButton = new vgui::Button( this, "GotoButton", "", this, "GotoButton" );
m_pPlaybackScroll = new vgui::ScrollBar( this, "PlaybackScroll", false );
m_pPlaybackScroll->SetRange( 0, 1000 );
m_pPlaybackScroll->SetRangeWindow( 30 );
m_pPlaybackScroll->AddActionSignalTarget( this );
m_iLastPlaybackTick = -1;
LoadControlSettings("Resource\\VProfPanel.res");
SetBounds( x, y, wide, tall );
vgui::ivgui()->AddTickSignal( GetVPanel() );
}
CVProfPanel::~CVProfPanel( void )
{
Assert( g_pVProfPanel == this );
g_pVProfPanel = NULL;
}
#define DATA_FMT_STR "%-30.30s %-30.30s %-45.45s %-10.10s"
void CVProfPanel::PerformLayout()
{
BaseClass::PerformLayout();
int w, h;
GetSize( w, h );
int topoffset = 95;
int inset = 10;
m_pHierarchy->SetBounds( inset, topoffset, w - 2 * inset, h - inset - topoffset );
int treewide = w - 900 - 20;
treewide = max( treewide, 240 );
m_pHierarchy->SetColumnInfo( 0, "Tree", treewide );
m_pHierarchy->SetColumnInfo( 1, "Group", 125 );
char header[ 512 ];
Q_snprintf( header, sizeof( header ), DATA_FMT_STR,
"Frame Calls + Time + NoChild",
"Avg Calls + Time + NoChild",
"Sum Calls + Time + NoChild + Peak",
"L2Miss" );
m_pHierarchy->SetColumnInfo( 2, header, 775, CTreeViewListControl::CI_HEADER_LEFTALIGN );
}
//-----------------------------------------------------------------------------
// Scheme settings!
//-----------------------------------------------------------------------------
void CVProfPanel::ApplySchemeSettings( vgui::IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
IBorder *border = pScheme->GetBorder( "ToolTipBorder" );
SetBorder(border);
SetBgColor(Color(0, 0, 0, 175));
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : *command -
//-----------------------------------------------------------------------------
void CVProfPanel::Close()
{
UserCmd_HideVProf();
BaseClass::Close();
}
//-----------------------------------------------------------------------------
// Is it visible?
//-----------------------------------------------------------------------------
void CVProfPanel::OnTick()
{
BaseClass::OnTick();
// Did the CVProfile we're using switch behind our back?
if ( g_pVProfileForDisplay != m_pVProfile )
{
Reset();
m_pVProfile = g_pVProfileForDisplay;
bool bVisible = false;
#ifndef _XBOX
if ( VProfRecord_IsPlayingBack() )
{
bVisible = true;
m_iLastPlaybackTick = -1;
}
#endif
m_pStepForward->SetVisible( bVisible );
m_pStepBack->SetVisible( bVisible );
m_pPlaybackLabel->SetVisible( bVisible );
m_pPlaybackScroll->SetVisible( bVisible );
m_pGotoButton->SetVisible( bVisible );
}
#ifndef _XBOX
if ( VProfRecord_IsPlayingBack() )
{
// Update the playback tick.
int iCur = VProfPlayback_GetCurrentTick();
if ( iCur != m_iLastPlaybackTick )
{
char str[512];
Q_snprintf( str, sizeof( str ), "VPROF playback (tick %d, %d%%)", iCur, (int)(VProfPlayback_GetCurrentPercent() * 100) );
m_pPlaybackLabel->SetText( str );
}
}
#endif
SetVisible( m_fShowVprofHeld != 0 );
m_pRedoSort->SetVisible( !m_bHierarchicalView );
}
// 0 = By Time
// 1 = By "Text"
// 2 = By "group" then by "text"
static int g_nSortType = -1;
//-----------------------------------------------------------------------------
// Visualization changed
//-----------------------------------------------------------------------------
void CVProfPanel::OnTextChanged( KeyValues *data )
{
Panel *pPanel = reinterpret_cast<vgui::Panel *>( data->GetPtr("panel") );
vgui::ComboBox *pBox = dynamic_cast<vgui::ComboBox *>( pPanel );
if( pBox == m_pVProfCategory )
{
// The -1 here is for the 'All Categories' item
m_nCurrentBudgetGroup = pBox->GetActiveItem() - 1;
// NOTE: We have to reset the tree view so that it
// is populated with only the ones we want (need to eliminate everything
// that's not appropriate)
Reset();
return;
}
if ( pBox == m_pVProfSort )
{
g_nSortType = pBox->GetActiveItem();
Reset();
return;
}
}
//-----------------------------------------------------------------------------
// Sort function for hierarchical data
//-----------------------------------------------------------------------------
bool ChildCostSortFunc(KeyValues *pNode1, KeyValues *pNode2)
{
switch ( g_nSortType )
{
default:
case 0:
case -1:
{
float flTime1 = pNode1->GetFloat( "Time", -1.0f );
float flTime2 = pNode2->GetFloat( "Time", -1.0f );
return (flTime1 > flTime2);
}
case 1:
{
const char *t1 = pNode1->GetString( "Text", "" );
const char *t2 = pNode2->GetString( "Text", "" );
if( Q_stricmp( t1, t2 ) <= 0 )
return true;
return false;
}
break;
case 2:
{
const char *g1 = pNode1->GetString( "group", "" );
const char *g2 = pNode2->GetString( "group", "" );
int val = Q_stricmp( g1, g2 );
if( val < 0 )
return true;
if ( val > 0 )
return false;
const char *t1 = pNode1->GetString( "Text", "" );
const char *t2 = pNode2->GetString( "Text", "" );
if( Q_stricmp( t1, t2 ) <= 0 )
return true;
return false;
}
}
return false;
}
//-----------------------------------------------------------------------------
// Changes the visualization method for vprof data
//-----------------------------------------------------------------------------
void CVProfPanel::OnCheckButtonChecked(Panel *panel)
{
if ( panel == m_pHierarchicalView )
{
bool bSelected = m_pHierarchicalView->IsSelected() ? 1 : 0;
if ( bSelected != m_bHierarchicalView )
{
m_bHierarchicalView = bSelected;
m_pHierarchy->GetTree()->SetSortFunc( m_bHierarchicalView ? NULL : ChildCostSortFunc );
m_pRedoSort->SetVisible( !m_bHierarchicalView );
Reset();
}
return;
}
if ( panel == m_pVerbose )
{
vprof_verbose.SetValue( m_pVerbose->IsSelected() ? 1 : 0 );
return;
}
}
//-----------------------------------------------------------------------------
// Methods related to expand/collapse in hierarchy
//-----------------------------------------------------------------------------
void CVProfPanel::ExpandAll( void )
{
int count = m_pHierarchy->GetTree()->GetHighestItemID();
int i;
for( i = 0; i < count; i++ )
{
if( m_pHierarchy->GetTree()->IsItemIDValid( i ) )
{
m_pHierarchy->GetTree()->ExpandItem( i, true );
}
}
}
void CVProfPanel::CollapseAll( void )
{
int count = m_pHierarchy->GetTree()->GetHighestItemID();
int i;
for( i = 1; i < count; i++ )
{
if( m_pHierarchy->GetTree()->IsItemIDValid( i ) )
{
m_pHierarchy->GetTree()->ExpandItem( i, false );
}
}
}
void CVProfPanel::ExpandGroupRecursive( int nBudgetGroupID, CVProfNode *pNode )
{
if( !pNode )
{
return;
}
if( pNode->GetBudgetGroupID() == nBudgetGroupID )
{
CVProfNode *pTempNode = pNode;
while( pTempNode )
{
if( pTempNode->GetParent() )
{
int id = pTempNode->GetParent()->GetClientData();
m_pHierarchy->ExpandItem( id, true );
}
pTempNode = pTempNode->GetParent();
}
}
ExpandGroupRecursive( nBudgetGroupID, pNode->GetChild() );
ExpandGroupRecursive( nBudgetGroupID, pNode->GetSibling() );
}
void CVProfPanel::ExpandGroup( const char *pGroupName )
{
int groupID = m_pVProfile->BudgetGroupNameToBudgetGroupID( pGroupName );
ExpandGroupRecursive( groupID, m_pVProfile->GetRoot() );
}
class CVProfLabel : public Label
{
DECLARE_CLASS_SIMPLE( CVProfLabel, Label );
public:
CVProfLabel( Panel *parent, const char *panelName ) :
BaseClass( parent, panelName, "" )
{
//SetPaintBackgroundEnabled( false );
}
virtual void ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
SetFont( pScheme->GetFont( PROF_FONT ) );
SetBgColor( Color( 0, 0, 0, 255 ) );
}
};
Label *AllocateVprofLabel( const char *panelName )
{
CVProfLabel *l = new CVProfLabel( NULL, panelName );
l->SetContentAlignment( Label::a_west );
return l;
}
void CVProfPanel::AddColumns( CProfileHierarchyPanel::ColumnPanels_t& cp )
{
cp.AddColumn( 1, "group", AllocateVprofLabel( "group" ) );
cp.AddColumn( 2, "data", AllocateVprofLabel( "data" ) );
}
//-----------------------------------------------------------------------------
// Populate the tree
//-----------------------------------------------------------------------------
int CVProfPanel::UpdateVProfTreeEntry( KeyValues *pKeyValues, CVProfNode *pNode, int parent )
{
VPROF( "UpdateVProfTreeEntry" );
CFmtStrN<1024> msg;
double curTimeLessChildren = pNode->GetCurTimeLessChildren();
double avgLessChildren = ( pNode->GetTotalCalls() > 0 ) ? pNode->GetTotalTimeLessChildren() / (double)pNode->GetTotalCalls() : 0;
pKeyValues->SetString( "Text", pNode->GetName() );
pKeyValues->SetString( "group", m_pVProfile->GetBudgetGroupName( pNode->GetBudgetGroupID() ) );
CFmtStrN< 256 > frame;
CFmtStrN< 256 > avgstr;
CFmtStrN< 256 > sum;
CFmtStrN< 256 > l2miss;
frame.sprintf( "[%4d] %7.2f %7.2f", pNode->GetCurCalls(), pNode->GetCurTime(), curTimeLessChildren );
pKeyValues->SetString( "frame", msg );
if( vprof_verbose.GetBool() )
{
double avgCalls = ( m_pVProfile->NumFramesSampled() > 0 ) ? (double)pNode->GetTotalCalls() / (double)m_pVProfile->NumFramesSampled() : 0;
double avg = ( m_pVProfile->NumFramesSampled() > 0 ) ? (double)pNode->GetTotalTime() / (double)m_pVProfile->NumFramesSampled() : 0;
double avgLessChildrenVerbose = ( m_pVProfile->NumFramesSampled() > 0 ) ? (double)pNode->GetTotalTimeLessChildren() / (double)m_pVProfile->NumFramesSampled() : 0;
avgstr.sprintf( "[%6.2f] %6.3f %6.3f", avgCalls, avg, avgLessChildrenVerbose );
sum.sprintf( "[%7d] %9.2f %9.2f %8.2fp", pNode->GetTotalCalls(), pNode->GetTotalTime(), pNode->GetTotalTimeLessChildren(), pNode->GetPeakTime() );
}
if ( m_pVProfile->UsePME() )
{
l2miss.sprintf( "%5d", pNode->GetL2CacheMisses() );
}
msg.sprintf( DATA_FMT_STR,
(const char *)frame,
(const char *)avgstr,
(const char *)sum,
(const char *)l2miss );
pKeyValues->SetString( "data", msg );
pKeyValues->SetFloat( "Time", avgLessChildren );
// Add or modify a line in the hierarchy
int id = pNode->GetClientData();
if ( id == -1 )
{
CProfileHierarchyPanel::ColumnPanels_t cp;
AddColumns( cp );
id = m_pHierarchy->AddItem( pKeyValues, parent, cp ) ;
pNode->SetClientData( id );
}
else
{
VPROF( "UpdateVProfTreeEntry:Modify" );
m_pHierarchy->ModifyItem( pKeyValues, id );
}
// Apply color to the item
int r,g,b,a;
m_pVProfile->GetBudgetGroupColor( pNode->GetBudgetGroupID(), r, g, b, a );
m_pHierarchy->SetItemColors( id, Color( r, g, b, a ), Color( 0, 0, 0, 255 ) );
if( pNode->GetBudgetGroupID() == VPROF_BUDGET_GROUP_ID_UNACCOUNTED )
{
if ( curTimeLessChildren > vprof_unaccounted_limit.GetFloat() )
{
m_pHierarchy->SetItemColors( id, Color( 255, 0, 0, 255 ), Color( 0, 0, 0, 255 ) );
}
}
if ( pNode->GetCurTime() > vprof_warningmsec.GetFloat() ||
curTimeLessChildren > vprof_warningmsec.GetFloat() )
{
m_pHierarchy->SetItemColumnColors( id, 2, Color( 255, 0, 0, 255 ), Color( 63, 0, 0, 255 ) );
}
return id;
}
//-----------------------------------------------------------------------------
// Populate the tree
//-----------------------------------------------------------------------------
void CVProfPanel::FillTree( KeyValues *pKeyValues, CVProfNode *pNode, int parent )
{
#ifdef VPROF_ENABLED
bool fIsRoot = ( pNode == m_pVProfile->GetRoot() );
if ( fIsRoot )
{
if( pNode->GetChild() )
{
FillTree( pKeyValues, pNode->GetChild(), parent );
}
return;
}
int id = parent;
if (( m_nCurrentBudgetGroup < 0 ) || ( pNode->GetBudgetGroupID() == m_nCurrentBudgetGroup ))
{
id = UpdateVProfTreeEntry( pKeyValues, pNode, parent );
}
if( pNode->GetSibling() )
{
FillTree( pKeyValues, pNode->GetSibling(), parent );
}
if( pNode->GetChild() )
{
FillTree( pKeyValues, pNode->GetChild(), m_bHierarchicalView ? id : parent );
}
#endif
}
//-----------------------------------------------------------------------------
// Populates the budget group combo box
//-----------------------------------------------------------------------------
void CVProfPanel::PopulateBudgetGroupComboBox()
{
int nBudgetGroupCount = m_pVProfile->GetNumBudgetGroups();
while( m_nLastBudgetGroupCount < nBudgetGroupCount )
{
m_pVProfCategory->AddItem( m_pVProfile->GetBudgetGroupName(m_nLastBudgetGroupCount), NULL );
++m_nLastBudgetGroupCount;
}
}
//-----------------------------------------------------------------------------
// Populates the tree
//-----------------------------------------------------------------------------
void CVProfPanel::UpdateProfile( float filteredtime )
{
#ifdef VPROF_ENABLED
//ExecuteDeferredOp();
if (IsVisible())
{
PopulateBudgetGroupComboBox();
SetTitle( CFmtStr( "VProf (%s) -- %d frames sampled",
m_pVProfile->IsEnabled() ? "running" : "not running",
m_pVProfile->NumFramesSampled() ), false );
// It's important to cache bEnabled since calling pause can disable.
bool bEnabled = m_pVProfile->IsEnabled();
if( bEnabled )
{
m_pVProfile->Pause();
}
KeyValues * pVal = new KeyValues("");
if ( !m_pHierarchy->GetTree()->GetItemCount() )
{
pVal->SetString( "Text", "Call tree" );
CProfileHierarchyPanel::ColumnPanels_t cp;
AddColumns( cp );
m_RootItem = m_pHierarchy->AddItem( pVal, -1, cp );
m_pHierarchy->SetItemColors( m_RootItem, Color( 255, 255, 255, 255 ), Color( 0, 0, 0, 255 ) );
m_pHierarchy->ExpandItem( m_RootItem, true );
}
m_pHierarchy->ExpandItem( m_RootItem, true );
const char *pScope = vprof_scope.GetString();
CVProfNode *pStartNode = ( pScope[0] == 0 ) ? m_pVProfile->GetRoot() : m_pVProfile->FindNode(m_pVProfile->GetRoot(), pScope );
if ( pStartNode )
{
FillTree( pVal, pStartNode, m_RootItem );
}
pVal->deleteThis();
if( bEnabled )
{
m_pVProfile->Resume();
}
}
if ( m_pVProfile->IsEnabled() )
{
Assert( m_pVProfile->AtRoot() );
if ( GetBudgetPanel()->IsBudgetPanelShown() )
GetBudgetPanel()->SnapshotVProfHistory( filteredtime );
WriteRemoteVProfData(); // send out the vprof data to remote endpoints
}
#endif
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CVProfPanel::UserCmd_ShowVProf( void )
{
m_fShowVprofHeld = true;
SetVisible( true );
// This is hacky . . need to at least remember the previous value to set it back.
ConVarRef cl_mouseenable( "cl_mouseenable" );
if ( cl_mouseenable.IsValid() )
{
cl_mouseenable.SetValue( 0 );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CVProfPanel::UserCmd_HideVProf( void )
{
m_fShowVprofHeld = false;
SetVisible( false );
// This is hacky . . need to at least remember the previous value to set it back.
ConVarRef cl_mouseenable( "cl_mouseenable" );
if ( cl_mouseenable.IsValid() )
{
cl_mouseenable.SetValue( 1 );
}
}
//-----------------------------------------------------------------------------
void CVProfPanel::Paint()
{
m_pVProfile->Pause();
BaseClass::Paint();
m_pVProfile->Resume();
}
void CVProfPanel::OnCommand( const char *pCommand )
{
#ifndef _XBOX
if ( !Q_stricmp( pCommand, "StepForward" ) )
{
VProfPlayback_Step();
}
else if ( !Q_stricmp( pCommand, "StepBack" ) )
{
int shouldReset = VProfPlayback_StepBack();
if ( shouldReset == 2 )
{
Reset();
}
}
else if ( !Q_stricmp( pCommand, "GotoButton" ) )
{
int shouldReset = VProfPlayback_SeekToPercent( (float)m_pPlaybackScroll->GetValue() / 1000.0 );
if ( shouldReset == 2 )
{
Reset();
}
}
else if ( !Q_stricmp( pCommand, "redosort" ) )
{
//
Assert( !m_bHierarchicalView );
Reset();
}
#endif
}
#endif