Draw CPU zones using a precalculated list.

This commit is contained in:
Bartosz Taudul 2023-03-19 18:22:23 +01:00
parent 85d541124e
commit 66d3c2a472
No known key found for this signature in database
GPG Key ID: B7FE2008B7575DF3
3 changed files with 233 additions and 468 deletions

View File

@ -258,7 +258,7 @@ void TimelineItemThread::HeaderExtraContents( const TimelineContext& ctx, int of
bool TimelineItemThread::DrawContents( const TimelineContext& ctx, int& offset )
{
const auto res = m_view.DrawThread( ctx, *m_thread, offset, m_ghost );
const auto res = m_view.DrawThread( ctx, *m_thread, m_draw, offset, m_depth );
if( !res )
{
auto& crash = m_worker.GetCrashEvent();

View File

@ -41,6 +41,7 @@ struct MemoryPage;
class FileRead;
class SourceView;
struct TimelineContext;
struct TimelineDraw;
class View
{
@ -121,7 +122,7 @@ public:
void HighlightThread( uint64_t thread );
void ZoomToRange( int64_t start, int64_t end, bool pause = true );
bool DrawPlot( const TimelineContext& ctx, PlotData& plot, int& offset );
bool DrawThread( const TimelineContext& ctx, const ThreadData& thread, int& offset, bool ghostMode );
bool DrawThread( const TimelineContext& ctx, const ThreadData& thread, const std::vector<TimelineDraw>& draw, int& offset, int depth );
void DrawThreadMessages( const TimelineContext& ctx, const ThreadData& thread, int offset );
void DrawThreadOverlays( const ThreadData& thread, const ImVec2& ul, const ImVec2& dr );
bool DrawGpu( const TimelineContext& ctx, const GpuCtxData& gpu, int& offset );
@ -206,16 +207,7 @@ private:
void DrawTimeline();
void DrawContextSwitches( const ContextSwitch* ctx, const Vector<SampleData>& sampleData, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int offset, int endOffset, bool isFiber );
void DrawSamples( const Vector<SampleData>& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int offset );
#ifndef TRACY_NO_STATISTICS
int DispatchGhostLevel( const Vector<GhostZone>& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int offset, int depth, float yMin, float yMax, uint64_t tid );
int DrawGhostLevel( const Vector<GhostZone>& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int offset, int depth, float yMin, float yMax, uint64_t tid );
int SkipGhostLevel( const Vector<GhostZone>& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int offset, int depth, float yMin, float yMax, uint64_t tid );
#endif
int DispatchZoneLevel( const Vector<short_ptr<ZoneEvent>>& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int offset, int depth, float yMin, float yMax, uint64_t tid );
template<typename Adapter, typename V>
int 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 );
template<typename Adapter, typename V>
int 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 );
void DrawZoneList( const TimelineContext& ctx, const std::vector<TimelineDraw>& drawList, int offset, uint64_t tid );
int DispatchGpuZoneLevel( const Vector<short_ptr<GpuEvent>>& 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 );
template<typename Adapter, typename V>
int 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 );

View File

@ -5,6 +5,7 @@
#include "TracyMouse.hpp"
#include "TracyPrint.hpp"
#include "TracyTimelineContext.hpp"
#include "TracyTimelineDraw.hpp"
#include "TracyView.hpp"
namespace tracy
@ -22,7 +23,7 @@ static tracy_force_inline uint32_t MixGhostColor( uint32_t c0, uint32_t c1 )
( ( ( ( ( c0 & 0x000000FF ) ) + 3 * ( ( c1 & 0x000000FF ) ) ) >> 2 ) );
}
bool View::DrawThread( const TimelineContext& ctx, const ThreadData& thread, int& offset, bool ghostMode )
bool View::DrawThread( const TimelineContext& ctx, const ThreadData& thread, const std::vector<TimelineDraw>& draw, int& offset, int depth )
{
const auto& wpos = ctx.wpos;
const auto ty = ctx.ty;
@ -38,7 +39,6 @@ bool View::DrawThread( const TimelineContext& ctx, const ThreadData& thread, int
const auto sstep = sty + 1;
ImGui::PopFont();
int depth = 0;
const auto sampleOffset = offset;
const auto hasSamples = m_vd.drawSamples && !thread.samples.empty();
const auto hasCtxSwitch = m_vd.drawContextSwitches && m_worker.GetContextSwitchData( thread.id );
@ -61,15 +61,10 @@ bool View::DrawThread( const TimelineContext& ctx, const ThreadData& thread, int
offset += round( ostep * 0.75f );
}
#ifndef TRACY_NO_STATISTICS
if( m_worker.AreGhostZonesReady() && ( ghostMode || ( m_vd.ghostZones && thread.timeline.empty() ) ) )
const auto yPos = wpos.y + offset;
if( !draw.empty() && yPos <= yMax && yPos + ostep * depth >= yMin )
{
depth = DispatchGhostLevel( thread.ghostZones, hover, pxns, int64_t( nspx ), wpos, offset, 0, yMin, yMax, thread.id );
}
else
#endif
{
depth = DispatchZoneLevel( thread.timeline, hover, pxns, int64_t( nspx ), wpos, offset, 0, yMin, yMax, thread.id );
DrawZoneList( ctx, draw, offset, thread.id );
}
offset += ostep * depth;
@ -236,71 +231,226 @@ void View::DrawThreadOverlays( const ThreadData& thread, const ImVec2& ul, const
}
}
#ifndef TRACY_NO_STATISTICS
int View::DispatchGhostLevel( const Vector<GhostZone>& vec, bool hover, double pxns, int64_t nspx, const ImVec2& wpos, int _offset, int depth, float yMin, float yMax, uint64_t tid )
void View::DrawZoneList( const TimelineContext& ctx, const std::vector<TimelineDraw>& drawList, int _offset, 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 64 (arbitrary limit for inline frames in client)
if( yPos + 64 * 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<GhostZone>& 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<int64_t>( 0, m_vd.zvStart - 2 * MinVisSize * nspx ), [] ( 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;
if( (zitend-1)->end.Val() < 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 w = ctx.w;
const auto& wpos = ctx.wpos;
const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
const auto ty = ctx.ty;
const auto ostep = ty + 1;
const auto yMin = ctx.yMin;
const auto yMax = ctx.yMax;
const auto pxns = ctx.pxns;
const auto hover = ctx.hover;
const auto vStart = ctx.vStart;
const auto dsz = m_worker.GetDelay() * pxns;
const auto rsz = m_worker.GetResolution() * pxns;
depth++;
int maxdepth = depth;
const auto ty025 = round( ty * 0.25f );
const auto ty05 = round( ty * 0.5f );
const auto ty075 = round( ty * 0.75f );
while( it < zitend )
for( auto& v : drawList )
{
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 offset = _offset + ostep * v.depth;
const auto yPos = wpos.y + offset;
if( yPos > yMax || yPos + ostep < yMin ) continue;
switch( v.type )
{
const auto MinVisNs = MinVisSize * nspx;
const auto color = m_vd.dynamicColors == 2 ? 0xFF666666 : 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(;;)
case TimelineDrawType::Folded:
{
auto& ev = *(const ZoneEvent*)v.ev.get();
const auto color = m_vd.dynamicColors == 2 ? 0xFF666666 : GetThreadColor( tid, v.depth );
const auto rend = v.rend.Val();
const auto px0 = ( ev.Start() - vStart ) * pxns;
const auto px1 = ( rend - vStart ) * 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 ) ) )
{
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;
if( IsMouseClickReleased( 1 ) ) m_setRangePopup = RangeSlim { ev.Start(), rend, true };
if( v.num > 1 )
{
ImGui::BeginTooltip();
TextFocused( "Zones too small to display:", RealToString( v.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 px1 = px1ns * pxns;
const auto tmp = RealToString( v.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 );
}
break;
}
case TimelineDrawType::Zone:
{
auto& ev = *(const ZoneEvent*)v.ev.get();
const auto end = m_worker.GetZoneEnd( ev );
const auto zsz = std::max( ( end - ev.Start() ) * pxns, pxns * 0.5 );
const auto zoneColor = GetZoneColorData( ev, tid, v.depth );
const char* zoneName = m_worker.GetZoneName( ev );
auto tsz = ImGui::CalcTextSize( zoneName );
if( m_shortenName == ShortenName::Always || ( ( m_shortenName == ShortenName::NoSpace || m_shortenName == ShortenName::NoSpaceAndNormalize ) && tsz.x > zsz ) )
{
zoneName = ShortenZoneName( m_shortenName, zoneName, tsz, zsz );
}
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 )
{
if( zoneColor.thickness > 1.f )
{
draw->AddRect( wpos + ImVec2( px0 + 1, offset + 1 ), wpos + ImVec2( px1 - 1, offset + tsz.y - 1 ), zoneColor.accentColor, 0.f, -1, zoneColor.thickness );
}
else
{
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( std::max( int64_t( 0 ), 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;
}
break;
}
#ifndef TRACY_NO_STATISTICS
case TimelineDrawType::GhostFolded:
{
auto& ev = *(const GhostZone*)v.ev.get();
const auto color = m_vd.dynamicColors == 2 ? 0xFF666666 : MixGhostColor( GetThreadColor( tid, v.depth ), 0x665555 );
const auto rend = v.rend.Val();
const auto px0 = ( ev.start.Val() - m_vd.zvStart ) * pxns;
const auto px1 = ( rend - ev.end.Val() ) * 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 ) ) )
@ -317,9 +467,14 @@ int View::DrawGhostLevel( const Vector<GhostZone>& vec, bool hover, double pxns,
ZoomToRange( ev.start.Val(), rend );
}
}
break;
}
else
case TimelineDrawType::Ghost:
{
auto& ev = *(const GhostZone*)v.ev.get();
const auto end = ev.end.Val();
const auto zsz = std::max( ( end - ev.start.Val() ) * pxns, pxns * 0.5 );
const auto& ghostKey = m_worker.GetGhostFrame( ev.frame );
const auto frame = m_worker.GetCallstackFrame( ghostKey.frame );
@ -329,16 +484,16 @@ int View::DrawGhostLevel( const Vector<GhostZone>& vec, bool hover, double pxns,
if( frame )
{
const auto& sym = frame->data[ghostKey.inlineFrame];
color = GetHsvColor( sym.name.Idx(), depth );
color = GetHsvColor( sym.name.Idx(), v.depth );
}
else
{
color = GetHsvColor( ghostKey.frame.data, depth );
color = GetHsvColor( ghostKey.frame.data, v.depth );
}
}
else
{
color = MixGhostColor( GetThreadColor( tid, depth ), 0x665555 );
color = MixGhostColor( GetThreadColor( tid, v.depth ), 0x665555 );
}
const auto pr0 = ( ev.start.Val() - m_vd.zvStart ) * pxns;
@ -520,396 +675,14 @@ int View::DrawGhostLevel( const Vector<GhostZone>& vec, bool hover, double pxns,
}
}
}
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;
break;
}
}
return maxdepth;
}
int View::SkipGhostLevel( const Vector<GhostZone>& 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<int64_t>( 0, m_vd.zvStart - 2 * MinVisSize * nspx ), [] ( 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;
if( (zitend-1)->end.Val() < m_vd.zvStart ) 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<short_ptr<ZoneEvent>>& 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<VectorAdapterDirect<ZoneEvent>>( *(Vector<ZoneEvent>*)( &vec ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
}
else
{
return DrawZoneLevel<VectorAdapterPointer<ZoneEvent>>( vec, hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
}
}
else
{
if( vec.is_magic() )
{
return SkipZoneLevel<VectorAdapterDirect<ZoneEvent>>( *(Vector<ZoneEvent>*)( &vec ), hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
}
else
{
return SkipZoneLevel<VectorAdapterPointer<ZoneEvent>>( vec, hover, pxns, nspx, wpos, _offset, depth, yMin, yMax, tid );
default:
assert( false );
break;
}
}
}
template<typename Adapter, typename V>
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<int64_t>( 0, m_vd.zvStart - std::max<int64_t>( delay, 2 * MinVisSize * nspx ) ), [] ( 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;
if( m_worker.GetZoneEnd( a(*(zitend-1)) ) < 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 = m_vd.dynamicColors == 2 ? 0xFF666666 : 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( m_shortenName == ShortenName::Always || ( ( m_shortenName == ShortenName::NoSpace || m_shortenName == ShortenName::NoSpaceAndNormalize ) && tsz.x > zsz ) )
{
zoneName = ShortenZoneName( m_shortenName, zoneName, tsz, zsz );
}
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 )
{
if( zoneColor.thickness > 1.f )
{
draw->AddRect( wpos + ImVec2( px0 + 1, offset + 1 ), wpos + ImVec2( px1 - 1, offset + tsz.y - 1 ), zoneColor.accentColor, 0.f, -1, zoneColor.thickness );
}
else
{
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( std::max( int64_t( 0 ), 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<typename Adapter, typename V>
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<int64_t>( 0, m_vd.zvStart - std::max<int64_t>( delay, 2 * MinVisSize * nspx ) ), [] ( const auto& l, const auto& r ) { Adapter a; return (uint64_t)a(l).End() < (uint64_t)r; } );
if( it == vec.end() ) return depth;
Adapter a;
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;
if( m_worker.GetZoneEnd( a(*(zitend-1)) ) < m_vd.zvStart ) return depth;
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;
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;
}
}