From e3b11821c396e65d67e2f399baa788c69625f40d Mon Sep 17 00:00:00 2001 From: Bartosz Taudul Date: Sat, 2 Jul 2022 15:36:36 +0200 Subject: [PATCH] Extract zone timeline rendering from View. --- profiler/build/win32/Tracy.vcxproj | 2 + profiler/build/win32/Tracy.vcxproj.filters | 6 + server/TracyView.cpp | 1864 -------------------- server/TracyView_Timeline.cpp | 948 ++++++++++ server/TracyView_ZoneTimeline.cpp | 942 ++++++++++ 5 files changed, 1898 insertions(+), 1864 deletions(-) create mode 100644 server/TracyView_Timeline.cpp create mode 100644 server/TracyView_ZoneTimeline.cpp 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; +} + +}