From c9f77ee5fa80187f98a405795d2fb4b00653add7 Mon Sep 17 00:00:00 2001 From: Bartosz Taudul Date: Sat, 2 Jul 2022 13:35:47 +0200 Subject: [PATCH] Move zone info UI out of View. --- profiler/build/win32/Tracy.vcxproj | 2 + profiler/build/win32/Tracy.vcxproj.filters | 6 + server/TracyView.cpp | 1923 -------------------- server/TracyView.hpp | 5 + server/TracyView_ContextSwitch.cpp | 130 ++ server/TracyView_ZoneInfo.cpp | 1812 ++++++++++++++++++ 6 files changed, 1955 insertions(+), 1923 deletions(-) create mode 100644 server/TracyView_ContextSwitch.cpp create mode 100644 server/TracyView_ZoneInfo.cpp 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