//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $Workfile: $ // $Date: $ // $NoKeywords: $ //=============================================================================// #include "client_pch.h" #include "ivideomode.h" #include "VGuiMatSurface/IMatSystemSurface.h" #include #include #include #include #include "materialsystem/imaterialsystem.h" #include "materialsystem/imesh.h" #include "materialsystem/imaterial.h" #include "materialsystem/MaterialSystemUtil.h" #include "client.h" #include "gl_matsysiface.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" #ifdef VPROF_ENABLED static ConVar vprof_graph ( "vprof_graph","0", 0, "Draw the vprof graph." ); static ConVar vprof_graphwidth ( "vprof_graphwidth", "512", FCVAR_ARCHIVE ); static ConVar vprof_graphheight( "vprof_graphheight", "256", FCVAR_ARCHIVE ); #define TIMINGS 256 // Number of values to track (must be power of 2) b/c of masking #define GRAPH_RED (0.9f * 255) #define GRAPH_GREEN (0.9f * 255) #define GRAPH_BLUE (0.7f * 255) #define LERP_HEIGHT 24 //----------------------------------------------------------------------------- // Purpose: Displays the netgraph //----------------------------------------------------------------------------- class CVProfGraphPanel : public vgui::Panel { typedef vgui::Panel BaseClass; private: vgui::HFont m_hFont; public: CVProfGraphPanel( vgui::VPANEL parent ); virtual ~CVProfGraphPanel( void ); virtual void ApplySchemeSettings(vgui::IScheme *pScheme); virtual void Paint(); virtual void OnTick( void ); virtual bool ShouldDraw( void ); struct CLineSegment { int x1, y1, x2, y2; byte color[4]; }; inline void DrawLine( vrect_t *rect, unsigned char *color, unsigned char alpha ); void DrawLineSegments(); void GraphGetXY( vrect_t *rect, int width, int *x, int *y ); private: void PaintLineArt( int x, int y, int w ); CMaterialReference m_WhiteMaterial; // VProf interface: float m_Samples[ TIMINGS ][3]; CVProfNode* m_Components; int m_CurrentSample; void GetNextSample(); public: static CVProfNode* m_CurrentNode; }; CVProfNode* CVProfGraphPanel::m_CurrentNode = NULL; void IN_VProfPrevSibling(void) { CVProfNode* n = CVProfGraphPanel::m_CurrentNode->GetPrevSibling(); if( n ) CVProfGraphPanel::m_CurrentNode = n; } void IN_VProfNextSibling(void) { CVProfNode* n = CVProfGraphPanel::m_CurrentNode->GetSibling(); if( n ) CVProfGraphPanel::m_CurrentNode = n; } void IN_VProfParent(void) { CVProfNode* n = CVProfGraphPanel::m_CurrentNode->GetParent(); if( n ) CVProfGraphPanel::m_CurrentNode = n; } void IN_VProfChild(void) { CVProfNode* n = CVProfGraphPanel::m_CurrentNode->GetChild(); if( n ) { // Find the largest child: CVProfGraphPanel::m_CurrentNode = n; for( ; n; n = n->GetSibling() ) { if( n->GetPrevTime() > CVProfGraphPanel::m_CurrentNode->GetPrevTime() ) CVProfGraphPanel::m_CurrentNode = n; } } } static ConCommand vprof_siblingprev ("vprof_prevsibling", IN_VProfPrevSibling); static ConCommand vprof_siblingnext ("vprof_nextsibling", IN_VProfNextSibling); static ConCommand vprof_parent ("vprof_parent", IN_VProfParent); static ConCommand vprof_child ("vprof_child", IN_VProfChild); //----------------------------------------------------------------------------- // Purpose: // Input : *parent - //----------------------------------------------------------------------------- CVProfGraphPanel::CVProfGraphPanel( vgui::VPANEL parent ) : BaseClass( NULL, "CVProfGraphPanel" ) { SetParent( parent ); SetSize( videomode->GetModeStereoWidth(), videomode->GetModeStereoHeight() ); SetPos( 0, 0 ); SetVisible( false ); SetCursor( null ); m_hFont = 0; SetFgColor( Color( 0, 0, 0, 255 ) ); SetPaintBackgroundEnabled( false ); memset( m_Samples, 0, sizeof( m_Samples ) ); m_CurrentSample = 0; m_CurrentNode = g_VProfCurrentProfile.GetRoot(); // Move down to an interesting node ( the render / sound / etc level) if( m_CurrentNode->GetChild() ) { m_CurrentNode = m_CurrentNode->GetChild(); if( m_CurrentNode->GetChild() ) { m_CurrentNode = m_CurrentNode->GetChild(); } } vgui::ivgui()->AddTickSignal( GetVPanel() ); m_WhiteMaterial.Init( "vgui/white", TEXTURE_GROUP_OTHER ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- CVProfGraphPanel::~CVProfGraphPanel( void ) { } void CVProfGraphPanel::ApplySchemeSettings(vgui::IScheme *pScheme) { BaseClass::ApplySchemeSettings(pScheme); m_hFont = pScheme->GetFont( "DefaultVerySmall" ); Assert( m_hFont ); } //----------------------------------------------------------------------------- // Purpose: Figure out x and y position for graph based on vprof_graphpos // value. // Input : *rect - // width - // *x - // *y - //----------------------------------------------------------------------------- void CVProfGraphPanel::GraphGetXY( vrect_t *rect, int width, int *x, int *y ) { *x = rect->x + rect->width - 5 - width; *y = rect->y+rect->height - LERP_HEIGHT - 5; } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CVProfGraphPanel::OnTick( void ) { SetVisible( ShouldDraw() ); } bool CVProfGraphPanel::ShouldDraw( void ) { return vprof_graph.GetBool(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CVProfGraphPanel::Paint() { int x, y, w; vrect_t vrect; if ( ( ShouldDraw() ) == false ) return; // Get screen rectangle vrect.x = 0; vrect.y = 0; vrect.width = videomode->GetModeStereoWidth(); vrect.height = videomode->GetModeStereoHeight(); // Determine graph width w = vprof_graphwidth.GetInt(); if ( vrect.width < w + 10 ) { w = vrect.width - 10; } // Get the graph's location: GraphGetXY( &vrect, w, &x, &y ); PaintLineArt( x, y, w ); // Draw the text overlays: // Print it out y -= vprof_graphheight.GetInt(); double RootTime = g_VProfCurrentProfile.GetRoot()->GetPrevTime(); char sz[256]; if ( ( g_ClientGlobalVariables.absoluteframetime) > 0.f ) { Q_snprintf( sz, sizeof( sz ), "%s - %0.1f%%%%", m_CurrentNode->GetName(), ( m_CurrentNode->GetPrevTime() / RootTime ) * 100.f); g_pMatSystemSurface->DrawColoredText( m_hFont, x, y, GRAPH_RED, GRAPH_GREEN, GRAPH_BLUE, 255, "%s", sz ); } byte color[3][3] = { { 255, 0, 0 }, { 0, 0, 255 }, { 255, 255, 255 }, }; const char *pTitles[3]; pTitles[0] = m_CurrentNode->GetName(); pTitles[1] = "Parent"; pTitles[2] = "Total"; // Draw the legend: x += w / 2; for( int i = 3; --i >= 0; ) { Q_snprintf( sz, sizeof( sz ), "%07.3f ms (%s)", m_Samples[m_CurrentSample][i], pTitles[i] ); y -= 10; g_pMatSystemSurface->DrawColoredText( m_hFont, x, y, color[i][0], color[i][1], color[i][2], 180, "%s", sz ); } } // VProf interface: void CVProfGraphPanel::GetNextSample() { // Increment to the next sample: m_CurrentSample = ( m_CurrentSample + 1 ) % TIMINGS; m_Samples[m_CurrentSample][0] = m_CurrentNode->GetPrevTime(); m_Samples[m_CurrentSample][1] = m_CurrentNode->GetParent() ? m_CurrentNode->GetParent()->GetPrevTime() : m_CurrentNode->GetPrevTime(); m_Samples[m_CurrentSample][2] = g_VProfCurrentProfile.GetRoot()->GetPrevTime(); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CVProfGraphPanel::PaintLineArt( int x, int y, int w ) { int nPanelHeight = vprof_graphheight.GetFloat() - LERP_HEIGHT - 2; int h, a; // Update the sample graph: GetNextSample(); CMatRenderContextPtr pRenderContext( materials ); IMesh* m_pMesh = pRenderContext->GetDynamicMesh( true, NULL, NULL, m_WhiteMaterial ); CMeshBuilder meshBuilder; meshBuilder.Begin( m_pMesh, MATERIAL_LINES, 3 * w + 4 ); // Draw lines at 20, 30, 60 hz, and baseline int i; for ( i = 0; i < 4; ++i ) { int nLineY = y - (nPanelHeight / 3) * i; if ( i == 0 ) { meshBuilder.Color4ub( 255, 255, 255, 255 ); } else { meshBuilder.Color4ub( 128, 128, 128, 255 ); } meshBuilder.TexCoord2f( 0, 0.0f, 0.0f ); meshBuilder.Position3f( x, nLineY, 0 ); meshBuilder.AdvanceVertex(); if ( i == 0 ) { meshBuilder.Color4ub( 255, 255, 255, 255 ); } else { meshBuilder.Color4ub( 128, 128, 128, 255 ); } meshBuilder.TexCoord2f( 0, 0.0f, 0.0f ); meshBuilder.Position3f( x + w, nLineY, 0 ); meshBuilder.AdvanceVertex(); } byte color[3][4] = { { 255, 0, 0, 255 }, { 0, 0, 255, 255 }, { 255, 255, 255, 255 }, }; // 0.05f = 1/20 = 20Hz float flMsToPixel = nPanelHeight / 50.0f; float flDxDSample = (w <= TIMINGS) ? 1.0f : (float)w / (float)TIMINGS; for( i = 3; --i >= 0; ) { int sample = m_CurrentSample; for (a=w; a >= 0; a-- ) { h = (int)(m_Samples[sample][i] * flMsToPixel + 0.5f); // Clamp the height: (though it shouldn't need it) if ( h > nPanelHeight ) { h = nPanelHeight; } int px = (int)(x + (w - a - 1) * flDxDSample + 0.5f); int py = y - h; meshBuilder.Color4ubv( color[i] ); meshBuilder.TexCoord2f( 0, 0.0f, 0.0f ); meshBuilder.Position3f( px, py, 0 ); meshBuilder.AdvanceVertex(); if ( ( a != w ) && ( a != 0 ) ) { meshBuilder.Color4ubv( color[i] ); meshBuilder.TexCoord2f( 0, 0.0f, 0.0f ); meshBuilder.Position3f( px, py, 0 ); meshBuilder.AdvanceVertex(); } // Move on to the next sample: sample--; if ( sample < 0 ) { sample = TIMINGS - 1; } } } meshBuilder.End(); m_pMesh->Draw(); } #endif // VPROF_ENABLED //----------------------------------------------------------------------------- // Creates/destroys the vprof graph panel //----------------------------------------------------------------------------- #ifdef VPROF_ENABLED static CVProfGraphPanel *s_pVProfGraphPanel = NULL; #endif void CreateVProfGraphPanel( vgui::Panel *pParent ) { #ifdef VPROF_ENABLED s_pVProfGraphPanel = new CVProfGraphPanel( pParent->GetVPanel() ); #endif } void DestroyVProfGraphPanel() { #ifdef VPROF_ENABLED if ( s_pVProfGraphPanel ) { s_pVProfGraphPanel->SetParent( (vgui::Panel *)NULL ); delete s_pVProfGraphPanel; s_pVProfGraphPanel = NULL; } #endif }