diff --git a/profiler/build/win32/Tracy.vcxproj b/profiler/build/win32/Tracy.vcxproj
index d746d984..30da4b75 100644
--- a/profiler/build/win32/Tracy.vcxproj
+++ b/profiler/build/win32/Tracy.vcxproj
@@ -146,6 +146,7 @@
+
diff --git a/profiler/build/win32/Tracy.vcxproj.filters b/profiler/build/win32/Tracy.vcxproj.filters
index 77f61e5d..4e57efaa 100644
--- a/profiler/build/win32/Tracy.vcxproj.filters
+++ b/profiler/build/win32/Tracy.vcxproj.filters
@@ -285,6 +285,9 @@
server
+
+ server
+
diff --git a/server/TracyView.cpp b/server/TracyView.cpp
index 8e45c53f..d5aeaf07 100644
--- a/server/TracyView.cpp
+++ b/server/TracyView.cpp
@@ -2224,7 +2224,7 @@ void View::DrawZoneFrames( const FrameData& frames )
}
}
-static float AdjustThreadPosition( View::VisData& vis, float wy, int& offset )
+float View::AdjustThreadPosition( View::VisData& vis, float wy, int& offset )
{
if( vis.offset < offset )
{
@@ -5501,493 +5501,6 @@ int View::DrawCpuData( int offset, double pxns, const ImVec2& wpos, bool hover,
return offset;
}
-static const char* FormatPlotValue( double val, PlotValueFormatting format )
-{
- static char buf[64];
- switch( format )
- {
- case PlotValueFormatting::Number:
- return RealToString( val );
- break;
- case PlotValueFormatting::Memory:
- return MemSizeToString( val );
- break;
- case PlotValueFormatting::Percentage:
- sprintf( buf, "%.2f%%", val );
- break;
- default:
- assert( false );
- break;
- }
- return buf;
-}
-
-int View::DrawPlots( int offset, double pxns, const ImVec2& wpos, bool hover, float yMin, float yMax )
-{
- const auto PlotHeight = 100 * GetScale();
-
- enum { MaxPoints = 128 };
- float tmpvec[MaxPoints*2];
-
- const auto w = ImGui::GetContentRegionAvail().x - 1;
- const auto ty = ImGui::GetTextLineHeight();
- auto draw = ImGui::GetWindowDrawList();
- const auto to = 9.f;
- const auto th = ( ty - to ) * sqrt( 3 ) * 0.5;
- const auto nspx = 1.0 / pxns;
- const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
-
- for( const auto& v : m_worker.GetPlots() )
- {
- auto& vis = Vis( v );
- if( !vis.visible )
- {
- vis.height = 0;
- vis.offset = 0;
- continue;
- }
- if( v->data.empty() ) continue;
- bool& showFull = vis.showFull;
-
- float txtx = 0;
- const auto yPos = AdjustThreadPosition( vis, wpos.y, offset );
- const auto oldOffset = offset;
- ImGui::PushClipRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + vis.height ), true );
- if( yPos + ty >= yMin && yPos <= yMax )
- {
- if( showFull )
- {
- draw->AddTriangleFilled( wpos + ImVec2( to/2, offset + to/2 ), wpos + ImVec2( ty - to/2, offset + to/2 ), wpos + ImVec2( ty * 0.5, offset + to/2 + th ), 0xFF44DDDD );
- }
- else
- {
- draw->AddTriangle( wpos + ImVec2( to/2, offset + to/2 ), wpos + ImVec2( to/2, offset + ty - to/2 ), wpos + ImVec2( to/2 + th, offset + ty * 0.5 ), 0xFF226E6E, 2.0f );
- }
- const auto txt = GetPlotName( v );
- txtx = ImGui::CalcTextSize( txt ).x;
- DrawTextContrast( draw, wpos + ImVec2( ty, offset ), showFull ? 0xFF44DDDD : 0xFF226E6E, txt );
- DrawLine( draw, dpos + ImVec2( 0, offset + ty - 1 ), dpos + ImVec2( w, offset + ty - 1 ), 0x8844DDDD );
-
- if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( ty + txtx, offset + ty ) ) )
- {
- ImGui::BeginTooltip();
- ImGui::Text( "Plot \"%s\"", txt );
- ImGui::Separator();
-
- const auto first = v->data.front().time.Val();
- const auto last = v->data.back().time.Val();
- const auto activity = last - first;
- const auto traceLen = m_worker.GetLastTime();
-
- TextFocused( "Appeared at", TimeToString( first ) );
- TextFocused( "Last event at", TimeToString( last ) );
- TextFocused( "Activity time:", TimeToString( activity ) );
- ImGui::SameLine();
- char buf[64];
- PrintStringPercent( buf, activity / double( traceLen ) * 100 );
- TextDisabledUnformatted( buf );
- ImGui::Separator();
- TextFocused( "Data points:", RealToString( v->data.size() ) );
- TextFocused( "Data range:", FormatPlotValue( v->max - v->min, v->format ) );
- TextFocused( "Min value:", FormatPlotValue( v->min, v->format ) );
- TextFocused( "Max value:", FormatPlotValue( v->max, v->format ) );
- TextFocused( "Avg value:", FormatPlotValue( v->sum / v->data.size(), v->format ) );
- TextFocused( "Data/second:", RealToString( double( v->data.size() ) / activity * 1000000000ll ) );
-
- const auto it = std::lower_bound( v->data.begin(), v->data.end(), last - 1000000000ll * 10, [] ( const auto& l, const auto& r ) { return l.time.Val() < r; } );
- const auto tr10 = last - it->time.Val();
- if( tr10 != 0 )
- {
- TextFocused( "D/s (10s):", RealToString( double( std::distance( it, v->data.end() ) ) / tr10 * 1000000000ll ) );
- }
- ImGui::EndTooltip();
-
- if( IsMouseClicked( 0 ) )
- {
- showFull = !showFull;
- }
- if( IsMouseClicked( 2 ) )
- {
- ZoomToRange( first, last );
- }
- }
- }
-
- offset += ty;
-
- if( showFull )
- {
- auto yPos = wpos.y + offset;
- if( yPos + PlotHeight >= yMin && yPos <= yMax )
- {
- auto& vec = v->data;
- vec.ensure_sorted();
-
- if( v->type == PlotType::Memory )
- {
- auto& mem = m_worker.GetMemoryNamed( v->name );
-
- if( m_memoryAllocInfoPool == v->name && m_memoryAllocInfoWindow >= 0 )
- {
- const auto& ev = mem.data[m_memoryAllocInfoWindow];
-
- const auto tStart = ev.TimeAlloc();
- const auto tEnd = ev.TimeFree() < 0 ? m_worker.GetLastTime() : ev.TimeFree();
-
- const auto px0 = ( tStart - m_vd.zvStart ) * pxns;
- const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( tEnd - m_vd.zvStart ) * pxns );
- draw->AddRectFilled( ImVec2( wpos.x + px0, yPos ), ImVec2( wpos.x + px1, yPos + PlotHeight ), 0x2288DD88 );
- draw->AddRect( ImVec2( wpos.x + px0, yPos ), ImVec2( wpos.x + px1, yPos + PlotHeight ), 0x4488DD88 );
- }
- if( m_memoryAllocHover >= 0 && m_memoryAllocHoverPool == v->name && ( m_memoryAllocInfoPool != v->name || m_memoryAllocHover != m_memoryAllocInfoWindow ) )
- {
- const auto& ev = mem.data[m_memoryAllocHover];
-
- const auto tStart = ev.TimeAlloc();
- const auto tEnd = ev.TimeFree() < 0 ? m_worker.GetLastTime() : ev.TimeFree();
-
- const auto px0 = ( tStart - m_vd.zvStart ) * pxns;
- const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( tEnd - m_vd.zvStart ) * pxns );
- draw->AddRectFilled( ImVec2( wpos.x + px0, yPos ), ImVec2( wpos.x + px1, yPos + PlotHeight ), 0x228888DD );
- draw->AddRect( ImVec2( wpos.x + px0, yPos ), ImVec2( wpos.x + px1, yPos + PlotHeight ), 0x448888DD );
-
- if( m_memoryAllocHoverWait > 0 )
- {
- m_memoryAllocHoverWait--;
- }
- else
- {
- m_memoryAllocHover = -1;
- }
- }
- }
-
- auto it = std::lower_bound( vec.begin(), vec.end(), m_vd.zvStart - m_worker.GetDelay(), [] ( const auto& l, const auto& r ) { return l.time.Val() < r; } );
- auto end = std::lower_bound( it, vec.end(), m_vd.zvEnd + m_worker.GetResolution(), [] ( const auto& l, const auto& r ) { return l.time.Val() < r; } );
-
- if( end != vec.end() ) end++;
- if( it != vec.begin() ) it--;
-
- double min = it->val;
- double max = it->val;
- const auto num = std::distance( it, end );
- if( num > 1000000 )
- {
- min = v->min;
- max = v->max;
- }
- else
- {
- auto tmp = it;
- ++tmp;
- const auto sz = end - tmp;
- for( ptrdiff_t i=0; i max ? tmp[i].val : max;
- }
- }
- if( min == max )
- {
- min--;
- max++;
- }
-
- const auto rMin = min;
- const auto rMax = max;
-
- auto pvit = m_plotView.find( v );
- if( pvit == m_plotView.end() )
- {
- pvit = m_plotView.emplace( v, PlotView { min, max } ).first;
- }
- auto& pv = pvit->second;
- if( pv.min != min || pv.max != max )
- {
- const auto dt = ImGui::GetIO().DeltaTime;
- const auto minDiff = min - pv.min;
- const auto maxDiff = max - pv.max;
-
- pv.min += minDiff * 15.0 * dt;
- pv.max += maxDiff * 15.0 * dt;
-
- const auto minDiffNew = min - pv.min;
- const auto maxDiffNew = max - pv.max;
-
- if( minDiff * minDiffNew < 0 ) pv.min = min;
- if( maxDiff * maxDiffNew < 0 ) pv.max = max;
-
- min = pv.min;
- max = pv.max;
- }
-
- const auto revrange = 1.0 / ( max - min );
-
- if( it == vec.begin() )
- {
- const auto x = ( it->time.Val() - m_vd.zvStart ) * pxns;
- const auto y = PlotHeight - ( it->val - min ) * revrange * PlotHeight;
- DrawPlotPoint( wpos, x, y, offset, 0xFF44DDDD, hover, false, it, 0, false, v->type, v->format, PlotHeight, v->name );
- }
-
- auto prevx = it;
- auto prevy = it;
- ++it;
- ptrdiff_t skip = 0;
- while( it < end )
- {
- const auto x0 = ( prevx->time.Val() - m_vd.zvStart ) * pxns;
- const auto x1 = ( it->time.Val() - m_vd.zvStart ) * pxns;
- const auto y0 = PlotHeight - ( prevy->val - min ) * revrange * PlotHeight;
- const auto y1 = PlotHeight - ( it->val - min ) * revrange * PlotHeight;
-
- DrawLine( draw, dpos + ImVec2( x0, offset + y0 ), dpos + ImVec2( x1, offset + y1 ), 0xFF44DDDD );
-
- const auto rx = skip == 0 ? 2.0 : ( skip == 1 ? 2.5 : 4.0 );
-
- auto range = std::upper_bound( it, end, int64_t( it->time.Val() + nspx * rx ), [] ( const auto& l, const auto& r ) { return l < r.time.Val(); } );
- assert( range > it );
- const auto rsz = std::distance( it, range );
- if( rsz == 1 )
- {
- DrawPlotPoint( wpos, x1, y1, offset, 0xFF44DDDD, hover, true, it, prevy->val, false, v->type, v->format, PlotHeight, v->name );
- prevx = it;
- prevy = it;
- ++it;
- }
- else
- {
- prevx = it;
-
- skip = rsz / MaxPoints;
- const auto skip1 = std::max( 1, skip );
- const auto sz = rsz / skip1 + 1;
- assert( sz <= MaxPoints*2 );
-
- auto dst = tmpvec;
- const auto rsz = std::distance( it, range );
- const auto ssz = rsz / skip1;
- for( int64_t i=0; ival );
- it += skip1;
- }
- pdqsort_branchless( tmpvec, dst );
-
- if( rsz > MaxPoints )
- {
- DrawLine( draw, dpos + ImVec2( x1, offset + PlotHeight - ( tmpvec[0] - min ) * revrange * PlotHeight ), dpos + ImVec2( x1, offset + PlotHeight - ( dst[-1] - min ) * revrange * PlotHeight ), 0xFF44DDDD, 4.f );
-
- if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( x1 - 2, offset ), wpos + ImVec2( x1 + 2, offset + PlotHeight ) ) )
- {
- ImGui::BeginTooltip();
- TextFocused( "Number of values:", RealToString( rsz ) );
- TextDisabledUnformatted( "Estimated range:" );
- ImGui::SameLine();
- ImGui::Text( "%s - %s", FormatPlotValue( tmpvec[0], v->format ), FormatPlotValue( dst[-1], v->format ) );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%s)", FormatPlotValue( dst[-1] - tmpvec[0], v->format ) );
- ImGui::EndTooltip();
- }
- }
- else
- {
- DrawLine( draw, dpos + ImVec2( x1, offset + PlotHeight - ( tmpvec[0] - min ) * revrange * PlotHeight ), dpos + ImVec2( x1, offset + PlotHeight - ( dst[-1] - min ) * revrange * PlotHeight ), 0xFF44DDDD );
-
- auto vit = tmpvec;
- while( vit != dst )
- {
- auto vrange = std::upper_bound( vit, dst, *vit + 3.0 / ( revrange * PlotHeight ), [] ( const auto& l, const auto& r ) { return l < r; } );
- assert( vrange > vit );
- if( std::distance( vit, vrange ) == 1 )
- {
- DrawPlotPoint( wpos, x1, PlotHeight - ( *vit - min ) * revrange * PlotHeight, offset, 0xFF44DDDD, hover, false, *vit, 0, false, v->format, PlotHeight );
- }
- else
- {
- DrawPlotPoint( wpos, x1, PlotHeight - ( *vit - min ) * revrange * PlotHeight, offset, 0xFF44DDDD, hover, false, *vit, 0, true, v->format, PlotHeight );
- }
- vit = vrange;
- }
- }
-
- prevy = it - 1;
- }
- }
-
- if( yPos + ty >= yMin && yPos <= yMax )
- {
- char tmp[64];
- sprintf( tmp, "(y-range: %s, visible data points: %s)", FormatPlotValue( rMax - rMin, v->format ), RealToString( num ) );
- draw->AddText( wpos + ImVec2( ty * 1.5f + txtx, offset - ty ), 0x8844DDDD, tmp );
- }
- auto tmp = FormatPlotValue( rMax, v->format );
- DrawTextContrast( draw, wpos + ImVec2( 0, offset ), 0x8844DDDD, tmp );
- offset += PlotHeight - ty;
- tmp = FormatPlotValue( rMin, v->format );
- DrawTextContrast( draw, wpos + ImVec2( 0, offset ), 0x8844DDDD, tmp );
-
- DrawLine( draw, dpos + ImVec2( 0, offset + ty - 1 ), dpos + ImVec2( w, offset + ty - 1 ), 0x8844DDDD );
- offset += ty;
- }
- else
- {
- offset += PlotHeight;
- }
- }
- offset += 0.2 * ty;
- AdjustThreadHeight( vis, oldOffset, offset );
- ImGui::PopClipRect();
- }
-
- return offset;
-}
-
-void View::DrawPlotPoint( const ImVec2& wpos, float x, float y, int offset, uint32_t color, bool hover, bool hasPrev, double val, double prev, bool merged, PlotValueFormatting format, float PlotHeight )
-{
- auto draw = ImGui::GetWindowDrawList();
- if( merged )
- {
- draw->AddRectFilled( wpos + ImVec2( x - 1.5f, offset + y - 1.5f ), wpos + ImVec2( x + 2.5f, offset + y + 2.5f ), color );
- }
- else
- {
- draw->AddRect( wpos + ImVec2( x - 1.5f, offset + y - 1.5f ), wpos + ImVec2( x + 2.5f, offset + y + 2.5f ), color );
- }
-
- if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( x - 2, offset ), wpos + ImVec2( x + 2, offset + PlotHeight ) ) )
- {
- ImGui::BeginTooltip();
- TextFocused( "Value:", FormatPlotValue( val, format ) );
- if( hasPrev )
- {
- TextFocused( "Change:", FormatPlotValue( val - prev, format ) );
- }
- ImGui::EndTooltip();
- }
-}
-
-void View::DrawPlotPoint( const ImVec2& wpos, float x, float y, int offset, uint32_t color, bool hover, bool hasPrev, const PlotItem* item, double prev, bool merged, PlotType type, PlotValueFormatting format, float PlotHeight, uint64_t name )
-{
- auto draw = ImGui::GetWindowDrawList();
- if( merged )
- {
- draw->AddRectFilled( wpos + ImVec2( x - 1.5f, offset + y - 1.5f ), wpos + ImVec2( x + 2.5f, offset + y + 2.5f ), color );
- }
- else
- {
- draw->AddRect( wpos + ImVec2( x - 1.5f, offset + y - 1.5f ), wpos + ImVec2( x + 2.5f, offset + y + 2.5f ), color );
- }
-
- if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( x - 2, offset ), wpos + ImVec2( x + 2, offset + PlotHeight ) ) )
- {
- ImGui::BeginTooltip();
- TextFocused( "Time:", TimeToStringExact( item->time.Val() ) );
- if( type == PlotType::Memory )
- {
- TextDisabledUnformatted( "Value:" );
- ImGui::SameLine();
- if( item->val < 10000ll )
- {
- ImGui::TextUnformatted( MemSizeToString( item->val ) );
- }
- else
- {
- ImGui::TextUnformatted( MemSizeToString( item->val ) );
- ImGui::SameLine();
- ImGui::TextDisabled( "(%s)", RealToString( item->val ) );
- }
- }
- else
- {
- TextFocused( "Value:", FormatPlotValue( item->val, format ) );
- }
- if( hasPrev )
- {
- const auto change = item->val - prev;
- TextFocused( "Change:", FormatPlotValue( change, format ) );
-
- if( type == PlotType::Memory )
- {
- auto& mem = m_worker.GetMemoryNamed( name );
- const MemEvent* ev = nullptr;
- if( change > 0 )
- {
- auto it = std::lower_bound( mem.data.begin(), mem.data.end(), item->time.Val(), [] ( const auto& lhs, const auto& rhs ) { return lhs.TimeAlloc() < rhs; } );
- if( it != mem.data.end() && it->TimeAlloc() == item->time.Val() )
- {
- ev = it;
- }
- }
- else
- {
- const auto& data = mem.data;
- auto it = std::lower_bound( mem.frees.begin(), mem.frees.end(), item->time.Val(), [&data] ( const auto& lhs, const auto& rhs ) { return data[lhs].TimeFree() < rhs; } );
- if( it != mem.frees.end() && data[*it].TimeFree() == item->time.Val() )
- {
- ev = &data[*it];
- }
- }
- if( ev )
- {
- ImGui::Separator();
- TextDisabledUnformatted( "Address:" );
- ImGui::SameLine();
- ImGui::Text( "0x%" PRIx64, ev->Ptr() );
- TextFocused( "Appeared at", TimeToStringExact( ev->TimeAlloc() ) );
- if( change > 0 )
- {
- ImGui::SameLine();
- ImGui::TextDisabled( "(this event)" );
- }
- if( ev->TimeFree() < 0 )
- {
- ImGui::TextUnformatted( "Allocation still active" );
- }
- else
- {
- TextFocused( "Freed at", TimeToStringExact( ev->TimeFree() ) );
- if( change < 0 )
- {
- ImGui::SameLine();
- TextDisabledUnformatted( "(this event)" );
- }
- TextFocused( "Duration:", TimeToString( ev->TimeFree() - ev->TimeAlloc() ) );
- }
- uint64_t tid;
- if( change > 0 )
- {
- tid = m_worker.DecompressThread( ev->ThreadAlloc() );
- }
- else
- {
- tid = m_worker.DecompressThread( ev->ThreadFree() );
- }
- 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" );
- }
- m_memoryAllocHover = std::distance( mem.data.begin(), ev );
- m_memoryAllocHoverWait = 2;
- m_memoryAllocHoverPool = name;
- if( IsMouseClicked( 0 ) )
- {
- m_memoryAllocInfoWindow = m_memoryAllocHover;
- m_memoryAllocInfoPool = name;
- }
- }
- }
- }
- ImGui::EndTooltip();
- }
-}
-
void View::DrawMessages()
{
const auto& msgs = m_worker.GetMessages();
@@ -9509,31 +9022,6 @@ void View::DrawAllocList()
ImGui::End();
}
-const char* View::GetPlotName( const PlotData* plot ) const
-{
- static char tmp[1024];
- switch( plot->type )
- {
- case PlotType::User:
- return m_worker.GetString( plot->name );
- case PlotType::Memory:
- if( plot->name == 0 )
- {
- return ICON_FA_MEMORY " Memory usage";
- }
- else
- {
- sprintf( tmp, ICON_FA_MEMORY " %s", m_worker.GetString( plot->name ) );
- return tmp;
- }
- case PlotType::SysTime:
- return ICON_FA_TACHOMETER_ALT " CPU usage";
- default:
- assert( false );
- return nullptr;
- }
-}
-
void View::CrashTooltip()
{
auto& crash = m_worker.GetCrashEvent();
diff --git a/server/TracyView.hpp b/server/TracyView.hpp
index 32cf65d1..1ee8b188 100644
--- a/server/TracyView.hpp
+++ b/server/TracyView.hpp
@@ -388,6 +388,8 @@ private:
}
void AdjustThreadHeight( View::VisData& vis, int oldOffset, int& offset );
+ float AdjustThreadPosition( View::VisData& vis, float wy, int& offset );
+
void DrawHistogramMinMaxLabel( ImDrawList* draw, int64_t tmin, int64_t tmax, ImVec2 wpos, float w, float ty );
static int64_t AdjustGpuTime( int64_t time, int64_t begin, int drift );
diff --git a/server/TracyView_Plots.cpp b/server/TracyView_Plots.cpp
new file mode 100644
index 00000000..b21006fc
--- /dev/null
+++ b/server/TracyView_Plots.cpp
@@ -0,0 +1,522 @@
+#include
+
+#include "TracyMouse.hpp"
+#include "TracyPrint.hpp"
+#include "TracyView.hpp"
+
+namespace tracy
+{
+
+const char* View::GetPlotName( const PlotData* plot ) const
+{
+ static char tmp[1024];
+ switch( plot->type )
+ {
+ case PlotType::User:
+ return m_worker.GetString( plot->name );
+ case PlotType::Memory:
+ if( plot->name == 0 )
+ {
+ return ICON_FA_MEMORY " Memory usage";
+ }
+ else
+ {
+ sprintf( tmp, ICON_FA_MEMORY " %s", m_worker.GetString( plot->name ) );
+ return tmp;
+ }
+ case PlotType::SysTime:
+ return ICON_FA_TACHOMETER_ALT " CPU usage";
+ default:
+ assert( false );
+ return nullptr;
+ }
+}
+
+static const char* FormatPlotValue( double val, PlotValueFormatting format )
+{
+ static char buf[64];
+ switch( format )
+ {
+ case PlotValueFormatting::Number:
+ return RealToString( val );
+ break;
+ case PlotValueFormatting::Memory:
+ return MemSizeToString( val );
+ break;
+ case PlotValueFormatting::Percentage:
+ sprintf( buf, "%.2f%%", val );
+ break;
+ default:
+ assert( false );
+ break;
+ }
+ return buf;
+}
+
+int View::DrawPlots( int offset, double pxns, const ImVec2& wpos, bool hover, float yMin, float yMax )
+{
+ const auto PlotHeight = 100 * GetScale();
+
+ enum { MaxPoints = 128 };
+ float tmpvec[MaxPoints*2];
+
+ const auto w = ImGui::GetContentRegionAvail().x - 1;
+ const auto ty = ImGui::GetTextLineHeight();
+ auto draw = ImGui::GetWindowDrawList();
+ const auto to = 9.f;
+ const auto th = ( ty - to ) * sqrt( 3 ) * 0.5;
+ const auto nspx = 1.0 / pxns;
+ const auto dpos = wpos + ImVec2( 0.5f, 0.5f );
+
+ for( const auto& v : m_worker.GetPlots() )
+ {
+ auto& vis = Vis( v );
+ if( !vis.visible )
+ {
+ vis.height = 0;
+ vis.offset = 0;
+ continue;
+ }
+ if( v->data.empty() ) continue;
+ bool& showFull = vis.showFull;
+
+ float txtx = 0;
+ const auto yPos = AdjustThreadPosition( vis, wpos.y, offset );
+ const auto oldOffset = offset;
+ ImGui::PushClipRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( w, offset + vis.height ), true );
+ if( yPos + ty >= yMin && yPos <= yMax )
+ {
+ if( showFull )
+ {
+ draw->AddTriangleFilled( wpos + ImVec2( to/2, offset + to/2 ), wpos + ImVec2( ty - to/2, offset + to/2 ), wpos + ImVec2( ty * 0.5, offset + to/2 + th ), 0xFF44DDDD );
+ }
+ else
+ {
+ draw->AddTriangle( wpos + ImVec2( to/2, offset + to/2 ), wpos + ImVec2( to/2, offset + ty - to/2 ), wpos + ImVec2( to/2 + th, offset + ty * 0.5 ), 0xFF226E6E, 2.0f );
+ }
+ const auto txt = GetPlotName( v );
+ txtx = ImGui::CalcTextSize( txt ).x;
+ DrawTextContrast( draw, wpos + ImVec2( ty, offset ), showFull ? 0xFF44DDDD : 0xFF226E6E, txt );
+ DrawLine( draw, dpos + ImVec2( 0, offset + ty - 1 ), dpos + ImVec2( w, offset + ty - 1 ), 0x8844DDDD );
+
+ if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( 0, offset ), wpos + ImVec2( ty + txtx, offset + ty ) ) )
+ {
+ ImGui::BeginTooltip();
+ ImGui::Text( "Plot \"%s\"", txt );
+ ImGui::Separator();
+
+ const auto first = v->data.front().time.Val();
+ const auto last = v->data.back().time.Val();
+ const auto activity = last - first;
+ const auto traceLen = m_worker.GetLastTime();
+
+ TextFocused( "Appeared at", TimeToString( first ) );
+ TextFocused( "Last event at", TimeToString( last ) );
+ TextFocused( "Activity time:", TimeToString( activity ) );
+ ImGui::SameLine();
+ char buf[64];
+ PrintStringPercent( buf, activity / double( traceLen ) * 100 );
+ TextDisabledUnformatted( buf );
+ ImGui::Separator();
+ TextFocused( "Data points:", RealToString( v->data.size() ) );
+ TextFocused( "Data range:", FormatPlotValue( v->max - v->min, v->format ) );
+ TextFocused( "Min value:", FormatPlotValue( v->min, v->format ) );
+ TextFocused( "Max value:", FormatPlotValue( v->max, v->format ) );
+ TextFocused( "Avg value:", FormatPlotValue( v->sum / v->data.size(), v->format ) );
+ TextFocused( "Data/second:", RealToString( double( v->data.size() ) / activity * 1000000000ll ) );
+
+ const auto it = std::lower_bound( v->data.begin(), v->data.end(), last - 1000000000ll * 10, [] ( const auto& l, const auto& r ) { return l.time.Val() < r; } );
+ const auto tr10 = last - it->time.Val();
+ if( tr10 != 0 )
+ {
+ TextFocused( "D/s (10s):", RealToString( double( std::distance( it, v->data.end() ) ) / tr10 * 1000000000ll ) );
+ }
+ ImGui::EndTooltip();
+
+ if( IsMouseClicked( 0 ) )
+ {
+ showFull = !showFull;
+ }
+ if( IsMouseClicked( 2 ) )
+ {
+ ZoomToRange( first, last );
+ }
+ }
+ }
+
+ offset += ty;
+
+ if( showFull )
+ {
+ auto yPos = wpos.y + offset;
+ if( yPos + PlotHeight >= yMin && yPos <= yMax )
+ {
+ auto& vec = v->data;
+ vec.ensure_sorted();
+
+ if( v->type == PlotType::Memory )
+ {
+ auto& mem = m_worker.GetMemoryNamed( v->name );
+
+ if( m_memoryAllocInfoPool == v->name && m_memoryAllocInfoWindow >= 0 )
+ {
+ const auto& ev = mem.data[m_memoryAllocInfoWindow];
+
+ const auto tStart = ev.TimeAlloc();
+ const auto tEnd = ev.TimeFree() < 0 ? m_worker.GetLastTime() : ev.TimeFree();
+
+ const auto px0 = ( tStart - m_vd.zvStart ) * pxns;
+ const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( tEnd - m_vd.zvStart ) * pxns );
+ draw->AddRectFilled( ImVec2( wpos.x + px0, yPos ), ImVec2( wpos.x + px1, yPos + PlotHeight ), 0x2288DD88 );
+ draw->AddRect( ImVec2( wpos.x + px0, yPos ), ImVec2( wpos.x + px1, yPos + PlotHeight ), 0x4488DD88 );
+ }
+ if( m_memoryAllocHover >= 0 && m_memoryAllocHoverPool == v->name && ( m_memoryAllocInfoPool != v->name || m_memoryAllocHover != m_memoryAllocInfoWindow ) )
+ {
+ const auto& ev = mem.data[m_memoryAllocHover];
+
+ const auto tStart = ev.TimeAlloc();
+ const auto tEnd = ev.TimeFree() < 0 ? m_worker.GetLastTime() : ev.TimeFree();
+
+ const auto px0 = ( tStart - m_vd.zvStart ) * pxns;
+ const auto px1 = std::max( px0 + std::max( 1.0, pxns * 0.5 ), ( tEnd - m_vd.zvStart ) * pxns );
+ draw->AddRectFilled( ImVec2( wpos.x + px0, yPos ), ImVec2( wpos.x + px1, yPos + PlotHeight ), 0x228888DD );
+ draw->AddRect( ImVec2( wpos.x + px0, yPos ), ImVec2( wpos.x + px1, yPos + PlotHeight ), 0x448888DD );
+
+ if( m_memoryAllocHoverWait > 0 )
+ {
+ m_memoryAllocHoverWait--;
+ }
+ else
+ {
+ m_memoryAllocHover = -1;
+ }
+ }
+ }
+
+ auto it = std::lower_bound( vec.begin(), vec.end(), m_vd.zvStart - m_worker.GetDelay(), [] ( const auto& l, const auto& r ) { return l.time.Val() < r; } );
+ auto end = std::lower_bound( it, vec.end(), m_vd.zvEnd + m_worker.GetResolution(), [] ( const auto& l, const auto& r ) { return l.time.Val() < r; } );
+
+ if( end != vec.end() ) end++;
+ if( it != vec.begin() ) it--;
+
+ double min = it->val;
+ double max = it->val;
+ const auto num = std::distance( it, end );
+ if( num > 1000000 )
+ {
+ min = v->min;
+ max = v->max;
+ }
+ else
+ {
+ auto tmp = it;
+ ++tmp;
+ const auto sz = end - tmp;
+ for( ptrdiff_t i=0; i max ? tmp[i].val : max;
+ }
+ }
+ if( min == max )
+ {
+ min--;
+ max++;
+ }
+
+ const auto rMin = min;
+ const auto rMax = max;
+
+ auto pvit = m_plotView.find( v );
+ if( pvit == m_plotView.end() )
+ {
+ pvit = m_plotView.emplace( v, PlotView { min, max } ).first;
+ }
+ auto& pv = pvit->second;
+ if( pv.min != min || pv.max != max )
+ {
+ const auto dt = ImGui::GetIO().DeltaTime;
+ const auto minDiff = min - pv.min;
+ const auto maxDiff = max - pv.max;
+
+ pv.min += minDiff * 15.0 * dt;
+ pv.max += maxDiff * 15.0 * dt;
+
+ const auto minDiffNew = min - pv.min;
+ const auto maxDiffNew = max - pv.max;
+
+ if( minDiff * minDiffNew < 0 ) pv.min = min;
+ if( maxDiff * maxDiffNew < 0 ) pv.max = max;
+
+ min = pv.min;
+ max = pv.max;
+ }
+
+ const auto revrange = 1.0 / ( max - min );
+
+ if( it == vec.begin() )
+ {
+ const auto x = ( it->time.Val() - m_vd.zvStart ) * pxns;
+ const auto y = PlotHeight - ( it->val - min ) * revrange * PlotHeight;
+ DrawPlotPoint( wpos, x, y, offset, 0xFF44DDDD, hover, false, it, 0, false, v->type, v->format, PlotHeight, v->name );
+ }
+
+ auto prevx = it;
+ auto prevy = it;
+ ++it;
+ ptrdiff_t skip = 0;
+ while( it < end )
+ {
+ const auto x0 = ( prevx->time.Val() - m_vd.zvStart ) * pxns;
+ const auto x1 = ( it->time.Val() - m_vd.zvStart ) * pxns;
+ const auto y0 = PlotHeight - ( prevy->val - min ) * revrange * PlotHeight;
+ const auto y1 = PlotHeight - ( it->val - min ) * revrange * PlotHeight;
+
+ DrawLine( draw, dpos + ImVec2( x0, offset + y0 ), dpos + ImVec2( x1, offset + y1 ), 0xFF44DDDD );
+
+ const auto rx = skip == 0 ? 2.0 : ( skip == 1 ? 2.5 : 4.0 );
+
+ auto range = std::upper_bound( it, end, int64_t( it->time.Val() + nspx * rx ), [] ( const auto& l, const auto& r ) { return l < r.time.Val(); } );
+ assert( range > it );
+ const auto rsz = std::distance( it, range );
+ if( rsz == 1 )
+ {
+ DrawPlotPoint( wpos, x1, y1, offset, 0xFF44DDDD, hover, true, it, prevy->val, false, v->type, v->format, PlotHeight, v->name );
+ prevx = it;
+ prevy = it;
+ ++it;
+ }
+ else
+ {
+ prevx = it;
+
+ skip = rsz / MaxPoints;
+ const auto skip1 = std::max( 1, skip );
+ const auto sz = rsz / skip1 + 1;
+ assert( sz <= MaxPoints*2 );
+
+ auto dst = tmpvec;
+ const auto rsz = std::distance( it, range );
+ const auto ssz = rsz / skip1;
+ for( int64_t i=0; ival );
+ it += skip1;
+ }
+ pdqsort_branchless( tmpvec, dst );
+
+ if( rsz > MaxPoints )
+ {
+ DrawLine( draw, dpos + ImVec2( x1, offset + PlotHeight - ( tmpvec[0] - min ) * revrange * PlotHeight ), dpos + ImVec2( x1, offset + PlotHeight - ( dst[-1] - min ) * revrange * PlotHeight ), 0xFF44DDDD, 4.f );
+
+ if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( x1 - 2, offset ), wpos + ImVec2( x1 + 2, offset + PlotHeight ) ) )
+ {
+ ImGui::BeginTooltip();
+ TextFocused( "Number of values:", RealToString( rsz ) );
+ TextDisabledUnformatted( "Estimated range:" );
+ ImGui::SameLine();
+ ImGui::Text( "%s - %s", FormatPlotValue( tmpvec[0], v->format ), FormatPlotValue( dst[-1], v->format ) );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%s)", FormatPlotValue( dst[-1] - tmpvec[0], v->format ) );
+ ImGui::EndTooltip();
+ }
+ }
+ else
+ {
+ DrawLine( draw, dpos + ImVec2( x1, offset + PlotHeight - ( tmpvec[0] - min ) * revrange * PlotHeight ), dpos + ImVec2( x1, offset + PlotHeight - ( dst[-1] - min ) * revrange * PlotHeight ), 0xFF44DDDD );
+
+ auto vit = tmpvec;
+ while( vit != dst )
+ {
+ auto vrange = std::upper_bound( vit, dst, *vit + 3.0 / ( revrange * PlotHeight ), [] ( const auto& l, const auto& r ) { return l < r; } );
+ assert( vrange > vit );
+ if( std::distance( vit, vrange ) == 1 )
+ {
+ DrawPlotPoint( wpos, x1, PlotHeight - ( *vit - min ) * revrange * PlotHeight, offset, 0xFF44DDDD, hover, false, *vit, 0, false, v->format, PlotHeight );
+ }
+ else
+ {
+ DrawPlotPoint( wpos, x1, PlotHeight - ( *vit - min ) * revrange * PlotHeight, offset, 0xFF44DDDD, hover, false, *vit, 0, true, v->format, PlotHeight );
+ }
+ vit = vrange;
+ }
+ }
+
+ prevy = it - 1;
+ }
+ }
+
+ if( yPos + ty >= yMin && yPos <= yMax )
+ {
+ char tmp[64];
+ sprintf( tmp, "(y-range: %s, visible data points: %s)", FormatPlotValue( rMax - rMin, v->format ), RealToString( num ) );
+ draw->AddText( wpos + ImVec2( ty * 1.5f + txtx, offset - ty ), 0x8844DDDD, tmp );
+ }
+ auto tmp = FormatPlotValue( rMax, v->format );
+ DrawTextContrast( draw, wpos + ImVec2( 0, offset ), 0x8844DDDD, tmp );
+ offset += PlotHeight - ty;
+ tmp = FormatPlotValue( rMin, v->format );
+ DrawTextContrast( draw, wpos + ImVec2( 0, offset ), 0x8844DDDD, tmp );
+
+ DrawLine( draw, dpos + ImVec2( 0, offset + ty - 1 ), dpos + ImVec2( w, offset + ty - 1 ), 0x8844DDDD );
+ offset += ty;
+ }
+ else
+ {
+ offset += PlotHeight;
+ }
+ }
+ offset += 0.2 * ty;
+ AdjustThreadHeight( vis, oldOffset, offset );
+ ImGui::PopClipRect();
+ }
+
+ return offset;
+}
+
+void View::DrawPlotPoint( const ImVec2& wpos, float x, float y, int offset, uint32_t color, bool hover, bool hasPrev, double val, double prev, bool merged, PlotValueFormatting format, float PlotHeight )
+{
+ auto draw = ImGui::GetWindowDrawList();
+ if( merged )
+ {
+ draw->AddRectFilled( wpos + ImVec2( x - 1.5f, offset + y - 1.5f ), wpos + ImVec2( x + 2.5f, offset + y + 2.5f ), color );
+ }
+ else
+ {
+ draw->AddRect( wpos + ImVec2( x - 1.5f, offset + y - 1.5f ), wpos + ImVec2( x + 2.5f, offset + y + 2.5f ), color );
+ }
+
+ if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( x - 2, offset ), wpos + ImVec2( x + 2, offset + PlotHeight ) ) )
+ {
+ ImGui::BeginTooltip();
+ TextFocused( "Value:", FormatPlotValue( val, format ) );
+ if( hasPrev )
+ {
+ TextFocused( "Change:", FormatPlotValue( val - prev, format ) );
+ }
+ ImGui::EndTooltip();
+ }
+}
+
+void View::DrawPlotPoint( const ImVec2& wpos, float x, float y, int offset, uint32_t color, bool hover, bool hasPrev, const PlotItem* item, double prev, bool merged, PlotType type, PlotValueFormatting format, float PlotHeight, uint64_t name )
+{
+ auto draw = ImGui::GetWindowDrawList();
+ if( merged )
+ {
+ draw->AddRectFilled( wpos + ImVec2( x - 1.5f, offset + y - 1.5f ), wpos + ImVec2( x + 2.5f, offset + y + 2.5f ), color );
+ }
+ else
+ {
+ draw->AddRect( wpos + ImVec2( x - 1.5f, offset + y - 1.5f ), wpos + ImVec2( x + 2.5f, offset + y + 2.5f ), color );
+ }
+
+ if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( x - 2, offset ), wpos + ImVec2( x + 2, offset + PlotHeight ) ) )
+ {
+ ImGui::BeginTooltip();
+ TextFocused( "Time:", TimeToStringExact( item->time.Val() ) );
+ if( type == PlotType::Memory )
+ {
+ TextDisabledUnformatted( "Value:" );
+ ImGui::SameLine();
+ if( item->val < 10000ll )
+ {
+ ImGui::TextUnformatted( MemSizeToString( item->val ) );
+ }
+ else
+ {
+ ImGui::TextUnformatted( MemSizeToString( item->val ) );
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(%s)", RealToString( item->val ) );
+ }
+ }
+ else
+ {
+ TextFocused( "Value:", FormatPlotValue( item->val, format ) );
+ }
+ if( hasPrev )
+ {
+ const auto change = item->val - prev;
+ TextFocused( "Change:", FormatPlotValue( change, format ) );
+
+ if( type == PlotType::Memory )
+ {
+ auto& mem = m_worker.GetMemoryNamed( name );
+ const MemEvent* ev = nullptr;
+ if( change > 0 )
+ {
+ auto it = std::lower_bound( mem.data.begin(), mem.data.end(), item->time.Val(), [] ( const auto& lhs, const auto& rhs ) { return lhs.TimeAlloc() < rhs; } );
+ if( it != mem.data.end() && it->TimeAlloc() == item->time.Val() )
+ {
+ ev = it;
+ }
+ }
+ else
+ {
+ const auto& data = mem.data;
+ auto it = std::lower_bound( mem.frees.begin(), mem.frees.end(), item->time.Val(), [&data] ( const auto& lhs, const auto& rhs ) { return data[lhs].TimeFree() < rhs; } );
+ if( it != mem.frees.end() && data[*it].TimeFree() == item->time.Val() )
+ {
+ ev = &data[*it];
+ }
+ }
+ if( ev )
+ {
+ ImGui::Separator();
+ TextDisabledUnformatted( "Address:" );
+ ImGui::SameLine();
+ ImGui::Text( "0x%" PRIx64, ev->Ptr() );
+ TextFocused( "Appeared at", TimeToStringExact( ev->TimeAlloc() ) );
+ if( change > 0 )
+ {
+ ImGui::SameLine();
+ ImGui::TextDisabled( "(this event)" );
+ }
+ if( ev->TimeFree() < 0 )
+ {
+ ImGui::TextUnformatted( "Allocation still active" );
+ }
+ else
+ {
+ TextFocused( "Freed at", TimeToStringExact( ev->TimeFree() ) );
+ if( change < 0 )
+ {
+ ImGui::SameLine();
+ TextDisabledUnformatted( "(this event)" );
+ }
+ TextFocused( "Duration:", TimeToString( ev->TimeFree() - ev->TimeAlloc() ) );
+ }
+ uint64_t tid;
+ if( change > 0 )
+ {
+ tid = m_worker.DecompressThread( ev->ThreadAlloc() );
+ }
+ else
+ {
+ tid = m_worker.DecompressThread( ev->ThreadFree() );
+ }
+ 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" );
+ }
+ m_memoryAllocHover = std::distance( mem.data.begin(), ev );
+ m_memoryAllocHoverWait = 2;
+ m_memoryAllocHoverPool = name;
+ if( IsMouseClicked( 0 ) )
+ {
+ m_memoryAllocInfoWindow = m_memoryAllocHover;
+ m_memoryAllocInfoPool = name;
+ }
+ }
+ }
+ }
+ ImGui::EndTooltip();
+ }
+}
+
+}