mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-24 23:26:50 +00:00
1156 lines
29 KiB
C++
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
|