diff --git a/profiler/build/win32/Tracy.vcxproj b/profiler/build/win32/Tracy.vcxproj
index 72050189..c12b9350 100644
--- a/profiler/build/win32/Tracy.vcxproj
+++ b/profiler/build/win32/Tracy.vcxproj
@@ -136,10 +136,12 @@
+
+
diff --git a/profiler/build/win32/Tracy.vcxproj.filters b/profiler/build/win32/Tracy.vcxproj.filters
index be312937..762bf6aa 100644
--- a/profiler/build/win32/Tracy.vcxproj.filters
+++ b/profiler/build/win32/Tracy.vcxproj.filters
@@ -255,6 +255,12 @@
server
+
+ server
+
+
+ server
+
diff --git a/server/TracyView.cpp b/server/TracyView.cpp
index 6adf2ddc..ecc411c4 100644
--- a/server/TracyView.cpp
+++ b/server/TracyView.cpp
@@ -3767,130 +3767,6 @@ void View::DrawZones()
}
}
-static const char* DecodeContextSwitchReasonCode( uint8_t reason )
-{
- switch( reason )
- {
- case 0: return "Executive";
- case 1: return "FreePage";
- case 2: return "PageIn";
- case 3: return "PoolAllocation";
- case 4: return "DelayExecution";
- case 5: return "Suspended";
- case 6: return "UserRequest";
- case 7: return "WrExecutive";
- case 8: return "WrFreePage";
- case 9: return "WrPageIn";
- case 10: return "WrPoolAllocation";
- case 11: return "WrDelayExecution";
- case 12: return "WrSuspended";
- case 13: return "WrUserRequest";
- case 14: return "WrEventPair";
- case 15: return "WrQueue";
- case 16: return "WrLpcReceive";
- case 17: return "WrLpcReply";
- case 18: return "WrVirtualMemory";
- case 19: return "WrPageOut";
- case 20: return "WrRendezvous";
- case 21: return "WrKeyedEvent";
- case 22: return "WrTerminated";
- case 23: return "WrProcessInSwap";
- case 24: return "WrCpuRateControl";
- case 25: return "WrCalloutStack";
- case 26: return "WrKernel";
- case 27: return "WrResource";
- case 28: return "WrPushLock";
- case 29: return "WrMutex";
- case 30: return "WrQuantumEnd";
- case 31: return "WrDispatchInt";
- case 32: return "WrPreempted";
- case 33: return "WrYieldExecution";
- case 34: return "WrFastMutex";
- case 35: return "WrGuardedMutex";
- case 36: return "WrRundown";
- case 37: return "WrAlertByThreadId";
- case 38: return "WrDeferredPreempt";
- case 39: return "WrPhysicalFault";
- case 40: return "MaximumWaitReason";
- default: return "unknown";
- }
-}
-
-static const char* DecodeContextSwitchReason( uint8_t reason )
-{
- switch( reason )
- {
- case 0: return "(Thread is waiting for the scheduler)";
- case 1: return "(Thread is waiting for a free virtual memory page)";
- case 2: return "(Thread is waiting for a virtual memory page to arrive in memory)";
- case 4: return "(Thread execution is delayed)";
- case 5: return "(Thread execution is suspended)";
- case 6: return "(Thread is waiting on object - WaitForSingleObject, etc.)";
- case 7: return "(Thread is waiting for the scheduler)";
- case 8: return "(Thread is waiting for a free virtual memory page)";
- case 9: return "(Thread is waiting for a virtual memory page to arrive in memory)";
- case 11: return "(Thread execution is delayed)";
- case 12: return "(Thread execution is suspended)";
- case 13: return "(Thread is waiting for window messages)";
- case 15: return "(Thread is waiting on KQUEUE)";
- case 24: return "(CPU rate limiting)";
- case 34: return "(Waiting for a Fast Mutex)";
- default: return "";
- }
-}
-
-static const char* DecodeContextSwitchStateCode( uint8_t state )
-{
- switch( state )
- {
- case 0: return "Initialized";
- case 1: return "Ready";
- case 2: return "Running";
- case 3: return "Standby";
- case 4: return "Terminated";
- case 5: return "Waiting";
- case 6: return "Transition";
- case 7: return "DeferredReady";
- case 101: return "D (disk sleep)";
- case 102: return "I (idle)";
- case 103: return "R (running)";
- case 104: return "S (sleeping)";
- case 105: return "T (stopped)";
- case 106: return "t (tracing stop)";
- case 107: return "W";
- case 108: return "X (dead)";
- case 109: return "Z (zombie)";
- case 110: return "P (parked)";
- default: return "unknown";
- }
-}
-
-static const char* DecodeContextSwitchState( uint8_t state )
-{
- switch( state )
- {
- case 0: return "(Thread has been initialized, but has not yet started)";
- case 1: return "(Thread is waiting to use a processor because no processor is free. The thread is prepared to run on the next available processor)";
- case 2: return "(Thread is currently using a processor)";
- case 3: return "(Thread is about to use a processor)";
- case 4: return "(Thread has finished executing and has exited)";
- case 5: return "(Thread is not ready to use the processor because it is waiting for a peripheral operation to complete or a resource to become free)";
- case 6: return "(Thread is waiting for a resource, other than the processor, before it can execute)";
- case 7: return "(Thread has been selected to run on a specific processor but have not yet beed scheduled)";
- case 101: return "(Uninterruptible sleep, usually IO)";
- case 102: return "(Idle kernel thread)";
- case 103: return "(Running or on run queue)";
- case 104: return "(Interruptible sleep, waiting for an event to complete)";
- case 105: return "(Stopped by job control signal)";
- case 106: return "(Stopped by debugger during the tracing)";
- case 107: return "(Paging)";
- case 108: return "(Dead task is scheduling one last time)";
- case 109: return "(Zombie process)";
- case 110: return "(Parked)";
- default: return "";
- }
-}
-
void View::DrawContextSwitches( const ContextSwitch* ctx, const Vector& sampleData, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int offset, int endOffset, bool isFiber )
{
const auto lineSize = 2 * GetScale();
@@ -6960,1774 +6836,6 @@ void View::DrawPlotPoint( const ImVec2& wpos, float x, float y, int offset, uint
}
}
-void View::DrawInfoWindow()
-{
- if( m_zoneInfoWindow )
- {
- DrawZoneInfoWindow();
- }
- else if( m_gpuInfoWindow )
- {
- DrawGpuInfoWindow();
- }
-}
-
-template
-static inline uint32_t GetZoneCallstack( const T& ev, const Worker& worker );
-
-template<>
-inline uint32_t GetZoneCallstack( const ZoneEvent& ev, const Worker& worker )
-{
- return worker.GetZoneExtra( ev ).callstack.Val();
-}
-
-template<>
-inline uint32_t GetZoneCallstack( const GpuEvent& ev, const Worker& worker )
-{
- return ev.callstack.Val();
-}
-
-template
-void DrawZoneTrace( T zone, const std::vector& trace, const Worker& worker, BuzzAnim& anim, View& view, bool& showUnknownFrames, std::function showZone )
-{
- bool expand = ImGui::TreeNode( "Zone trace" );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%s)", RealToString( trace.size() ) );
- if( !expand ) return;
-
- 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; idata + frameData->size - 1;
- ImGui::TextDisabled( "%i.", fidx++ );
- ImGui::SameLine();
- TextDisabledUnformatted( worker.GetString( frame->name ) );
- 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; idata + frameData->size - 1;
- ImGui::TextDisabled( "%i.", fidx++ );
- ImGui::SameLine();
- TextDisabledUnformatted( worker.GetString( frame->name ) );
- 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::CalcZoneTimeData( unordered_flat_map& data, int64_t& ztime, const ZoneEvent& zone )
-{
- assert( zone.HasChildren() );
- const auto& children = m_worker.GetZoneChildren( zone.Child() );
- if( children.is_magic() )
- {
- CalcZoneTimeDataImpl>( *(Vector*)( &children ), data, ztime, zone );
- }
- else
- {
- CalcZoneTimeDataImpl>( children, data, ztime, zone );
- }
-}
-
-template
-void View::CalcZoneTimeDataImpl( const V& children, unordered_flat_map& 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& data, int64_t& ztime, const ZoneEvent& zone )
-{
- assert( zone.HasChildren() );
- const auto& children = m_worker.GetZoneChildren( zone.Child() );
- if( children.is_magic() )
- {
- CalcZoneTimeDataImpl>( *(Vector*)( &children ), ctx, data, ztime, zone );
- }
- else
- {
- CalcZoneTimeDataImpl>( children, ctx, data, ztime, zone );
- }
-}
-
-template
-void View::CalcZoneTimeDataImpl( const V& children, const ContextSwitch* ctx, unordered_flat_map& 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) );
- }
-}
-
-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; iEnd() - 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( 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; iisFiber )
- {
- 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 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 );
- if( ImGui::BeginTable( "##messages", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersInnerV, ImVec2( 0, ImGui::GetTextLineHeightWithSpacing() * std::min( msgend-msgit+1, 15 ) ) ) )
- {
- ImGui::TableSetupScrollFreeze( 0, 1 );
- ImGui::TableSetupColumn( "Time", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
- ImGui::TableSetupColumn( "Message" );
- ImGui::TableHeadersRow();
- do
- {
- 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 zoneTrace;
- while( parent )
- {
- zoneTrace.emplace_back( parent );
- parent = GetZoneParent( *parent );
- }
- int idx = 0;
- DrawZoneTrace( &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>( *(Vector*)( &children ), ztime );
- }
- else
- {
- DrawZoneInfoChildren>( 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::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
-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 v;
- };
- uint64_t ctime = 0;
- unordered_flat_map cmap;
- cmap.reserve( 128 );
- for( size_t i=0; isecond.t += ct;
- it->second.v.push_back( i );
- }
-
- auto msz = cmap.size();
- Vector 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( cgr.v.size() );
- auto cti = std::make_unique( cgr.v.size() );
- for( size_t i=0; i ctt[rhs]; } );
-
- ImGuiListClipper clipper;
- clipper.Begin( cgr.v.size() );
- while( clipper.Step() )
- {
- for( auto i=clipper.DisplayStart; i( children.size() );
- auto cti = std::make_unique( children.size() );
- uint64_t ctime = 0;
- for( size_t i=0; i 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; iSkipItems )
- {
- 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*)&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 zoneTrace;
- while( parent )
- {
- zoneTrace.emplace_back( parent );
- parent = GetZoneParent( *parent );
- }
- int idx = 0;
- DrawZoneTrace( &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>( *(Vector*)( &children ), ztime );
- }
- else
- {
- DrawGpuInfoChildren>( children, ztime );
- }
- ImGui::TreePop();
- }
- }
-
- ImGui::EndChild();
- }
- ImGui::End();
-
- if( !show )
- {
- m_gpuInfoWindow = nullptr;
- m_gpuInfoStack.clear();
- }
-}
-
-template
-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 v;
- };
- uint64_t ctime = 0;
- unordered_flat_map cmap;
- cmap.reserve( 128 );
- for( size_t i=0; isecond.t += ct;
- it->second.v.push_back( i );
- }
-
- auto msz = cmap.size();
- Vector 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( cgr.v.size() );
- auto cti = std::make_unique( cgr.v.size() );
- for( size_t i=0; i ctt[rhs]; } );
-
- for( size_t i=0; i( children.size() );
- auto cti = std::make_unique( children.size() );
- uint64_t ctime = 0;
- for( size_t i=0; i 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
+
+#include "TracyFilesystem.hpp"
+#include "TracyPrint.hpp"
+#include "TracyMouse.hpp"
+#include "TracyView.hpp"
+
+namespace tracy
+{
+
+extern double s_time;
+
+template
+static inline uint32_t GetZoneCallstack( const T& ev, const Worker& worker );
+
+template<>
+inline uint32_t GetZoneCallstack( const ZoneEvent& ev, const Worker& worker )
+{
+ return worker.GetZoneExtra( ev ).callstack.Val();
+}
+
+template<>
+inline uint32_t GetZoneCallstack( const GpuEvent& ev, const Worker& worker )
+{
+ return ev.callstack.Val();
+}
+
+void View::CalcZoneTimeData( unordered_flat_map& data, int64_t& ztime, const ZoneEvent& zone )
+{
+ assert( zone.HasChildren() );
+ const auto& children = m_worker.GetZoneChildren( zone.Child() );
+ if( children.is_magic() )
+ {
+ CalcZoneTimeDataImpl>( *(Vector*)( &children ), data, ztime, zone );
+ }
+ else
+ {
+ CalcZoneTimeDataImpl>( children, data, ztime, zone );
+ }
+}
+
+template
+void View::CalcZoneTimeDataImpl( const V& children, unordered_flat_map& 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& data, int64_t& ztime, const ZoneEvent& zone )
+{
+ assert( zone.HasChildren() );
+ const auto& children = m_worker.GetZoneChildren( zone.Child() );
+ if( children.is_magic() )
+ {
+ CalcZoneTimeDataImpl>( *(Vector*)( &children ), ctx, data, ztime, zone );
+ }
+ else
+ {
+ CalcZoneTimeDataImpl>( children, ctx, data, ztime, zone );
+ }
+}
+
+template
+void View::CalcZoneTimeDataImpl( const V& children, const ContextSwitch* ctx, unordered_flat_map& 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
+void DrawZoneTrace( T zone, const std::vector& trace, const Worker& worker, BuzzAnim& anim, View& view, bool& showUnknownFrames, std::function showZone )
+{
+ bool expand = ImGui::TreeNode( "Zone trace" );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%s)", RealToString( trace.size() ) );
+ if( !expand ) return;
+
+ 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; idata + frameData->size - 1;
+ ImGui::TextDisabled( "%i.", fidx++ );
+ ImGui::SameLine();
+ TextDisabledUnformatted( worker.GetString( frame->name ) );
+ 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; idata + frameData->size - 1;
+ ImGui::TextDisabled( "%i.", fidx++ );
+ ImGui::SameLine();
+ TextDisabledUnformatted( worker.GetString( frame->name ) );
+ 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; iEnd() - 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( 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; iisFiber )
+ {
+ 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 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 );
+ if( ImGui::BeginTable( "##messages", 2, ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersInnerV, ImVec2( 0, ImGui::GetTextLineHeightWithSpacing() * std::min( msgend-msgit+1, 15 ) ) ) )
+ {
+ ImGui::TableSetupScrollFreeze( 0, 1 );
+ ImGui::TableSetupColumn( "Time", ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
+ ImGui::TableSetupColumn( "Message" );
+ ImGui::TableHeadersRow();
+ do
+ {
+ 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 zoneTrace;
+ while( parent )
+ {
+ zoneTrace.emplace_back( parent );
+ parent = GetZoneParent( *parent );
+ }
+ int idx = 0;
+ DrawZoneTrace( &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>( *(Vector*)( &children ), ztime );
+ }
+ else
+ {
+ DrawZoneInfoChildren>( 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::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
+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 v;
+ };
+ uint64_t ctime = 0;
+ unordered_flat_map cmap;
+ cmap.reserve( 128 );
+ for( size_t i=0; isecond.t += ct;
+ it->second.v.push_back( i );
+ }
+
+ auto msz = cmap.size();
+ Vector 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( cgr.v.size() );
+ auto cti = std::make_unique( cgr.v.size() );
+ for( size_t i=0; i ctt[rhs]; } );
+
+ ImGuiListClipper clipper;
+ clipper.Begin( cgr.v.size() );
+ while( clipper.Step() )
+ {
+ for( auto i=clipper.DisplayStart; i( children.size() );
+ auto cti = std::make_unique( children.size() );
+ uint64_t ctime = 0;
+ for( size_t i=0; i 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; iSkipItems )
+ {
+ 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*)&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 zoneTrace;
+ while( parent )
+ {
+ zoneTrace.emplace_back( parent );
+ parent = GetZoneParent( *parent );
+ }
+ int idx = 0;
+ DrawZoneTrace( &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>( *(Vector*)( &children ), ztime );
+ }
+ else
+ {
+ DrawGpuInfoChildren>( children, ztime );
+ }
+ ImGui::TreePop();
+ }
+ }
+
+ ImGui::EndChild();
+ }
+ ImGui::End();
+
+ if( !show )
+ {
+ m_gpuInfoWindow = nullptr;
+ m_gpuInfoStack.clear();
+ }
+}
+
+template
+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 v;
+ };
+ uint64_t ctime = 0;
+ unordered_flat_map cmap;
+ cmap.reserve( 128 );
+ for( size_t i=0; isecond.t += ct;
+ it->second.v.push_back( i );
+ }
+
+ auto msz = cmap.size();
+ Vector 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( cgr.v.size() );
+ auto cti = std::make_unique( cgr.v.size() );
+ for( size_t i=0; i ctt[rhs]; } );
+
+ for( size_t i=0; i( children.size() );
+ auto cti = std::make_unique( children.size() );
+ uint64_t ctime = 0;
+ for( size_t i=0; i 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