tracy/server/TracyView_ZoneInfo.cpp
2022-08-15 22:32:42 +02:00

1965 lines
79 KiB
C++

#include <inttypes.h>
#include "TracyFilesystem.hpp"
#include "TracyImGui.hpp"
#include "TracyPrint.hpp"
#include "TracyMouse.hpp"
#include "TracyView.hpp"
namespace tracy
{
extern double s_time;
template<typename T>
static inline uint32_t GetZoneCallstack( const T& ev, const Worker& worker );
template<>
inline uint32_t GetZoneCallstack<ZoneEvent>( const ZoneEvent& ev, const Worker& worker )
{
return worker.GetZoneExtra( ev ).callstack.Val();
}
template<>
inline uint32_t GetZoneCallstack<GpuEvent>( const GpuEvent& ev, const Worker& worker )
{
return ev.callstack.Val();
}
void View::CalcZoneTimeData( unordered_flat_map<int16_t, ZoneTimeData>& data, int64_t& ztime, const ZoneEvent& zone )
{
assert( zone.HasChildren() );
const auto& children = m_worker.GetZoneChildren( zone.Child() );
if( children.is_magic() )
{
CalcZoneTimeDataImpl<VectorAdapterDirect<ZoneEvent>>( *(Vector<ZoneEvent>*)( &children ), data, ztime, zone );
}
else
{
CalcZoneTimeDataImpl<VectorAdapterPointer<ZoneEvent>>( children, data, ztime, zone );
}
}
template<typename Adapter, typename V>
void View::CalcZoneTimeDataImpl( const V& children, unordered_flat_map<int16_t, ZoneTimeData>& data, int64_t& ztime, const ZoneEvent& zone )
{
Adapter a;
if( m_timeDist.exclusiveTime )
{
int64_t zt = ztime;
for( auto& child : children )
{
const auto t = m_worker.GetZoneEnd( a(child) ) - a(child).Start();
zt -= t;
}
ztime = zt;
}
for( auto& child : children )
{
const auto srcloc = a(child).SrcLoc();
const auto t = m_worker.GetZoneEnd( a(child) ) - a(child).Start();
auto it = data.find( srcloc );
if( it == data.end() )
{
it = data.emplace( srcloc, ZoneTimeData { t, 1 } ).first;
}
else
{
it->second.time += t;
it->second.count++;
}
if( a(child).Child() >= 0 ) CalcZoneTimeData( data, it->second.time, a(child) );
}
}
void View::CalcZoneTimeData( const ContextSwitch* ctx, unordered_flat_map<int16_t, ZoneTimeData>& data, int64_t& ztime, const ZoneEvent& zone )
{
assert( zone.HasChildren() );
const auto& children = m_worker.GetZoneChildren( zone.Child() );
if( children.is_magic() )
{
CalcZoneTimeDataImpl<VectorAdapterDirect<ZoneEvent>>( *(Vector<ZoneEvent>*)( &children ), ctx, data, ztime, zone );
}
else
{
CalcZoneTimeDataImpl<VectorAdapterPointer<ZoneEvent>>( children, ctx, data, ztime, zone );
}
}
template<typename Adapter, typename V>
void View::CalcZoneTimeDataImpl( const V& children, const ContextSwitch* ctx, unordered_flat_map<int16_t, ZoneTimeData>& data, int64_t& ztime, const ZoneEvent& zone )
{
Adapter a;
if( m_timeDist.exclusiveTime )
{
int64_t zt = ztime;
for( auto& child : children )
{
int64_t t;
uint64_t cnt;
const auto res = GetZoneRunningTime( ctx, a(child), t, cnt );
assert( res );
zt -= t;
}
ztime = zt;
}
for( auto& child : children )
{
const auto srcloc = a(child).SrcLoc();
int64_t t;
uint64_t cnt;
const auto res = GetZoneRunningTime( ctx, a(child), t, cnt );
assert( res );
auto it = data.find( srcloc );
if( it == data.end() )
{
it = data.emplace( srcloc, ZoneTimeData { t, 1 } ).first;
}
else
{
it->second.time += t;
it->second.count++;
}
if( a(child).HasChildren() ) CalcZoneTimeData( ctx, data, it->second.time, a(child) );
}
}
template<typename T>
void DrawZoneTrace( T zone, const std::vector<T>& trace, const Worker& worker, BuzzAnim<const void*>& anim, View& view, bool& showUnknownFrames, std::function<void(T, int&)> showZone )
{
bool expand = ImGui::TreeNode( "Zone trace" );
ImGui::SameLine();
ImGui::TextDisabled( "(%s)", RealToString( trace.size() ) );
if( !expand ) return;
const auto shortenName = view.GetShortenName();
ImGui::SameLine();
SmallCheckbox( "Show unknown frames", &showUnknownFrames );
int fidx = 1;
TextDisabledUnformatted( "0." );
ImGui::SameLine();
TextDisabledUnformatted( "[this zone]" );
if( !trace.empty() )
{
T prev = zone;
const auto sz = trace.size();
for( size_t i=0; i<sz; i++ )
{
auto curr = trace[i];
const auto pcv = GetZoneCallstack( *prev, worker );
const auto ccv = GetZoneCallstack( *curr, worker );
if( pcv == 0 || ccv == 0 )
{
if( showUnknownFrames )
{
ImGui::TextDisabled( "%i.", fidx++ );
ImGui::SameLine();
TextDisabledUnformatted( "[unknown frames]" );
}
}
else if( pcv != ccv )
{
auto& prevCs = worker.GetCallstack( pcv );
auto& currCs = worker.GetCallstack( ccv );
const auto psz = int( prevCs.size() );
int idx;
for( idx=0; idx<psz; idx++ )
{
auto pf = prevCs[idx];
bool found = false;
for( auto& cf : currCs )
{
if( cf.data == pf.data )
{
idx--;
found = true;
break;
}
}
if( found ) break;
}
for( int j=1; j<idx; j++ )
{
auto frameData = worker.GetCallstackFrame( prevCs[j] );
auto frame = frameData->data + frameData->size - 1;
ImGui::TextDisabled( "%i.", fidx++ );
ImGui::SameLine();
const auto frameName = worker.GetString( frame->name );
const auto normalized = shortenName != ShortenName::Never ? ShortenZoneName( ShortenName::OnlyNormalize, frameName ) : frameName;
TextDisabledUnformatted( normalized );
TooltipNormalizedName( frameName, normalized );
ImGui::SameLine();
ImGui::Spacing();
if( anim.Match( frame ) )
{
const auto time = anim.Time();
const auto indentVal = sin( time * 60.f ) * 10.f * time;
ImGui::SameLine( 0, ImGui::GetStyle().ItemSpacing.x + indentVal );
}
else
{
ImGui::SameLine();
}
const auto fileName = worker.GetString( frame->file );
TextDisabledUnformatted( LocationToString( fileName, frame->line ) );
if( ImGui::IsItemClicked( 1 ) )
{
if( !view.ViewDispatch( fileName, frame->line, frame->symAddr ) )
{
anim.Enable( frame, 0.5f );
}
}
}
}
showZone( curr, fidx );
prev = curr;
}
}
auto last = trace.empty() ? zone : trace.back();
const auto lcv = GetZoneCallstack( *last, worker );
if( lcv == 0 )
{
if( showUnknownFrames )
{
ImGui::TextDisabled( "%i.", fidx++ );
ImGui::SameLine();
TextDisabledUnformatted( "[unknown frames]" );
}
}
else
{
auto& cs = worker.GetCallstack( lcv );
const auto csz = cs.size();
for( uint16_t i=1; i<csz; i++ )
{
auto frameData = worker.GetCallstackFrame( cs[i] );
auto frame = frameData->data + frameData->size - 1;
ImGui::TextDisabled( "%i.", fidx++ );
ImGui::SameLine();
const auto frameName = worker.GetString( frame->name );
const auto normalized = shortenName != ShortenName::Never ? ShortenZoneName( ShortenName::OnlyNormalize, frameName ) : frameName;
TextDisabledUnformatted( normalized );
TooltipNormalizedName( frameName, normalized );
ImGui::SameLine();
ImGui::Spacing();
if( anim.Match( frame ) )
{
const auto time = anim.Time();
const auto indentVal = sin( time * 60.f ) * 10.f * time;
ImGui::SameLine( 0, ImGui::GetStyle().ItemSpacing.x + indentVal );
}
else
{
ImGui::SameLine();
}
const auto fileName = worker.GetString( frame->file );
TextDisabledUnformatted( LocationToString( fileName, frame->line ) );
if( ImGui::IsItemClicked( 1 ) )
{
if( !view.ViewDispatch( fileName, frame->line, frame->symAddr ) )
{
anim.Enable( frame, 0.5f );
}
}
}
}
ImGui::TreePop();
}
void View::DrawInfoWindow()
{
if( m_zoneInfoWindow )
{
DrawZoneInfoWindow();
}
else if( m_gpuInfoWindow )
{
DrawGpuInfoWindow();
}
}
void View::DrawZoneInfoWindow()
{
auto& ev = *m_zoneInfoWindow;
const auto& srcloc = m_worker.GetSourceLocation( ev.SrcLoc() );
const auto scale = GetScale();
ImGui::SetNextWindowSize( ImVec2( 500 * scale, 600 * scale ), ImGuiCond_FirstUseEver );
bool show = true;
ImGui::Begin( "Zone info", &show, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse );
if( !ImGui::GetCurrentWindowRead()->SkipItems )
{
if( ImGui::Button( ICON_FA_MICROSCOPE " Zoom to zone" ) )
{
ZoomToZone( ev );
}
auto parent = GetZoneParent( ev );
if( parent )
{
ImGui::SameLine();
if( ImGui::Button( ICON_FA_ARROW_UP " Go to parent" ) )
{
ShowZoneInfo( *parent );
}
}
#ifndef TRACY_NO_STATISTICS
if( m_worker.AreSourceLocationZonesReady() )
{
const auto sl = ev.SrcLoc();
const auto& slz = m_worker.GetZonesForSourceLocation( sl );
if( !slz.zones.empty() )
{
ImGui::SameLine();
if( ImGui::Button( ICON_FA_CHART_BAR " Statistics" ) )
{
m_findZone.ShowZone( sl, m_worker.GetString( srcloc.name.active ? srcloc.name : srcloc.function ) );
}
}
}
#endif
if( m_worker.HasZoneExtra( ev ) && m_worker.GetZoneExtra( ev ).callstack.Val() != 0 )
{
const auto& extra = m_worker.GetZoneExtra( ev );
ImGui::SameLine();
bool hilite = m_callstackInfoWindow == extra.callstack.Val();
if( hilite )
{
SetButtonHighlightColor();
}
if( ImGui::Button( ICON_FA_ALIGN_JUSTIFY " Call stack" ) )
{
m_callstackInfoWindow = extra.callstack.Val();
}
if( hilite )
{
ImGui::PopStyleColor( 3 );
}
}
const auto fileName = m_worker.GetString( srcloc.file );
if( SourceFileValid( fileName, m_worker.GetCaptureTime(), *this, m_worker ) )
{
ImGui::SameLine();
bool hilite = m_sourceViewFile == fileName;
if( hilite )
{
SetButtonHighlightColor();
}
if( ImGui::Button( ICON_FA_FILE_ALT " Source" ) )
{
ViewSource( fileName, srcloc.line );
}
if( hilite )
{
ImGui::PopStyleColor( 3 );
}
}
if( !m_zoneInfoStack.empty() )
{
ImGui::SameLine();
if( ImGui::Button( ICON_FA_ARROW_LEFT " Go back" ) )
{
m_zoneInfoWindow = m_zoneInfoStack.back_and_pop();
}
}
ImGui::Separator();
auto threadData = GetZoneThreadData( ev );
assert( threadData );
const auto tid = threadData->id;
if( m_worker.HasZoneExtra( ev ) && m_worker.GetZoneExtra( ev ).name.Active() )
{
ImGui::PushFont( m_bigFont );
TextFocused( "Zone name:", m_worker.GetString( m_worker.GetZoneExtra( ev ).name ) );
ImGui::PopFont();
if( srcloc.name.active )
{
ImGui::SameLine();
ImGui::TextDisabled( "(%s)", m_worker.GetString( srcloc.name ) );
}
ImGui::SameLine();
if( ClipboardButton( 1 ) )
{
if( srcloc.name.active )
{
char tmp[1024];
sprintf( tmp, "%s (%s)", m_worker.GetString( m_worker.GetZoneExtra( ev ).name ), m_worker.GetString( srcloc.name ) );
ImGui::SetClipboardText( tmp );
}
else
{
ImGui::SetClipboardText( m_worker.GetString( m_worker.GetZoneExtra( ev ).name ) );
}
}
TextFocused( "Function:", m_worker.GetString( srcloc.function ) );
ImGui::SameLine();
if( ClipboardButton( 2 ) ) ImGui::SetClipboardText( m_worker.GetString( srcloc.function ) );
}
else if( srcloc.name.active )
{
ImGui::PushFont( m_bigFont );
TextFocused( "Zone name:", m_worker.GetString( srcloc.name ) );
ImGui::PopFont();
ImGui::SameLine();
if( ClipboardButton( 1 ) ) ImGui::SetClipboardText( m_worker.GetString( srcloc.name ) );
TextFocused( "Function:", m_worker.GetString( srcloc.function ) );
ImGui::SameLine();
if( ClipboardButton( 2 ) ) ImGui::SetClipboardText( m_worker.GetString( srcloc.function ) );
}
else
{
ImGui::PushFont( m_bigFont );
TextFocused( "Function:", m_worker.GetString( srcloc.function ) );
ImGui::PopFont();
ImGui::SameLine();
if( ClipboardButton( 1 ) ) ImGui::SetClipboardText( m_worker.GetString( srcloc.function ) );
}
SmallColorBox( GetSrcLocColor( m_worker.GetSourceLocation( ev.SrcLoc() ), 0 ) );
ImGui::SameLine();
TextDisabledUnformatted( "Location:" );
ImGui::SameLine();
ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
ImGui::SameLine();
if( ClipboardButton( 3 ) )
{
ImGui::SetClipboardText( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
}
SmallColorBox( GetThreadColor( tid, 0 ) );
ImGui::SameLine();
TextFocused( "Thread:", m_worker.GetThreadName( tid ) );
ImGui::SameLine();
ImGui::TextDisabled( "(%s)", RealToString( tid ) );
if( m_worker.IsThreadFiber( tid ) )
{
ImGui::SameLine();
TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" );
}
if( m_worker.HasZoneExtra( ev ) && m_worker.GetZoneExtra( ev ).text.Active() )
{
TextFocused( "User text:", m_worker.GetString( m_worker.GetZoneExtra( ev ).text ) );
}
ImGui::Separator();
ImGui::BeginChild( "##zoneinfo" );
const auto end = m_worker.GetZoneEnd( ev );
const auto ztime = end - ev.Start();
const auto selftime = GetZoneSelfTime( ev );
TextFocused( "Time from start of program:", TimeToStringExact( ev.Start() ) );
TextFocused( "Execution time:", TimeToString( ztime ) );
#ifndef TRACY_NO_STATISTICS
if( m_worker.AreSourceLocationZonesReady() )
{
auto& zoneData = m_worker.GetZonesForSourceLocation( ev.SrcLoc() );
if( zoneData.total > 0 )
{
ImGui::SameLine();
ImGui::TextDisabled( "(%.2f%% of mean time)", float( ztime ) / zoneData.total * zoneData.zones.size() * 100 );
}
}
#endif
TextFocused( "Self time:", TimeToString( selftime ) );
if( ztime != 0 )
{
char buf[64];
PrintStringPercent( buf, 100.f * selftime / ztime );
ImGui::SameLine();
TextDisabledUnformatted( buf );
}
const auto ctx = m_worker.GetContextSwitchData( tid );
if( ctx )
{
auto it = std::lower_bound( ctx->v.begin(), ctx->v.end(), ev.Start(), [] ( const auto& l, const auto& r ) { return (uint64_t)l.End() < (uint64_t)r; } );
if( it != ctx->v.end() )
{
const auto end = m_worker.GetZoneEnd( ev );
auto eit = std::upper_bound( it, ctx->v.end(), end, [] ( const auto& l, const auto& r ) { return l < r.Start(); } );
bool incomplete = eit == ctx->v.end() && !m_worker.IsThreadFiber( tid );
uint64_t cnt = std::distance( it, eit );
if( cnt == 1 )
{
if( !incomplete )
{
TextFocused( "Running state time:", TimeToString( ztime ) );
ImGui::SameLine();
TextDisabledUnformatted( "(100%)" );
ImGui::Separator();
TextFocused( "Running state regions:", "1" );
if( !threadData->isFiber ) TextFocused( "CPU:", RealToString( it->Cpu() ) );
}
}
else if( cnt > 1 )
{
uint8_t cpus[256] = {};
auto bit = it;
int64_t running = it->End() - ev.Start();
cpus[it->Cpu()] = 1;
++it;
for( uint64_t i=0; i<cnt-2; i++ )
{
running += it->End() - it->Start();
cpus[it->Cpu()] = 1;
++it;
}
running += end - it->Start();
cpus[it->Cpu()] = 1;
TextFocused( "Running state time:", TimeToString( running ) );
if( ztime != 0 )
{
char buf[64];
PrintStringPercent( buf, 100.f * running / ztime );
ImGui::SameLine();
TextDisabledUnformatted( buf );
}
ImGui::Separator();
if( incomplete )
{
TextColoredUnformatted( ImVec4( 1, 0, 0, 1 ), "Incomplete context switch data!" );
}
TextFocused( "Running state regions:", RealToString( cnt ) );
if( !threadData->isFiber )
{
int numCpus = 0;
for( int i=0; i<256; i++ ) numCpus += cpus[i];
if( numCpus == 1 )
{
TextFocused( "CPU:", RealToString( it->Cpu() ) );
}
else
{
ImGui::TextDisabled( "CPUs (%i):", numCpus );
for( int i=0;; i++ )
{
if( cpus[i] != 0 )
{
ImGui::SameLine();
numCpus--;
if( numCpus == 0 )
{
ImGui::Text( "%i", i );
break;
}
else
{
int consecutive = 1;
int remaining = numCpus;
for(;;)
{
if( cpus[i+consecutive] == 0 ) break;
consecutive++;
if( --remaining == 0 ) break;
}
if( consecutive > 2 )
{
if( remaining == 0 )
{
ImGui::Text( "%i \xE2\x80\x93 %i", i, i+consecutive-1 );
break;
}
else
{
ImGui::Text( "%i \xE2\x80\x93 %i,", i, i+consecutive-1 );
i += consecutive - 1;
numCpus = remaining;
}
}
else
{
ImGui::Text( "%i,", i );
}
}
}
}
}
}
--eit;
if( ImGui::TreeNode( "Wait regions" ) )
{
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
SmallCheckbox( "Time relative to zone start", &m_ctxSwitchTimeRelativeToZone );
const int64_t adjust = m_ctxSwitchTimeRelativeToZone ? ev.Start() : 0;
const auto wrsz = eit - bit;
const auto numColumns = threadData->isFiber ? 4 : 6;
if( ImGui::BeginTable( "##waitregions", numColumns, ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable, ImVec2( 0, ImGui::GetTextLineHeightWithSpacing() * std::min<int64_t>( 1+wrsz, 15 ) ) ) )
{
ImGui::TableSetupScrollFreeze( 0, 1 );
ImGui::TableSetupColumn( "Begin" );
ImGui::TableSetupColumn( "End" );
ImGui::TableSetupColumn( "Time" );
if( threadData->isFiber )
{
ImGui::TableSetupColumn( "Thread" );
}
else
{
ImGui::TableSetupColumn( "Wakeup" );
ImGui::TableSetupColumn( "CPU" );
ImGui::TableSetupColumn( "State" );
}
ImGui::TableHeadersRow();
ImGuiListClipper clipper;
clipper.Begin( wrsz );
while( clipper.Step() )
{
for( auto i=clipper.DisplayStart; i<clipper.DisplayEnd; i++ )
{
const auto cend = bit[i].End();
const auto cstart = bit[i+1].Start();
const auto cwakeup = bit[i+1].WakeupVal();
ImGui::PushID( i );
ImGui::TableNextRow();
ImGui::TableNextColumn();
auto tt = adjust == 0 ? TimeToStringExact( cend ) : TimeToString( cend - adjust );
if( ImGui::Selectable( tt ) )
{
CenterAtTime( cend );
}
ImGui::TableNextColumn();
tt = adjust == 0 ? TimeToStringExact( cstart ) : TimeToString( cstart - adjust );
if( ImGui::Selectable( tt ) )
{
CenterAtTime( cstart );
}
ImGui::TableNextColumn();
if( ImGui::Selectable( TimeToString( cwakeup - cend ) ) )
{
ZoomToRange( cend, cwakeup );
}
ImGui::TableNextColumn();
if( threadData->isFiber )
{
const auto ftid = m_worker.DecompressThread( bit[i].Thread() );
ImGui::TextUnformatted( m_worker.GetThreadName( ftid ) );
ImGui::SameLine();
ImGui::TextDisabled( "(%s)", RealToString( ftid ) );
}
else
{
const auto cpu0 = bit[i].Cpu();
const auto reason = bit[i].Reason();
const auto state = bit[i].State();
const auto cpu1 = bit[i+1].Cpu();
if( cstart != cwakeup )
{
if( ImGui::Selectable( TimeToString( cstart - cwakeup ) ) )
{
ZoomToRange( cwakeup, cstart );
}
}
else
{
ImGui::TextUnformatted( "-" );
}
ImGui::TableNextColumn();
if( cpu0 == cpu1 )
{
ImGui::TextUnformatted( RealToString( cpu0 ) );
}
else
{
ImGui::Text( "%i " ICON_FA_LONG_ARROW_ALT_RIGHT " %i", cpu0, cpu1 );
const auto tt0 = m_worker.GetThreadTopology( cpu0 );
const auto tt1 = m_worker.GetThreadTopology( cpu1 );
if( tt0 && tt1 )
{
if( tt0->package != tt1->package )
{
ImGui::SameLine();
TextDisabledUnformatted( "P" );
}
else if( tt0->core != tt1->core )
{
ImGui::SameLine();
TextDisabledUnformatted( "C" );
}
}
}
ImGui::TableNextColumn();
const char* desc;
if( reason == ContextSwitchData::NoState )
{
ImGui::TextUnformatted( DecodeContextSwitchStateCode( state ) );
desc = DecodeContextSwitchState( state );
}
else
{
ImGui::TextUnformatted( DecodeContextSwitchReasonCode( reason ) );
desc = DecodeContextSwitchReason( reason );
}
if( *desc ) TooltipIfHovered( desc );
}
ImGui::PopID();
}
}
ImGui::EndTable();
}
ImGui::TreePop();
}
}
}
}
ImGui::Separator();
auto& memNameMap = m_worker.GetMemNameMap();
if( memNameMap.size() > 1 )
{
ImGui::AlignTextToFramePadding();
TextDisabledUnformatted( ICON_FA_ARCHIVE " Memory pool:" );
ImGui::SameLine();
if( ImGui::BeginCombo( "##memoryPool", m_zoneInfoMemPool == 0 ? "Default allocator" : m_worker.GetString( m_zoneInfoMemPool ) ) )
{
for( auto& v : memNameMap )
{
if( ImGui::Selectable( v.first == 0 ? "Default allocator" : m_worker.GetString( v.first ) ) )
{
m_zoneInfoMemPool = v.first;
}
}
ImGui::EndCombo();
}
}
auto& mem = m_worker.GetMemoryNamed( m_zoneInfoMemPool );
if( mem.data.empty() )
{
TextDisabledUnformatted( "No memory events." );
}
else
{
if( !mem.plot )
{
ImGui::Text( "Please wait, computing data..." );
DrawWaitingDots( s_time );
}
else
{
const auto thread = m_worker.CompressThread( tid );
auto ait = std::lower_bound( mem.data.begin(), mem.data.end(), ev.Start(), [] ( const auto& l, const auto& r ) { return l.TimeAlloc() < r; } );
const auto aend = std::upper_bound( ait, mem.data.end(), end, [] ( const auto& l, const auto& r ) { return l < r.TimeAlloc(); } );
auto fit = std::lower_bound( mem.frees.begin(), mem.frees.end(), ev.Start(), [&mem] ( const auto& l, const auto& r ) { return mem.data[l].TimeFree() < r; } );
const auto fend = std::upper_bound( fit, mem.frees.end(), end, [&mem] ( const auto& l, const auto& r ) { return l < mem.data[r].TimeFree(); } );
const auto aDist = std::distance( ait, aend );
const auto fDist = std::distance( fit, fend );
if( aDist == 0 && fDist == 0 )
{
TextDisabledUnformatted( "No memory events." );
}
else
{
int64_t cAlloc = 0;
int64_t cFree = 0;
int64_t nAlloc = 0;
int64_t nFree = 0;
auto ait2 = ait;
auto fit2 = fit;
while( ait != aend )
{
if( ait->ThreadAlloc() == thread )
{
cAlloc += ait->Size();
nAlloc++;
}
ait++;
}
while( fit != fend )
{
if( mem.data[*fit].ThreadFree() == thread )
{
cFree += mem.data[*fit].Size();
nFree++;
}
fit++;
}
if( nAlloc == 0 && nFree == 0 )
{
TextDisabledUnformatted( "No memory events." );
}
else
{
ImGui::TextUnformatted( RealToString( nAlloc + nFree ) );
ImGui::SameLine();
TextDisabledUnformatted( "memory events." );
ImGui::TextUnformatted( RealToString( nAlloc ) );
ImGui::SameLine();
TextDisabledUnformatted( "allocs," );
ImGui::SameLine();
ImGui::TextUnformatted( RealToString( nFree ) );
ImGui::SameLine();
TextDisabledUnformatted( "frees." );
TextFocused( "Memory allocated:", MemSizeToString( cAlloc ) );
TextFocused( "Memory freed:", MemSizeToString( cFree ) );
TextFocused( "Overall change:", MemSizeToString( cAlloc - cFree ) );
if( ImGui::TreeNode( "Allocations list" ) )
{
ImGui::SameLine();
ImGui::Spacing();
ImGui::SameLine();
SmallCheckbox( "Time relative to zone start", &m_allocTimeRelativeToZone );
std::vector<const MemEvent*> v;
v.reserve( nAlloc + nFree );
auto it = ait2;
while( it != aend )
{
if( it->ThreadAlloc() == thread )
{
v.emplace_back( it );
}
it++;
}
while( fit2 != fend )
{
const auto ptr = &mem.data[*fit2++];
if( ptr->ThreadFree() == thread )
{
if( ptr < ait2 || ptr >= aend )
{
v.emplace_back( ptr );
}
}
}
pdqsort_branchless( v.begin(), v.end(), [] ( const auto& l, const auto& r ) { return l->TimeAlloc() < r->TimeAlloc(); } );
ListMemData( v, []( auto v ) {
ImGui::Text( "0x%" PRIx64, v->Ptr() );
}, nullptr, m_allocTimeRelativeToZone ? ev.Start() : -1, m_zoneInfoMemPool );
ImGui::TreePop();
}
}
}
}
}
ImGui::Separator();
{
if( threadData->messages.empty() )
{
TextDisabledUnformatted( "No messages" );
}
else
{
auto msgit = std::lower_bound( threadData->messages.begin(), threadData->messages.end(), ev.Start(), [] ( const auto& lhs, const auto& rhs ) { return lhs->time < rhs; } );
auto msgend = std::lower_bound( msgit, threadData->messages.end(), end+1, [] ( const auto& lhs, const auto& rhs ) { return lhs->time < rhs; } );
const auto dist = std::distance( msgit, msgend );
if( dist == 0 )
{
TextDisabledUnformatted( "No messages" );
}
else
{
bool expand = ImGui::TreeNode( "Messages" );
ImGui::SameLine();
ImGui::TextDisabled( "(%s)", RealToString( dist ) );
if( expand )
{
ImGui::SameLine();
SmallCheckbox( "Time relative to zone start", &m_messageTimeRelativeToZone );
ImGui::SameLine();
SmallCheckbox( "Exclude children", &m_messagesExcludeChildren );
if( ImGui::BeginTable( "##messages", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersInnerV, ImVec2( 0, ImGui::GetTextLineHeightWithSpacing() * std::min<int64_t>( msgend-msgit+1, 15 ) ) ) )
{
ImGui::TableSetupScrollFreeze( 0, 1 );
ImGui::TableSetupColumn( "Time", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
ImGui::TableSetupColumn( "Message" );
ImGui::TableHeadersRow();
do
{
if( m_messagesExcludeChildren && GetZoneChild( ev, (*msgit)->time ) ) continue;
ImGui::PushID( *msgit );
ImGui::TableNextRow();
ImGui::TableNextColumn();
if( ImGui::Selectable( m_messageTimeRelativeToZone ? TimeToString( (*msgit)->time - ev.Start() ) : TimeToStringExact( (*msgit)->time ), m_msgHighlight == *msgit, ImGuiSelectableFlags_SpanAllColumns ) )
{
CenterAtTime( (*msgit)->time );
}
if( ImGui::IsItemHovered() )
{
m_msgHighlight = *msgit;
}
ImGui::PopID();
ImGui::TableNextColumn();
ImGui::PushStyleColor( ImGuiCol_Text, (*msgit)->color );
ImGui::TextWrapped( "%s", m_worker.GetString( (*msgit)->ref ) );
ImGui::PopStyleColor();
}
while( ++msgit != msgend );
ImGui::EndTable();
}
ImGui::TreePop();
ImGui::Spacing();
}
}
}
}
ImGui::Separator();
std::vector<const ZoneEvent*> zoneTrace;
while( parent )
{
zoneTrace.emplace_back( parent );
parent = GetZoneParent( *parent );
}
int idx = 0;
DrawZoneTrace<const ZoneEvent*>( &ev, zoneTrace, m_worker, m_zoneinfoBuzzAnim, *this, m_showUnknownFrames, [&idx, this] ( const ZoneEvent* v, int& fidx ) {
ImGui::TextDisabled( "%i.", fidx++ );
ImGui::SameLine();
const auto& srcloc = m_worker.GetSourceLocation( v->SrcLoc() );
SmallColorBox( GetSrcLocColor( srcloc, 0 ) );
ImGui::SameLine();
const auto txt = m_worker.GetZoneName( *v, srcloc );
ImGui::PushID( idx++ );
auto sel = ImGui::Selectable( txt, false );
auto hover = ImGui::IsItemHovered();
const auto fileName = m_worker.GetString( srcloc.file );
if( m_zoneinfoBuzzAnim.Match( v ) )
{
const auto time = m_zoneinfoBuzzAnim.Time();
const auto indentVal = sin( time * 60.f ) * 10.f * time;
ImGui::SameLine( 0, ImGui::GetStyle().ItemSpacing.x + indentVal );
}
else
{
ImGui::SameLine();
}
ImGui::TextDisabled( "(%s) %s", TimeToString( m_worker.GetZoneEnd( *v ) - v->Start() ), LocationToString( fileName, srcloc.line ) );
ImGui::PopID();
if( ImGui::IsItemClicked( 1 ) )
{
if( SourceFileValid( fileName, m_worker.GetCaptureTime(), *this, m_worker ) )
{
ViewSource( fileName, srcloc.line );
}
else
{
m_zoneinfoBuzzAnim.Enable( v, 0.5f );
}
}
if( sel )
{
ShowZoneInfo( *v );
}
if( hover )
{
m_zoneHighlight = v;
if( IsMouseClicked( 2 ) )
{
ZoomToZone( *v );
}
ZoneTooltip( *v );
}
} );
if( ev.HasChildren() )
{
const auto& children = m_worker.GetZoneChildren( ev.Child() );
bool expand = ImGui::TreeNode( "Child zones" );
ImGui::SameLine();
ImGui::TextDisabled( "(%s)", RealToString( children.size() ) );
if( expand )
{
if( children.is_magic() )
{
DrawZoneInfoChildren<VectorAdapterDirect<ZoneEvent>>( *(Vector<ZoneEvent>*)( &children ), ztime );
}
else
{
DrawZoneInfoChildren<VectorAdapterPointer<ZoneEvent>>( children, ztime );
}
ImGui::TreePop();
}
expand = ImGui::TreeNode( "Time distribution" );
if( expand )
{
ImGui::SameLine();
if( SmallCheckbox( "Self time", &m_timeDist.exclusiveTime ) ) m_timeDist.dataValidFor = nullptr;
if( ctx )
{
ImGui::SameLine();
if( SmallCheckbox( "Running time", &m_timeDist.runningTime ) ) m_timeDist.dataValidFor = nullptr;
}
if( m_timeDist.dataValidFor != &ev )
{
m_timeDist.data.clear();
if( ev.IsEndValid() ) m_timeDist.dataValidFor = &ev;
if( m_timeDist.runningTime )
{
assert( ctx );
int64_t time;
uint64_t cnt;
if( !GetZoneRunningTime( ctx, ev, time, cnt ) )
{
TextDisabledUnformatted( "Incomplete context switch data." );
m_timeDist.dataValidFor = nullptr;
}
else
{
auto it = m_timeDist.data.emplace( ev.SrcLoc(), ZoneTimeData{ time, 1 } ).first;
CalcZoneTimeData( ctx, m_timeDist.data, it->second.time, ev );
}
m_timeDist.fztime = 100.f / time;
}
else
{
auto it = m_timeDist.data.emplace( ev.SrcLoc(), ZoneTimeData{ ztime, 1 } ).first;
CalcZoneTimeData( m_timeDist.data, it->second.time, ev );
m_timeDist.fztime = 100.f / ztime;
}
}
if( !m_timeDist.data.empty() )
{
std::vector<unordered_flat_map<int16_t, ZoneTimeData>::const_iterator> vec;
vec.reserve( m_timeDist.data.size() );
for( auto it = m_timeDist.data.cbegin(); it != m_timeDist.data.cend(); ++it ) vec.emplace_back( it );
if( ImGui::BeginTable( "##timedist", 3, ImGuiTableFlags_Sortable | ImGuiTableFlags_BordersInnerV ) )
{
ImGui::TableSetupColumn( "Zone", ImGuiTableColumnFlags_PreferSortDescending );
ImGui::TableSetupColumn( "Time", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
ImGui::TableSetupColumn( "MTPC", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
ImGui::TableHeadersRow();
const auto& sortspec = *ImGui::TableGetSortSpecs()->Specs;
switch( sortspec.ColumnIndex )
{
case 0:
if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
{
pdqsort_branchless( vec.begin(), vec.end(), []( const auto& lhs, const auto& rhs ) { return lhs->second.count < rhs->second.count; } );
}
else
{
pdqsort_branchless( vec.begin(), vec.end(), []( const auto& lhs, const auto& rhs ) { return lhs->second.count > rhs->second.count; } );
}
break;
case 1:
if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
{
pdqsort_branchless( vec.begin(), vec.end(), []( const auto& lhs, const auto& rhs ) { return lhs->second.time < rhs->second.time; } );
}
else
{
pdqsort_branchless( vec.begin(), vec.end(), []( const auto& lhs, const auto& rhs ) { return lhs->second.time > rhs->second.time; } );
}
break;
case 2:
if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
{
pdqsort_branchless( vec.begin(), vec.end(), []( const auto& lhs, const auto& rhs ) { return float( lhs->second.time ) / lhs->second.count < float( rhs->second.time ) / rhs->second.count; } );
}
else
{
pdqsort_branchless( vec.begin(), vec.end(), []( const auto& lhs, const auto& rhs ) { return float( lhs->second.time ) / lhs->second.count > float( rhs->second.time ) / rhs->second.count; } );
}
break;
default:
assert( false );
break;
}
for( auto& v : vec )
{
ImGui::TableNextRow();
ImGui::TableNextColumn();
const auto& sl = m_worker.GetSourceLocation( v->first );
SmallColorBox( GetSrcLocColor( sl, 0 ) );
ImGui::SameLine();
const auto name = m_worker.GetZoneName( sl );
if( ImGui::Selectable( name, false, ImGuiSelectableFlags_SpanAllColumns ) )
{
m_findZone.ShowZone( v->first, name, ev.Start(), m_worker.GetZoneEnd( ev ) );
}
ImGui::SameLine();
ImGui::TextDisabled( "(\xc3\x97%s)", RealToString( v->second.count ) );
ImGui::TableNextColumn();
ImGui::TextUnformatted( TimeToString( v->second.time ) );
ImGui::SameLine();
char buf[64];
PrintStringPercent( buf, v->second.time * m_timeDist.fztime );
TextDisabledUnformatted( buf );
ImGui::TableNextColumn();
ImGui::TextUnformatted( TimeToString( v->second.time / v->second.count ) );
}
ImGui::EndTable();
}
}
ImGui::TreePop();
}
}
ImGui::EndChild();
}
ImGui::End();
if( !show )
{
m_zoneInfoWindow = nullptr;
m_zoneInfoStack.clear();
}
}
template<typename Adapter, typename V>
void View::DrawZoneInfoChildren( const V& children, int64_t ztime )
{
Adapter a;
const auto rztime = 1.0 / ztime;
const auto ty = ImGui::GetTextLineHeight();
ImGui::SameLine();
SmallCheckbox( "Group children locations", &m_groupChildrenLocations );
if( m_groupChildrenLocations )
{
struct ChildGroup
{
int16_t srcloc;
uint64_t t;
Vector<uint32_t> v;
};
uint64_t ctime = 0;
unordered_flat_map<int16_t, ChildGroup> cmap;
cmap.reserve( 128 );
for( size_t i=0; i<children.size(); i++ )
{
const auto& child = a(children[i]);
const auto cend = m_worker.GetZoneEnd( child );
const auto ct = cend - child.Start();
const auto srcloc = child.SrcLoc();
ctime += ct;
auto it = cmap.find( srcloc );
if( it == cmap.end() ) it = cmap.emplace( srcloc, ChildGroup { srcloc } ).first;
it->second.t += ct;
it->second.v.push_back( i );
}
auto msz = cmap.size();
Vector<ChildGroup*> cgvec;
cgvec.reserve_and_use( msz );
size_t idx = 0;
for( auto& it : cmap )
{
cgvec[idx++] = &it.second;
}
pdqsort_branchless( cgvec.begin(), cgvec.end(), []( const auto& lhs, const auto& rhs ) { return lhs->t > rhs->t; } );
ImGui::Columns( 2 );
ImGui::Indent( ImGui::GetTreeNodeToLabelSpacing() * 2 );
TextColoredUnformatted( ImVec4( 1.0f, 1.0f, 0.4f, 1.0f ), "Self time" );
ImGui::Unindent( ImGui::GetTreeNodeToLabelSpacing() * 2 );
ImGui::NextColumn();
char buf[128];
PrintStringPercent( buf, TimeToString( ztime - ctime ), double( ztime - ctime ) / ztime * 100 );
ImGui::ProgressBar( double( ztime - ctime ) * rztime, ImVec2( -1, ty ), buf );
ImGui::NextColumn();
for( size_t i=0; i<msz; i++ )
{
bool expandGroup = false;
const auto& cgr = *cgvec[i];
const auto& srcloc = m_worker.GetSourceLocation( cgr.srcloc );
const auto txt = m_worker.GetZoneName( srcloc );
if( cgr.v.size() == 1 )
{
auto& cev = a(children[cgr.v.front()]);
const auto txt = m_worker.GetZoneName( cev );
SmallColorBox( GetSrcLocColor( srcloc, 0 ) );
ImGui::SameLine();
ImGui::PushID( (int)cgr.v.front() );
ImGui::TreeNodeEx( txt, ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen );
if( ImGui::IsItemClicked() )
{
ShowZoneInfo( cev );
}
if( ImGui::IsItemHovered() )
{
m_zoneHighlight = &cev;
if( IsMouseClicked( 2 ) )
{
ZoomToZone( cev );
}
ZoneTooltip( cev );
}
ImGui::PopID();
}
else
{
SmallColorBox( GetSrcLocColor( srcloc, 0 ) );
ImGui::SameLine();
ImGui::PushID( cgr.srcloc );
expandGroup = ImGui::TreeNode( txt );
ImGui::PopID();
if( ImGui::IsItemHovered() )
{
ImGui::BeginTooltip();
if( srcloc.name.active )
{
ImGui::TextUnformatted( m_worker.GetString( srcloc.name ) );
}
ImGui::TextUnformatted( m_worker.GetString( srcloc.function ) );
ImGui::Separator();
ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
ImGui::EndTooltip();
}
ImGui::SameLine();
ImGui::TextDisabled( "(\xc3\x97%s)", RealToString( cgr.v.size() ) );
}
ImGui::NextColumn();
const auto part = double( cgr.t ) * rztime;
char buf[128];
PrintStringPercent( buf, TimeToString( cgr.t ), part * 100 );
ImGui::ProgressBar( part, ImVec2( -1, ty ), buf );
ImGui::NextColumn();
if( expandGroup )
{
auto ctt = std::make_unique<uint64_t[]>( cgr.v.size() );
auto cti = std::make_unique<uint32_t[]>( cgr.v.size() );
for( size_t i=0; i<cgr.v.size(); i++ )
{
const auto& child = a(children[cgr.v[i]]);
const auto cend = m_worker.GetZoneEnd( child );
const auto ct = cend - child.Start();
ctt[i] = ct;
cti[i] = uint32_t( i );
}
pdqsort_branchless( cti.get(), cti.get() + cgr.v.size(), [&ctt] ( const auto& lhs, const auto& rhs ) { return ctt[lhs] > ctt[rhs]; } );
ImGuiListClipper clipper;
clipper.Begin( cgr.v.size() );
while( clipper.Step() )
{
for( auto i=clipper.DisplayStart; i<clipper.DisplayEnd; i++ )
{
auto& cev = a(children[cgr.v[cti[i]]]);
const auto txt = m_worker.GetZoneName( cev );
bool b = false;
ImGui::Indent();
ImGui::PushID( (int)cgr.v[cti[i]] );
if( ImGui::Selectable( txt, &b, ImGuiSelectableFlags_SpanAllColumns ) )
{
ShowZoneInfo( cev );
}
if( ImGui::IsItemHovered() )
{
m_zoneHighlight = &cev;
if( IsMouseClicked( 2 ) )
{
ZoomToZone( cev );
}
ZoneTooltip( cev );
}
ImGui::PopID();
ImGui::Unindent();
ImGui::NextColumn();
const auto part = double( ctt[cti[i]] ) * rztime;
char buf[128];
PrintStringPercent( buf, TimeToString( ctt[cti[i]] ), part * 100 );
ImGui::ProgressBar( part, ImVec2( -1, ty ), buf );
ImGui::NextColumn();
}
}
ImGui::TreePop();
}
}
ImGui::EndColumns();
}
else
{
auto ctt = std::make_unique<uint64_t[]>( children.size() );
auto cti = std::make_unique<uint32_t[]>( children.size() );
uint64_t ctime = 0;
for( size_t i=0; i<children.size(); i++ )
{
const auto& child = a(children[i]);
const auto cend = m_worker.GetZoneEnd( child );
const auto ct = cend - child.Start();
ctime += ct;
ctt[i] = ct;
cti[i] = uint32_t( i );
}
pdqsort_branchless( cti.get(), cti.get() + children.size(), [&ctt] ( const auto& lhs, const auto& rhs ) { return ctt[lhs] > ctt[rhs]; } );
ImGui::Columns( 2 );
ImGui::Indent( ImGui::GetTreeNodeToLabelSpacing() );
TextColoredUnformatted( ImVec4( 1.0f, 1.0f, 0.4f, 1.0f ), "Self time" );
ImGui::Unindent( ImGui::GetTreeNodeToLabelSpacing() );
ImGui::NextColumn();
char buf[128];
PrintStringPercent( buf, TimeToString( ztime - ctime ), double( ztime - ctime ) / ztime * 100 );
ImGui::ProgressBar( double( ztime - ctime ) * rztime, ImVec2( -1, ty ), buf );
ImGui::NextColumn();
ImGuiListClipper clipper;
clipper.Begin( children.size() );
while( clipper.Step() )
{
for( auto i=clipper.DisplayStart; i<clipper.DisplayEnd; i++ )
{
auto& cev = a(children[cti[i]]);
const auto txt = m_worker.GetZoneName( cev );
bool b = false;
SmallColorBox( GetSrcLocColor( m_worker.GetSourceLocation( cev.SrcLoc() ), 0 ) );
ImGui::SameLine();
ImGui::PushID( (int)i );
if( ImGui::Selectable( txt, &b, ImGuiSelectableFlags_SpanAllColumns ) )
{
ShowZoneInfo( cev );
}
if( ImGui::IsItemHovered() )
{
m_zoneHighlight = &cev;
if( IsMouseClicked( 2 ) )
{
ZoomToZone( cev );
}
ZoneTooltip( cev );
}
ImGui::PopID();
ImGui::NextColumn();
const auto part = double( ctt[cti[i]] ) * rztime;
char buf[128];
PrintStringPercent( buf, TimeToString( ctt[cti[i]] ), part * 100 );
ImGui::ProgressBar( part, ImVec2( -1, ty ), buf );
ImGui::NextColumn();
}
}
ImGui::EndColumns();
}
}
void View::DrawGpuInfoWindow()
{
auto& ev = *m_gpuInfoWindow;
const auto& srcloc = m_worker.GetSourceLocation( ev.SrcLoc() );
const auto scale = GetScale();
ImGui::SetNextWindowSize( ImVec2( 500 * scale, 600 * scale), ImGuiCond_FirstUseEver );
bool show = true;
ImGui::Begin( "Zone info", &show, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse );
if( !ImGui::GetCurrentWindowRead()->SkipItems )
{
if( ImGui::Button( ICON_FA_MICROSCOPE " Zoom to zone" ) )
{
ZoomToZone( ev );
}
auto parent = GetZoneParent( ev );
if( parent )
{
ImGui::SameLine();
if( ImGui::Button( ICON_FA_ARROW_UP " Go to parent" ) )
{
ShowZoneInfo( *parent, m_gpuInfoWindowThread );
}
}
if( ev.callstack.Val() != 0 )
{
ImGui::SameLine();
bool hilite = m_callstackInfoWindow == ev.callstack.Val();
if( hilite )
{
SetButtonHighlightColor();
}
if( ImGui::Button( ICON_FA_ALIGN_JUSTIFY " Call stack" ) )
{
m_callstackInfoWindow = ev.callstack.Val();
}
if( hilite )
{
ImGui::PopStyleColor( 3 );
}
}
const auto fileName = m_worker.GetString( srcloc.file );
if( SourceFileValid( fileName, m_worker.GetCaptureTime(), *this, m_worker ) )
{
ImGui::SameLine();
bool hilite = m_sourceViewFile == fileName;
if( hilite )
{
SetButtonHighlightColor();
}
if( ImGui::Button( ICON_FA_FILE_ALT " Source" ) )
{
ViewSource( fileName, srcloc.line );
}
if( hilite )
{
ImGui::PopStyleColor( 3 );
}
}
if( !m_gpuInfoStack.empty() )
{
ImGui::SameLine();
if( ImGui::Button( ICON_FA_ARROW_LEFT " Go back" ) )
{
m_gpuInfoWindow = m_gpuInfoStack.back_and_pop();
}
}
ImGui::Separator();
const auto tid = GetZoneThread( ev );
ImGui::PushFont( m_bigFont );
TextFocused( "Zone name:", m_worker.GetString( srcloc.name ) );
ImGui::PopFont();
ImGui::SameLine();
if( ClipboardButton( 1 ) ) ImGui::SetClipboardText( m_worker.GetString( srcloc.name ) );
TextFocused( "Function:", m_worker.GetString( srcloc.function ) );
ImGui::SameLine();
if( ClipboardButton( 2 ) ) ImGui::SetClipboardText( m_worker.GetString( srcloc.function ) );
SmallColorBox( GetZoneColor( ev ) );
ImGui::SameLine();
TextDisabledUnformatted( "Location:" );
ImGui::SameLine();
ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
ImGui::SameLine();
if( ClipboardButton( 3 ) )
{
ImGui::SetClipboardText( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
}
SmallColorBox( GetThreadColor( tid, 0 ) );
ImGui::SameLine();
TextFocused( "Thread:", m_worker.GetThreadName( tid ) );
ImGui::SameLine();
ImGui::TextDisabled( "(%s)", RealToString( tid ) );
if( m_worker.IsThreadFiber( tid ) )
{
ImGui::SameLine();
TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" );
}
ImGui::Separator();
ImGui::BeginChild( "##gpuinfo" );
const auto end = m_worker.GetZoneEnd( ev );
const auto ztime = end - ev.GpuStart();
const auto selftime = GetZoneSelfTime( ev );
TextFocused( "Time from start of program:", TimeToStringExact( ev.GpuStart() ) );
TextFocused( "GPU execution time:", TimeToString( ztime ) );
TextFocused( "GPU self time:", TimeToString( selftime ) );
if( ztime != 0 )
{
char buf[64];
PrintStringPercent( buf, 100.f * selftime / ztime );
ImGui::SameLine();
TextDisabledUnformatted( buf );
}
TextFocused( "CPU command setup time:", TimeToString( ev.CpuEnd() - ev.CpuStart() ) );
auto ctx = GetZoneCtx( ev );
if( !ctx )
{
TextFocused( "Delay to execution:", TimeToString( ev.GpuStart() - ev.CpuStart() ) );
}
else
{
const auto td = ctx->threadData.size() == 1 ? ctx->threadData.begin() : ctx->threadData.find( m_worker.DecompressThread( ev.Thread() ) );
assert( td != ctx->threadData.end() );
int64_t begin;
if( td->second.timeline.is_magic() )
{
begin = ((Vector<GpuEvent>*)&td->second.timeline)->front().GpuStart();
}
else
{
begin = td->second.timeline.front()->GpuStart();
}
const auto drift = GpuDrift( ctx );
TextFocused( "Delay to execution:", TimeToString( AdjustGpuTime( ev.GpuStart(), begin, drift ) - ev.CpuStart() ) );
}
ImGui::Separator();
std::vector<const GpuEvent*> zoneTrace;
while( parent )
{
zoneTrace.emplace_back( parent );
parent = GetZoneParent( *parent );
}
int idx = 0;
DrawZoneTrace<const GpuEvent*>( &ev, zoneTrace, m_worker, m_zoneinfoBuzzAnim, *this, m_showUnknownFrames, [&idx, this] ( const GpuEvent* v, int& fidx ) {
ImGui::TextDisabled( "%i.", fidx++ );
ImGui::SameLine();
const auto& srcloc = m_worker.GetSourceLocation( v->SrcLoc() );
const auto txt = m_worker.GetZoneName( *v, srcloc );
ImGui::PushID( idx++ );
auto sel = ImGui::Selectable( txt, false );
auto hover = ImGui::IsItemHovered();
const auto fileName = m_worker.GetString( srcloc.file );
if( m_zoneinfoBuzzAnim.Match( v ) )
{
const auto time = m_zoneinfoBuzzAnim.Time();
const auto indentVal = sin( time * 60.f ) * 10.f * time;
ImGui::SameLine( 0, ImGui::GetStyle().ItemSpacing.x + indentVal );
}
else
{
ImGui::SameLine();
}
ImGui::TextDisabled( "(%s) %s", TimeToString( m_worker.GetZoneEnd( *v ) - v->GpuStart() ), LocationToString( fileName, srcloc.line ) );
ImGui::PopID();
if( ImGui::IsItemClicked( 1 ) )
{
if( SourceFileValid( fileName, m_worker.GetCaptureTime(), *this, m_worker ) )
{
ViewSource( fileName, srcloc.line );
}
else
{
m_zoneinfoBuzzAnim.Enable( v, 0.5f );
}
}
if( sel )
{
ShowZoneInfo( *v, m_gpuInfoWindowThread );
}
if( hover )
{
m_gpuHighlight = v;
if( IsMouseClicked( 2 ) )
{
ZoomToZone( *v );
}
ZoneTooltip( *v );
}
} );
if( ev.Child() >= 0 )
{
const auto& children = m_worker.GetGpuChildren( ev.Child() );
bool expand = ImGui::TreeNode( "Child zones" );
ImGui::SameLine();
ImGui::TextDisabled( "(%s)", RealToString( children.size() ) );
if( expand )
{
if( children.is_magic() )
{
DrawGpuInfoChildren<VectorAdapterDirect<GpuEvent>>( *(Vector<GpuEvent>*)( &children ), ztime );
}
else
{
DrawGpuInfoChildren<VectorAdapterPointer<GpuEvent>>( children, ztime );
}
ImGui::TreePop();
}
}
ImGui::EndChild();
}
ImGui::End();
if( !show )
{
m_gpuInfoWindow = nullptr;
m_gpuInfoStack.clear();
}
}
template<typename Adapter, typename V>
void View::DrawGpuInfoChildren( const V& children, int64_t ztime )
{
Adapter a;
const auto rztime = 1.0 / ztime;
const auto ty = ImGui::GetTextLineHeight();
ImGui::SameLine();
SmallCheckbox( "Group children locations", &m_groupChildrenLocations );
if( m_groupChildrenLocations )
{
struct ChildGroup
{
int16_t srcloc;
uint64_t t;
Vector<uint32_t> v;
};
uint64_t ctime = 0;
unordered_flat_map<int16_t, ChildGroup> cmap;
cmap.reserve( 128 );
for( size_t i=0; i<children.size(); i++ )
{
const auto& child = a(children[i]);
const auto cend = m_worker.GetZoneEnd( child );
const auto ct = cend - child.GpuStart();
const auto srcloc = child.SrcLoc();
ctime += ct;
auto it = cmap.find( srcloc );
if( it == cmap.end() ) it = cmap.emplace( srcloc, ChildGroup { srcloc } ).first;
it->second.t += ct;
it->second.v.push_back( i );
}
auto msz = cmap.size();
Vector<ChildGroup*> cgvec;
cgvec.reserve_and_use( msz );
size_t idx = 0;
for( auto& it : cmap )
{
cgvec[idx++] = &it.second;
}
pdqsort_branchless( cgvec.begin(), cgvec.end(), []( const auto& lhs, const auto& rhs ) { return lhs->t > rhs->t; } );
ImGui::Columns( 2 );
ImGui::Indent( ImGui::GetTreeNodeToLabelSpacing() );
TextColoredUnformatted( ImVec4( 1.0f, 1.0f, 0.4f, 1.0f ), "Self time" );
ImGui::Unindent( ImGui::GetTreeNodeToLabelSpacing() );
ImGui::NextColumn();
char buf[128];
PrintStringPercent( buf, TimeToString( ztime - ctime ), double( ztime - ctime ) / ztime * 100 );
ImGui::ProgressBar( double( ztime - ctime ) * rztime, ImVec2( -1, ty ), buf );
ImGui::NextColumn();
for( size_t i=0; i<msz; i++ )
{
bool expandGroup = false;
const auto& cgr = *cgvec[i];
const auto& srcloc = m_worker.GetSourceLocation( cgr.srcloc );
const auto txt = m_worker.GetZoneName( srcloc );
if( cgr.v.size() == 1 )
{
auto& cev = a(children[cgr.v.front()]);
const auto txt = m_worker.GetZoneName( cev );
ImGui::PushID( (int)cgr.v.front() );
ImGui::TreeNodeEx( txt, ImGuiTreeNodeFlags_Leaf | ImGuiTreeNodeFlags_NoTreePushOnOpen );
if( ImGui::IsItemClicked() )
{
ShowZoneInfo( cev, m_gpuInfoWindowThread );
}
if( ImGui::IsItemHovered() )
{
m_gpuHighlight = &cev;
if( IsMouseClicked( 2 ) )
{
ZoomToZone( cev );
}
ZoneTooltip( cev );
}
ImGui::PopID();
}
else
{
ImGui::PushID( cgr.srcloc );
expandGroup = ImGui::TreeNode( txt );
ImGui::PopID();
if( ImGui::IsItemHovered() )
{
ImGui::BeginTooltip();
if( srcloc.name.active )
{
ImGui::TextUnformatted( m_worker.GetString( srcloc.name ) );
}
ImGui::TextUnformatted( m_worker.GetString( srcloc.function ) );
ImGui::Separator();
ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
ImGui::EndTooltip();
}
ImGui::SameLine();
ImGui::TextDisabled( "(\xc3\x97%s)", RealToString( cgr.v.size() ) );
}
ImGui::NextColumn();
const auto part = double( cgr.t ) * rztime;
char buf[128];
PrintStringPercent( buf, TimeToString( cgr.t ), part * 100 );
ImGui::ProgressBar( part, ImVec2( -1, ty ), buf );
ImGui::NextColumn();
if( expandGroup )
{
auto ctt = std::make_unique<uint64_t[]>( cgr.v.size() );
auto cti = std::make_unique<uint32_t[]>( cgr.v.size() );
for( size_t i=0; i<cgr.v.size(); i++ )
{
const auto& child = a(children[cgr.v[i]]);
const auto cend = m_worker.GetZoneEnd( child );
const auto ct = cend - child.GpuStart();
ctt[i] = ct;
cti[i] = uint32_t( i );
}
pdqsort_branchless( cti.get(), cti.get() + cgr.v.size(), [&ctt] ( const auto& lhs, const auto& rhs ) { return ctt[lhs] > ctt[rhs]; } );
for( size_t i=0; i<cgr.v.size(); i++ )
{
auto& cev = a(children[cgr.v[cti[i]]]);
const auto txt = m_worker.GetZoneName( cev );
bool b = false;
ImGui::Indent();
ImGui::PushID( (int)cgr.v[cti[i]] );
if( ImGui::Selectable( txt, &b, ImGuiSelectableFlags_SpanAllColumns ) )
{
ShowZoneInfo( cev, m_gpuInfoWindowThread );
}
if( ImGui::IsItemHovered() )
{
m_gpuHighlight = &cev;
if( IsMouseClicked( 2 ) )
{
ZoomToZone( cev );
}
ZoneTooltip( cev );
}
ImGui::PopID();
ImGui::Unindent();
ImGui::NextColumn();
const auto part = double( ctt[cti[i]] ) * rztime;
char buf[128];
PrintStringPercent( buf, TimeToString( ctt[cti[i]] ), part * 100 );
ImGui::ProgressBar( part, ImVec2( -1, ty ), buf );
ImGui::NextColumn();
}
ImGui::TreePop();
}
}
ImGui::EndColumns();
}
else
{
auto ctt = std::make_unique<uint64_t[]>( children.size() );
auto cti = std::make_unique<uint32_t[]>( children.size() );
uint64_t ctime = 0;
for( size_t i=0; i<children.size(); i++ )
{
const auto& child = a(children[i]);
const auto cend = m_worker.GetZoneEnd( child );
const auto ct = cend - child.GpuStart();
ctime += ct;
ctt[i] = ct;
cti[i] = uint32_t( i );
}
pdqsort_branchless( cti.get(), cti.get() + children.size(), [&ctt] ( const auto& lhs, const auto& rhs ) { return ctt[lhs] > ctt[rhs]; } );
ImGui::Columns( 2 );
TextColoredUnformatted( ImVec4( 1.0f, 1.0f, 0.4f, 1.0f ), "Self time" );
ImGui::NextColumn();
char buf[128];
PrintStringPercent( buf, TimeToString( ztime - ctime ), double( ztime - ctime ) / ztime * 100 );
ImGui::ProgressBar( double( ztime - ctime ) / ztime, ImVec2( -1, ty ), buf );
ImGui::NextColumn();
for( size_t i=0; i<children.size(); i++ )
{
auto& cev = a(children[cti[i]]);
bool b = false;
ImGui::PushID( (int)i );
if( ImGui::Selectable( m_worker.GetZoneName( cev ), &b, ImGuiSelectableFlags_SpanAllColumns ) )
{
ShowZoneInfo( cev, m_gpuInfoWindowThread );
}
if( ImGui::IsItemHovered() )
{
m_gpuHighlight = &cev;
if( IsMouseClicked( 2 ) )
{
ZoomToZone( cev );
}
ZoneTooltip( cev );
}
ImGui::PopID();
ImGui::NextColumn();
const auto part = double( ctt[cti[i]] ) / ztime;
char buf[128];
PrintStringPercent( buf, TimeToString( ctt[cti[i]] ), part * 100 );
ImGui::ProgressBar( part, ImVec2( -1, ty ), buf );
ImGui::NextColumn();
}
ImGui::EndColumns();
}
}
void View::ShowZoneInfo( const ZoneEvent& ev )
{
if( m_zoneInfoWindow && m_zoneInfoWindow != &ev )
{
m_zoneInfoStack.push_back( m_zoneInfoWindow );
}
m_zoneInfoWindow = &ev;
if( m_gpuInfoWindow )
{
m_gpuInfoWindow = nullptr;
m_gpuInfoStack.clear();
}
}
void View::ShowZoneInfo( const GpuEvent& ev, uint64_t thread )
{
if( m_gpuInfoWindow && m_gpuInfoWindow != &ev )
{
m_gpuInfoStack.push_back( m_gpuInfoWindow );
}
m_gpuInfoWindow = &ev;
m_gpuInfoWindowThread = thread;
if( m_zoneInfoWindow )
{
m_zoneInfoWindow = nullptr;
m_zoneInfoStack.clear();
}
}
void View::ZoneTooltip( const ZoneEvent& ev )
{
const auto tid = GetZoneThread( ev );
auto& srcloc = m_worker.GetSourceLocation( ev.SrcLoc() );
const auto end = m_worker.GetZoneEnd( ev );
const auto ztime = end - ev.Start();
const auto selftime = GetZoneSelfTime( ev );
ImGui::BeginTooltip();
if( m_worker.HasZoneExtra( ev ) && m_worker.GetZoneExtra( ev ).name.Active() )
{
ImGui::TextUnformatted( m_worker.GetString( m_worker.GetZoneExtra( ev ).name ) );
}
if( srcloc.name.active )
{
ImGui::TextUnformatted( m_worker.GetString( srcloc.name ) );
}
ImGui::TextUnformatted( m_worker.GetString( srcloc.function ) );
ImGui::Separator();
SmallColorBox( GetSrcLocColor( srcloc, 0 ) );
ImGui::SameLine();
ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
SmallColorBox( GetThreadColor( tid, 0 ) );
ImGui::SameLine();
TextFocused( "Thread:", m_worker.GetThreadName( tid ) );
ImGui::SameLine();
ImGui::TextDisabled( "(%s)", RealToString( tid ) );
if( m_worker.IsThreadFiber( tid ) )
{
ImGui::SameLine();
TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" );
}
ImGui::Separator();
TextFocused( "Execution time:", TimeToString( ztime ) );
#ifndef TRACY_NO_STATISTICS
if( m_worker.AreSourceLocationZonesReady() )
{
auto& zoneData = m_worker.GetZonesForSourceLocation( ev.SrcLoc() );
if( zoneData.total > 0 )
{
ImGui::SameLine();
ImGui::TextDisabled( "(%.2f%% of mean time)", float( ztime ) / zoneData.total * zoneData.zones.size() * 100 );
}
}
#endif
TextFocused( "Self time:", TimeToString( selftime ) );
if( ztime != 0 )
{
char buf[64];
PrintStringPercent( buf, 100.f * selftime / ztime );
ImGui::SameLine();
TextDisabledUnformatted( buf );
}
const auto ctx = m_worker.GetContextSwitchData( tid );
if( ctx )
{
int64_t time;
uint64_t cnt;
if( GetZoneRunningTime( ctx, ev, time, cnt ) )
{
TextFocused( "Running state time:", TimeToString( time ) );
if( ztime != 0 )
{
char buf[64];
PrintStringPercent( buf, 100.f * time / ztime );
ImGui::SameLine();
TextDisabledUnformatted( buf );
}
TextFocused( "Running state regions:", RealToString( cnt ) );
}
}
if( m_worker.HasZoneExtra( ev ) && m_worker.GetZoneExtra( ev ).text.Active() )
{
ImGui::NewLine();
TextColoredUnformatted( ImVec4( 0xCC / 255.f, 0xCC / 255.f, 0x22 / 255.f, 1.f ), m_worker.GetString( m_worker.GetZoneExtra( ev ).text ) );
}
ImGui::EndTooltip();
}
void View::ZoneTooltip( const GpuEvent& ev )
{
const auto tid = GetZoneThread( ev );
const auto& srcloc = m_worker.GetSourceLocation( ev.SrcLoc() );
const auto end = m_worker.GetZoneEnd( ev );
const auto ztime = end - ev.GpuStart();
const auto selftime = GetZoneSelfTime( ev );
ImGui::BeginTooltip();
ImGui::TextUnformatted( m_worker.GetString( srcloc.name ) );
ImGui::TextUnformatted( m_worker.GetString( srcloc.function ) );
ImGui::Separator();
SmallColorBox( GetSrcLocColor( srcloc, 0 ) );
ImGui::SameLine();
ImGui::TextUnformatted( LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) );
SmallColorBox( GetThreadColor( tid, 0 ) );
ImGui::SameLine();
TextFocused( "Thread:", m_worker.GetThreadName( tid ) );
ImGui::SameLine();
ImGui::TextDisabled( "(%s)", RealToString( tid ) );
if( m_worker.IsThreadFiber( tid ) )
{
ImGui::SameLine();
TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" );
}
ImGui::Separator();
TextFocused( "GPU execution time:", TimeToString( ztime ) );
TextFocused( "GPU self time:", TimeToString( selftime ) );
if( ztime != 0 )
{
char buf[64];
PrintStringPercent( buf, 100.f * selftime / ztime );
ImGui::SameLine();
TextDisabledUnformatted( buf );
}
TextFocused( "CPU command setup time:", TimeToString( ev.CpuEnd() - ev.CpuStart() ) );
auto ctx = GetZoneCtx( ev );
if( !ctx )
{
TextFocused( "Delay to execution:", TimeToString( ev.GpuStart() - ev.CpuStart() ) );
}
else
{
const auto td = ctx->threadData.size() == 1 ? ctx->threadData.begin() : ctx->threadData.find( m_worker.DecompressThread( ev.Thread() ) );
assert( td != ctx->threadData.end() );
int64_t begin;
if( td->second.timeline.is_magic() )
{
begin = ((Vector<GpuEvent>*)&td->second.timeline)->front().GpuStart();
}
else
{
begin = td->second.timeline.front()->GpuStart();
}
const auto drift = GpuDrift( ctx );
TextFocused( "Delay to execution:", TimeToString( AdjustGpuTime( ev.GpuStart(), begin, drift ) - ev.CpuStart() ) );
}
ImGui::EndTooltip();
}
}