diff --git a/profiler/build/win32/Tracy.vcxproj b/profiler/build/win32/Tracy.vcxproj
index 7aeea162..597b225a 100644
--- a/profiler/build/win32/Tracy.vcxproj
+++ b/profiler/build/win32/Tracy.vcxproj
@@ -157,9 +157,11 @@
+
+
diff --git a/profiler/build/win32/Tracy.vcxproj.filters b/profiler/build/win32/Tracy.vcxproj.filters
index fe1bdfb9..c923c3c9 100644
--- a/profiler/build/win32/Tracy.vcxproj.filters
+++ b/profiler/build/win32/Tracy.vcxproj.filters
@@ -315,6 +315,12 @@
server
+
+ server
+
+
+ server
+
diff --git a/server/TracyView.cpp b/server/TracyView.cpp
index 8660e01c..a9018313 100644
--- a/server/TracyView.cpp
+++ b/server/TracyView.cpp
@@ -58,8 +58,6 @@ namespace tracy
double s_time = 0;
-enum { MinVisSize = 3 };
-
static View* s_instance = nullptr;
View::View( void(*cbMainThread)(std::function, bool), const char* addr, uint16_t port, ImFont* fixedWidth, ImFont* smallFont, ImFont* bigFont, SetTitleCallback stcb, GetWindowCallback gwcb, SetScaleCallback sscb )
@@ -1411,14 +1409,6 @@ void View::HandleZoneViewMouse( int64_t timespan, const ImVec2& wpos, float w, d
}
}
-static uint32_t MixGhostColor( uint32_t c0, uint32_t c1 )
-{
- return 0xFF000000 |
- ( ( ( ( ( c0 & 0x00FF0000 ) >> 16 ) + 3 * ( ( c1 & 0x00FF0000 ) >> 16 ) ) >> 2 ) << 16 ) |
- ( ( ( ( ( c0 & 0x0000FF00 ) >> 8 ) + 3 * ( ( c1 & 0x0000FF00 ) >> 8 ) ) >> 2 ) << 8 ) |
- ( ( ( ( ( c0 & 0x000000FF ) ) + 3 * ( ( c1 & 0x000000FF ) ) ) >> 2 ) );
-}
-
float View::AdjustThreadPosition( View::VisData& vis, float wy, int& offset )
{
if( vis.offset < offset )
@@ -1460,1860 +1450,6 @@ void View::AdjustThreadHeight( View::VisData& vis, int oldOffset, int& offset )
}
}
-void View::DrawZones()
-{
- m_msgHighlight.Decay( nullptr );
- m_zoneSrcLocHighlight.Decay( 0 );
- m_lockHoverHighlight.Decay( InvalidId );
- m_drawThreadMigrations.Decay( 0 );
- m_drawThreadHighlight.Decay( 0 );
- m_cpuDataThread.Decay( 0 );
- m_zoneHover = nullptr;
- m_zoneHover2.Decay( nullptr );
- m_findZone.range.StartFrame();
- m_statRange.StartFrame();
- m_waitStackRange.StartFrame();
- m_memInfo.range.StartFrame();
- m_yDelta = 0;
-
- if( m_vd.zvStart == m_vd.zvEnd ) return;
- assert( m_vd.zvStart < m_vd.zvEnd );
-
- if( ImGui::GetCurrentWindowRead()->SkipItems ) return;
-
- m_gpuThread = 0;
- m_gpuStart = 0;
- m_gpuEnd = 0;
-
- const auto linepos = ImGui::GetCursorScreenPos();
- const auto lineh = ImGui::GetContentRegionAvail().y;
-
- auto draw = ImGui::GetWindowDrawList();
- const auto w = ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ScrollbarSize;
- const auto timespan = m_vd.zvEnd - m_vd.zvStart;
- auto pxns = w / double( timespan );
-
- const auto winpos = ImGui::GetWindowPos();
- const auto winsize = ImGui::GetWindowSize();
- const bool drawMouseLine = ImGui::IsWindowHovered( ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem ) && ImGui::IsMouseHoveringRect( winpos, winpos + winsize, false );
- if( drawMouseLine )
- {
- HandleRange( m_findZone.range, timespan, ImGui::GetCursorScreenPos(), w );
- HandleRange( m_statRange, timespan, ImGui::GetCursorScreenPos(), w );
- HandleRange( m_waitStackRange, timespan, ImGui::GetCursorScreenPos(), w );
- HandleRange( m_memInfo.range, timespan, ImGui::GetCursorScreenPos(), w );
- for( auto& v : m_annotations )
- {
- v->range.StartFrame();
- HandleRange( v->range, timespan, ImGui::GetCursorScreenPos(), w );
- }
- HandleZoneViewMouse( timespan, ImGui::GetCursorScreenPos(), w, pxns );
- }
-
- {
- const auto tbegin = 0;
- const auto tend = m_worker.GetLastTime();
- if( tbegin > m_vd.zvStart )
- {
- draw->AddRectFilled( linepos, linepos + ImVec2( ( tbegin - m_vd.zvStart ) * pxns, lineh ), 0x44000000 );
- }
- if( tend < m_vd.zvEnd )
- {
- draw->AddRectFilled( linepos + ImVec2( ( tend - m_vd.zvStart ) * pxns, 0 ), linepos + ImVec2( w, lineh ), 0x44000000 );
- }
- }
-
- DrawZoneFramesHeader();
- auto& frames = m_worker.GetFrames();
- for( auto fd : frames )
- {
- if( Vis( fd ).visible )
- {
- DrawZoneFrames( *fd );
- }
- }
-
- const auto yMin = ImGui::GetCursorScreenPos().y;
- const auto yMax = linepos.y + lineh;
-
- ImGui::BeginChild( "##zoneWin", ImVec2( ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y ), false, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_NoScrollWithMouse );
-
- if( m_yDelta != 0 )
- {
- auto& io = ImGui::GetIO();
- auto y = ImGui::GetScrollY();
- ImGui::SetScrollY( y - m_yDelta );
- io.MouseClickedPos[1].y = io.MousePos.y;
- }
-
- const auto wpos = ImGui::GetCursorScreenPos();
- const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
- const auto h = std::max( m_vd.zvHeight, ImGui::GetContentRegionAvail().y - 4 ); // magic border value
-
- ImGui::InvisibleButton( "##zones", ImVec2( w, h ) );
- bool hover = ImGui::IsItemHovered();
- draw = ImGui::GetWindowDrawList();
-
- const auto nspx = 1.0 / pxns;
-
- const auto ty = ImGui::GetTextLineHeight();
- const auto ostep = ty + 1;
- int offset = 0;
- const auto to = 9.f;
- const auto th = ( ty - to ) * sqrt( 3 ) * 0.5;
-
- // gpu zones
- if( m_vd.drawGpuZones )
- {
- for( size_t i=0; ithreadData.size() == 1;
- int depth = 0;
- offset += ostep;
- if( showFull && !v->threadData.empty() )
- {
- for( auto& td : v->threadData )
- {
- auto& tl = td.second.timeline;
- assert( !tl.empty() );
- if( tl.is_magic() )
- {
- auto& tlm = *(Vector*)&tl;
- if( tlm.front().GpuStart() >= 0 )
- {
- const auto begin = tlm.front().GpuStart();
- const auto drift = GpuDrift( v );
- if( !singleThread ) offset += sstep;
- const auto partDepth = DispatchGpuZoneLevel( tl, hover, pxns, int64_t( nspx ), wpos, offset, 0, v->thread, yMin, yMax, begin, drift );
- if( partDepth != 0 )
- {
- if( !singleThread )
- {
- ImGui::PushFont( m_smallFont );
- DrawTextContrast( draw, wpos + ImVec2( ty, offset-1-sstep ), 0xFFFFAAAA, m_worker.GetThreadName( td.first ) );
- DrawLine( draw, dpos + ImVec2( 0, offset+sty-sstep ), dpos + ImVec2( w, offset+sty-sstep ), 0x22FFAAAA );
- ImGui::PopFont();
- }
-
- offset += ostep * partDepth;
- depth += partDepth;
- }
- else if( !singleThread )
- {
- offset -= sstep;
- }
- }
- }
- else
- {
- if( tl.front()->GpuStart() >= 0 )
- {
- const auto begin = tl.front()->GpuStart();
- const auto drift = GpuDrift( v );
- if( !singleThread ) offset += sstep;
- const auto partDepth = DispatchGpuZoneLevel( tl, hover, pxns, int64_t( nspx ), wpos, offset, 0, v->thread, yMin, yMax, begin, drift );
- if( partDepth != 0 )
- {
- if( !singleThread )
- {
- ImGui::PushFont( m_smallFont );
- DrawTextContrast( draw, wpos + ImVec2( ty, offset-1-sstep ), 0xFFFFAAAA, m_worker.GetThreadName( td.first ) );
- DrawLine( draw, dpos + ImVec2( 0, offset+sty-sstep ), dpos + ImVec2( w, offset+sty-sstep ), 0x22FFAAAA );
- ImGui::PopFont();
- }
-
- offset += ostep * partDepth;
- depth += partDepth;
- }
- else if( !singleThread )
- {
- offset -= sstep;
- }
- }
- }
- }
- }
- offset += ostep * 0.2f;
-
- if( !m_vd.drawEmptyLabels && showFull && depth == 0 )
- {
- vis.height = 0;
- vis.offset = 0;
- offset = oldOffset;
- }
- else if( yPos + ostep >= yMin && yPos <= yMax )
- {
- DrawLine( draw, dpos + ImVec2( 0, oldOffset + ostep - 1 ), dpos + ImVec2( w, oldOffset + ostep - 1 ), 0x33FFFFFF );
-
- if( showFull )
- {
- draw->AddTriangleFilled( wpos + ImVec2( to/2, oldOffset + to/2 ), wpos + ImVec2( ty - to/2, oldOffset + to/2 ), wpos + ImVec2( ty * 0.5, oldOffset + to/2 + th ), 0xFFFFAAAA );
- }
- else
- {
- draw->AddTriangle( wpos + ImVec2( to/2, oldOffset + to/2 ), wpos + ImVec2( to/2, oldOffset + ty - to/2 ), wpos + ImVec2( to/2 + th, oldOffset + ty * 0.5 ), 0xFF886666, 2.0f );
- }
-
- const bool isMultithreaded = (v->type == GpuContextType::Vulkan) || (v->type == GpuContextType::OpenCL) || (v->type == GpuContextType::Direct3D12);
-
- float boxwidth;
- char buf[64];
- sprintf( buf, "%s context %zu", GpuContextNames[(int)v->type], i );
- if( v->name.Active() )
- {
- char tmp[4096];
- sprintf( tmp, "%s: %s", buf, m_worker.GetString( v->name ) );
- DrawTextContrast( draw, wpos + ImVec2( ty, oldOffset ), showFull ? 0xFFFFAAAA : 0xFF886666, tmp );
- boxwidth = ImGui::CalcTextSize( tmp ).x;
- }
- else
- {
- DrawTextContrast( draw, wpos + ImVec2( ty, oldOffset ), showFull ? 0xFFFFAAAA : 0xFF886666, buf );
- boxwidth = ImGui::CalcTextSize( buf ).x;
- }
-
- if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( ty + boxwidth, oldOffset + ty ) ) )
- {
- if( IsMouseClicked( 0 ) )
- {
- showFull = !showFull;
- }
- if( IsMouseClicked( 2 ) )
- {
- int64_t t0 = std::numeric_limits::max();
- int64_t t1 = std::numeric_limits::min();
- for( auto& td : v->threadData )
- {
- int64_t _t0;
- if( td.second.timeline.is_magic() )
- {
- _t0 = ((Vector*)&td.second.timeline)->front().GpuStart();
- }
- else
- {
- _t0 = td.second.timeline.front()->GpuStart();
- }
- if( _t0 >= 0 )
- {
- // FIXME
- t0 = std::min( t0, _t0 );
- if( td.second.timeline.is_magic() )
- {
- t1 = std::max( t1, std::min( m_worker.GetLastTime(), m_worker.GetZoneEnd( ((Vector*)&td.second.timeline)->back() ) ) );
- }
- else
- {
- t1 = std::max( t1, std::min( m_worker.GetLastTime(), m_worker.GetZoneEnd( *td.second.timeline.back() ) ) );
- }
- }
- }
- if( t0 < t1 )
- {
- ZoomToRange( t0, t1 );
- }
- }
-
- ImGui::BeginTooltip();
- ImGui::TextUnformatted( buf );
- if( v->name.Active() ) TextFocused( "Name:", m_worker.GetString( v->name ) );
- ImGui::Separator();
- if( !isMultithreaded )
- {
- SmallColorBox( GetThreadColor( v->thread, 0 ) );
- ImGui::SameLine();
- TextFocused( "Thread:", m_worker.GetThreadName( v->thread ) );
- }
- else
- {
- if( !v->threadData.empty() )
- {
- if( v->threadData.size() == 1 )
- {
- auto it = v->threadData.begin();
- auto tid = it->first;
- if( tid == 0 )
- {
- if( !it->second.timeline.empty() )
- {
- if( it->second.timeline.is_magic() )
- {
- auto& tl = *(Vector*)&it->second.timeline;
- tid = m_worker.DecompressThread( tl.begin()->Thread() );
- }
- else
- {
- tid = m_worker.DecompressThread( (*it->second.timeline.begin())->Thread() );
- }
- }
- }
- 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" );
- }
- }
- else
- {
- ImGui::TextDisabled( "Threads:" );
- ImGui::Indent();
- for( auto& td : v->threadData )
- {
- SmallColorBox( GetThreadColor( td.first, 0 ) );
- ImGui::SameLine();
- ImGui::TextUnformatted( m_worker.GetThreadName( td.first ) );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%s)", RealToString( td.first ) );
- }
- ImGui::Unindent();
- }
- }
- }
- if( !v->threadData.empty() )
- {
- int64_t t0 = std::numeric_limits::max();
- for( auto& td : v->threadData )
- {
- int64_t _t0;
- if( td.second.timeline.is_magic() )
- {
- _t0 = ((Vector*)&td.second.timeline)->front().GpuStart();
- }
- else
- {
- _t0 = td.second.timeline.front()->GpuStart();
- }
- if( _t0 >= 0 )
- {
- t0 = std::min( t0, _t0 );
- }
- }
- if( t0 != std::numeric_limits::max() )
- {
- TextFocused( "Appeared at", TimeToString( t0 ) );
- }
- }
- TextFocused( "Zone count:", RealToString( v->count ) );
- if( v->period != 1.f )
- {
- TextFocused( "Timestamp accuracy:", TimeToString( v->period ) );
- }
- if( v->overflow != 0 )
- {
- ImGui::Separator();
- ImGui::TextUnformatted( "GPU timer overflow has been detected." );
- TextFocused( "Timer resolution:", RealToString( 63 - TracyLzcnt( v->overflow ) ) );
- ImGui::SameLine();
- TextDisabledUnformatted( "bits" );
- }
- ImGui::EndTooltip();
- }
- }
-
- AdjustThreadHeight( vis, oldOffset, offset );
- ImGui::PopClipRect();
- }
- }
-
- // zones
- if( m_vd.drawCpuData && m_worker.HasContextSwitches() )
- {
- offset = DrawCpuData( offset, pxns, wpos, hover, yMin, yMax );
- }
-
- const auto& threadData = m_worker.GetThreadData();
- if( threadData.size() != m_threadOrder.size() )
- {
- m_threadOrder.reserve( threadData.size() );
- for( size_t i=m_threadOrder.size(); isamples.empty();
- const auto hasCtxSwitch = m_vd.drawContextSwitches && m_worker.GetContextSwitchData( v->id );
-
- if( hasSamples )
- {
- if( hasCtxSwitch )
- {
- offset += round( ostep * 0.5f );
- }
- else
- {
- offset += round( ostep * 0.75f );
- }
- }
-
- const auto ctxOffset = offset;
- if( hasCtxSwitch ) offset += round( ostep * 0.75f );
-
- if( m_vd.drawZones )
- {
-#ifndef TRACY_NO_STATISTICS
- if( m_worker.AreGhostZonesReady() && ( vis.ghost || ( m_vd.ghostZones && v->timeline.empty() ) ) )
- {
- depth = DispatchGhostLevel( v->ghostZones, hover, pxns, int64_t( nspx ), wpos, offset, 0, yMin, yMax, v->id );
- }
- else
-#endif
- {
- depth = DispatchZoneLevel( v->timeline, hover, pxns, int64_t( nspx ), wpos, offset, 0, yMin, yMax, v->id );
- }
- offset += ostep * depth;
- }
-
- if( hasCtxSwitch )
- {
- auto ctxSwitch = m_worker.GetContextSwitchData( v->id );
- if( ctxSwitch )
- {
- DrawContextSwitches( ctxSwitch, v->samples, hover, pxns, int64_t( nspx ), wpos, ctxOffset, offset, v->isFiber );
- }
- }
-
- if( hasSamples )
- {
- DrawSamples( v->samples, hover, pxns, int64_t( nspx ), wpos, sampleOffset );
- }
-
- if( m_vd.drawLocks )
- {
- const auto lockDepth = DrawLocks( v->id, hover, pxns, wpos, offset, nextLockHighlight, yMin, yMax );
- offset += ostep * lockDepth;
- depth += lockDepth;
- }
- }
- offset += ostep * 0.2f;
-
- auto msgit = std::lower_bound( v->messages.begin(), v->messages.end(), m_vd.zvStart, [] ( const auto& lhs, const auto& rhs ) { return lhs->time < rhs; } );
- auto msgend = std::lower_bound( msgit, v->messages.end(), m_vd.zvEnd+1, [] ( const auto& lhs, const auto& rhs ) { return lhs->time < rhs; } );
-
- if( !m_vd.drawEmptyLabels && showFull && depth == 0 && msgit == msgend && crash.thread != v->id )
- {
- auto& vis = Vis( v );
- vis.height = 0;
- vis.offset = 0;
- offset = oldOffset;
- }
- else if( yPos + ostep >= yMin && yPos <= yMax )
- {
- DrawLine( draw, dpos + ImVec2( 0, oldOffset + ostep - 1 ), dpos + ImVec2( w, oldOffset + ostep - 1 ), 0x33FFFFFF );
-
- uint32_t labelColor;
- if( crash.thread == v->id ) labelColor = showFull ? 0xFF2222FF : 0xFF111188;
- else if( v->isFiber ) labelColor = showFull ? 0xFF88FF88 : 0xFF448844;
- else labelColor = showFull ? 0xFFFFFFFF : 0xFF888888;
-
- if( showFull )
- {
- draw->AddTriangleFilled( wpos + ImVec2( to/2, oldOffset + to/2 ), wpos + ImVec2( ty - to/2, oldOffset + to/2 ), wpos + ImVec2( ty * 0.5, oldOffset + to/2 + th ), labelColor );
-
- while( msgit < msgend )
- {
- const auto next = std::upper_bound( msgit, v->messages.end(), (*msgit)->time + MinVisSize * nspx, [] ( const auto& lhs, const auto& rhs ) { return lhs < rhs->time; } );
- const auto dist = std::distance( msgit, next );
-
- const auto px = ( (*msgit)->time - m_vd.zvStart ) * pxns;
- const bool isMsgHovered = hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px - (ty - to) * 0.5 - 1, oldOffset ), wpos + ImVec2( px + (ty - to) * 0.5 + 1, oldOffset + ty ) );
-
- unsigned int color = 0xFFDDDDDD;
- float animOff = 0;
- if( dist > 1 )
- {
- if( m_msgHighlight && m_worker.DecompressThread( m_msgHighlight->thread ) == v->id )
- {
- const auto hTime = m_msgHighlight->time;
- if( (*msgit)->time <= hTime && ( next == v->messages.end() || (*next)->time > hTime ) )
- {
- color = 0xFF4444FF;
- if( !isMsgHovered )
- {
- animOff = -fabs( sin( s_time * 8 ) ) * th;
- }
- }
- }
- draw->AddTriangleFilled( wpos + ImVec2( px - (ty - to) * 0.5, animOff + oldOffset + to ), wpos + ImVec2( px + (ty - to) * 0.5, animOff + oldOffset + to ), wpos + ImVec2( px, animOff + oldOffset + to + th ), color );
- draw->AddTriangle( wpos + ImVec2( px - (ty - to) * 0.5, animOff + oldOffset + to ), wpos + ImVec2( px + (ty - to) * 0.5, animOff + oldOffset + to ), wpos + ImVec2( px, animOff + oldOffset + to + th ), color, 2.0f );
- }
- else
- {
- if( m_msgHighlight == *msgit )
- {
- color = 0xFF4444FF;
- if( !isMsgHovered )
- {
- animOff = -fabs( sin( s_time * 8 ) ) * th;
- }
- }
- draw->AddTriangle( wpos + ImVec2( px - (ty - to) * 0.5, animOff + oldOffset + to ), wpos + ImVec2( px + (ty - to) * 0.5, animOff + oldOffset + to ), wpos + ImVec2( px, animOff + oldOffset + to + th ), color, 2.0f );
- }
- if( isMsgHovered )
- {
- ImGui::BeginTooltip();
- if( dist > 1 )
- {
- ImGui::Text( "%i messages", (int)dist );
- }
- else
- {
- TextFocused( "Message at", TimeToStringExact( (*msgit)->time ) );
- ImGui::PushStyleColor( ImGuiCol_Text, (*msgit)->color );
- ImGui::TextUnformatted( m_worker.GetString( (*msgit)->ref ) );
- ImGui::PopStyleColor();
- }
- ImGui::EndTooltip();
- m_msgHighlight = *msgit;
-
- if( IsMouseClicked( 0 ) )
- {
- m_showMessages = true;
- m_msgToFocus = *msgit;
- }
- if( IsMouseClicked( 2 ) )
- {
- CenterAtTime( (*msgit)->time );
- }
- }
- msgit = next;
- }
-
- if( crash.thread == v->id && crash.time >= m_vd.zvStart && crash.time <= m_vd.zvEnd )
- {
- const auto px = ( crash.time - m_vd.zvStart ) * pxns;
-
- draw->AddTriangleFilled( wpos + ImVec2( px - (ty - to) * 0.25f, oldOffset + to + th * 0.5f ), wpos + ImVec2( px + (ty - to) * 0.25f, oldOffset + to + th * 0.5f ), wpos + ImVec2( px, oldOffset + to + th ), 0xFF2222FF );
- draw->AddTriangle( wpos + ImVec2( px - (ty - to) * 0.25f, oldOffset + to + th * 0.5f ), wpos + ImVec2( px + (ty - to) * 0.25f, oldOffset + to + th * 0.5f ), wpos + ImVec2( px, oldOffset + to + th ), 0xFF2222FF, 2.0f );
-
- const auto crashText = ICON_FA_SKULL " crash " ICON_FA_SKULL;
- auto ctw = ImGui::CalcTextSize( crashText ).x;
- DrawTextContrast( draw, wpos + ImVec2( px - ctw * 0.5f, oldOffset + to + th * 0.5f - ty ), 0xFF2222FF, crashText );
-
- if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px - (ty - to) * 0.5 - 1, oldOffset ), wpos + ImVec2( px + (ty - to) * 0.5 + 1, oldOffset + ty ) ) )
- {
- CrashTooltip();
- if( IsMouseClicked( 0 ) )
- {
- m_showInfo = true;
- }
- if( IsMouseClicked( 2 ) )
- {
- CenterAtTime( crash.time );
- }
- }
- }
- }
- else
- {
- draw->AddTriangle( wpos + ImVec2( to/2, oldOffset + to/2 ), wpos + ImVec2( to/2, oldOffset + ty - to/2 ), wpos + ImVec2( to/2 + th, oldOffset + ty * 0.5 ), labelColor, 2.0f );
- }
- const auto txt = m_worker.GetThreadName( v->id );
- const auto txtsz = ImGui::CalcTextSize( txt );
- if( m_gpuThread == v->id )
- {
- draw->AddRectFilled( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( w, offset ), 0x228888DD );
- draw->AddRect( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( w, offset ), 0x448888DD );
- }
- if( m_gpuInfoWindow && m_gpuInfoWindowThread == v->id )
- {
- draw->AddRectFilled( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( w, offset ), 0x2288DD88 );
- draw->AddRect( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( w, offset ), 0x4488DD88 );
- }
- if( m_cpuDataThread == v->id )
- {
- draw->AddRectFilled( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( w, offset ), 0x2DFF8888 );
- draw->AddRect( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( w, offset ), 0x4DFF8888 );
- }
- DrawTextContrast( draw, wpos + ImVec2( ty, oldOffset ), labelColor, txt );
-
-#ifndef TRACY_NO_STATISTICS
- const bool hasGhostZones = showFull && m_worker.AreGhostZonesReady() && !v->ghostZones.empty();
- float ghostSz;
- if( hasGhostZones && !v->timeline.empty() )
- {
- auto& vis = Vis( v );
- const auto color = vis.ghost ? 0xFFAA9999 : 0x88AA7777;
- draw->AddText( wpos + ImVec2( 1.5f * ty + txtsz.x, oldOffset ), color, ICON_FA_GHOST );
- ghostSz = ImGui::CalcTextSize( ICON_FA_GHOST ).x;
- }
-#endif
-
- if( hover )
- {
-#ifndef TRACY_NO_STATISTICS
- if( hasGhostZones && !v->timeline.empty() && ImGui::IsMouseHoveringRect( wpos + ImVec2( 1.5f * ty + txtsz.x, oldOffset ), wpos + ImVec2( 1.5f * ty + txtsz.x + ghostSz, oldOffset + ty ) ) )
- {
- if( IsMouseClicked( 0 ) )
- {
- auto& vis = Vis( v );
- vis.ghost = !vis.ghost;
- }
- }
- else
-#endif
- if( ImGui::IsMouseHoveringRect( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( ty + txtsz.x, oldOffset + ty ) ) )
- {
- m_drawThreadMigrations = v->id;
- m_drawThreadHighlight = v->id;
- ImGui::BeginTooltip();
- SmallColorBox( GetThreadColor( v->id, 0 ) );
- ImGui::SameLine();
- ImGui::TextUnformatted( m_worker.GetThreadName( v->id ) );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%s)", RealToString( v->id ) );
- if( crash.thread == v->id )
- {
- ImGui::SameLine();
- TextColoredUnformatted( ImVec4( 1.f, 0.2f, 0.2f, 1.f ), ICON_FA_SKULL " Crashed" );
- }
- if( v->isFiber )
- {
- ImGui::SameLine();
- TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" );
- }
-
- const auto ctx = m_worker.GetContextSwitchData( v->id );
-
- ImGui::Separator();
- int64_t first = std::numeric_limits::max();
- int64_t last = -1;
- if( ctx && !ctx->v.empty() )
- {
- const auto& back = ctx->v.back();
- first = ctx->v.begin()->Start();
- last = back.IsEndValid() ? back.End() : back.Start();
- }
- if( !v->timeline.empty() )
- {
- if( v->timeline.is_magic() )
- {
- auto& tl = *((Vector*)&v->timeline);
- first = std::min( first, tl.front().Start() );
- last = std::max( last, m_worker.GetZoneEnd( tl.back() ) );
- }
- else
- {
- first = std::min( first, v->timeline.front()->Start() );
- last = std::max( last, m_worker.GetZoneEnd( *v->timeline.back() ) );
- }
- }
- if( !v->messages.empty() )
- {
- first = std::min( first, v->messages.front()->time );
- last = std::max( last, v->messages.back()->time );
- }
- size_t lockCnt = 0;
- for( const auto& lock : m_worker.GetLockMap() )
- {
- const auto& lockmap = *lock.second;
- if( !lockmap.valid ) continue;
- auto it = lockmap.threadMap.find( v->id );
- if( it == lockmap.threadMap.end() ) continue;
- lockCnt++;
- const auto thread = it->second;
- auto lptr = lockmap.timeline.data();
- auto eptr = lptr + lockmap.timeline.size() - 1;
- while( lptr->ptr->thread != thread ) lptr++;
- if( lptr->ptr->Time() < first ) first = lptr->ptr->Time();
- while( eptr->ptr->thread != thread ) eptr--;
- if( eptr->ptr->Time() > last ) last = eptr->ptr->Time();
- }
-
- if( last >= 0 )
- {
- const auto lifetime = last - first;
- const auto traceLen = m_worker.GetLastTime();
-
- TextFocused( "Appeared at", TimeToString( first ) );
- TextFocused( "Last event at", TimeToString( last ) );
- TextFocused( "Lifetime:", TimeToString( lifetime ) );
- ImGui::SameLine();
- char buf[64];
- PrintStringPercent( buf, lifetime / double( traceLen ) * 100 );
- TextDisabledUnformatted( buf );
-
- if( ctx )
- {
- TextFocused( "Time in running state:", TimeToString( ctx->runningTime ) );
- ImGui::SameLine();
- PrintStringPercent( buf, ctx->runningTime / double( lifetime ) * 100 );
- TextDisabledUnformatted( buf );
- }
- }
-
- ImGui::Separator();
- if( !v->timeline.empty() )
- {
- TextFocused( "Zone count:", RealToString( v->count ) );
- TextFocused( "Top-level zones:", RealToString( v->timeline.size() ) );
- }
- if( !v->messages.empty() )
- {
- TextFocused( "Messages:", RealToString( v->messages.size() ) );
- }
- if( lockCnt != 0 )
- {
- TextFocused( "Locks:", RealToString( lockCnt ) );
- }
- if( ctx )
- {
- TextFocused( "Running state regions:", RealToString( ctx->v.size() ) );
- }
- if( !v->samples.empty() )
- {
- TextFocused( "Call stack samples:", RealToString( v->samples.size() ) );
- if( v->kernelSampleCnt != 0 )
- {
- TextFocused( "Kernel samples:", RealToString( v->kernelSampleCnt ) );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%.2f%%)", 100.f * v->kernelSampleCnt / v->samples.size() );
- }
- }
- ImGui::EndTooltip();
-
- if( IsMouseClicked( 0 ) )
- {
- Vis( v ).showFull = !showFull;
- }
- if( last >= 0 && IsMouseClicked( 2 ) )
- {
- ZoomToRange( first, last );
- }
- }
- }
- }
-
- AdjustThreadHeight( Vis( v ), oldOffset, offset );
- ImGui::PopClipRect();
- }
- m_lockHighlight = nextLockHighlight;
-
- if( m_vd.drawPlots )
- {
- offset = DrawPlots( offset, pxns, wpos, hover, yMin, yMax );
- }
-
- const auto scrollPos = ImGui::GetScrollY();
- if( scrollPos == 0 && m_vd.zvScroll != 0 )
- {
- m_vd.zvHeight = 0;
- }
- else
- {
- if( offset > m_vd.zvHeight ) m_vd.zvHeight = offset;
- }
- m_vd.zvScroll = scrollPos;
-
- ImGui::EndChild();
-
- for( auto& ann : m_annotations )
- {
- if( ann->range.min < m_vd.zvEnd && ann->range.max > m_vd.zvStart )
- {
- uint32_t c0 = ( ann->color & 0xFFFFFF ) | ( m_selectedAnnotation == ann.get() ? 0x44000000 : 0x22000000 );
- uint32_t c1 = ( ann->color & 0xFFFFFF ) | ( m_selectedAnnotation == ann.get() ? 0x66000000 : 0x44000000 );
- uint32_t c2 = ( ann->color & 0xFFFFFF ) | ( m_selectedAnnotation == ann.get() ? 0xCC000000 : 0xAA000000 );
- draw->AddRectFilled( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns, 0 ), linepos + ImVec2( ( ann->range.max - m_vd.zvStart ) * pxns, lineh ), c0 );
- DrawLine( draw, linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + 0.5f, 0.5f ), linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + 0.5f, lineh + 0.5f ), ann->range.hiMin ? c2 : c1, ann->range.hiMin ? 2 : 1 );
- DrawLine( draw, linepos + ImVec2( ( ann->range.max - m_vd.zvStart ) * pxns + 0.5f, 0.5f ), linepos + ImVec2( ( ann->range.max - m_vd.zvStart ) * pxns + 0.5f, lineh + 0.5f ), ann->range.hiMax ? c2 : c1, ann->range.hiMax ? 2 : 1 );
- if( drawMouseLine && ImGui::IsMouseHoveringRect( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns, 0 ), linepos + ImVec2( ( ann->range.max - m_vd.zvStart ) * pxns, lineh ) ) )
- {
- ImGui::BeginTooltip();
- if( ann->text.empty() )
- {
- TextDisabledUnformatted( "Empty annotation" );
- }
- else
- {
- ImGui::TextUnformatted( ann->text.c_str() );
- }
- ImGui::Separator();
- TextFocused( "Annotation begin:", TimeToStringExact( ann->range.min ) );
- TextFocused( "Annotation end:", TimeToStringExact( ann->range.max ) );
- TextFocused( "Annotation length:", TimeToString( ann->range.max - ann->range.min ) );
- ImGui::EndTooltip();
- }
- const auto aw = ( ann->range.max - ann->range.min ) * pxns;
- if( aw > th * 4 )
- {
- draw->AddCircleFilled( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th * 2, th * 2 ), th, 0x88AABB22 );
- draw->AddCircle( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th * 2, th * 2 ), th, 0xAAAABB22 );
- if( drawMouseLine && IsMouseClicked( 0 ) && ImGui::IsMouseHoveringRect( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th, th ), linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th * 3, th * 3 ) ) )
- {
- m_selectedAnnotation = ann.get();
- }
-
- if( !ann->text.empty() )
- {
- const auto tw = ImGui::CalcTextSize( ann->text.c_str() ).x;
- if( aw - th*4 > tw )
- {
- draw->AddText( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th * 4, th * 0.5 ), 0xFFFFFFFF, ann->text.c_str() );
- }
- else
- {
- draw->PushClipRect( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns, 0 ), linepos + ImVec2( ( ann->range.max - m_vd.zvStart ) * pxns, lineh ), true );
- draw->AddText( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th * 4, th * 0.5 ), 0xFFFFFFFF, ann->text.c_str() );
- draw->PopClipRect();
- }
- }
- }
- }
- }
-
- if( m_gpuStart != 0 && m_gpuEnd != 0 )
- {
- const auto px0 = ( m_gpuStart - m_vd.zvStart ) * pxns;
- const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( m_gpuEnd - m_vd.zvStart ) * pxns );
- draw->AddRectFilled( ImVec2( wpos.x + px0, linepos.y ), ImVec2( wpos.x + px1, linepos.y + lineh ), 0x228888DD );
- draw->AddRect( ImVec2( wpos.x + px0, linepos.y ), ImVec2( wpos.x + px1, linepos.y + lineh ), 0x448888DD );
- }
- if( m_gpuInfoWindow )
- {
- const auto px0 = ( m_gpuInfoWindow->CpuStart() - m_vd.zvStart ) * pxns;
- const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( m_gpuInfoWindow->CpuEnd() - m_vd.zvStart ) * pxns );
- draw->AddRectFilled( ImVec2( wpos.x + px0, linepos.y ), ImVec2( wpos.x + px1, linepos.y + lineh ), 0x2288DD88 );
- draw->AddRect( ImVec2( wpos.x + px0, linepos.y ), ImVec2( wpos.x + px1, linepos.y + lineh ), 0x4488DD88 );
- }
-
- const auto scale = GetScale();
- if( m_findZone.range.active && ( m_findZone.show || m_showRanges ) )
- {
- const auto px0 = ( m_findZone.range.min - m_vd.zvStart ) * pxns;
- const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( m_findZone.range.max - m_vd.zvStart ) * pxns );
- DrawStripedRect( draw, wpos, px0, linepos.y, px1, linepos.y + lineh, 10 * scale, 0x2288DD88, true, true );
- DrawLine( draw, ImVec2( dpos.x + px0, linepos.y + 0.5f ), ImVec2( dpos.x + px0, linepos.y + lineh + 0.5f ), m_findZone.range.hiMin ? 0x9988DD88 : 0x3388DD88, m_findZone.range.hiMin ? 2 : 1 );
- DrawLine( draw, ImVec2( dpos.x + px1, linepos.y + 0.5f ), ImVec2( dpos.x + px1, linepos.y + lineh + 0.5f ), m_findZone.range.hiMax ? 0x9988DD88 : 0x3388DD88, m_findZone.range.hiMax ? 2 : 1 );
- }
-
- if( m_statRange.active && ( m_showStatistics || m_showRanges || ( m_sourceViewFile && m_sourceView->IsSymbolView() ) ) )
- {
- const auto px0 = ( m_statRange.min - m_vd.zvStart ) * pxns;
- const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( m_statRange.max - m_vd.zvStart ) * pxns );
- DrawStripedRect( draw, wpos, px0, linepos.y, px1, linepos.y + lineh, 10 * scale, 0x228888EE, true, false );
- DrawLine( draw, ImVec2( dpos.x + px0, linepos.y + 0.5f ), ImVec2( dpos.x + px0, linepos.y + lineh + 0.5f ), m_statRange.hiMin ? 0x998888EE : 0x338888EE, m_statRange.hiMin ? 2 : 1 );
- DrawLine( draw, ImVec2( dpos.x + px1, linepos.y + 0.5f ), ImVec2( dpos.x + px1, linepos.y + lineh + 0.5f ), m_statRange.hiMax ? 0x998888EE : 0x338888EE, m_statRange.hiMax ? 2 : 1 );
- }
-
- if( m_waitStackRange.active && ( m_showWaitStacks || m_showRanges ) )
- {
- const auto px0 = ( m_waitStackRange.min - m_vd.zvStart ) * pxns;
- const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( m_waitStackRange.max - m_vd.zvStart ) * pxns );
- DrawStripedRect( draw, wpos, px0, linepos.y, px1, linepos.y + lineh, 10 * scale, 0x22EEB588, true, true );
- DrawLine( draw, ImVec2( dpos.x + px0, linepos.y + 0.5f ), ImVec2( dpos.x + px0, linepos.y + lineh + 0.5f ), m_waitStackRange.hiMin ? 0x99EEB588 : 0x33EEB588, m_waitStackRange.hiMin ? 2 : 1 );
- DrawLine( draw, ImVec2( dpos.x + px1, linepos.y + 0.5f ), ImVec2( dpos.x + px1, linepos.y + lineh + 0.5f ), m_waitStackRange.hiMax ? 0x99EEB588 : 0x33EEB588, m_waitStackRange.hiMax ? 2 : 1 );
- }
-
- if( m_memInfo.range.active && ( m_memInfo.show || m_showRanges ) )
- {
- const auto px0 = ( m_memInfo.range.min - m_vd.zvStart ) * pxns;
- const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( m_memInfo.range.max - m_vd.zvStart ) * pxns );
- DrawStripedRect( draw, wpos, px0, linepos.y, px1, linepos.y + lineh, 10 * scale, 0x2288EEE3, true, false );
- DrawLine( draw, ImVec2( dpos.x + px0, linepos.y + 0.5f ), ImVec2( dpos.x + px0, linepos.y + lineh + 0.5f ), m_memInfo.range.hiMin ? 0x9988EEE3 : 0x3388EEE3, m_memInfo.range.hiMin ? 2 : 1 );
- DrawLine( draw, ImVec2( dpos.x + px1, linepos.y + 0.5f ), ImVec2( dpos.x + px1, linepos.y + lineh + 0.5f ), m_memInfo.range.hiMax ? 0x9988EEE3 : 0x3388EEE3, m_memInfo.range.hiMax ? 2 : 1 );
- }
-
- if( m_setRangePopup.active || m_setRangePopupOpen )
- {
- const auto s = std::min( m_setRangePopup.min, m_setRangePopup.max );
- const auto e = std::max( m_setRangePopup.min, m_setRangePopup.max );
- DrawStripedRect( draw, wpos, ( s - m_vd.zvStart ) * pxns, linepos.y, ( e - m_vd.zvStart ) * pxns, linepos.y + lineh, 5 * scale, 0x55DD8888, true, false );
- draw->AddRect( ImVec2( wpos.x + ( s - m_vd.zvStart ) * pxns, linepos.y ), ImVec2( wpos.x + ( e - m_vd.zvStart ) * pxns, linepos.y + lineh ), 0x77DD8888 );
- }
-
- if( m_highlight.active && m_highlight.start != m_highlight.end )
- {
- const auto s = std::min( m_highlight.start, m_highlight.end );
- const auto e = std::max( m_highlight.start, m_highlight.end );
- draw->AddRectFilled( ImVec2( wpos.x + ( s - m_vd.zvStart ) * pxns, linepos.y ), ImVec2( wpos.x + ( e - m_vd.zvStart ) * pxns, linepos.y + lineh ), 0x22DD8888 );
- draw->AddRect( ImVec2( wpos.x + ( s - m_vd.zvStart ) * pxns, linepos.y ), ImVec2( wpos.x + ( e - m_vd.zvStart ) * pxns, linepos.y + lineh ), 0x44DD8888 );
-
- ImGui::BeginTooltip();
- ImGui::TextUnformatted( TimeToString( e - s ) );
- ImGui::EndTooltip();
- }
- else if( drawMouseLine )
- {
- auto& io = ImGui::GetIO();
- DrawLine( draw, ImVec2( io.MousePos.x + 0.5f, linepos.y + 0.5f ), ImVec2( io.MousePos.x + 0.5f, linepos.y + lineh + 0.5f ), 0x33FFFFFF );
- }
-
- if( m_highlightZoom.active && m_highlightZoom.start != m_highlightZoom.end )
- {
- const auto s = std::min( m_highlightZoom.start, m_highlightZoom.end );
- const auto e = std::max( m_highlightZoom.start, m_highlightZoom.end );
- draw->AddRectFilled( ImVec2( wpos.x + ( s - m_vd.zvStart ) * pxns, linepos.y ), ImVec2( wpos.x + ( e - m_vd.zvStart ) * pxns, linepos.y + lineh ), 0x1688DD88 );
- draw->AddRect( ImVec2( wpos.x + ( s - m_vd.zvStart ) * pxns, linepos.y ), ImVec2( wpos.x + ( e - m_vd.zvStart ) * pxns, linepos.y + lineh ), 0x2C88DD88 );
- }
-}
-
-#ifndef TRACY_NO_STATISTICS
-int View::DispatchGhostLevel( const Vector& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, float yMin, float yMax, uint64_t tid )
-{
- const auto ty = ImGui::GetTextLineHeight();
- const auto ostep = ty + 1;
- const auto offset = _offset + ostep * depth;
-
- const auto yPos = wpos.y + offset;
- // Inline frames have to be taken into account, hence the multiply by 16 (arbitrary limit for inline frames in client)
- if( yPos + 16 * ostep >= yMin && yPos <= yMax )
- {
- return DrawGhostLevel( vec, hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
- }
- else
- {
- return SkipGhostLevel( vec, hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
- }
-}
-
-int View::DrawGhostLevel( const Vector& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, float yMin, float yMax, uint64_t tid )
-{
- auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart ), [] ( const auto& l, const auto& r ) { return l.end.Val() < r; } );
- if( it == vec.end() ) return depth;
-
- const auto zitend = std::lower_bound( it, vec.end(), m_vd.zvEnd, [] ( const auto& l, const auto& r ) { return l.start.Val() < r; } );
- if( it == zitend ) return depth;
-
- const auto w = ImGui::GetContentRegionAvail().x - 1;
- const auto ty = ImGui::GetTextLineHeight();
- const auto ostep = ty + 1;
- const auto offset = _offset + ostep * depth;
- auto draw = ImGui::GetWindowDrawList();
- const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
-
- depth++;
- int maxdepth = depth;
-
- while( it < zitend )
- {
- auto& ev = *it;
- const auto end = ev.end.Val();
- const auto zsz = std::max( ( end - ev.start.Val() ) * pxns, pxns * 0.5 );
- if( zsz < MinVisSize )
- {
- const auto MinVisNs = MinVisSize * nspx;
- const auto color = MixGhostColor( GetThreadColor( tid, depth ), 0x665555 );
- const auto px0 = ( ev.start.Val() - m_vd.zvStart ) * pxns;
- auto px1ns = ev.end.Val() - m_vd.zvStart;
- auto rend = end;
- auto nextTime = end + MinVisNs;
- for(;;)
- {
- const auto prevIt = it;
- it = std::lower_bound( it, zitend, nextTime, [] ( const auto& l, const auto& r ) { return l.end.Val() < r; } );
- if( it == prevIt ) ++it;
- if( it == zitend ) break;
- const auto nend = it->end.Val();
- const auto nsnext = nend - m_vd.zvStart;
- if( nsnext - px1ns >= MinVisNs * 2 ) break;
- px1ns = nsnext;
- rend = nend;
- nextTime = nend + nspx;
- }
- const auto px1 = px1ns * pxns;
- draw->AddRectFilled( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty ), color );
- DrawZigZag( draw, wpos + ImVec2( 0, offset + ty/2 ), std::max( px0, -10.0 ), std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), ty/4, DarkenColor( color ) );
- if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty + 1 ) ) )
- {
- if( IsMouseClickReleased( 1 ) ) m_setRangePopup = RangeSlim { ev.start.Val(), rend , true };
- ImGui::BeginTooltip();
- ImGui::TextUnformatted( "Multiple ghost zones" );
- ImGui::Separator();
- TextFocused( "Execution time:", TimeToString( rend - ev.start.Val() ) );
- ImGui::EndTooltip();
-
- if( IsMouseClicked( 2 ) && rend - ev.start.Val() > 0 )
- {
- ZoomToRange( ev.start.Val(), rend );
- }
- }
- }
- else
- {
- const auto& ghostKey = m_worker.GetGhostFrame( ev.frame );
- const auto frame = m_worker.GetCallstackFrame( ghostKey.frame );
-
- uint32_t color;
- if( m_vd.dynamicColors == 2 )
- {
- if( frame )
- {
- const auto& sym = frame->data[ghostKey.inlineFrame];
- color = GetHsvColor( sym.name.Idx(), depth );
- }
- else
- {
- color = GetHsvColor( ghostKey.frame.data, depth );
- }
- }
- else
- {
- color = MixGhostColor( GetThreadColor( tid, depth ), 0x665555 );
- }
-
- const auto pr0 = ( ev.start.Val() - m_vd.zvStart ) * pxns;
- const auto pr1 = ( ev.end.Val() - m_vd.zvStart ) * pxns;
- const auto px0 = std::max( pr0, -10.0 );
- const auto px1 = std::max( { std::min( pr1, double( w + 10 ) ), px0 + pxns * 0.5, px0 + MinVisSize } );
- if( !frame )
- {
- char symName[64];
- sprintf( symName, "0x%" PRIx64, m_worker.GetCanonicalPointer( ghostKey.frame ) );
- const auto tsz = ImGui::CalcTextSize( symName );
-
- const auto accentColor = HighlightColor( color );
- const auto darkColor = DarkenColor( color );
- const auto txtColor = 0xFF888888;
- draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), DarkenColor( color ) );
- DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px0, offset ), dpos + ImVec2( px1-1, offset ), accentColor, 1.f );
- DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px1-1, offset + tsz.y ), dpos + ImVec2( px1-1, offset ), darkColor, 1.f );
-
- if( tsz.x < zsz )
- {
- const auto x = ( ev.start.Val() - m_vd.zvStart ) * pxns + ( ( end - ev.start.Val() ) * pxns - tsz.x ) / 2;
- if( x < 0 || x > w - tsz.x )
- {
- ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
- DrawTextContrast( draw, wpos + ImVec2( std::max( std::max( 0., px0 ), std::min( double( w - tsz.x ), x ) ), offset ), txtColor, symName );
- ImGui::PopClipRect();
- }
- else if( ev.start.Val() == ev.end.Val() )
- {
- DrawTextContrast( draw, wpos + ImVec2( px0 + ( px1 - px0 - tsz.x ) * 0.5, offset ), txtColor, symName );
- }
- else
- {
- DrawTextContrast( draw, wpos + ImVec2( x, offset ), txtColor, symName );
- }
- }
- else
- {
- ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
- DrawTextContrast( draw, wpos + ImVec2( ( ev.start.Val() - m_vd.zvStart ) * pxns, offset ), txtColor, symName );
- ImGui::PopClipRect();
- }
-
- if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y + 1 ) ) )
- {
- if( IsMouseClickReleased( 1 ) ) m_setRangePopup = RangeSlim { ev.start.Val(), ev.end.Val() , true };
- ImGui::BeginTooltip();
- TextDisabledUnformatted( ICON_FA_GHOST " Ghost zone" );
- ImGui::Separator();
- TextFocused( "Unknown frame:", symName );
- TextFocused( "Thread:", m_worker.GetThreadName( tid ) );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%s)", RealToString( tid ) );
- if( m_worker.IsThreadFiber( tid ) )
- {
- ImGui::SameLine();
- TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" );
- }
- ImGui::Separator();
- TextFocused( "Execution time:", TimeToString( ev.end.Val() - ev.start.Val() ) );
- ImGui::EndTooltip();
- if( !m_zoomAnim.active && IsMouseClicked( 2 ) )
- {
- ZoomToRange( ev.start.Val(), ev.end.Val() );
- }
- }
- }
- else
- {
- const auto& sym = frame->data[ghostKey.inlineFrame];
- const auto isInline = ghostKey.inlineFrame != frame->size-1;
- const auto col = isInline ? DarkenColor( color ) : color;
- auto symName = m_worker.GetString( sym.name );
- uint32_t txtColor;
- if( symName[0] == '[' )
- {
- txtColor = 0xFF999999;
- }
- else if( !isInline && ( m_worker.GetCanonicalPointer( ghostKey.frame ) >> 63 != 0 ) )
- {
- txtColor = 0xFF8888FF;
- }
- else
- {
- txtColor = 0xFFFFFFFF;
- }
- auto tsz = ImGui::CalcTextSize( symName );
-
- const auto accentColor = HighlightColor( col );
- const auto darkColor = DarkenColor( col );
- draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), col );
- DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px0, offset ), dpos + ImVec2( px1-1, offset ), accentColor, 1.f );
- DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px1-1, offset + tsz.y ), dpos + ImVec2( px1-1, offset ), darkColor, 1.f );
-
- auto origSymName = symName;
- if( tsz.x > zsz )
- {
- symName = ShortenNamespace( symName );
- tsz = ImGui::CalcTextSize( symName );
- }
-
- if( tsz.x < zsz )
- {
- const auto x = ( ev.start.Val() - m_vd.zvStart ) * pxns + ( ( end - ev.start.Val() ) * pxns - tsz.x ) / 2;
- if( x < 0 || x > w - tsz.x )
- {
- ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
- DrawTextContrast( draw, wpos + ImVec2( std::max( std::max( 0., px0 ), std::min( double( w - tsz.x ), x ) ), offset ), txtColor, symName );
- ImGui::PopClipRect();
- }
- else if( ev.start.Val() == ev.end.Val() )
- {
- DrawTextContrast( draw, wpos + ImVec2( px0 + ( px1 - px0 - tsz.x ) * 0.5, offset ), txtColor, symName );
- }
- else
- {
- DrawTextContrast( draw, wpos + ImVec2( x, offset ), txtColor, symName );
- }
- }
- else
- {
- ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
- DrawTextContrast( draw, wpos + ImVec2( ( ev.start.Val() - m_vd.zvStart ) * pxns, offset ), txtColor, symName );
- ImGui::PopClipRect();
- }
-
- if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y + 1 ) ) )
- {
- if( IsMouseClickReleased( 1 ) ) m_setRangePopup = RangeSlim { ev.start.Val(), ev.end.Val(), true };
- ImGui::BeginTooltip();
- TextDisabledUnformatted( ICON_FA_GHOST " Ghost zone" );
- if( sym.symAddr >> 63 != 0 )
- {
- ImGui::SameLine();
- TextDisabledUnformatted( ICON_FA_HAT_WIZARD " kernel" );
- }
- ImGui::Separator();
- ImGui::TextUnformatted( origSymName );
- if( isInline )
- {
- ImGui::SameLine();
- TextDisabledUnformatted( "[inline]" );
- }
- const auto symbol = m_worker.GetSymbolData( sym.symAddr );
- if( symbol ) TextFocused( "Image:", m_worker.GetString( symbol->imageName ) );
- TextDisabledUnformatted( "Location:" );
- ImGui::SameLine();
- const char* file = m_worker.GetString( sym.file );
- uint32_t line = sym.line;
- ImGui::TextUnformatted( LocationToString( file, line ) );
- ImGui::SameLine();
- ImGui::TextDisabled( "(0x%" PRIx64 ")", sym.symAddr );
- TextFocused( "Thread:", m_worker.GetThreadName( tid ) );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%s)", RealToString( tid ) );
- if( m_worker.IsThreadFiber( tid ) )
- {
- ImGui::SameLine();
- TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" );
- }
- ImGui::Separator();
- TextFocused( "Execution time:", TimeToString( ev.end.Val() - ev.start.Val() ) );
- ImGui::EndTooltip();
-
- if( IsMouseClicked( 0 ) )
- {
- ViewDispatch( file, line, sym.symAddr );
- }
- else if( !m_zoomAnim.active && IsMouseClicked( 2 ) )
- {
- ZoomToRange( ev.start.Val(), ev.end.Val() );
- }
- }
- }
-
- if( ev.child >= 0 )
- {
- const auto d = DispatchGhostLevel( m_worker.GetGhostChildren( ev.child ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
- if( d > maxdepth ) maxdepth = d;
- }
- ++it;
- }
- }
-
- return maxdepth;
-}
-
-int View::SkipGhostLevel( const Vector& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, float yMin, float yMax, uint64_t tid )
-{
- auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart ), [] ( const auto& l, const auto& r ) { return l.end.Val() < r; } );
- if( it == vec.end() ) return depth;
-
- const auto zitend = std::lower_bound( it, vec.end(), m_vd.zvEnd, [] ( const auto& l, const auto& r ) { return l.start.Val() < r; } );
- if( it == zitend ) return depth;
-
- depth++;
- int maxdepth = depth;
-
- while( it < zitend )
- {
- auto& ev = *it;
- const auto end = ev.end.Val();
- const auto zsz = std::max( ( end - ev.start.Val() ) * pxns, pxns * 0.5 );
- if( zsz < MinVisSize )
- {
- const auto MinVisNs = MinVisSize * nspx;
- auto px1ns = ev.end.Val() - m_vd.zvStart;
- auto nextTime = end + MinVisNs;
- for(;;)
- {
- const auto prevIt = it;
- it = std::lower_bound( it, zitend, nextTime, [] ( const auto& l, const auto& r ) { return l.end.Val() < r; } );
- if( it == prevIt ) ++it;
- if( it == zitend ) break;
- const auto nend = it->end.Val();
- const auto nsnext = nend - m_vd.zvStart;
- if( nsnext - px1ns >= MinVisNs * 2 ) break;
- px1ns = nsnext;
- nextTime = nend + nspx;
- }
- }
- else
- {
- if( ev.child >= 0 )
- {
- const auto d = DispatchGhostLevel( m_worker.GetGhostChildren( ev.child ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
- if( d > maxdepth ) maxdepth = d;
- }
- ++it;
- }
- }
-
- return maxdepth;
-}
-#endif
-
-int View::DispatchZoneLevel( const Vector>& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, float yMin, float yMax, uint64_t tid )
-{
- const auto ty = ImGui::GetTextLineHeight();
- const auto ostep = ty + 1;
- const auto offset = _offset + ostep * depth;
-
- const auto yPos = wpos.y + offset;
- if( yPos + ostep >= yMin && yPos <= yMax )
- {
- if( vec.is_magic() )
- {
- return DrawZoneLevel>( *(Vector*)( &vec ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
- }
- else
- {
- return DrawZoneLevel>( vec, hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
- }
- }
- else
- {
- if( vec.is_magic() )
- {
- return SkipZoneLevel>( *(Vector*)( &vec ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
- }
- else
- {
- return SkipZoneLevel>( vec, hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
- }
- }
-}
-
-template
-int View::DrawZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, float yMin, float yMax, uint64_t tid )
-{
- const auto delay = m_worker.GetDelay();
- const auto resolution = m_worker.GetResolution();
- // cast to uint64_t, so that unended zones (end = -1) are still drawn
- auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart - delay ), [] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)a(l).End() < (uint64_t)r; } );
- if( it == vec.end() ) return depth;
-
- const auto zitend = std::lower_bound( it, vec.end(), m_vd.zvEnd + resolution, [] ( const auto& l, const auto& r ) { Adapter a; return a(l).Start() < r; } );
- if( it == zitend ) return depth;
- Adapter a;
- if( !a(*it).IsEndValid() && m_worker.GetZoneEnd( a(*it) ) < m_vd.zvStart ) return depth;
-
- const auto w = ImGui::GetContentRegionAvail().x - 1;
- const auto ty = ImGui::GetTextLineHeight();
- const auto ostep = ty + 1;
- const auto offset = _offset + ostep * depth;
- auto draw = ImGui::GetWindowDrawList();
- const auto dsz = delay * pxns;
- const auto rsz = resolution * pxns;
- const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
-
- const auto ty025 = round( ty * 0.25f );
- const auto ty05 = round( ty * 0.5f );
- const auto ty075 = round( ty * 0.75f );
-
- depth++;
- int maxdepth = depth;
-
- while( it < zitend )
- {
- auto& ev = a(*it);
- const auto end = m_worker.GetZoneEnd( ev );
- const auto zsz = std::max( ( end - ev.Start() ) * pxns, pxns * 0.5 );
- if( zsz < MinVisSize )
- {
- const auto MinVisNs = MinVisSize * nspx;
- const auto color = GetThreadColor( tid, depth );
- int num = 0;
- const auto px0 = ( ev.Start() - m_vd.zvStart ) * pxns;
- auto px1ns = end - m_vd.zvStart;
- auto rend = end;
- auto nextTime = end + MinVisNs;
- for(;;)
- {
- const auto prevIt = it;
- it = std::lower_bound( it, zitend, nextTime, [] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)a(l).End() < (uint64_t)r; } );
- if( it == prevIt ) ++it;
- num += std::distance( prevIt, it );
- if( it == zitend ) break;
- const auto nend = m_worker.GetZoneEnd( a(*it) );
- const auto nsnext = nend - m_vd.zvStart;
- if( nsnext - px1ns >= MinVisNs * 2 ) break;
- px1ns = nsnext;
- rend = nend;
- nextTime = nend + nspx;
- }
- const auto px1 = px1ns * pxns;
- draw->AddRectFilled( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty ), color );
- DrawZigZag( draw, wpos + ImVec2( 0, offset + ty/2 ), std::max( px0, -10.0 ), std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), ty/4, DarkenColor( color ) );
- if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty + 1 ) ) )
- {
- if( IsMouseClickReleased( 1 ) ) m_setRangePopup = RangeSlim { ev.Start(), rend, true };
- if( num > 1 )
- {
- ImGui::BeginTooltip();
- TextFocused( "Zones too small to display:", RealToString( num ) );
- ImGui::Separator();
- TextFocused( "Execution time:", TimeToString( rend - ev.Start() ) );
- ImGui::EndTooltip();
-
- if( IsMouseClicked( 2 ) && rend - ev.Start() > 0 )
- {
- ZoomToRange( ev.Start(), rend );
- }
- }
- else
- {
- ZoneTooltip( ev );
-
- if( IsMouseClicked( 2 ) && rend - ev.Start() > 0 )
- {
- ZoomToZone( ev );
- }
- if( IsMouseClicked( 0 ) )
- {
- if( ImGui::GetIO().KeyCtrl )
- {
- auto& srcloc = m_worker.GetSourceLocation( ev.SrcLoc() );
- m_findZone.ShowZone( ev.SrcLoc(), m_worker.GetString( srcloc.name.active ? srcloc.name : srcloc.function ) );
- }
- else
- {
- ShowZoneInfo( ev );
- }
- }
-
- m_zoneSrcLocHighlight = ev.SrcLoc();
- m_zoneHover = &ev;
- }
- }
- const auto tmp = RealToString( num );
- const auto tsz = ImGui::CalcTextSize( tmp );
- if( tsz.x < px1 - px0 )
- {
- const auto x = px0 + ( px1 - px0 - tsz.x ) / 2;
- DrawTextContrast( draw, wpos + ImVec2( x, offset ), 0xFF4488DD, tmp );
- }
- }
- else
- {
- const auto zoneColor = GetZoneColorData( ev, tid, depth );
- const char* zoneName = m_worker.GetZoneName( ev );
-
- if( ev.HasChildren() )
- {
- const auto d = DispatchZoneLevel( m_worker.GetZoneChildren( ev.Child() ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
- if( d > maxdepth ) maxdepth = d;
- }
-
- auto tsz = ImGui::CalcTextSize( zoneName );
- if( tsz.x > zsz )
- {
- zoneName = ShortenNamespace( zoneName );
- tsz = ImGui::CalcTextSize( zoneName );
- }
-
- const auto pr0 = ( ev.Start() - m_vd.zvStart ) * pxns;
- const auto pr1 = ( end - m_vd.zvStart ) * pxns;
- const auto px0 = std::max( pr0, -10.0 );
- const auto px1 = std::max( { std::min( pr1, double( w + 10 ) ), px0 + pxns * 0.5, px0 + MinVisSize } );
- draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.color );
- if( zoneColor.highlight )
- {
- draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.accentColor, 0.f, -1, zoneColor.thickness );
- }
- else
- {
- const auto darkColor = DarkenColor( zoneColor.color );
- DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px0, offset ), dpos + ImVec2( px1-1, offset ), zoneColor.accentColor, zoneColor.thickness );
- DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px1-1, offset + tsz.y ), dpos + ImVec2( px1-1, offset ), darkColor, zoneColor.thickness );
- }
- if( dsz > MinVisSize )
- {
- const auto diff = dsz - MinVisSize;
- uint32_t color;
- if( diff < 1 )
- {
- color = ( uint32_t( diff * 0x88 ) << 24 ) | 0x2222DD;
- }
- else
- {
- color = 0x882222DD;
- }
-
- draw->AddRectFilled( wpos + ImVec2( pr0, offset ), wpos + ImVec2( std::min( pr0+dsz, pr1 ), offset + tsz.y ), color );
- draw->AddRectFilled( wpos + ImVec2( pr1, offset ), wpos + ImVec2( pr1+dsz, offset + tsz.y ), color );
- }
- if( rsz > MinVisSize )
- {
- const auto diff = rsz - MinVisSize;
- uint32_t color;
- if( diff < 1 )
- {
- color = ( uint32_t( diff * 0xAA ) << 24 ) | 0xFFFFFF;
- }
- else
- {
- color = 0xAAFFFFFF;
- }
-
- DrawLine( draw, dpos + ImVec2( pr0 + rsz, offset + ty05 ), dpos + ImVec2( pr0 - rsz, offset + ty05 ), color );
- DrawLine( draw, dpos + ImVec2( pr0 + rsz, offset + ty025 ), dpos + ImVec2( pr0 + rsz, offset + ty075 ), color );
- DrawLine( draw, dpos + ImVec2( pr0 - rsz, offset + ty025 ), dpos + ImVec2( pr0 - rsz, offset + ty075 ), color );
-
- DrawLine( draw, dpos + ImVec2( pr1 + rsz, offset + ty05 ), dpos + ImVec2( pr1 - rsz, offset + ty05 ), color );
- DrawLine( draw, dpos + ImVec2( pr1 + rsz, offset + ty025 ), dpos + ImVec2( pr1 + rsz, offset + ty075 ), color );
- DrawLine( draw, dpos + ImVec2( pr1 - rsz, offset + ty025 ), dpos + ImVec2( pr1 - rsz, offset + ty075 ), color );
- }
- if( tsz.x < zsz )
- {
- const auto x = ( ev.Start() - m_vd.zvStart ) * pxns + ( ( end - ev.Start() ) * pxns - tsz.x ) / 2;
- if( x < 0 || x > w - tsz.x )
- {
- ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
- DrawTextContrast( draw, wpos + ImVec2( std::max( std::max( 0., px0 ), std::min( double( w - tsz.x ), x ) ), offset ), 0xFFFFFFFF, zoneName );
- ImGui::PopClipRect();
- }
- else if( ev.Start() == ev.End() )
- {
- DrawTextContrast( draw, wpos + ImVec2( px0 + ( px1 - px0 - tsz.x ) * 0.5, offset ), 0xFFFFFFFF, zoneName );
- }
- else
- {
- DrawTextContrast( draw, wpos + ImVec2( x, offset ), 0xFFFFFFFF, zoneName );
- }
- }
- else
- {
- ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
- DrawTextContrast( draw, wpos + ImVec2( ( ev.Start() - m_vd.zvStart ) * pxns, offset ), 0xFFFFFFFF, zoneName );
- ImGui::PopClipRect();
- }
-
- if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y + 1 ) ) )
- {
- ZoneTooltip( ev );
- if( IsMouseClickReleased( 1 ) ) m_setRangePopup = RangeSlim { ev.Start(), m_worker.GetZoneEnd( ev ), true };
-
- if( !m_zoomAnim.active && IsMouseClicked( 2 ) )
- {
- ZoomToZone( ev );
- }
- if( IsMouseClicked( 0 ) )
- {
- if( ImGui::GetIO().KeyCtrl )
- {
- auto& srcloc = m_worker.GetSourceLocation( ev.SrcLoc() );
- m_findZone.ShowZone( ev.SrcLoc(), m_worker.GetString( srcloc.name.active ? srcloc.name : srcloc.function ) );
- }
- else
- {
- ShowZoneInfo( ev );
- }
- }
-
- m_zoneSrcLocHighlight = ev.SrcLoc();
- m_zoneHover = &ev;
- }
-
- ++it;
- }
- }
- return maxdepth;
-}
-
-template
-int View::SkipZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, float yMin, float yMax, uint64_t tid )
-{
- const auto delay = m_worker.GetDelay();
- const auto resolution = m_worker.GetResolution();
- // cast to uint64_t, so that unended zones (end = -1) are still drawn
- auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart - delay ), [] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)a(l).End() < (uint64_t)r; } );
- if( it == vec.end() ) return depth;
-
- const auto zitend = std::lower_bound( it, vec.end(), m_vd.zvEnd + resolution, [] ( const auto& l, const auto& r ) { Adapter a; return a(l).Start() < r; } );
- if( it == zitend ) return depth;
-
- depth++;
- int maxdepth = depth;
-
- Adapter a;
- while( it < zitend )
- {
- auto& ev = a(*it);
- const auto end = m_worker.GetZoneEnd( ev );
- const auto zsz = std::max( ( end - ev.Start() ) * pxns, pxns * 0.5 );
- if( zsz < MinVisSize )
- {
- const auto MinVisNs = MinVisSize * nspx;
- auto px1ns = end - m_vd.zvStart;
- auto nextTime = end + MinVisNs;
- for(;;)
- {
- const auto prevIt = it;
- it = std::lower_bound( it, zitend, nextTime, [] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)a(l).End() < (uint64_t)r; } );
- if( it == prevIt ) ++it;
- if( it == zitend ) break;
- const auto nend = m_worker.GetZoneEnd( a(*it) );
- const auto nsnext = nend - m_vd.zvStart;
- if( nsnext - px1ns >= MinVisNs * 2 ) break;
- px1ns = nsnext;
- nextTime = nend + nspx;
- }
- }
- else
- {
- if( ev.HasChildren() )
- {
- const auto d = DispatchZoneLevel( m_worker.GetZoneChildren( ev.Child() ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
- if( d > maxdepth ) maxdepth = d;
- }
- ++it;
- }
- }
- return maxdepth;
-}
-
-int View::DispatchGpuZoneLevel( const Vector>& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, uint64_t thread, float yMin, float yMax, int64_t begin, int drift )
-{
- const auto ty = ImGui::GetTextLineHeight();
- const auto ostep = ty + 1;
- const auto offset = _offset + ostep * depth;
-
- const auto yPos = wpos.y + offset;
- if( yPos + ostep >= yMin && yPos <= yMax )
- {
- if( vec.is_magic() )
- {
- return DrawGpuZoneLevel>( *(Vector*)&vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift );
- }
- else
- {
- return DrawGpuZoneLevel>( vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift );
- }
- }
- else
- {
- if( vec.is_magic() )
- {
- return SkipGpuZoneLevel>( *(Vector*)&vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift );
- }
- else
- {
- return SkipGpuZoneLevel>( vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift );
- }
- }
-}
-
-template
-int View::DrawGpuZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, uint64_t thread, float yMin, float yMax, int64_t begin, int drift )
-{
- const auto delay = m_worker.GetDelay();
- const auto resolution = m_worker.GetResolution();
- // cast to uint64_t, so that unended zones (end = -1) are still drawn
- auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart - delay ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } );
- if( it == vec.end() ) return depth;
-
- const auto zitend = std::lower_bound( it, vec.end(), std::max( 0, m_vd.zvEnd + resolution ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuStart(), begin, drift ) < (uint64_t)r; } );
- if( it == zitend ) return depth;
-
- const auto w = ImGui::GetContentRegionAvail().x - 1;
- const auto ty = ImGui::GetTextLineHeight();
- const auto ostep = ty + 1;
- const auto offset = _offset + ostep * depth;
- auto draw = ImGui::GetWindowDrawList();
- const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
-
- depth++;
- int maxdepth = depth;
-
- Adapter a;
- while( it < zitend )
- {
- auto& ev = a(*it);
- auto end = m_worker.GetZoneEnd( ev );
- if( end == std::numeric_limits::max() ) break;
- const auto start = AdjustGpuTime( ev.GpuStart(), begin, drift );
- end = AdjustGpuTime( end, begin, drift );
- const auto zsz = std::max( ( end - start ) * pxns, pxns * 0.5 );
- if( zsz < MinVisSize )
- {
- const auto color = GetZoneColor( ev );
- const auto MinVisNs = MinVisSize * nspx;
- int num = 0;
- const auto px0 = ( start - m_vd.zvStart ) * pxns;
- auto px1ns = end - m_vd.zvStart;
- auto rend = end;
- auto nextTime = end + MinVisNs;
- for(;;)
- {
- const auto prevIt = it;
- it = std::lower_bound( it, zitend, std::max( 0, nextTime ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } );
- if( it == prevIt ) ++it;
- num += std::distance( prevIt, it );
- if( it == zitend ) break;
- const auto nend = AdjustGpuTime( m_worker.GetZoneEnd( a(*it) ), begin, drift );
- const auto nsnext = nend - m_vd.zvStart;
- if( nsnext < 0 || nsnext - px1ns >= MinVisNs * 2 ) break;
- px1ns = nsnext;
- rend = nend;
- nextTime = nend + nspx;
- }
- const auto px1 = px1ns * pxns;
- draw->AddRectFilled( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty ), color );
- DrawZigZag( draw, wpos + ImVec2( 0, offset + ty/2 ), std::max( px0, -10.0 ), std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), ty/4, DarkenColor( color ) );
- if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty + 1 ) ) )
- {
- if( num > 1 )
- {
- ImGui::BeginTooltip();
- TextFocused( "Zones too small to display:", RealToString( num ) );
- ImGui::Separator();
- TextFocused( "Execution time:", TimeToString( rend - start ) );
- ImGui::EndTooltip();
-
- if( IsMouseClicked( 2 ) && rend - start > 0 )
- {
- ZoomToRange( start, rend );
- }
- }
- else
- {
- const auto zoneThread = thread != 0 ? thread : m_worker.DecompressThread( ev.Thread() );
- ZoneTooltip( ev );
-
- if( IsMouseClicked( 2 ) && rend - start > 0 )
- {
- ZoomToZone( ev );
- }
- if( IsMouseClicked( 0 ) )
- {
- ShowZoneInfo( ev, zoneThread );
- }
-
- m_gpuThread = zoneThread;
- m_gpuStart = ev.CpuStart();
- m_gpuEnd = ev.CpuEnd();
- }
- }
- const auto tmp = RealToString( num );
- const auto tsz = ImGui::CalcTextSize( tmp );
- if( tsz.x < px1 - px0 )
- {
- const auto x = px0 + ( px1 - px0 - tsz.x ) / 2;
- DrawTextContrast( draw, wpos + ImVec2( x, offset ), 0xFF4488DD, tmp );
- }
- }
- else
- {
- if( ev.Child() >= 0 )
- {
- const auto d = DispatchGpuZoneLevel( m_worker.GetGpuChildren( ev.Child() ), hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift );
- if( d > maxdepth ) maxdepth = d;
- }
-
- const char* zoneName = m_worker.GetZoneName( ev );
- auto tsz = ImGui::CalcTextSize( zoneName );
-
- const auto pr0 = ( start - m_vd.zvStart ) * pxns;
- const auto pr1 = ( end - m_vd.zvStart ) * pxns;
- const auto px0 = std::max( pr0, -10.0 );
- const auto px1 = std::max( { std::min( pr1, double( w + 10 ) ), px0 + pxns * 0.5, px0 + MinVisSize } );
- const auto zoneColor = GetZoneColorData( ev );
- draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.color );
- if( zoneColor.highlight )
- {
- draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.accentColor, 0.f, -1, zoneColor.thickness );
- }
- else
- {
- const auto darkColor = DarkenColor( zoneColor.color );
- DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px0, offset ), dpos + ImVec2( px1-1, offset ), zoneColor.accentColor, zoneColor.thickness );
- DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px1-1, offset + tsz.y ), dpos + ImVec2( px1-1, offset ), darkColor, zoneColor.thickness );
- }
- if( tsz.x < zsz )
- {
- const auto x = ( start - m_vd.zvStart ) * pxns + ( ( end - start ) * pxns - tsz.x ) / 2;
- if( x < 0 || x > w - tsz.x )
- {
- ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
- DrawTextContrast( draw, wpos + ImVec2( std::max( std::max( 0., px0 ), std::min( double( w - tsz.x ), x ) ), offset ), 0xFFFFFFFF, zoneName );
- ImGui::PopClipRect();
- }
- else if( ev.GpuStart() == ev.GpuEnd() )
- {
- DrawTextContrast( draw, wpos + ImVec2( px0 + ( px1 - px0 - tsz.x ) * 0.5, offset ), 0xFFFFFFFF, zoneName );
- }
- else
- {
- DrawTextContrast( draw, wpos + ImVec2( x, offset ), 0xFFFFFFFF, zoneName );
- }
- }
- else
- {
- ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
- DrawTextContrast( draw, wpos + ImVec2( ( start - m_vd.zvStart ) * pxns, offset ), 0xFFFFFFFF, zoneName );
- ImGui::PopClipRect();
- }
-
- if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y + 1 ) ) )
- {
- const auto zoneThread = thread != 0 ? thread : m_worker.DecompressThread( ev.Thread() );
- ZoneTooltip( ev );
-
- if( !m_zoomAnim.active && IsMouseClicked( 2 ) )
- {
- ZoomToZone( ev );
- }
- if( IsMouseClicked( 0 ) )
- {
- ShowZoneInfo( ev, zoneThread );
- }
-
- m_gpuThread = zoneThread;
- m_gpuStart = ev.CpuStart();
- m_gpuEnd = ev.CpuEnd();
- }
-
- ++it;
- }
- }
- return maxdepth;
-}
-
-template
-int View::SkipGpuZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, uint64_t thread, float yMin, float yMax, int64_t begin, int drift )
-{
- const auto delay = m_worker.GetDelay();
- const auto resolution = m_worker.GetResolution();
- // cast to uint64_t, so that unended zones (end = -1) are still drawn
- auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart - delay ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } );
- if( it == vec.end() ) return depth;
-
- const auto zitend = std::lower_bound( it, vec.end(), std::max( 0, m_vd.zvEnd + resolution ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuStart(), begin, drift ) < (uint64_t)r; } );
- if( it == zitend ) return depth;
-
- depth++;
- int maxdepth = depth;
-
- Adapter a;
- while( it < zitend )
- {
- auto& ev = a(*it);
- auto end = m_worker.GetZoneEnd( ev );
- if( end == std::numeric_limits::max() ) break;
- const auto start = AdjustGpuTime( ev.GpuStart(), begin, drift );
- end = AdjustGpuTime( end, begin, drift );
- const auto zsz = std::max( ( end - start ) * pxns, pxns * 0.5 );
- if( zsz < MinVisSize )
- {
- const auto MinVisNs = MinVisSize * nspx;
- auto px1ns = end - m_vd.zvStart;
- auto nextTime = end + MinVisNs;
- for(;;)
- {
- const auto prevIt = it;
- it = std::lower_bound( it, zitend, nextTime, [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } );
- if( it == prevIt ) ++it;
- if( it == zitend ) break;
- const auto nend = AdjustGpuTime( m_worker.GetZoneEnd( a(*it) ), begin, drift );
- const auto nsnext = nend - m_vd.zvStart;
- if( nsnext - px1ns >= MinVisNs * 2 ) break;
- px1ns = nsnext;
- nextTime = nend + nspx;
- }
- }
- else
- {
- if( ev.Child() >= 0 )
- {
- const auto d = DispatchGpuZoneLevel( m_worker.GetGpuChildren( ev.Child() ), hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift );
- if( d > maxdepth ) maxdepth = d;
- }
- ++it;
- }
- }
- return maxdepth;
-}
-
void View::DrawTextEditor()
{
const auto scale = GetScale();
diff --git a/server/TracyView_Timeline.cpp b/server/TracyView_Timeline.cpp
new file mode 100644
index 00000000..c5a85627
--- /dev/null
+++ b/server/TracyView_Timeline.cpp
@@ -0,0 +1,948 @@
+#include
+
+#include "TracyMouse.hpp"
+#include "TracyPrint.hpp"
+#include "TracySourceView.hpp"
+#include "TracyView.hpp"
+
+namespace tracy
+{
+
+enum { MinVisSize = 3 };
+
+extern double s_time;
+
+void View::DrawZones()
+{
+ m_msgHighlight.Decay( nullptr );
+ m_zoneSrcLocHighlight.Decay( 0 );
+ m_lockHoverHighlight.Decay( InvalidId );
+ m_drawThreadMigrations.Decay( 0 );
+ m_drawThreadHighlight.Decay( 0 );
+ m_cpuDataThread.Decay( 0 );
+ m_zoneHover = nullptr;
+ m_zoneHover2.Decay( nullptr );
+ m_findZone.range.StartFrame();
+ m_statRange.StartFrame();
+ m_waitStackRange.StartFrame();
+ m_memInfo.range.StartFrame();
+ m_yDelta = 0;
+
+ if( m_vd.zvStart == m_vd.zvEnd ) return;
+ assert( m_vd.zvStart < m_vd.zvEnd );
+
+ if( ImGui::GetCurrentWindowRead()->SkipItems ) return;
+
+ m_gpuThread = 0;
+ m_gpuStart = 0;
+ m_gpuEnd = 0;
+
+ const auto linepos = ImGui::GetCursorScreenPos();
+ const auto lineh = ImGui::GetContentRegionAvail().y;
+
+ auto draw = ImGui::GetWindowDrawList();
+ const auto w = ImGui::GetContentRegionAvail().x - ImGui::GetStyle().ScrollbarSize;
+ const auto timespan = m_vd.zvEnd - m_vd.zvStart;
+ auto pxns = w / double( timespan );
+
+ const auto winpos = ImGui::GetWindowPos();
+ const auto winsize = ImGui::GetWindowSize();
+ const bool drawMouseLine = ImGui::IsWindowHovered( ImGuiHoveredFlags_ChildWindows | ImGuiHoveredFlags_AllowWhenBlockedByActiveItem ) && ImGui::IsMouseHoveringRect( winpos, winpos + winsize, false );
+ if( drawMouseLine )
+ {
+ HandleRange( m_findZone.range, timespan, ImGui::GetCursorScreenPos(), w );
+ HandleRange( m_statRange, timespan, ImGui::GetCursorScreenPos(), w );
+ HandleRange( m_waitStackRange, timespan, ImGui::GetCursorScreenPos(), w );
+ HandleRange( m_memInfo.range, timespan, ImGui::GetCursorScreenPos(), w );
+ for( auto& v : m_annotations )
+ {
+ v->range.StartFrame();
+ HandleRange( v->range, timespan, ImGui::GetCursorScreenPos(), w );
+ }
+ HandleZoneViewMouse( timespan, ImGui::GetCursorScreenPos(), w, pxns );
+ }
+
+ {
+ const auto tbegin = 0;
+ const auto tend = m_worker.GetLastTime();
+ if( tbegin > m_vd.zvStart )
+ {
+ draw->AddRectFilled( linepos, linepos + ImVec2( ( tbegin - m_vd.zvStart ) * pxns, lineh ), 0x44000000 );
+ }
+ if( tend < m_vd.zvEnd )
+ {
+ draw->AddRectFilled( linepos + ImVec2( ( tend - m_vd.zvStart ) * pxns, 0 ), linepos + ImVec2( w, lineh ), 0x44000000 );
+ }
+ }
+
+ DrawZoneFramesHeader();
+ auto& frames = m_worker.GetFrames();
+ for( auto fd : frames )
+ {
+ if( Vis( fd ).visible )
+ {
+ DrawZoneFrames( *fd );
+ }
+ }
+
+ const auto yMin = ImGui::GetCursorScreenPos().y;
+ const auto yMax = linepos.y + lineh;
+
+ ImGui::BeginChild( "##zoneWin", ImVec2( ImGui::GetContentRegionAvail().x, ImGui::GetContentRegionAvail().y ), false, ImGuiWindowFlags_AlwaysVerticalScrollbar | ImGuiWindowFlags_NoScrollWithMouse );
+
+ if( m_yDelta != 0 )
+ {
+ auto& io = ImGui::GetIO();
+ auto y = ImGui::GetScrollY();
+ ImGui::SetScrollY( y - m_yDelta );
+ io.MouseClickedPos[1].y = io.MousePos.y;
+ }
+
+ const auto wpos = ImGui::GetCursorScreenPos();
+ const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
+ const auto h = std::max( m_vd.zvHeight, ImGui::GetContentRegionAvail().y - 4 ); // magic border value
+
+ ImGui::InvisibleButton( "##zones", ImVec2( w, h ) );
+ bool hover = ImGui::IsItemHovered();
+ draw = ImGui::GetWindowDrawList();
+
+ const auto nspx = 1.0 / pxns;
+
+ const auto ty = ImGui::GetTextLineHeight();
+ const auto ostep = ty + 1;
+ int offset = 0;
+ const auto to = 9.f;
+ const auto th = ( ty - to ) * sqrt( 3 ) * 0.5;
+
+ // gpu zones
+ if( m_vd.drawGpuZones )
+ {
+ for( size_t i=0; ithreadData.size() == 1;
+ int depth = 0;
+ offset += ostep;
+ if( showFull && !v->threadData.empty() )
+ {
+ for( auto& td : v->threadData )
+ {
+ auto& tl = td.second.timeline;
+ assert( !tl.empty() );
+ if( tl.is_magic() )
+ {
+ auto& tlm = *(Vector*)&tl;
+ if( tlm.front().GpuStart() >= 0 )
+ {
+ const auto begin = tlm.front().GpuStart();
+ const auto drift = GpuDrift( v );
+ if( !singleThread ) offset += sstep;
+ const auto partDepth = DispatchGpuZoneLevel( tl, hover, pxns, int64_t( nspx ), wpos, offset, 0, v->thread, yMin, yMax, begin, drift );
+ if( partDepth != 0 )
+ {
+ if( !singleThread )
+ {
+ ImGui::PushFont( m_smallFont );
+ DrawTextContrast( draw, wpos + ImVec2( ty, offset-1-sstep ), 0xFFFFAAAA, m_worker.GetThreadName( td.first ) );
+ DrawLine( draw, dpos + ImVec2( 0, offset+sty-sstep ), dpos + ImVec2( w, offset+sty-sstep ), 0x22FFAAAA );
+ ImGui::PopFont();
+ }
+
+ offset += ostep * partDepth;
+ depth += partDepth;
+ }
+ else if( !singleThread )
+ {
+ offset -= sstep;
+ }
+ }
+ }
+ else
+ {
+ if( tl.front()->GpuStart() >= 0 )
+ {
+ const auto begin = tl.front()->GpuStart();
+ const auto drift = GpuDrift( v );
+ if( !singleThread ) offset += sstep;
+ const auto partDepth = DispatchGpuZoneLevel( tl, hover, pxns, int64_t( nspx ), wpos, offset, 0, v->thread, yMin, yMax, begin, drift );
+ if( partDepth != 0 )
+ {
+ if( !singleThread )
+ {
+ ImGui::PushFont( m_smallFont );
+ DrawTextContrast( draw, wpos + ImVec2( ty, offset-1-sstep ), 0xFFFFAAAA, m_worker.GetThreadName( td.first ) );
+ DrawLine( draw, dpos + ImVec2( 0, offset+sty-sstep ), dpos + ImVec2( w, offset+sty-sstep ), 0x22FFAAAA );
+ ImGui::PopFont();
+ }
+
+ offset += ostep * partDepth;
+ depth += partDepth;
+ }
+ else if( !singleThread )
+ {
+ offset -= sstep;
+ }
+ }
+ }
+ }
+ }
+ offset += ostep * 0.2f;
+
+ if( !m_vd.drawEmptyLabels && showFull && depth == 0 )
+ {
+ vis.height = 0;
+ vis.offset = 0;
+ offset = oldOffset;
+ }
+ else if( yPos + ostep >= yMin && yPos <= yMax )
+ {
+ DrawLine( draw, dpos + ImVec2( 0, oldOffset + ostep - 1 ), dpos + ImVec2( w, oldOffset + ostep - 1 ), 0x33FFFFFF );
+
+ if( showFull )
+ {
+ draw->AddTriangleFilled( wpos + ImVec2( to/2, oldOffset + to/2 ), wpos + ImVec2( ty - to/2, oldOffset + to/2 ), wpos + ImVec2( ty * 0.5, oldOffset + to/2 + th ), 0xFFFFAAAA );
+ }
+ else
+ {
+ draw->AddTriangle( wpos + ImVec2( to/2, oldOffset + to/2 ), wpos + ImVec2( to/2, oldOffset + ty - to/2 ), wpos + ImVec2( to/2 + th, oldOffset + ty * 0.5 ), 0xFF886666, 2.0f );
+ }
+
+ const bool isMultithreaded = (v->type == GpuContextType::Vulkan) || (v->type == GpuContextType::OpenCL) || (v->type == GpuContextType::Direct3D12);
+
+ float boxwidth;
+ char buf[64];
+ sprintf( buf, "%s context %zu", GpuContextNames[(int)v->type], i );
+ if( v->name.Active() )
+ {
+ char tmp[4096];
+ sprintf( tmp, "%s: %s", buf, m_worker.GetString( v->name ) );
+ DrawTextContrast( draw, wpos + ImVec2( ty, oldOffset ), showFull ? 0xFFFFAAAA : 0xFF886666, tmp );
+ boxwidth = ImGui::CalcTextSize( tmp ).x;
+ }
+ else
+ {
+ DrawTextContrast( draw, wpos + ImVec2( ty, oldOffset ), showFull ? 0xFFFFAAAA : 0xFF886666, buf );
+ boxwidth = ImGui::CalcTextSize( buf ).x;
+ }
+
+ if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( ty + boxwidth, oldOffset + ty ) ) )
+ {
+ if( IsMouseClicked( 0 ) )
+ {
+ showFull = !showFull;
+ }
+ if( IsMouseClicked( 2 ) )
+ {
+ int64_t t0 = std::numeric_limits::max();
+ int64_t t1 = std::numeric_limits::min();
+ for( auto& td : v->threadData )
+ {
+ int64_t _t0;
+ if( td.second.timeline.is_magic() )
+ {
+ _t0 = ((Vector*)&td.second.timeline)->front().GpuStart();
+ }
+ else
+ {
+ _t0 = td.second.timeline.front()->GpuStart();
+ }
+ if( _t0 >= 0 )
+ {
+ // FIXME
+ t0 = std::min( t0, _t0 );
+ if( td.second.timeline.is_magic() )
+ {
+ t1 = std::max( t1, std::min( m_worker.GetLastTime(), m_worker.GetZoneEnd( ((Vector*)&td.second.timeline)->back() ) ) );
+ }
+ else
+ {
+ t1 = std::max( t1, std::min( m_worker.GetLastTime(), m_worker.GetZoneEnd( *td.second.timeline.back() ) ) );
+ }
+ }
+ }
+ if( t0 < t1 )
+ {
+ ZoomToRange( t0, t1 );
+ }
+ }
+
+ ImGui::BeginTooltip();
+ ImGui::TextUnformatted( buf );
+ if( v->name.Active() ) TextFocused( "Name:", m_worker.GetString( v->name ) );
+ ImGui::Separator();
+ if( !isMultithreaded )
+ {
+ SmallColorBox( GetThreadColor( v->thread, 0 ) );
+ ImGui::SameLine();
+ TextFocused( "Thread:", m_worker.GetThreadName( v->thread ) );
+ }
+ else
+ {
+ if( !v->threadData.empty() )
+ {
+ if( v->threadData.size() == 1 )
+ {
+ auto it = v->threadData.begin();
+ auto tid = it->first;
+ if( tid == 0 )
+ {
+ if( !it->second.timeline.empty() )
+ {
+ if( it->second.timeline.is_magic() )
+ {
+ auto& tl = *(Vector*)&it->second.timeline;
+ tid = m_worker.DecompressThread( tl.begin()->Thread() );
+ }
+ else
+ {
+ tid = m_worker.DecompressThread( (*it->second.timeline.begin())->Thread() );
+ }
+ }
+ }
+ 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" );
+ }
+ }
+ else
+ {
+ ImGui::TextDisabled( "Threads:" );
+ ImGui::Indent();
+ for( auto& td : v->threadData )
+ {
+ SmallColorBox( GetThreadColor( td.first, 0 ) );
+ ImGui::SameLine();
+ ImGui::TextUnformatted( m_worker.GetThreadName( td.first ) );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%s)", RealToString( td.first ) );
+ }
+ ImGui::Unindent();
+ }
+ }
+ }
+ if( !v->threadData.empty() )
+ {
+ int64_t t0 = std::numeric_limits::max();
+ for( auto& td : v->threadData )
+ {
+ int64_t _t0;
+ if( td.second.timeline.is_magic() )
+ {
+ _t0 = ((Vector*)&td.second.timeline)->front().GpuStart();
+ }
+ else
+ {
+ _t0 = td.second.timeline.front()->GpuStart();
+ }
+ if( _t0 >= 0 )
+ {
+ t0 = std::min( t0, _t0 );
+ }
+ }
+ if( t0 != std::numeric_limits::max() )
+ {
+ TextFocused( "Appeared at", TimeToString( t0 ) );
+ }
+ }
+ TextFocused( "Zone count:", RealToString( v->count ) );
+ if( v->period != 1.f )
+ {
+ TextFocused( "Timestamp accuracy:", TimeToString( v->period ) );
+ }
+ if( v->overflow != 0 )
+ {
+ ImGui::Separator();
+ ImGui::TextUnformatted( "GPU timer overflow has been detected." );
+ TextFocused( "Timer resolution:", RealToString( 63 - TracyLzcnt( v->overflow ) ) );
+ ImGui::SameLine();
+ TextDisabledUnformatted( "bits" );
+ }
+ ImGui::EndTooltip();
+ }
+ }
+
+ AdjustThreadHeight( vis, oldOffset, offset );
+ ImGui::PopClipRect();
+ }
+ }
+
+ // zones
+ if( m_vd.drawCpuData && m_worker.HasContextSwitches() )
+ {
+ offset = DrawCpuData( offset, pxns, wpos, hover, yMin, yMax );
+ }
+
+ const auto& threadData = m_worker.GetThreadData();
+ if( threadData.size() != m_threadOrder.size() )
+ {
+ m_threadOrder.reserve( threadData.size() );
+ for( size_t i=m_threadOrder.size(); isamples.empty();
+ const auto hasCtxSwitch = m_vd.drawContextSwitches && m_worker.GetContextSwitchData( v->id );
+
+ if( hasSamples )
+ {
+ if( hasCtxSwitch )
+ {
+ offset += round( ostep * 0.5f );
+ }
+ else
+ {
+ offset += round( ostep * 0.75f );
+ }
+ }
+
+ const auto ctxOffset = offset;
+ if( hasCtxSwitch ) offset += round( ostep * 0.75f );
+
+ if( m_vd.drawZones )
+ {
+#ifndef TRACY_NO_STATISTICS
+ if( m_worker.AreGhostZonesReady() && ( vis.ghost || ( m_vd.ghostZones && v->timeline.empty() ) ) )
+ {
+ depth = DispatchGhostLevel( v->ghostZones, hover, pxns, int64_t( nspx ), wpos, offset, 0, yMin, yMax, v->id );
+ }
+ else
+#endif
+ {
+ depth = DispatchZoneLevel( v->timeline, hover, pxns, int64_t( nspx ), wpos, offset, 0, yMin, yMax, v->id );
+ }
+ offset += ostep * depth;
+ }
+
+ if( hasCtxSwitch )
+ {
+ auto ctxSwitch = m_worker.GetContextSwitchData( v->id );
+ if( ctxSwitch )
+ {
+ DrawContextSwitches( ctxSwitch, v->samples, hover, pxns, int64_t( nspx ), wpos, ctxOffset, offset, v->isFiber );
+ }
+ }
+
+ if( hasSamples )
+ {
+ DrawSamples( v->samples, hover, pxns, int64_t( nspx ), wpos, sampleOffset );
+ }
+
+ if( m_vd.drawLocks )
+ {
+ const auto lockDepth = DrawLocks( v->id, hover, pxns, wpos, offset, nextLockHighlight, yMin, yMax );
+ offset += ostep * lockDepth;
+ depth += lockDepth;
+ }
+ }
+ offset += ostep * 0.2f;
+
+ auto msgit = std::lower_bound( v->messages.begin(), v->messages.end(), m_vd.zvStart, [] ( const auto& lhs, const auto& rhs ) { return lhs->time < rhs; } );
+ auto msgend = std::lower_bound( msgit, v->messages.end(), m_vd.zvEnd+1, [] ( const auto& lhs, const auto& rhs ) { return lhs->time < rhs; } );
+
+ if( !m_vd.drawEmptyLabels && showFull && depth == 0 && msgit == msgend && crash.thread != v->id )
+ {
+ auto& vis = Vis( v );
+ vis.height = 0;
+ vis.offset = 0;
+ offset = oldOffset;
+ }
+ else if( yPos + ostep >= yMin && yPos <= yMax )
+ {
+ DrawLine( draw, dpos + ImVec2( 0, oldOffset + ostep - 1 ), dpos + ImVec2( w, oldOffset + ostep - 1 ), 0x33FFFFFF );
+
+ uint32_t labelColor;
+ if( crash.thread == v->id ) labelColor = showFull ? 0xFF2222FF : 0xFF111188;
+ else if( v->isFiber ) labelColor = showFull ? 0xFF88FF88 : 0xFF448844;
+ else labelColor = showFull ? 0xFFFFFFFF : 0xFF888888;
+
+ if( showFull )
+ {
+ draw->AddTriangleFilled( wpos + ImVec2( to/2, oldOffset + to/2 ), wpos + ImVec2( ty - to/2, oldOffset + to/2 ), wpos + ImVec2( ty * 0.5, oldOffset + to/2 + th ), labelColor );
+
+ while( msgit < msgend )
+ {
+ const auto next = std::upper_bound( msgit, v->messages.end(), (*msgit)->time + MinVisSize * nspx, [] ( const auto& lhs, const auto& rhs ) { return lhs < rhs->time; } );
+ const auto dist = std::distance( msgit, next );
+
+ const auto px = ( (*msgit)->time - m_vd.zvStart ) * pxns;
+ const bool isMsgHovered = hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px - (ty - to) * 0.5 - 1, oldOffset ), wpos + ImVec2( px + (ty - to) * 0.5 + 1, oldOffset + ty ) );
+
+ unsigned int color = 0xFFDDDDDD;
+ float animOff = 0;
+ if( dist > 1 )
+ {
+ if( m_msgHighlight && m_worker.DecompressThread( m_msgHighlight->thread ) == v->id )
+ {
+ const auto hTime = m_msgHighlight->time;
+ if( (*msgit)->time <= hTime && ( next == v->messages.end() || (*next)->time > hTime ) )
+ {
+ color = 0xFF4444FF;
+ if( !isMsgHovered )
+ {
+ animOff = -fabs( sin( s_time * 8 ) ) * th;
+ }
+ }
+ }
+ draw->AddTriangleFilled( wpos + ImVec2( px - (ty - to) * 0.5, animOff + oldOffset + to ), wpos + ImVec2( px + (ty - to) * 0.5, animOff + oldOffset + to ), wpos + ImVec2( px, animOff + oldOffset + to + th ), color );
+ draw->AddTriangle( wpos + ImVec2( px - (ty - to) * 0.5, animOff + oldOffset + to ), wpos + ImVec2( px + (ty - to) * 0.5, animOff + oldOffset + to ), wpos + ImVec2( px, animOff + oldOffset + to + th ), color, 2.0f );
+ }
+ else
+ {
+ if( m_msgHighlight == *msgit )
+ {
+ color = 0xFF4444FF;
+ if( !isMsgHovered )
+ {
+ animOff = -fabs( sin( s_time * 8 ) ) * th;
+ }
+ }
+ draw->AddTriangle( wpos + ImVec2( px - (ty - to) * 0.5, animOff + oldOffset + to ), wpos + ImVec2( px + (ty - to) * 0.5, animOff + oldOffset + to ), wpos + ImVec2( px, animOff + oldOffset + to + th ), color, 2.0f );
+ }
+ if( isMsgHovered )
+ {
+ ImGui::BeginTooltip();
+ if( dist > 1 )
+ {
+ ImGui::Text( "%i messages", (int)dist );
+ }
+ else
+ {
+ TextFocused( "Message at", TimeToStringExact( (*msgit)->time ) );
+ ImGui::PushStyleColor( ImGuiCol_Text, (*msgit)->color );
+ ImGui::TextUnformatted( m_worker.GetString( (*msgit)->ref ) );
+ ImGui::PopStyleColor();
+ }
+ ImGui::EndTooltip();
+ m_msgHighlight = *msgit;
+
+ if( IsMouseClicked( 0 ) )
+ {
+ m_showMessages = true;
+ m_msgToFocus = *msgit;
+ }
+ if( IsMouseClicked( 2 ) )
+ {
+ CenterAtTime( (*msgit)->time );
+ }
+ }
+ msgit = next;
+ }
+
+ if( crash.thread == v->id && crash.time >= m_vd.zvStart && crash.time <= m_vd.zvEnd )
+ {
+ const auto px = ( crash.time - m_vd.zvStart ) * pxns;
+
+ draw->AddTriangleFilled( wpos + ImVec2( px - (ty - to) * 0.25f, oldOffset + to + th * 0.5f ), wpos + ImVec2( px + (ty - to) * 0.25f, oldOffset + to + th * 0.5f ), wpos + ImVec2( px, oldOffset + to + th ), 0xFF2222FF );
+ draw->AddTriangle( wpos + ImVec2( px - (ty - to) * 0.25f, oldOffset + to + th * 0.5f ), wpos + ImVec2( px + (ty - to) * 0.25f, oldOffset + to + th * 0.5f ), wpos + ImVec2( px, oldOffset + to + th ), 0xFF2222FF, 2.0f );
+
+ const auto crashText = ICON_FA_SKULL " crash " ICON_FA_SKULL;
+ auto ctw = ImGui::CalcTextSize( crashText ).x;
+ DrawTextContrast( draw, wpos + ImVec2( px - ctw * 0.5f, oldOffset + to + th * 0.5f - ty ), 0xFF2222FF, crashText );
+
+ if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px - (ty - to) * 0.5 - 1, oldOffset ), wpos + ImVec2( px + (ty - to) * 0.5 + 1, oldOffset + ty ) ) )
+ {
+ CrashTooltip();
+ if( IsMouseClicked( 0 ) )
+ {
+ m_showInfo = true;
+ }
+ if( IsMouseClicked( 2 ) )
+ {
+ CenterAtTime( crash.time );
+ }
+ }
+ }
+ }
+ else
+ {
+ draw->AddTriangle( wpos + ImVec2( to/2, oldOffset + to/2 ), wpos + ImVec2( to/2, oldOffset + ty - to/2 ), wpos + ImVec2( to/2 + th, oldOffset + ty * 0.5 ), labelColor, 2.0f );
+ }
+ const auto txt = m_worker.GetThreadName( v->id );
+ const auto txtsz = ImGui::CalcTextSize( txt );
+ if( m_gpuThread == v->id )
+ {
+ draw->AddRectFilled( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( w, offset ), 0x228888DD );
+ draw->AddRect( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( w, offset ), 0x448888DD );
+ }
+ if( m_gpuInfoWindow && m_gpuInfoWindowThread == v->id )
+ {
+ draw->AddRectFilled( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( w, offset ), 0x2288DD88 );
+ draw->AddRect( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( w, offset ), 0x4488DD88 );
+ }
+ if( m_cpuDataThread == v->id )
+ {
+ draw->AddRectFilled( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( w, offset ), 0x2DFF8888 );
+ draw->AddRect( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( w, offset ), 0x4DFF8888 );
+ }
+ DrawTextContrast( draw, wpos + ImVec2( ty, oldOffset ), labelColor, txt );
+
+#ifndef TRACY_NO_STATISTICS
+ const bool hasGhostZones = showFull && m_worker.AreGhostZonesReady() && !v->ghostZones.empty();
+ float ghostSz;
+ if( hasGhostZones && !v->timeline.empty() )
+ {
+ auto& vis = Vis( v );
+ const auto color = vis.ghost ? 0xFFAA9999 : 0x88AA7777;
+ draw->AddText( wpos + ImVec2( 1.5f * ty + txtsz.x, oldOffset ), color, ICON_FA_GHOST );
+ ghostSz = ImGui::CalcTextSize( ICON_FA_GHOST ).x;
+ }
+#endif
+
+ if( hover )
+ {
+#ifndef TRACY_NO_STATISTICS
+ if( hasGhostZones && !v->timeline.empty() && ImGui::IsMouseHoveringRect( wpos + ImVec2( 1.5f * ty + txtsz.x, oldOffset ), wpos + ImVec2( 1.5f * ty + txtsz.x + ghostSz, oldOffset + ty ) ) )
+ {
+ if( IsMouseClicked( 0 ) )
+ {
+ auto& vis = Vis( v );
+ vis.ghost = !vis.ghost;
+ }
+ }
+ else
+#endif
+ if( ImGui::IsMouseHoveringRect( wpos + ImVec2( 0, oldOffset ), wpos + ImVec2( ty + txtsz.x, oldOffset + ty ) ) )
+ {
+ m_drawThreadMigrations = v->id;
+ m_drawThreadHighlight = v->id;
+ ImGui::BeginTooltip();
+ SmallColorBox( GetThreadColor( v->id, 0 ) );
+ ImGui::SameLine();
+ ImGui::TextUnformatted( m_worker.GetThreadName( v->id ) );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%s)", RealToString( v->id ) );
+ if( crash.thread == v->id )
+ {
+ ImGui::SameLine();
+ TextColoredUnformatted( ImVec4( 1.f, 0.2f, 0.2f, 1.f ), ICON_FA_SKULL " Crashed" );
+ }
+ if( v->isFiber )
+ {
+ ImGui::SameLine();
+ TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" );
+ }
+
+ const auto ctx = m_worker.GetContextSwitchData( v->id );
+
+ ImGui::Separator();
+ int64_t first = std::numeric_limits::max();
+ int64_t last = -1;
+ if( ctx && !ctx->v.empty() )
+ {
+ const auto& back = ctx->v.back();
+ first = ctx->v.begin()->Start();
+ last = back.IsEndValid() ? back.End() : back.Start();
+ }
+ if( !v->timeline.empty() )
+ {
+ if( v->timeline.is_magic() )
+ {
+ auto& tl = *((Vector*)&v->timeline);
+ first = std::min( first, tl.front().Start() );
+ last = std::max( last, m_worker.GetZoneEnd( tl.back() ) );
+ }
+ else
+ {
+ first = std::min( first, v->timeline.front()->Start() );
+ last = std::max( last, m_worker.GetZoneEnd( *v->timeline.back() ) );
+ }
+ }
+ if( !v->messages.empty() )
+ {
+ first = std::min( first, v->messages.front()->time );
+ last = std::max( last, v->messages.back()->time );
+ }
+ size_t lockCnt = 0;
+ for( const auto& lock : m_worker.GetLockMap() )
+ {
+ const auto& lockmap = *lock.second;
+ if( !lockmap.valid ) continue;
+ auto it = lockmap.threadMap.find( v->id );
+ if( it == lockmap.threadMap.end() ) continue;
+ lockCnt++;
+ const auto thread = it->second;
+ auto lptr = lockmap.timeline.data();
+ auto eptr = lptr + lockmap.timeline.size() - 1;
+ while( lptr->ptr->thread != thread ) lptr++;
+ if( lptr->ptr->Time() < first ) first = lptr->ptr->Time();
+ while( eptr->ptr->thread != thread ) eptr--;
+ if( eptr->ptr->Time() > last ) last = eptr->ptr->Time();
+ }
+
+ if( last >= 0 )
+ {
+ const auto lifetime = last - first;
+ const auto traceLen = m_worker.GetLastTime();
+
+ TextFocused( "Appeared at", TimeToString( first ) );
+ TextFocused( "Last event at", TimeToString( last ) );
+ TextFocused( "Lifetime:", TimeToString( lifetime ) );
+ ImGui::SameLine();
+ char buf[64];
+ PrintStringPercent( buf, lifetime / double( traceLen ) * 100 );
+ TextDisabledUnformatted( buf );
+
+ if( ctx )
+ {
+ TextFocused( "Time in running state:", TimeToString( ctx->runningTime ) );
+ ImGui::SameLine();
+ PrintStringPercent( buf, ctx->runningTime / double( lifetime ) * 100 );
+ TextDisabledUnformatted( buf );
+ }
+ }
+
+ ImGui::Separator();
+ if( !v->timeline.empty() )
+ {
+ TextFocused( "Zone count:", RealToString( v->count ) );
+ TextFocused( "Top-level zones:", RealToString( v->timeline.size() ) );
+ }
+ if( !v->messages.empty() )
+ {
+ TextFocused( "Messages:", RealToString( v->messages.size() ) );
+ }
+ if( lockCnt != 0 )
+ {
+ TextFocused( "Locks:", RealToString( lockCnt ) );
+ }
+ if( ctx )
+ {
+ TextFocused( "Running state regions:", RealToString( ctx->v.size() ) );
+ }
+ if( !v->samples.empty() )
+ {
+ TextFocused( "Call stack samples:", RealToString( v->samples.size() ) );
+ if( v->kernelSampleCnt != 0 )
+ {
+ TextFocused( "Kernel samples:", RealToString( v->kernelSampleCnt ) );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%.2f%%)", 100.f * v->kernelSampleCnt / v->samples.size() );
+ }
+ }
+ ImGui::EndTooltip();
+
+ if( IsMouseClicked( 0 ) )
+ {
+ Vis( v ).showFull = !showFull;
+ }
+ if( last >= 0 && IsMouseClicked( 2 ) )
+ {
+ ZoomToRange( first, last );
+ }
+ }
+ }
+ }
+
+ AdjustThreadHeight( Vis( v ), oldOffset, offset );
+ ImGui::PopClipRect();
+ }
+ m_lockHighlight = nextLockHighlight;
+
+ if( m_vd.drawPlots )
+ {
+ offset = DrawPlots( offset, pxns, wpos, hover, yMin, yMax );
+ }
+
+ const auto scrollPos = ImGui::GetScrollY();
+ if( scrollPos == 0 && m_vd.zvScroll != 0 )
+ {
+ m_vd.zvHeight = 0;
+ }
+ else
+ {
+ if( offset > m_vd.zvHeight ) m_vd.zvHeight = offset;
+ }
+ m_vd.zvScroll = scrollPos;
+
+ ImGui::EndChild();
+
+ for( auto& ann : m_annotations )
+ {
+ if( ann->range.min < m_vd.zvEnd && ann->range.max > m_vd.zvStart )
+ {
+ uint32_t c0 = ( ann->color & 0xFFFFFF ) | ( m_selectedAnnotation == ann.get() ? 0x44000000 : 0x22000000 );
+ uint32_t c1 = ( ann->color & 0xFFFFFF ) | ( m_selectedAnnotation == ann.get() ? 0x66000000 : 0x44000000 );
+ uint32_t c2 = ( ann->color & 0xFFFFFF ) | ( m_selectedAnnotation == ann.get() ? 0xCC000000 : 0xAA000000 );
+ draw->AddRectFilled( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns, 0 ), linepos + ImVec2( ( ann->range.max - m_vd.zvStart ) * pxns, lineh ), c0 );
+ DrawLine( draw, linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + 0.5f, 0.5f ), linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + 0.5f, lineh + 0.5f ), ann->range.hiMin ? c2 : c1, ann->range.hiMin ? 2 : 1 );
+ DrawLine( draw, linepos + ImVec2( ( ann->range.max - m_vd.zvStart ) * pxns + 0.5f, 0.5f ), linepos + ImVec2( ( ann->range.max - m_vd.zvStart ) * pxns + 0.5f, lineh + 0.5f ), ann->range.hiMax ? c2 : c1, ann->range.hiMax ? 2 : 1 );
+ if( drawMouseLine && ImGui::IsMouseHoveringRect( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns, 0 ), linepos + ImVec2( ( ann->range.max - m_vd.zvStart ) * pxns, lineh ) ) )
+ {
+ ImGui::BeginTooltip();
+ if( ann->text.empty() )
+ {
+ TextDisabledUnformatted( "Empty annotation" );
+ }
+ else
+ {
+ ImGui::TextUnformatted( ann->text.c_str() );
+ }
+ ImGui::Separator();
+ TextFocused( "Annotation begin:", TimeToStringExact( ann->range.min ) );
+ TextFocused( "Annotation end:", TimeToStringExact( ann->range.max ) );
+ TextFocused( "Annotation length:", TimeToString( ann->range.max - ann->range.min ) );
+ ImGui::EndTooltip();
+ }
+ const auto aw = ( ann->range.max - ann->range.min ) * pxns;
+ if( aw > th * 4 )
+ {
+ draw->AddCircleFilled( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th * 2, th * 2 ), th, 0x88AABB22 );
+ draw->AddCircle( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th * 2, th * 2 ), th, 0xAAAABB22 );
+ if( drawMouseLine && IsMouseClicked( 0 ) && ImGui::IsMouseHoveringRect( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th, th ), linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th * 3, th * 3 ) ) )
+ {
+ m_selectedAnnotation = ann.get();
+ }
+
+ if( !ann->text.empty() )
+ {
+ const auto tw = ImGui::CalcTextSize( ann->text.c_str() ).x;
+ if( aw - th*4 > tw )
+ {
+ draw->AddText( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th * 4, th * 0.5 ), 0xFFFFFFFF, ann->text.c_str() );
+ }
+ else
+ {
+ draw->PushClipRect( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns, 0 ), linepos + ImVec2( ( ann->range.max - m_vd.zvStart ) * pxns, lineh ), true );
+ draw->AddText( linepos + ImVec2( ( ann->range.min - m_vd.zvStart ) * pxns + th * 4, th * 0.5 ), 0xFFFFFFFF, ann->text.c_str() );
+ draw->PopClipRect();
+ }
+ }
+ }
+ }
+ }
+
+ if( m_gpuStart != 0 && m_gpuEnd != 0 )
+ {
+ const auto px0 = ( m_gpuStart - m_vd.zvStart ) * pxns;
+ const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( m_gpuEnd - m_vd.zvStart ) * pxns );
+ draw->AddRectFilled( ImVec2( wpos.x + px0, linepos.y ), ImVec2( wpos.x + px1, linepos.y + lineh ), 0x228888DD );
+ draw->AddRect( ImVec2( wpos.x + px0, linepos.y ), ImVec2( wpos.x + px1, linepos.y + lineh ), 0x448888DD );
+ }
+ if( m_gpuInfoWindow )
+ {
+ const auto px0 = ( m_gpuInfoWindow->CpuStart() - m_vd.zvStart ) * pxns;
+ const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( m_gpuInfoWindow->CpuEnd() - m_vd.zvStart ) * pxns );
+ draw->AddRectFilled( ImVec2( wpos.x + px0, linepos.y ), ImVec2( wpos.x + px1, linepos.y + lineh ), 0x2288DD88 );
+ draw->AddRect( ImVec2( wpos.x + px0, linepos.y ), ImVec2( wpos.x + px1, linepos.y + lineh ), 0x4488DD88 );
+ }
+
+ const auto scale = GetScale();
+ if( m_findZone.range.active && ( m_findZone.show || m_showRanges ) )
+ {
+ const auto px0 = ( m_findZone.range.min - m_vd.zvStart ) * pxns;
+ const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( m_findZone.range.max - m_vd.zvStart ) * pxns );
+ DrawStripedRect( draw, wpos, px0, linepos.y, px1, linepos.y + lineh, 10 * scale, 0x2288DD88, true, true );
+ DrawLine( draw, ImVec2( dpos.x + px0, linepos.y + 0.5f ), ImVec2( dpos.x + px0, linepos.y + lineh + 0.5f ), m_findZone.range.hiMin ? 0x9988DD88 : 0x3388DD88, m_findZone.range.hiMin ? 2 : 1 );
+ DrawLine( draw, ImVec2( dpos.x + px1, linepos.y + 0.5f ), ImVec2( dpos.x + px1, linepos.y + lineh + 0.5f ), m_findZone.range.hiMax ? 0x9988DD88 : 0x3388DD88, m_findZone.range.hiMax ? 2 : 1 );
+ }
+
+ if( m_statRange.active && ( m_showStatistics || m_showRanges || ( m_sourceViewFile && m_sourceView->IsSymbolView() ) ) )
+ {
+ const auto px0 = ( m_statRange.min - m_vd.zvStart ) * pxns;
+ const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( m_statRange.max - m_vd.zvStart ) * pxns );
+ DrawStripedRect( draw, wpos, px0, linepos.y, px1, linepos.y + lineh, 10 * scale, 0x228888EE, true, false );
+ DrawLine( draw, ImVec2( dpos.x + px0, linepos.y + 0.5f ), ImVec2( dpos.x + px0, linepos.y + lineh + 0.5f ), m_statRange.hiMin ? 0x998888EE : 0x338888EE, m_statRange.hiMin ? 2 : 1 );
+ DrawLine( draw, ImVec2( dpos.x + px1, linepos.y + 0.5f ), ImVec2( dpos.x + px1, linepos.y + lineh + 0.5f ), m_statRange.hiMax ? 0x998888EE : 0x338888EE, m_statRange.hiMax ? 2 : 1 );
+ }
+
+ if( m_waitStackRange.active && ( m_showWaitStacks || m_showRanges ) )
+ {
+ const auto px0 = ( m_waitStackRange.min - m_vd.zvStart ) * pxns;
+ const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( m_waitStackRange.max - m_vd.zvStart ) * pxns );
+ DrawStripedRect( draw, wpos, px0, linepos.y, px1, linepos.y + lineh, 10 * scale, 0x22EEB588, true, true );
+ DrawLine( draw, ImVec2( dpos.x + px0, linepos.y + 0.5f ), ImVec2( dpos.x + px0, linepos.y + lineh + 0.5f ), m_waitStackRange.hiMin ? 0x99EEB588 : 0x33EEB588, m_waitStackRange.hiMin ? 2 : 1 );
+ DrawLine( draw, ImVec2( dpos.x + px1, linepos.y + 0.5f ), ImVec2( dpos.x + px1, linepos.y + lineh + 0.5f ), m_waitStackRange.hiMax ? 0x99EEB588 : 0x33EEB588, m_waitStackRange.hiMax ? 2 : 1 );
+ }
+
+ if( m_memInfo.range.active && ( m_memInfo.show || m_showRanges ) )
+ {
+ const auto px0 = ( m_memInfo.range.min - m_vd.zvStart ) * pxns;
+ const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( m_memInfo.range.max - m_vd.zvStart ) * pxns );
+ DrawStripedRect( draw, wpos, px0, linepos.y, px1, linepos.y + lineh, 10 * scale, 0x2288EEE3, true, false );
+ DrawLine( draw, ImVec2( dpos.x + px0, linepos.y + 0.5f ), ImVec2( dpos.x + px0, linepos.y + lineh + 0.5f ), m_memInfo.range.hiMin ? 0x9988EEE3 : 0x3388EEE3, m_memInfo.range.hiMin ? 2 : 1 );
+ DrawLine( draw, ImVec2( dpos.x + px1, linepos.y + 0.5f ), ImVec2( dpos.x + px1, linepos.y + lineh + 0.5f ), m_memInfo.range.hiMax ? 0x9988EEE3 : 0x3388EEE3, m_memInfo.range.hiMax ? 2 : 1 );
+ }
+
+ if( m_setRangePopup.active || m_setRangePopupOpen )
+ {
+ const auto s = std::min( m_setRangePopup.min, m_setRangePopup.max );
+ const auto e = std::max( m_setRangePopup.min, m_setRangePopup.max );
+ DrawStripedRect( draw, wpos, ( s - m_vd.zvStart ) * pxns, linepos.y, ( e - m_vd.zvStart ) * pxns, linepos.y + lineh, 5 * scale, 0x55DD8888, true, false );
+ draw->AddRect( ImVec2( wpos.x + ( s - m_vd.zvStart ) * pxns, linepos.y ), ImVec2( wpos.x + ( e - m_vd.zvStart ) * pxns, linepos.y + lineh ), 0x77DD8888 );
+ }
+
+ if( m_highlight.active && m_highlight.start != m_highlight.end )
+ {
+ const auto s = std::min( m_highlight.start, m_highlight.end );
+ const auto e = std::max( m_highlight.start, m_highlight.end );
+ draw->AddRectFilled( ImVec2( wpos.x + ( s - m_vd.zvStart ) * pxns, linepos.y ), ImVec2( wpos.x + ( e - m_vd.zvStart ) * pxns, linepos.y + lineh ), 0x22DD8888 );
+ draw->AddRect( ImVec2( wpos.x + ( s - m_vd.zvStart ) * pxns, linepos.y ), ImVec2( wpos.x + ( e - m_vd.zvStart ) * pxns, linepos.y + lineh ), 0x44DD8888 );
+
+ ImGui::BeginTooltip();
+ ImGui::TextUnformatted( TimeToString( e - s ) );
+ ImGui::EndTooltip();
+ }
+ else if( drawMouseLine )
+ {
+ auto& io = ImGui::GetIO();
+ DrawLine( draw, ImVec2( io.MousePos.x + 0.5f, linepos.y + 0.5f ), ImVec2( io.MousePos.x + 0.5f, linepos.y + lineh + 0.5f ), 0x33FFFFFF );
+ }
+
+ if( m_highlightZoom.active && m_highlightZoom.start != m_highlightZoom.end )
+ {
+ const auto s = std::min( m_highlightZoom.start, m_highlightZoom.end );
+ const auto e = std::max( m_highlightZoom.start, m_highlightZoom.end );
+ draw->AddRectFilled( ImVec2( wpos.x + ( s - m_vd.zvStart ) * pxns, linepos.y ), ImVec2( wpos.x + ( e - m_vd.zvStart ) * pxns, linepos.y + lineh ), 0x1688DD88 );
+ draw->AddRect( ImVec2( wpos.x + ( s - m_vd.zvStart ) * pxns, linepos.y ), ImVec2( wpos.x + ( e - m_vd.zvStart ) * pxns, linepos.y + lineh ), 0x2C88DD88 );
+ }
+}
+
+}
diff --git a/server/TracyView_ZoneTimeline.cpp b/server/TracyView_ZoneTimeline.cpp
new file mode 100644
index 00000000..23f8a105
--- /dev/null
+++ b/server/TracyView_ZoneTimeline.cpp
@@ -0,0 +1,942 @@
+#include
+
+#include "TracyColor.hpp"
+#include "TracyMouse.hpp"
+#include "TracyPrint.hpp"
+#include "TracyView.hpp"
+
+namespace tracy
+{
+
+enum { MinVisSize = 3 };
+
+static tracy_force_inline uint32_t MixGhostColor( uint32_t c0, uint32_t c1 )
+{
+ return 0xFF000000 |
+ ( ( ( ( ( c0 & 0x00FF0000 ) >> 16 ) + 3 * ( ( c1 & 0x00FF0000 ) >> 16 ) ) >> 2 ) << 16 ) |
+ ( ( ( ( ( c0 & 0x0000FF00 ) >> 8 ) + 3 * ( ( c1 & 0x0000FF00 ) >> 8 ) ) >> 2 ) << 8 ) |
+ ( ( ( ( ( c0 & 0x000000FF ) ) + 3 * ( ( c1 & 0x000000FF ) ) ) >> 2 ) );
+}
+
+#ifndef TRACY_NO_STATISTICS
+int View::DispatchGhostLevel( const Vector& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, float yMin, float yMax, uint64_t tid )
+{
+ const auto ty = ImGui::GetTextLineHeight();
+ const auto ostep = ty + 1;
+ const auto offset = _offset + ostep * depth;
+
+ const auto yPos = wpos.y + offset;
+ // Inline frames have to be taken into account, hence the multiply by 16 (arbitrary limit for inline frames in client)
+ if( yPos + 16 * ostep >= yMin && yPos <= yMax )
+ {
+ return DrawGhostLevel( vec, hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
+ }
+ else
+ {
+ return SkipGhostLevel( vec, hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
+ }
+}
+
+int View::DrawGhostLevel( const Vector& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, float yMin, float yMax, uint64_t tid )
+{
+ auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart ), [] ( const auto& l, const auto& r ) { return l.end.Val() < r; } );
+ if( it == vec.end() ) return depth;
+
+ const auto zitend = std::lower_bound( it, vec.end(), m_vd.zvEnd, [] ( const auto& l, const auto& r ) { return l.start.Val() < r; } );
+ if( it == zitend ) return depth;
+
+ const auto w = ImGui::GetContentRegionAvail().x - 1;
+ const auto ty = ImGui::GetTextLineHeight();
+ const auto ostep = ty + 1;
+ const auto offset = _offset + ostep * depth;
+ auto draw = ImGui::GetWindowDrawList();
+ const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
+
+ depth++;
+ int maxdepth = depth;
+
+ while( it < zitend )
+ {
+ auto& ev = *it;
+ const auto end = ev.end.Val();
+ const auto zsz = std::max( ( end - ev.start.Val() ) * pxns, pxns * 0.5 );
+ if( zsz < MinVisSize )
+ {
+ const auto MinVisNs = MinVisSize * nspx;
+ const auto color = MixGhostColor( GetThreadColor( tid, depth ), 0x665555 );
+ const auto px0 = ( ev.start.Val() - m_vd.zvStart ) * pxns;
+ auto px1ns = ev.end.Val() - m_vd.zvStart;
+ auto rend = end;
+ auto nextTime = end + MinVisNs;
+ for(;;)
+ {
+ const auto prevIt = it;
+ it = std::lower_bound( it, zitend, nextTime, [] ( const auto& l, const auto& r ) { return l.end.Val() < r; } );
+ if( it == prevIt ) ++it;
+ if( it == zitend ) break;
+ const auto nend = it->end.Val();
+ const auto nsnext = nend - m_vd.zvStart;
+ if( nsnext - px1ns >= MinVisNs * 2 ) break;
+ px1ns = nsnext;
+ rend = nend;
+ nextTime = nend + nspx;
+ }
+ const auto px1 = px1ns * pxns;
+ draw->AddRectFilled( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty ), color );
+ DrawZigZag( draw, wpos + ImVec2( 0, offset + ty/2 ), std::max( px0, -10.0 ), std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), ty/4, DarkenColor( color ) );
+ if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty + 1 ) ) )
+ {
+ if( IsMouseClickReleased( 1 ) ) m_setRangePopup = RangeSlim { ev.start.Val(), rend , true };
+ ImGui::BeginTooltip();
+ ImGui::TextUnformatted( "Multiple ghost zones" );
+ ImGui::Separator();
+ TextFocused( "Execution time:", TimeToString( rend - ev.start.Val() ) );
+ ImGui::EndTooltip();
+
+ if( IsMouseClicked( 2 ) && rend - ev.start.Val() > 0 )
+ {
+ ZoomToRange( ev.start.Val(), rend );
+ }
+ }
+ }
+ else
+ {
+ const auto& ghostKey = m_worker.GetGhostFrame( ev.frame );
+ const auto frame = m_worker.GetCallstackFrame( ghostKey.frame );
+
+ uint32_t color;
+ if( m_vd.dynamicColors == 2 )
+ {
+ if( frame )
+ {
+ const auto& sym = frame->data[ghostKey.inlineFrame];
+ color = GetHsvColor( sym.name.Idx(), depth );
+ }
+ else
+ {
+ color = GetHsvColor( ghostKey.frame.data, depth );
+ }
+ }
+ else
+ {
+ color = MixGhostColor( GetThreadColor( tid, depth ), 0x665555 );
+ }
+
+ const auto pr0 = ( ev.start.Val() - m_vd.zvStart ) * pxns;
+ const auto pr1 = ( ev.end.Val() - m_vd.zvStart ) * pxns;
+ const auto px0 = std::max( pr0, -10.0 );
+ const auto px1 = std::max( { std::min( pr1, double( w + 10 ) ), px0 + pxns * 0.5, px0 + MinVisSize } );
+ if( !frame )
+ {
+ char symName[64];
+ sprintf( symName, "0x%" PRIx64, m_worker.GetCanonicalPointer( ghostKey.frame ) );
+ const auto tsz = ImGui::CalcTextSize( symName );
+
+ const auto accentColor = HighlightColor( color );
+ const auto darkColor = DarkenColor( color );
+ const auto txtColor = 0xFF888888;
+ draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), DarkenColor( color ) );
+ DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px0, offset ), dpos + ImVec2( px1-1, offset ), accentColor, 1.f );
+ DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px1-1, offset + tsz.y ), dpos + ImVec2( px1-1, offset ), darkColor, 1.f );
+
+ if( tsz.x < zsz )
+ {
+ const auto x = ( ev.start.Val() - m_vd.zvStart ) * pxns + ( ( end - ev.start.Val() ) * pxns - tsz.x ) / 2;
+ if( x < 0 || x > w - tsz.x )
+ {
+ ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
+ DrawTextContrast( draw, wpos + ImVec2( std::max( std::max( 0., px0 ), std::min( double( w - tsz.x ), x ) ), offset ), txtColor, symName );
+ ImGui::PopClipRect();
+ }
+ else if( ev.start.Val() == ev.end.Val() )
+ {
+ DrawTextContrast( draw, wpos + ImVec2( px0 + ( px1 - px0 - tsz.x ) * 0.5, offset ), txtColor, symName );
+ }
+ else
+ {
+ DrawTextContrast( draw, wpos + ImVec2( x, offset ), txtColor, symName );
+ }
+ }
+ else
+ {
+ ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
+ DrawTextContrast( draw, wpos + ImVec2( ( ev.start.Val() - m_vd.zvStart ) * pxns, offset ), txtColor, symName );
+ ImGui::PopClipRect();
+ }
+
+ if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y + 1 ) ) )
+ {
+ if( IsMouseClickReleased( 1 ) ) m_setRangePopup = RangeSlim { ev.start.Val(), ev.end.Val() , true };
+ ImGui::BeginTooltip();
+ TextDisabledUnformatted( ICON_FA_GHOST " Ghost zone" );
+ ImGui::Separator();
+ TextFocused( "Unknown frame:", symName );
+ TextFocused( "Thread:", m_worker.GetThreadName( tid ) );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%s)", RealToString( tid ) );
+ if( m_worker.IsThreadFiber( tid ) )
+ {
+ ImGui::SameLine();
+ TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" );
+ }
+ ImGui::Separator();
+ TextFocused( "Execution time:", TimeToString( ev.end.Val() - ev.start.Val() ) );
+ ImGui::EndTooltip();
+ if( !m_zoomAnim.active && IsMouseClicked( 2 ) )
+ {
+ ZoomToRange( ev.start.Val(), ev.end.Val() );
+ }
+ }
+ }
+ else
+ {
+ const auto& sym = frame->data[ghostKey.inlineFrame];
+ const auto isInline = ghostKey.inlineFrame != frame->size-1;
+ const auto col = isInline ? DarkenColor( color ) : color;
+ auto symName = m_worker.GetString( sym.name );
+ uint32_t txtColor;
+ if( symName[0] == '[' )
+ {
+ txtColor = 0xFF999999;
+ }
+ else if( !isInline && ( m_worker.GetCanonicalPointer( ghostKey.frame ) >> 63 != 0 ) )
+ {
+ txtColor = 0xFF8888FF;
+ }
+ else
+ {
+ txtColor = 0xFFFFFFFF;
+ }
+ auto tsz = ImGui::CalcTextSize( symName );
+
+ const auto accentColor = HighlightColor( col );
+ const auto darkColor = DarkenColor( col );
+ draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), col );
+ DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px0, offset ), dpos + ImVec2( px1-1, offset ), accentColor, 1.f );
+ DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px1-1, offset + tsz.y ), dpos + ImVec2( px1-1, offset ), darkColor, 1.f );
+
+ auto origSymName = symName;
+ if( tsz.x > zsz )
+ {
+ symName = ShortenNamespace( symName );
+ tsz = ImGui::CalcTextSize( symName );
+ }
+
+ if( tsz.x < zsz )
+ {
+ const auto x = ( ev.start.Val() - m_vd.zvStart ) * pxns + ( ( end - ev.start.Val() ) * pxns - tsz.x ) / 2;
+ if( x < 0 || x > w - tsz.x )
+ {
+ ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
+ DrawTextContrast( draw, wpos + ImVec2( std::max( std::max( 0., px0 ), std::min( double( w - tsz.x ), x ) ), offset ), txtColor, symName );
+ ImGui::PopClipRect();
+ }
+ else if( ev.start.Val() == ev.end.Val() )
+ {
+ DrawTextContrast( draw, wpos + ImVec2( px0 + ( px1 - px0 - tsz.x ) * 0.5, offset ), txtColor, symName );
+ }
+ else
+ {
+ DrawTextContrast( draw, wpos + ImVec2( x, offset ), txtColor, symName );
+ }
+ }
+ else
+ {
+ ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
+ DrawTextContrast( draw, wpos + ImVec2( ( ev.start.Val() - m_vd.zvStart ) * pxns, offset ), txtColor, symName );
+ ImGui::PopClipRect();
+ }
+
+ if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y + 1 ) ) )
+ {
+ if( IsMouseClickReleased( 1 ) ) m_setRangePopup = RangeSlim { ev.start.Val(), ev.end.Val(), true };
+ ImGui::BeginTooltip();
+ TextDisabledUnformatted( ICON_FA_GHOST " Ghost zone" );
+ if( sym.symAddr >> 63 != 0 )
+ {
+ ImGui::SameLine();
+ TextDisabledUnformatted( ICON_FA_HAT_WIZARD " kernel" );
+ }
+ ImGui::Separator();
+ ImGui::TextUnformatted( origSymName );
+ if( isInline )
+ {
+ ImGui::SameLine();
+ TextDisabledUnformatted( "[inline]" );
+ }
+ const auto symbol = m_worker.GetSymbolData( sym.symAddr );
+ if( symbol ) TextFocused( "Image:", m_worker.GetString( symbol->imageName ) );
+ TextDisabledUnformatted( "Location:" );
+ ImGui::SameLine();
+ const char* file = m_worker.GetString( sym.file );
+ uint32_t line = sym.line;
+ ImGui::TextUnformatted( LocationToString( file, line ) );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(0x%" PRIx64 ")", sym.symAddr );
+ TextFocused( "Thread:", m_worker.GetThreadName( tid ) );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%s)", RealToString( tid ) );
+ if( m_worker.IsThreadFiber( tid ) )
+ {
+ ImGui::SameLine();
+ TextColoredUnformatted( ImVec4( 0.2f, 0.6f, 0.2f, 1.f ), "Fiber" );
+ }
+ ImGui::Separator();
+ TextFocused( "Execution time:", TimeToString( ev.end.Val() - ev.start.Val() ) );
+ ImGui::EndTooltip();
+
+ if( IsMouseClicked( 0 ) )
+ {
+ ViewDispatch( file, line, sym.symAddr );
+ }
+ else if( !m_zoomAnim.active && IsMouseClicked( 2 ) )
+ {
+ ZoomToRange( ev.start.Val(), ev.end.Val() );
+ }
+ }
+ }
+
+ if( ev.child >= 0 )
+ {
+ const auto d = DispatchGhostLevel( m_worker.GetGhostChildren( ev.child ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
+ if( d > maxdepth ) maxdepth = d;
+ }
+ ++it;
+ }
+ }
+
+ return maxdepth;
+}
+
+int View::SkipGhostLevel( const Vector& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, float yMin, float yMax, uint64_t tid )
+{
+ auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart ), [] ( const auto& l, const auto& r ) { return l.end.Val() < r; } );
+ if( it == vec.end() ) return depth;
+
+ const auto zitend = std::lower_bound( it, vec.end(), m_vd.zvEnd, [] ( const auto& l, const auto& r ) { return l.start.Val() < r; } );
+ if( it == zitend ) return depth;
+
+ depth++;
+ int maxdepth = depth;
+
+ while( it < zitend )
+ {
+ auto& ev = *it;
+ const auto end = ev.end.Val();
+ const auto zsz = std::max( ( end - ev.start.Val() ) * pxns, pxns * 0.5 );
+ if( zsz < MinVisSize )
+ {
+ const auto MinVisNs = MinVisSize * nspx;
+ auto px1ns = ev.end.Val() - m_vd.zvStart;
+ auto nextTime = end + MinVisNs;
+ for(;;)
+ {
+ const auto prevIt = it;
+ it = std::lower_bound( it, zitend, nextTime, [] ( const auto& l, const auto& r ) { return l.end.Val() < r; } );
+ if( it == prevIt ) ++it;
+ if( it == zitend ) break;
+ const auto nend = it->end.Val();
+ const auto nsnext = nend - m_vd.zvStart;
+ if( nsnext - px1ns >= MinVisNs * 2 ) break;
+ px1ns = nsnext;
+ nextTime = nend + nspx;
+ }
+ }
+ else
+ {
+ if( ev.child >= 0 )
+ {
+ const auto d = DispatchGhostLevel( m_worker.GetGhostChildren( ev.child ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
+ if( d > maxdepth ) maxdepth = d;
+ }
+ ++it;
+ }
+ }
+
+ return maxdepth;
+}
+#endif
+
+int View::DispatchZoneLevel( const Vector>& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, float yMin, float yMax, uint64_t tid )
+{
+ const auto ty = ImGui::GetTextLineHeight();
+ const auto ostep = ty + 1;
+ const auto offset = _offset + ostep * depth;
+
+ const auto yPos = wpos.y + offset;
+ if( yPos + ostep >= yMin && yPos <= yMax )
+ {
+ if( vec.is_magic() )
+ {
+ return DrawZoneLevel>( *(Vector*)( &vec ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
+ }
+ else
+ {
+ return DrawZoneLevel>( vec, hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
+ }
+ }
+ else
+ {
+ if( vec.is_magic() )
+ {
+ return SkipZoneLevel>( *(Vector*)( &vec ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
+ }
+ else
+ {
+ return SkipZoneLevel>( vec, hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
+ }
+ }
+}
+
+template
+int View::DrawZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, float yMin, float yMax, uint64_t tid )
+{
+ const auto delay = m_worker.GetDelay();
+ const auto resolution = m_worker.GetResolution();
+ // cast to uint64_t, so that unended zones (end = -1) are still drawn
+ auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart - delay ), [] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)a(l).End() < (uint64_t)r; } );
+ if( it == vec.end() ) return depth;
+
+ const auto zitend = std::lower_bound( it, vec.end(), m_vd.zvEnd + resolution, [] ( const auto& l, const auto& r ) { Adapter a; return a(l).Start() < r; } );
+ if( it == zitend ) return depth;
+ Adapter a;
+ if( !a(*it).IsEndValid() && m_worker.GetZoneEnd( a(*it) ) < m_vd.zvStart ) return depth;
+
+ const auto w = ImGui::GetContentRegionAvail().x - 1;
+ const auto ty = ImGui::GetTextLineHeight();
+ const auto ostep = ty + 1;
+ const auto offset = _offset + ostep * depth;
+ auto draw = ImGui::GetWindowDrawList();
+ const auto dsz = delay * pxns;
+ const auto rsz = resolution * pxns;
+ const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
+
+ const auto ty025 = round( ty * 0.25f );
+ const auto ty05 = round( ty * 0.5f );
+ const auto ty075 = round( ty * 0.75f );
+
+ depth++;
+ int maxdepth = depth;
+
+ while( it < zitend )
+ {
+ auto& ev = a(*it);
+ const auto end = m_worker.GetZoneEnd( ev );
+ const auto zsz = std::max( ( end - ev.Start() ) * pxns, pxns * 0.5 );
+ if( zsz < MinVisSize )
+ {
+ const auto MinVisNs = MinVisSize * nspx;
+ const auto color = GetThreadColor( tid, depth );
+ int num = 0;
+ const auto px0 = ( ev.Start() - m_vd.zvStart ) * pxns;
+ auto px1ns = end - m_vd.zvStart;
+ auto rend = end;
+ auto nextTime = end + MinVisNs;
+ for(;;)
+ {
+ const auto prevIt = it;
+ it = std::lower_bound( it, zitend, nextTime, [] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)a(l).End() < (uint64_t)r; } );
+ if( it == prevIt ) ++it;
+ num += std::distance( prevIt, it );
+ if( it == zitend ) break;
+ const auto nend = m_worker.GetZoneEnd( a(*it) );
+ const auto nsnext = nend - m_vd.zvStart;
+ if( nsnext - px1ns >= MinVisNs * 2 ) break;
+ px1ns = nsnext;
+ rend = nend;
+ nextTime = nend + nspx;
+ }
+ const auto px1 = px1ns * pxns;
+ draw->AddRectFilled( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty ), color );
+ DrawZigZag( draw, wpos + ImVec2( 0, offset + ty/2 ), std::max( px0, -10.0 ), std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), ty/4, DarkenColor( color ) );
+ if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty + 1 ) ) )
+ {
+ if( IsMouseClickReleased( 1 ) ) m_setRangePopup = RangeSlim { ev.Start(), rend, true };
+ if( num > 1 )
+ {
+ ImGui::BeginTooltip();
+ TextFocused( "Zones too small to display:", RealToString( num ) );
+ ImGui::Separator();
+ TextFocused( "Execution time:", TimeToString( rend - ev.Start() ) );
+ ImGui::EndTooltip();
+
+ if( IsMouseClicked( 2 ) && rend - ev.Start() > 0 )
+ {
+ ZoomToRange( ev.Start(), rend );
+ }
+ }
+ else
+ {
+ ZoneTooltip( ev );
+
+ if( IsMouseClicked( 2 ) && rend - ev.Start() > 0 )
+ {
+ ZoomToZone( ev );
+ }
+ if( IsMouseClicked( 0 ) )
+ {
+ if( ImGui::GetIO().KeyCtrl )
+ {
+ auto& srcloc = m_worker.GetSourceLocation( ev.SrcLoc() );
+ m_findZone.ShowZone( ev.SrcLoc(), m_worker.GetString( srcloc.name.active ? srcloc.name : srcloc.function ) );
+ }
+ else
+ {
+ ShowZoneInfo( ev );
+ }
+ }
+
+ m_zoneSrcLocHighlight = ev.SrcLoc();
+ m_zoneHover = &ev;
+ }
+ }
+ const auto tmp = RealToString( num );
+ const auto tsz = ImGui::CalcTextSize( tmp );
+ if( tsz.x < px1 - px0 )
+ {
+ const auto x = px0 + ( px1 - px0 - tsz.x ) / 2;
+ DrawTextContrast( draw, wpos + ImVec2( x, offset ), 0xFF4488DD, tmp );
+ }
+ }
+ else
+ {
+ const auto zoneColor = GetZoneColorData( ev, tid, depth );
+ const char* zoneName = m_worker.GetZoneName( ev );
+
+ if( ev.HasChildren() )
+ {
+ const auto d = DispatchZoneLevel( m_worker.GetZoneChildren( ev.Child() ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
+ if( d > maxdepth ) maxdepth = d;
+ }
+
+ auto tsz = ImGui::CalcTextSize( zoneName );
+ if( tsz.x > zsz )
+ {
+ zoneName = ShortenNamespace( zoneName );
+ tsz = ImGui::CalcTextSize( zoneName );
+ }
+
+ const auto pr0 = ( ev.Start() - m_vd.zvStart ) * pxns;
+ const auto pr1 = ( end - m_vd.zvStart ) * pxns;
+ const auto px0 = std::max( pr0, -10.0 );
+ const auto px1 = std::max( { std::min( pr1, double( w + 10 ) ), px0 + pxns * 0.5, px0 + MinVisSize } );
+ draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.color );
+ if( zoneColor.highlight )
+ {
+ draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.accentColor, 0.f, -1, zoneColor.thickness );
+ }
+ else
+ {
+ const auto darkColor = DarkenColor( zoneColor.color );
+ DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px0, offset ), dpos + ImVec2( px1-1, offset ), zoneColor.accentColor, zoneColor.thickness );
+ DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px1-1, offset + tsz.y ), dpos + ImVec2( px1-1, offset ), darkColor, zoneColor.thickness );
+ }
+ if( dsz > MinVisSize )
+ {
+ const auto diff = dsz - MinVisSize;
+ uint32_t color;
+ if( diff < 1 )
+ {
+ color = ( uint32_t( diff * 0x88 ) << 24 ) | 0x2222DD;
+ }
+ else
+ {
+ color = 0x882222DD;
+ }
+
+ draw->AddRectFilled( wpos + ImVec2( pr0, offset ), wpos + ImVec2( std::min( pr0+dsz, pr1 ), offset + tsz.y ), color );
+ draw->AddRectFilled( wpos + ImVec2( pr1, offset ), wpos + ImVec2( pr1+dsz, offset + tsz.y ), color );
+ }
+ if( rsz > MinVisSize )
+ {
+ const auto diff = rsz - MinVisSize;
+ uint32_t color;
+ if( diff < 1 )
+ {
+ color = ( uint32_t( diff * 0xAA ) << 24 ) | 0xFFFFFF;
+ }
+ else
+ {
+ color = 0xAAFFFFFF;
+ }
+
+ DrawLine( draw, dpos + ImVec2( pr0 + rsz, offset + ty05 ), dpos + ImVec2( pr0 - rsz, offset + ty05 ), color );
+ DrawLine( draw, dpos + ImVec2( pr0 + rsz, offset + ty025 ), dpos + ImVec2( pr0 + rsz, offset + ty075 ), color );
+ DrawLine( draw, dpos + ImVec2( pr0 - rsz, offset + ty025 ), dpos + ImVec2( pr0 - rsz, offset + ty075 ), color );
+
+ DrawLine( draw, dpos + ImVec2( pr1 + rsz, offset + ty05 ), dpos + ImVec2( pr1 - rsz, offset + ty05 ), color );
+ DrawLine( draw, dpos + ImVec2( pr1 + rsz, offset + ty025 ), dpos + ImVec2( pr1 + rsz, offset + ty075 ), color );
+ DrawLine( draw, dpos + ImVec2( pr1 - rsz, offset + ty025 ), dpos + ImVec2( pr1 - rsz, offset + ty075 ), color );
+ }
+ if( tsz.x < zsz )
+ {
+ const auto x = ( ev.Start() - m_vd.zvStart ) * pxns + ( ( end - ev.Start() ) * pxns - tsz.x ) / 2;
+ if( x < 0 || x > w - tsz.x )
+ {
+ ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
+ DrawTextContrast( draw, wpos + ImVec2( std::max( std::max( 0., px0 ), std::min( double( w - tsz.x ), x ) ), offset ), 0xFFFFFFFF, zoneName );
+ ImGui::PopClipRect();
+ }
+ else if( ev.Start() == ev.End() )
+ {
+ DrawTextContrast( draw, wpos + ImVec2( px0 + ( px1 - px0 - tsz.x ) * 0.5, offset ), 0xFFFFFFFF, zoneName );
+ }
+ else
+ {
+ DrawTextContrast( draw, wpos + ImVec2( x, offset ), 0xFFFFFFFF, zoneName );
+ }
+ }
+ else
+ {
+ ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
+ DrawTextContrast( draw, wpos + ImVec2( ( ev.Start() - m_vd.zvStart ) * pxns, offset ), 0xFFFFFFFF, zoneName );
+ ImGui::PopClipRect();
+ }
+
+ if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y + 1 ) ) )
+ {
+ ZoneTooltip( ev );
+ if( IsMouseClickReleased( 1 ) ) m_setRangePopup = RangeSlim { ev.Start(), m_worker.GetZoneEnd( ev ), true };
+
+ if( !m_zoomAnim.active && IsMouseClicked( 2 ) )
+ {
+ ZoomToZone( ev );
+ }
+ if( IsMouseClicked( 0 ) )
+ {
+ if( ImGui::GetIO().KeyCtrl )
+ {
+ auto& srcloc = m_worker.GetSourceLocation( ev.SrcLoc() );
+ m_findZone.ShowZone( ev.SrcLoc(), m_worker.GetString( srcloc.name.active ? srcloc.name : srcloc.function ) );
+ }
+ else
+ {
+ ShowZoneInfo( ev );
+ }
+ }
+
+ m_zoneSrcLocHighlight = ev.SrcLoc();
+ m_zoneHover = &ev;
+ }
+
+ ++it;
+ }
+ }
+ return maxdepth;
+}
+
+template
+int View::SkipZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, float yMin, float yMax, uint64_t tid )
+{
+ const auto delay = m_worker.GetDelay();
+ const auto resolution = m_worker.GetResolution();
+ // cast to uint64_t, so that unended zones (end = -1) are still drawn
+ auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart - delay ), [] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)a(l).End() < (uint64_t)r; } );
+ if( it == vec.end() ) return depth;
+
+ const auto zitend = std::lower_bound( it, vec.end(), m_vd.zvEnd + resolution, [] ( const auto& l, const auto& r ) { Adapter a; return a(l).Start() < r; } );
+ if( it == zitend ) return depth;
+
+ depth++;
+ int maxdepth = depth;
+
+ Adapter a;
+ while( it < zitend )
+ {
+ auto& ev = a(*it);
+ const auto end = m_worker.GetZoneEnd( ev );
+ const auto zsz = std::max( ( end - ev.Start() ) * pxns, pxns * 0.5 );
+ if( zsz < MinVisSize )
+ {
+ const auto MinVisNs = MinVisSize * nspx;
+ auto px1ns = end - m_vd.zvStart;
+ auto nextTime = end + MinVisNs;
+ for(;;)
+ {
+ const auto prevIt = it;
+ it = std::lower_bound( it, zitend, nextTime, [] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)a(l).End() < (uint64_t)r; } );
+ if( it == prevIt ) ++it;
+ if( it == zitend ) break;
+ const auto nend = m_worker.GetZoneEnd( a(*it) );
+ const auto nsnext = nend - m_vd.zvStart;
+ if( nsnext - px1ns >= MinVisNs * 2 ) break;
+ px1ns = nsnext;
+ nextTime = nend + nspx;
+ }
+ }
+ else
+ {
+ if( ev.HasChildren() )
+ {
+ const auto d = DispatchZoneLevel( m_worker.GetZoneChildren( ev.Child() ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
+ if( d > maxdepth ) maxdepth = d;
+ }
+ ++it;
+ }
+ }
+ return maxdepth;
+}
+
+int View::DispatchGpuZoneLevel( const Vector>& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, uint64_t thread, float yMin, float yMax, int64_t begin, int drift )
+{
+ const auto ty = ImGui::GetTextLineHeight();
+ const auto ostep = ty + 1;
+ const auto offset = _offset + ostep * depth;
+
+ const auto yPos = wpos.y + offset;
+ if( yPos + ostep >= yMin && yPos <= yMax )
+ {
+ if( vec.is_magic() )
+ {
+ return DrawGpuZoneLevel>( *(Vector*)&vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift );
+ }
+ else
+ {
+ return DrawGpuZoneLevel>( vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift );
+ }
+ }
+ else
+ {
+ if( vec.is_magic() )
+ {
+ return SkipGpuZoneLevel>( *(Vector*)&vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift );
+ }
+ else
+ {
+ return SkipGpuZoneLevel>( vec, hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift );
+ }
+ }
+}
+
+template
+int View::DrawGpuZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, uint64_t thread, float yMin, float yMax, int64_t begin, int drift )
+{
+ const auto delay = m_worker.GetDelay();
+ const auto resolution = m_worker.GetResolution();
+ // cast to uint64_t, so that unended zones (end = -1) are still drawn
+ auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart - delay ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } );
+ if( it == vec.end() ) return depth;
+
+ const auto zitend = std::lower_bound( it, vec.end(), std::max( 0, m_vd.zvEnd + resolution ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuStart(), begin, drift ) < (uint64_t)r; } );
+ if( it == zitend ) return depth;
+
+ const auto w = ImGui::GetContentRegionAvail().x - 1;
+ const auto ty = ImGui::GetTextLineHeight();
+ const auto ostep = ty + 1;
+ const auto offset = _offset + ostep * depth;
+ auto draw = ImGui::GetWindowDrawList();
+ const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
+
+ depth++;
+ int maxdepth = depth;
+
+ Adapter a;
+ while( it < zitend )
+ {
+ auto& ev = a(*it);
+ auto end = m_worker.GetZoneEnd( ev );
+ if( end == std::numeric_limits::max() ) break;
+ const auto start = AdjustGpuTime( ev.GpuStart(), begin, drift );
+ end = AdjustGpuTime( end, begin, drift );
+ const auto zsz = std::max( ( end - start ) * pxns, pxns * 0.5 );
+ if( zsz < MinVisSize )
+ {
+ const auto color = GetZoneColor( ev );
+ const auto MinVisNs = MinVisSize * nspx;
+ int num = 0;
+ const auto px0 = ( start - m_vd.zvStart ) * pxns;
+ auto px1ns = end - m_vd.zvStart;
+ auto rend = end;
+ auto nextTime = end + MinVisNs;
+ for(;;)
+ {
+ const auto prevIt = it;
+ it = std::lower_bound( it, zitend, std::max( 0, nextTime ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } );
+ if( it == prevIt ) ++it;
+ num += std::distance( prevIt, it );
+ if( it == zitend ) break;
+ const auto nend = AdjustGpuTime( m_worker.GetZoneEnd( a(*it) ), begin, drift );
+ const auto nsnext = nend - m_vd.zvStart;
+ if( nsnext < 0 || nsnext - px1ns >= MinVisNs * 2 ) break;
+ px1ns = nsnext;
+ rend = nend;
+ nextTime = nend + nspx;
+ }
+ const auto px1 = px1ns * pxns;
+ draw->AddRectFilled( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty ), color );
+ DrawZigZag( draw, wpos + ImVec2( 0, offset + ty/2 ), std::max( px0, -10.0 ), std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), ty/4, DarkenColor( color ) );
+ if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( std::max( px0, -10.0 ), offset ), wpos + ImVec2( std::min( std::max( px1, px0+MinVisSize ), double( w + 10 ) ), offset + ty + 1 ) ) )
+ {
+ if( num > 1 )
+ {
+ ImGui::BeginTooltip();
+ TextFocused( "Zones too small to display:", RealToString( num ) );
+ ImGui::Separator();
+ TextFocused( "Execution time:", TimeToString( rend - start ) );
+ ImGui::EndTooltip();
+
+ if( IsMouseClicked( 2 ) && rend - start > 0 )
+ {
+ ZoomToRange( start, rend );
+ }
+ }
+ else
+ {
+ const auto zoneThread = thread != 0 ? thread : m_worker.DecompressThread( ev.Thread() );
+ ZoneTooltip( ev );
+
+ if( IsMouseClicked( 2 ) && rend - start > 0 )
+ {
+ ZoomToZone( ev );
+ }
+ if( IsMouseClicked( 0 ) )
+ {
+ ShowZoneInfo( ev, zoneThread );
+ }
+
+ m_gpuThread = zoneThread;
+ m_gpuStart = ev.CpuStart();
+ m_gpuEnd = ev.CpuEnd();
+ }
+ }
+ const auto tmp = RealToString( num );
+ const auto tsz = ImGui::CalcTextSize( tmp );
+ if( tsz.x < px1 - px0 )
+ {
+ const auto x = px0 + ( px1 - px0 - tsz.x ) / 2;
+ DrawTextContrast( draw, wpos + ImVec2( x, offset ), 0xFF4488DD, tmp );
+ }
+ }
+ else
+ {
+ if( ev.Child() >= 0 )
+ {
+ const auto d = DispatchGpuZoneLevel( m_worker.GetGpuChildren( ev.Child() ), hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift );
+ if( d > maxdepth ) maxdepth = d;
+ }
+
+ const char* zoneName = m_worker.GetZoneName( ev );
+ auto tsz = ImGui::CalcTextSize( zoneName );
+
+ const auto pr0 = ( start - m_vd.zvStart ) * pxns;
+ const auto pr1 = ( end - m_vd.zvStart ) * pxns;
+ const auto px0 = std::max( pr0, -10.0 );
+ const auto px1 = std::max( { std::min( pr1, double( w + 10 ) ), px0 + pxns * 0.5, px0 + MinVisSize } );
+ const auto zoneColor = GetZoneColorData( ev );
+ draw->AddRectFilled( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.color );
+ if( zoneColor.highlight )
+ {
+ draw->AddRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y ), zoneColor.accentColor, 0.f, -1, zoneColor.thickness );
+ }
+ else
+ {
+ const auto darkColor = DarkenColor( zoneColor.color );
+ DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px0, offset ), dpos + ImVec2( px1-1, offset ), zoneColor.accentColor, zoneColor.thickness );
+ DrawLine( draw, dpos + ImVec2( px0, offset + tsz.y ), dpos + ImVec2( px1-1, offset + tsz.y ), dpos + ImVec2( px1-1, offset ), darkColor, zoneColor.thickness );
+ }
+ if( tsz.x < zsz )
+ {
+ const auto x = ( start - m_vd.zvStart ) * pxns + ( ( end - start ) * pxns - tsz.x ) / 2;
+ if( x < 0 || x > w - tsz.x )
+ {
+ ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
+ DrawTextContrast( draw, wpos + ImVec2( std::max( std::max( 0., px0 ), std::min( double( w - tsz.x ), x ) ), offset ), 0xFFFFFFFF, zoneName );
+ ImGui::PopClipRect();
+ }
+ else if( ev.GpuStart() == ev.GpuEnd() )
+ {
+ DrawTextContrast( draw, wpos + ImVec2( px0 + ( px1 - px0 - tsz.x ) * 0.5, offset ), 0xFFFFFFFF, zoneName );
+ }
+ else
+ {
+ DrawTextContrast( draw, wpos + ImVec2( x, offset ), 0xFFFFFFFF, zoneName );
+ }
+ }
+ else
+ {
+ ImGui::PushClipRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y * 2 ), true );
+ DrawTextContrast( draw, wpos + ImVec2( ( start - m_vd.zvStart ) * pxns, offset ), 0xFFFFFFFF, zoneName );
+ ImGui::PopClipRect();
+ }
+
+ if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( px0, offset ), wpos + ImVec2( px1, offset + tsz.y + 1 ) ) )
+ {
+ const auto zoneThread = thread != 0 ? thread : m_worker.DecompressThread( ev.Thread() );
+ ZoneTooltip( ev );
+
+ if( !m_zoomAnim.active && IsMouseClicked( 2 ) )
+ {
+ ZoomToZone( ev );
+ }
+ if( IsMouseClicked( 0 ) )
+ {
+ ShowZoneInfo( ev, zoneThread );
+ }
+
+ m_gpuThread = zoneThread;
+ m_gpuStart = ev.CpuStart();
+ m_gpuEnd = ev.CpuEnd();
+ }
+
+ ++it;
+ }
+ }
+ return maxdepth;
+}
+
+template
+int View::SkipGpuZoneLevel( const V& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, uint64_t thread, float yMin, float yMax, int64_t begin, int drift )
+{
+ const auto delay = m_worker.GetDelay();
+ const auto resolution = m_worker.GetResolution();
+ // cast to uint64_t, so that unended zones (end = -1) are still drawn
+ auto it = std::lower_bound( vec.begin(), vec.end(), std::max( 0, m_vd.zvStart - delay ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } );
+ if( it == vec.end() ) return depth;
+
+ const auto zitend = std::lower_bound( it, vec.end(), std::max( 0, m_vd.zvEnd + resolution ), [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuStart(), begin, drift ) < (uint64_t)r; } );
+ if( it == zitend ) return depth;
+
+ depth++;
+ int maxdepth = depth;
+
+ Adapter a;
+ while( it < zitend )
+ {
+ auto& ev = a(*it);
+ auto end = m_worker.GetZoneEnd( ev );
+ if( end == std::numeric_limits::max() ) break;
+ const auto start = AdjustGpuTime( ev.GpuStart(), begin, drift );
+ end = AdjustGpuTime( end, begin, drift );
+ const auto zsz = std::max( ( end - start ) * pxns, pxns * 0.5 );
+ if( zsz < MinVisSize )
+ {
+ const auto MinVisNs = MinVisSize * nspx;
+ auto px1ns = end - m_vd.zvStart;
+ auto nextTime = end + MinVisNs;
+ for(;;)
+ {
+ const auto prevIt = it;
+ it = std::lower_bound( it, zitend, nextTime, [begin, drift] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)AdjustGpuTime( a(l).GpuEnd(), begin, drift ) < (uint64_t)r; } );
+ if( it == prevIt ) ++it;
+ if( it == zitend ) break;
+ const auto nend = AdjustGpuTime( m_worker.GetZoneEnd( a(*it) ), begin, drift );
+ const auto nsnext = nend - m_vd.zvStart;
+ if( nsnext - px1ns >= MinVisNs * 2 ) break;
+ px1ns = nsnext;
+ nextTime = nend + nspx;
+ }
+ }
+ else
+ {
+ if( ev.Child() >= 0 )
+ {
+ const auto d = DispatchGpuZoneLevel( m_worker.GetGpuChildren( ev.Child() ), hover, pxns, nspx, wpos, _offset, depth, thread, yMin, yMax, begin, drift );
+ if( d > maxdepth ) maxdepth = d;
+ }
+ ++it;
+ }
+ }
+ return maxdepth;
+}
+
+}