Last active
October 21, 2025 19:53
-
-
Save samisalreadytaken/6069a082871d8076ceee5ea4d5de1740 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //----------------------------------------------------------------------- | |
| // github.com/samisalreadytaken | |
| //----------------------------------------------------------------------- | |
| // | |
| // sqdbg profiler visualiser | |
| // | |
| // cvars: sqdbg_profvis_server, sqdbg_profvis_server_perframe, sqdbg_profvis_server_callgraph | |
| // sqdbg_profvis_client, sqdbg_profvis_client_perframe, sqdbg_profvis_client_callgraph | |
| // | |
| // Manual: | |
| // local out = sqdbg_prof_gets(0); | |
| // g_ProfVis <- CProfVis(); | |
| // g_ProfVis.ParseOutput( out ); | |
| // | |
| local g_bPerFrame = false; | |
| local g_bProfOutType = 0; | |
| local g_flThinkRate = 0.1; | |
| if ( SERVER_DLL ) | |
| { | |
| local NetMsg = NetMsg, split = split; | |
| local function SendProfDataToClient( player ) | |
| { | |
| sqdbg_prof_pause(); | |
| local out = sqdbg_prof_gets( g_bProfOutType ); | |
| if ( out ) | |
| { | |
| if ( g_bPerFrame ) | |
| sqdbg_prof_reset(); | |
| local lines = split( out, "\n" ); | |
| local linecount = lines.len(); | |
| NetMsg.Start( "sqdbg_profvis_data_init" ); | |
| NetMsg.WriteShort( linecount - 1 ); | |
| NetMsg.Send( player, true ); | |
| for ( local i = 1; i < linecount; ++i ) | |
| { | |
| NetMsg.Start( "sqdbg_profvis_data" ); | |
| NetMsg.WriteShort( i - 1 ); | |
| NetMsg.WriteString( lines[i] ); | |
| NetMsg.Send( player, true ); | |
| } | |
| } | |
| sqdbg_prof_resume(); | |
| return g_flThinkRate; | |
| } | |
| Convars.RegisterConvar( "sqdbg_profvis_server_perframe", "0", "", FCVAR_GAMEDLL ); | |
| Convars.SetChangeCallback( "sqdbg_profvis_server_perframe", function( cvar, szOld, flOld, szNew, flNew ) | |
| { | |
| if ( flNew == 2.0 ) | |
| { | |
| g_flThinkRate = 0.0; | |
| } | |
| else | |
| { | |
| g_flThinkRate = 0.1; | |
| } | |
| g_bPerFrame = !!flNew; | |
| } ); | |
| Convars.RegisterConvar( "sqdbg_profvis_server_callgraph", "1", "", FCVAR_CLIENTDLL ); | |
| Convars.SetChangeCallback( "sqdbg_profvis_server_callgraph", function( cvar, szOld, flOld, szNew, flNew ) | |
| { | |
| if ( flNew ) | |
| { | |
| g_bProfOutType = 0; | |
| } | |
| else | |
| { | |
| g_bProfOutType = 1; | |
| } | |
| } ); | |
| Convars.RegisterConvar( "sqdbg_profvis_server", "0", "", FCVAR_GAMEDLL ); | |
| Convars.SetChangeCallback( "sqdbg_profvis_server", function( cvar, szOld, flOld, szNew, flNew ) | |
| { | |
| local ent = Entities.GetLocalPlayer(); | |
| if ( !ent ) | |
| return; | |
| if ( flNew ) | |
| { | |
| ent.SetContextThink( "sqdbg_profvis_data", SendProfDataToClient, 0.1 ); | |
| } | |
| else | |
| { | |
| ent.SetContextThink( "sqdbg_profvis_data", null, 0.0 ); | |
| NetMsg.Start( "sqdbg_profvis_data_init" ); | |
| NetMsg.WriteShort( 0 ); | |
| NetMsg.Send( player, true ); | |
| } | |
| } ); | |
| } | |
| if ( CLIENT_DLL ) | |
| { | |
| local MOUSE_ENABLE_KEY = ButtonCode.KEY_F2; | |
| local sqdbg_prof_pause = dummy, sqdbg_prof_resume = dummy; | |
| local NetMsg = NetMsg, split = split, strip = strip, lstrip = lstrip, format = format; | |
| local surface = surface, input = input, vgui = vgui; | |
| local g_ServerLines = []; | |
| if ( "g_ServerProfVis" in getroottable() && ::g_ServerProfVis ) | |
| ::g_ServerProfVis.Destroy(); | |
| if ( "g_ClientProfVis" in getroottable() && ::g_ClientProfVis ) | |
| ::g_ClientProfVis.Destroy(); | |
| ::g_ServerProfVis <- null; | |
| ::g_ClientProfVis <- null; | |
| if ( "g_ProfVisNodePool" in getroottable() ) | |
| { | |
| ::g_ProfVisNodePool.clear(); | |
| } | |
| else | |
| { | |
| ::g_ProfVisNodePool <- []; | |
| } | |
| local g_ClientProfVis = ::g_ClientProfVis, | |
| g_ServerProfVis = ::g_ServerProfVis, | |
| g_ProfVisNodePool = ::g_ProfVisNodePool; | |
| local kAddrLen = 2 + _intsize_ * 2; | |
| const kMarginX = 16; | |
| const kMarginY = 24; | |
| { | |
| local CONST = getconsttable(); | |
| delete CONST.kMarginX; | |
| delete CONST.kMarginY; | |
| } | |
| class ::CProfVis | |
| { | |
| static node_t = class | |
| { | |
| percentage = 0.0; | |
| time = null; | |
| calls = null; | |
| funcname = null; | |
| longname = null; | |
| details = null; | |
| basenode = null; | |
| sub_end = null; | |
| } | |
| m_Panel = null; | |
| m_ResetTree = null; | |
| m_Nodes = null; | |
| m_Width = 0; | |
| m_Font = 0; | |
| m_CharWidth = 0; | |
| m_BarHeight = 0; | |
| m_iMouseOver = null; | |
| m_iMouseOverTmp = null; | |
| m_SelectedTree = null; | |
| m_iBaseNode = null; | |
| m_flPercentageMultiplier = 1.0; | |
| m_MouseX = 0; | |
| m_MouseY = 0; | |
| m_bCursorVisible = false; | |
| constructor() | |
| { | |
| sqdbg_prof_pause(); | |
| m_Nodes = []; | |
| m_SelectedTree = []; | |
| InitFont(); | |
| m_Panel = vgui.CreatePanel( "Frame", vgui.GetRootPanel(), "" ); | |
| m_Panel.SetVisible( true ); | |
| m_Panel.SetMoveable( true ) | |
| m_Panel.SetSizeable( true ) | |
| m_Panel.SetTitleBarVisible( false ) | |
| m_Panel.SetCloseButtonVisible( false ) | |
| m_Panel.SetDeleteSelfOnClose( false ) | |
| m_Panel.SetCallback( "PerformLayout", PerformLayout.bindenv(this) ); | |
| m_Panel.SetCallback( "Paint", Draw.bindenv(this) ); | |
| m_Panel.SetCallback( "OnMousePressed", OnMousePressed.bindenv(this) ); | |
| m_Panel.MakePopup(); | |
| m_Panel.SetMouseInputEnabled( false ); | |
| m_Panel.SetKeyBoardInputEnabled( false ); | |
| m_Panel.SetPos( XRES(32), YRES(32) ); | |
| m_Panel.SetSize( XRES(640 - 32 * 2), kMarginY * 2 + m_BarHeight * 11 ); | |
| sqdbg_prof_resume(); | |
| } | |
| function Destroy() | |
| { | |
| sqdbg_prof_pause(); | |
| m_Panel.Destroy(); | |
| m_Panel = | |
| m_ResetTree = | |
| m_SelectedTree = | |
| m_iBaseNode = | |
| m_iMouseOver = null; | |
| ClearNodes( m_Nodes ); | |
| sqdbg_prof_resume(); | |
| } | |
| function CreateTreeResetButton() | |
| { | |
| if ( m_ResetTree ) | |
| { | |
| m_ResetTree.SetVisible( true ); | |
| } | |
| else | |
| { | |
| m_ResetTree = vgui.CreatePanel( "Button", m_Panel, "" ); | |
| m_ResetTree.MakeReadyForUse(); | |
| PerformLayout_ResetButton(); | |
| m_ResetTree.SetVisible( true ); | |
| m_ResetTree.SetPaintBorderEnabled( true ); | |
| m_ResetTree.SetCallback( "DoClick", ResetTreeSelection.bindenv(this) ); | |
| } | |
| } | |
| function PerformLayout_ResetButton() | |
| { | |
| if ( m_ResetTree ) | |
| { | |
| m_ResetTree.SetDefaultColor( 0, 0, 0, 0, 255, 200, 50, 127 ); | |
| m_ResetTree.SetDepressedColor( 0, 0, 0, 0, 255, 255, 255, 127 ); | |
| m_ResetTree.SetArmedColor( 0, 0, 0, 0, 255, 255, 255, 127 ); | |
| m_ResetTree.SetPos( kMarginX, kMarginY / 2 ); | |
| m_ResetTree.SetSize( m_BarHeight, m_BarHeight ); | |
| } | |
| } | |
| function SetTreeSelection( iNode ) | |
| { | |
| local node = m_Nodes[ iNode ]; | |
| m_iBaseNode = iNode; | |
| m_flPercentageMultiplier = 1.0 / node.percentage; | |
| CreateTreeResetButton(); | |
| m_SelectedTree.clear(); | |
| do | |
| { | |
| local aidx = node.details.find( "0x" ); | |
| local addr = node.details.slice( aidx, aidx + kAddrLen ); | |
| m_SelectedTree.append( addr ); | |
| } | |
| while ( node = node.basenode ); | |
| } | |
| function ResetTreeSelection() | |
| { | |
| m_iBaseNode = null; | |
| m_flPercentageMultiplier = 1.0; | |
| m_ResetTree.SetVisible( false ); | |
| m_SelectedTree.clear(); | |
| } | |
| function InitFont() | |
| { | |
| m_Font = surface.GetFont( "DefaultFixedOutline", false, "Tracker" ); | |
| m_CharWidth = surface.GetCharacterWidth( m_Font, 'A' ); | |
| m_BarHeight = surface.GetFontTall( m_Font ) + 2; | |
| } | |
| function PerformLayout() | |
| { | |
| sqdbg_prof_pause(); | |
| InitFont(); | |
| m_Panel.SetBgColor( 0, 0, 0, 31 ); | |
| m_Width = m_Panel.GetWide() - kMarginX * 2; | |
| PerformLayout_ResetButton(); | |
| return sqdbg_prof_resume(); | |
| } | |
| function NewNode() | |
| { | |
| if ( 0 in g_ProfVisNodePool ) | |
| { | |
| local n = g_ProfVisNodePool.pop(); | |
| n.time = | |
| n.funcname = | |
| n.longname = | |
| n.details = | |
| n.basenode = | |
| n.sub_end = null; | |
| return n; | |
| } | |
| return node_t(); | |
| } | |
| function ClearNodes( mem ) | |
| { | |
| g_ProfVisNodePool.extend( mem ); | |
| return mem.clear(); | |
| } | |
| function FindNode( mem, idx, end, addr ) | |
| { | |
| for ( ; idx < end; ++idx ) | |
| { | |
| local n = mem[idx]; | |
| if ( n.details.find( addr ) != null ) | |
| return idx; | |
| } | |
| } | |
| function ParseOutput( lines ) | |
| { | |
| sqdbg_prof_pause(); | |
| ClearNodes( m_Nodes ); | |
| if ( 0 in lines ) | |
| { | |
| switch ( typeof lines ) | |
| { | |
| case "string": | |
| { | |
| local i = lines[3] == '%' ? lines.find( "\n" ) + 1 : 0; | |
| while ( i in lines ) | |
| i = ParseLines( m_Nodes, null, lines, i ); | |
| break; | |
| } | |
| case "array": | |
| { | |
| local i = ( lines[0][3] == '%' ).tointeger(); | |
| while ( i in lines ) | |
| i = ParseLineSplit( m_Nodes, null, lines, i ); | |
| break; | |
| } | |
| default: throw "invalid"; | |
| } | |
| if ( m_iBaseNode != null ) | |
| { | |
| local iSel = m_SelectedTree.len() - 1; | |
| local iEnd = m_Nodes.len(); | |
| local iNode = -1; | |
| local node; | |
| do | |
| { | |
| local addr = m_SelectedTree[ iSel ]; | |
| iNode = FindNode( m_Nodes, iNode + 1, iEnd, addr ); | |
| if ( iNode != null ) | |
| { | |
| node = m_Nodes[ iNode ]; | |
| iEnd = node.sub_end; | |
| } | |
| else | |
| { | |
| break; | |
| } | |
| } | |
| while ( iSel-- && iEnd ); | |
| if ( iNode != null ) | |
| { | |
| m_iBaseNode = iNode; | |
| m_flPercentageMultiplier = 1.0 / node.percentage; | |
| } | |
| else | |
| { | |
| ResetTreeSelection(); | |
| } | |
| } | |
| m_iMouseOver = null; | |
| } | |
| return sqdbg_prof_resume(); | |
| } | |
| function ParseLineSplit( nodes, baseNode, lines, i ) | |
| { | |
| for (;;) | |
| { | |
| local node = NewNode(); | |
| nodes.append( node ); | |
| local line = lines[i]; | |
| if ( line.find( " N/A" ) != 0 ) | |
| node.percentage = line.slice( 0, 6 ).tofloat() * 0.01; | |
| local time = node.time = strip( line.slice( 19, 28 ) ); | |
| if ( line.find( " N/A", 29 ) != 29 ) | |
| node.calls = lstrip( line.slice( 29, 39 ) ); | |
| local fidx = 41; | |
| while ( fidx in line && line[ fidx ] == '|' ) | |
| fidx += 3; | |
| --fidx; | |
| local extra = ( line[ fidx ] >= '1' && line[ fidx ] <= '9' ).tointeger(); | |
| ++fidx; | |
| local funcname = node.funcname = line.slice( fidx - extra, line.find( ",", fidx - extra + 1 ) ); | |
| node.details = line.slice( fidx - extra, line.len() ); | |
| node.longname = format( "%s (%s)", funcname, time ); | |
| node.basenode = baseNode; | |
| // next line | |
| ++i; | |
| if ( i in lines ) | |
| { | |
| line = lines[i]; | |
| // Has subcalls | |
| if ( fidx in line && line[ fidx ] == '|' ) | |
| { | |
| i = ParseLineSplit( nodes, node, lines, i ); | |
| node.sub_end = nodes.len(); | |
| if ( i in lines ) | |
| { | |
| line = lines[i]; | |
| } | |
| else | |
| { | |
| break; | |
| } | |
| } | |
| fidx -= 3; | |
| // Last call in tree | |
| if ( !( fidx in line && line[ fidx ] == '|' ) ) | |
| break; | |
| } | |
| else | |
| { | |
| break; | |
| } | |
| } | |
| return i; | |
| } | |
| function ParseLines( nodes, baseNode, lines, iBase ) | |
| { | |
| for (;;) | |
| { | |
| local node = NewNode(); | |
| nodes.append( node ); | |
| local end = lines.find( "\n", iBase ); | |
| if ( end == null ) | |
| end = lines.len(); | |
| if ( lines.find( " N/A", iBase ) != iBase ) | |
| node.percentage = lines.slice( iBase, iBase + 6 ).tofloat() * 0.01; | |
| iBase += 19; | |
| local time = node.time = strip( lines.slice( iBase, iBase + 9 ) ); | |
| iBase += 10; | |
| if ( lines.find( " N/A", iBase ) != iBase ) | |
| node.calls = lstrip( lines.slice( iBase, iBase + 10 ) ); | |
| iBase += 12; | |
| local fidx = 41; | |
| while ( iBase in lines && lines[ iBase ] == '|' ) | |
| { | |
| iBase += 3; | |
| fidx += 3; | |
| } | |
| --iBase; | |
| local extra = ( lines[ iBase ] >= '1' && lines[ iBase ] <= '9' ).tointeger(); | |
| ++iBase; | |
| local funcname = node.funcname = lines.slice( iBase - extra, lines.find( ",", iBase - extra + 1 ) ); | |
| node.details = lines.slice( iBase - extra, end ); | |
| node.longname = format( "%s (%s)", funcname, time ); | |
| node.basenode = baseNode; | |
| // next line | |
| iBase = end + 1; | |
| if ( iBase in lines ) | |
| { | |
| // Has subcalls | |
| if ( iBase + fidx in lines && lines[ iBase + fidx ] == '|' ) | |
| { | |
| iBase = ParseLines( nodes, node, lines, iBase ); | |
| node.sub_end = nodes.len(); | |
| if ( !( iBase in lines ) ) | |
| break; | |
| } | |
| fidx -= 3; | |
| // Last call in tree | |
| if ( !( iBase + fidx in lines && lines[ iBase + fidx ] == '|' ) ) | |
| break; | |
| } | |
| else | |
| { | |
| break; | |
| } | |
| } | |
| return iBase; | |
| } | |
| // returns int32 [ index (16), xpos (16) ] | |
| function DrawNode( i, x, y ) | |
| { | |
| local node = m_Nodes[i]; | |
| local barWidth = ( m_Width * node.percentage * m_flPercentageMultiplier + 0.5 ).tointeger(); | |
| if ( barWidth < 2 ) | |
| return i << 16; | |
| if ( m_bCursorVisible && | |
| m_MouseX >= x && m_MouseX < x + barWidth && | |
| m_MouseY >= y && m_MouseY < y + m_BarHeight - 1 ) | |
| { | |
| m_iMouseOverTmp = i; | |
| surface.SetColor( 255, 255, 255, 255 ); | |
| surface.DrawOutlinedRect( x, y, barWidth, m_BarHeight, 2 ); | |
| } | |
| else | |
| { | |
| surface.SetColor( 195, 195, 195, 255 ); | |
| surface.DrawOutlinedRect( x, y, barWidth, m_BarHeight, 1 ); | |
| } | |
| barWidth -= 2; | |
| surface.SetColor( 195, 100, 100, 63 ); | |
| surface.DrawFilledRect( x + 1, y + 1, barWidth, m_BarHeight - 2 ); | |
| surface.SetTextPos( x + 2, y + 2 ); | |
| local text = node.longname; | |
| local textWidth = m_CharWidth * text.len(); | |
| if ( textWidth > barWidth ) | |
| { | |
| text = node.funcname; | |
| textWidth = m_CharWidth * text.len(); | |
| } | |
| if ( textWidth <= barWidth ) | |
| { | |
| surface.DrawText( text, 0 ); | |
| } | |
| else if ( 1 in text && m_CharWidth * 5 <= barWidth ) | |
| { | |
| surface.DrawUnicodeChar( text[0], 0 ); | |
| surface.DrawUnicodeChar( text[1], 0 ); | |
| surface.DrawText( "...", 0 ); | |
| } | |
| local end = node.sub_end; | |
| if ( end ) for ( ++i;; ) | |
| { | |
| local ret = DrawNode( i, x, y + m_BarHeight - 1 ); | |
| x += ret & 0x0000ffff; | |
| i = ( ret & 0xffff0000 ) >>> 16; | |
| if ( ++i >= end ) | |
| { | |
| --i; | |
| break; | |
| } | |
| } | |
| return ( barWidth + 1 ) | ( i << 16 ); | |
| } | |
| function Draw() | |
| { | |
| sqdbg_prof_pause(); | |
| if ( m_bCursorVisible ) | |
| { | |
| if ( !input.IsButtonDown( MOUSE_ENABLE_KEY ) ) | |
| { | |
| m_Panel.SetMouseInputEnabled( m_bCursorVisible = false ); | |
| } | |
| } | |
| else | |
| { | |
| if ( input.IsButtonDown( MOUSE_ENABLE_KEY ) ) | |
| { | |
| m_Panel.SetMouseInputEnabled( m_bCursorVisible = true ); | |
| m_Panel.MoveToFront(); | |
| input.SetCursorPos( | |
| m_Panel.GetXPos() + m_Panel.GetWide() / 2, | |
| m_Panel.GetYPos() + m_Panel.GetTall() / 2 ); | |
| } | |
| } | |
| local x = kMarginX, y = kMarginY * 1.5; | |
| m_MouseX = input.GetAnalogValue( AnalogCode.MOUSE_X ) - m_Panel.GetXPos(); | |
| m_MouseY = input.GetAnalogValue( AnalogCode.MOUSE_Y ) - m_Panel.GetYPos(); | |
| surface.SetTextFont( m_Font ); | |
| surface.SetTextColor( 255, 255, 255, 255 ); | |
| if ( m_iBaseNode != null ) | |
| { | |
| DrawNode( m_iBaseNode, x, y ); | |
| } | |
| else | |
| { | |
| local i = 0; | |
| local count = m_Nodes.len(); | |
| for ( ; i < count; ++i ) | |
| { | |
| local ret = DrawNode( i, x, y ); | |
| x += ret & 0x0000ffff; | |
| i = ( ret & 0xffff0000 ) >>> 16; | |
| } | |
| } | |
| if ( m_iMouseOverTmp != null ) | |
| { | |
| local node = m_Nodes[ m_iMouseOverTmp ]; | |
| local h = surface.GetFontTall( m_Font ); | |
| local w = node.details.len() * m_CharWidth; | |
| local mx = m_MouseX; | |
| local my = m_MouseY; | |
| local pw = m_Panel.GetWide() - w; | |
| if ( mx > pw ) | |
| mx = pw; | |
| surface.SetColor( 100, 100, 100, 195 ); | |
| surface.DrawFilledRect( mx, my, w, h * 3 + 3 ); | |
| surface.SetTextPos( mx + 2, my + 2 ); | |
| surface.DrawText( node.details, 0 ); | |
| surface.SetTextPos( mx + 2, my + 2 + h + 1 ); | |
| surface.DrawText( node.time, 0 ); | |
| surface.SetTextPos( mx + 2, my + 2 + h + h + 2 ); | |
| surface.DrawText( node.calls, 0 ); | |
| m_iMouseOver = m_iMouseOverTmp; | |
| m_iMouseOverTmp = null; | |
| } | |
| else | |
| { | |
| m_iMouseOver = null; | |
| } | |
| return sqdbg_prof_resume(); | |
| } | |
| function OnMousePressed( code ) | |
| { | |
| if ( code == ButtonCode.MOUSE_LEFT ) | |
| { | |
| if ( m_iMouseOver != null ) | |
| { | |
| return SetTreeSelection( m_iMouseOver ); | |
| } | |
| } | |
| } | |
| } | |
| local function RecvProfVisDataLen() | |
| { | |
| sqdbg_prof_pause(); | |
| local size = NetMsg.ReadShort(); | |
| g_ServerLines.clear(); | |
| g_ServerLines.resize( size ); | |
| if ( size ) | |
| { | |
| if ( !g_ServerProfVis ) | |
| g_ServerProfVis = CProfVis(); | |
| } | |
| else if ( g_ServerProfVis ) | |
| { | |
| g_ServerProfVis.Destroy(); | |
| } | |
| return sqdbg_prof_resume(); | |
| } | |
| local function RecvProfVisData() | |
| { | |
| sqdbg_prof_pause(); | |
| local index = NetMsg.ReadShort(); | |
| local line = NetMsg.ReadString(); | |
| if ( 0 in g_ServerLines ) | |
| { | |
| g_ServerLines[ index ] = line; | |
| if ( index == g_ServerLines.len() - 1 ) | |
| g_ServerProfVis.ParseOutput( g_ServerLines ); | |
| } | |
| return sqdbg_prof_resume(); | |
| } | |
| NetMsg.Receive( "sqdbg_profvis_data_init", RecvProfVisDataLen ); | |
| NetMsg.Receive( "sqdbg_profvis_data", RecvProfVisData ); | |
| local function ThinkProfVis(_) | |
| { | |
| sqdbg_prof_pause(); | |
| local out = sqdbg_prof_gets( g_bProfOutType ); | |
| if ( out ) | |
| { | |
| if ( g_bPerFrame ) | |
| sqdbg_prof_reset(); | |
| g_ClientProfVis.ParseOutput( out ); | |
| } | |
| sqdbg_prof_resume(); | |
| return g_flThinkRate; | |
| } | |
| Convars.RegisterConvar( "sqdbg_profvis_client_perframe", "0", "", FCVAR_CLIENTDLL ); | |
| Convars.SetChangeCallback( "sqdbg_profvis_client_perframe", function( cvar, szOld, flOld, szNew, flNew ) | |
| { | |
| if ( flNew == 2.0 ) | |
| { | |
| g_flThinkRate = 0.0; | |
| } | |
| else | |
| { | |
| g_flThinkRate = 0.1; | |
| } | |
| g_bPerFrame = !!flNew; | |
| } ); | |
| Convars.RegisterConvar( "sqdbg_profvis_client_callgraph", "1", "", FCVAR_CLIENTDLL ); | |
| Convars.SetChangeCallback( "sqdbg_profvis_client_callgraph", function( cvar, szOld, flOld, szNew, flNew ) | |
| { | |
| if ( flNew ) | |
| { | |
| g_bProfOutType = 0; | |
| } | |
| else | |
| { | |
| g_bProfOutType = 1; | |
| } | |
| } ); | |
| Convars.RegisterConvar( "sqdbg_profvis_client", "0", "", FCVAR_CLIENTDLL ); | |
| Convars.SetChangeCallback( "sqdbg_profvis_client", function( cvar, szOld, flOld, szNew, flNew ) | |
| { | |
| local ent = Entities.First(); | |
| if ( !ent ) | |
| return; | |
| if ( flNew ) | |
| { | |
| ent.SetContextThink( "sqdbg_profvis_data", ThinkProfVis, 0.1 ); | |
| if ( !g_ClientProfVis ) | |
| g_ClientProfVis = CProfVis(); | |
| } | |
| else | |
| { | |
| ent.SetContextThink( "sqdbg_profvis_data", null, 0.0 ); | |
| if ( g_ClientProfVis ) | |
| { | |
| g_ClientProfVis.Destroy(); | |
| g_ClientProfVis = null; | |
| } | |
| } | |
| } ); | |
| Convars.RegisterConvar( "sqdbg_profvis_exclude_self", "1", "", FCVAR_CLIENTDLL ); | |
| Convars.SetChangeCallback( "sqdbg_profvis_exclude_self", function( cvar, szOld, flOld, szNew, flNew ) | |
| { | |
| if ( flNew ) | |
| { | |
| if ( !( "sqdbg_prof_pause" in getroottable() ) ) | |
| return Convars.SetInt( cvar, 0 ); | |
| sqdbg_prof_pause = ::sqdbg_prof_pause; | |
| sqdbg_prof_resume = ::sqdbg_prof_resume; | |
| return; | |
| } | |
| sqdbg_prof_pause = ::dummy; | |
| sqdbg_prof_resume = ::dummy; | |
| } ); | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment