diff --git a/profiler/build/win32/Tracy.vcxproj b/profiler/build/win32/Tracy.vcxproj
index edd494d3..e64138f7 100644
--- a/profiler/build/win32/Tracy.vcxproj
+++ b/profiler/build/win32/Tracy.vcxproj
@@ -143,6 +143,7 @@
+
diff --git a/profiler/build/win32/Tracy.vcxproj.filters b/profiler/build/win32/Tracy.vcxproj.filters
index ef36c24a..5cfa94e8 100644
--- a/profiler/build/win32/Tracy.vcxproj.filters
+++ b/profiler/build/win32/Tracy.vcxproj.filters
@@ -270,6 +270,9 @@
server
+
+ server
+
diff --git a/server/TracyView.cpp b/server/TracyView.cpp
index 947a32f5..a4a05dd0 100644
--- a/server/TracyView.cpp
+++ b/server/TracyView.cpp
@@ -6975,870 +6975,6 @@ void View::DrawZoneList( int id, const Vector>& zones )
ImGui::TreePop();
}
-struct SrcLocZonesSlim
-{
- int16_t srcloc;
- size_t numZones;
- int64_t total;
-};
-
-void View::AccumulationModeComboBox()
-{
- ImGui::TextUnformatted( "Timing" );
- ImGui::SameLine();
- const char* accumulationModeTable = m_statMode == 1 ? "Self only\0With children\0" : "Self only\0With children\0Non-reentrant\0";
- ImGui::SetNextItemWidth( ImGui::CalcTextSize( "Non-reentrant" ).x + ImGui::GetTextLineHeight() * 2 );
- if( m_statMode == 1 && m_statAccumulationMode == AccumulationMode::NonReentrantChildren )
- {
- m_statAccumulationMode = AccumulationMode::SelfOnly;
- }
- int accumulationMode = static_cast( m_statAccumulationMode );
- ImGui::Combo( "##accumulationMode", &accumulationMode, accumulationModeTable );
- m_statAccumulationMode = static_cast( accumulationMode );
-}
-
-void View::DrawStatistics()
-{
- const auto scale = GetScale();
- ImGui::SetNextWindowSize( ImVec2( 1400 * scale, 600 * scale ), ImGuiCond_FirstUseEver );
- ImGui::Begin( "Statistics", &m_showStatistics, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse );
- if( ImGui::GetCurrentWindowRead()->SkipItems ) { ImGui::End(); return; }
-#ifdef TRACY_NO_STATISTICS
- ImGui::TextWrapped( "Collection of statistical data is disabled in this build." );
- ImGui::TextWrapped( "Rebuild without the TRACY_NO_STATISTICS macro to enable statistics view." );
-#else
- if( !m_worker.AreSourceLocationZonesReady() && ( !m_worker.AreCallstackSamplesReady() || m_worker.GetCallstackSampleCount() == 0 ) )
- {
- ImGui::TextWrapped( "Please wait, computing data..." );
- DrawWaitingDots( s_time );
- ImGui::End();
- return;
- }
-
- ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 2, 2 ) );
- ImGui::RadioButton( ICON_FA_SYRINGE " Instrumentation", &m_statMode, 0 );
- if( m_worker.AreCallstackSamplesReady() )
- {
- ImGui::SameLine();
- if( m_worker.GetCallstackSampleCount() > 0 )
- {
- ImGui::Spacing();
- ImGui::SameLine();
- ImGui::RadioButton( ICON_FA_EYE_DROPPER " Sampling", &m_statMode, 1 );
- }
- else if( m_worker.GetSymbolsCount() > 0 )
- {
- ImGui::Spacing();
- ImGui::SameLine();
- ImGui::RadioButton( ICON_FA_PUZZLE_PIECE " Symbols", &m_statMode, 1 );
- }
- }
- if( m_worker.GetGpuZoneCount() > 0 )
- {
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- ImGui::RadioButton( ICON_FA_EYE " GPU", &m_statMode, 2 );
- }
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- ImGui::SeparatorEx( ImGuiSeparatorFlags_Vertical );
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
-
-
- Vector srcloc;
-
- if( m_statMode == 0 )
- {
- if( !m_worker.AreSourceLocationZonesReady() )
- {
- ImGui::Spacing();
- ImGui::Separator();
- ImGui::PopStyleVar();
- ImGui::TextWrapped( "Please wait, computing data..." );
- DrawWaitingDots( s_time );
- ImGui::End();
- return;
- }
-
- const auto filterActive = m_statisticsFilter.IsActive();
- auto& slz = m_worker.GetSourceLocationZones();
- srcloc.reserve( slz.size() );
- uint32_t slzcnt = 0;
- if( m_statRange.active )
- {
- const auto min = m_statRange.min;
- const auto max = m_statRange.max;
- const auto st = max - min;
- for( auto it = slz.begin(); it != slz.end(); ++it )
- {
- if( it->second.total != 0 && it->second.min <= st )
- {
- if( !filterActive )
- {
- auto cit = m_statCache.find( it->first );
- if( cit != m_statCache.end() && cit->second.range == m_statRange && cit->second.accumulationMode == m_statAccumulationMode && cit->second.sourceCount == it->second.zones.size() )
- {
- if( cit->second.count != 0 )
- {
- slzcnt++;
- srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total } );
- }
- }
- else
- {
- size_t cnt = 0;
- int64_t total = 0;
- for( auto& v : it->second.zones )
- {
- auto& z = *v.Zone();
- const auto start = z.Start();
- const auto end = z.End();
- if( start >= min && end <= max )
- {
- const auto zt = end - start;
- if( m_statAccumulationMode == AccumulationMode::SelfOnly )
- {
- total += zt - GetZoneChildTimeFast( z );
- cnt++;
- }
- else if( m_statAccumulationMode == AccumulationMode::AllChildren || !IsZoneReentry( z ) )
- {
- total += zt;
- cnt++;
- }
- }
- }
- if( cnt != 0 )
- {
- slzcnt++;
- srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total } );
- }
- m_statCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total };
- }
- }
- else
- {
- slzcnt++;
- auto& sl = m_worker.GetSourceLocation( it->first );
- auto name = m_worker.GetString( sl.name.active ? sl.name : sl.function );
- if( m_statisticsFilter.PassFilter( name ) )
- {
- auto cit = m_statCache.find( it->first );
- if( cit != m_statCache.end() && cit->second.range == m_statRange && cit->second.accumulationMode == m_statAccumulationMode && cit->second.sourceCount == it->second.zones.size() )
- {
- if( cit->second.count != 0 )
- {
- srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total } );
- }
- }
- else
- {
- size_t cnt = 0;
- int64_t total = 0;
- for( auto& v : it->second.zones )
- {
- auto& z = *v.Zone();
- const auto start = z.Start();
- const auto end = z.End();
- if( start >= min && end <= max )
- {
- const auto zt = end - start;
- if( m_statAccumulationMode == AccumulationMode::SelfOnly )
- {
- total += zt - GetZoneChildTimeFast( z );
- cnt++;
- }
- else if( m_statAccumulationMode == AccumulationMode::AllChildren || !IsZoneReentry( z ) )
- {
- total += zt;
- cnt++;
- }
- }
- }
- if( cnt != 0 )
- {
- srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total } );
- }
- m_statCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total };
- }
- }
- }
- }
- }
- }
- else
- {
- for( auto it = slz.begin(); it != slz.end(); ++it )
- {
- if( it->second.total != 0 )
- {
- slzcnt++;
- size_t count;
- int64_t total;
- switch( m_statAccumulationMode )
- {
- case AccumulationMode::SelfOnly:
- count = it->second.zones.size();
- total = it->second.selfTotal;
- break;
- case AccumulationMode::AllChildren:
- count = it->second.zones.size();
- total = it->second.total;
- break;
- case AccumulationMode::NonReentrantChildren:
- count = it->second.nonReentrantCount;
- total = it->second.nonReentrantTotal;
- break;
- }
- if( !filterActive )
- {
- srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, count, total } );
- }
- else
- {
- auto& sl = m_worker.GetSourceLocation( it->first );
- auto name = m_worker.GetString( sl.name.active ? sl.name : sl.function );
- if( m_statisticsFilter.PassFilter( name ) )
- {
- srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, count, total } );
- }
- }
- }
- }
- }
-
- TextFocused( "Total zone count:", RealToString( slzcnt ) );
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- TextFocused( "Visible zones:", RealToString( srcloc.size() ) );
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- AccumulationModeComboBox();
- }
- else if( m_statMode == 1 )
- {
- ImGui::Checkbox( ICON_FA_STOPWATCH " Show time", &m_statSampleTime );
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- if( m_statRange.active )
- {
- ImGui::BeginDisabled();
- m_statAccumulationMode = AccumulationMode::SelfOnly;
- AccumulationModeComboBox();
- ImGui::EndDisabled();
- }
- else
- {
- AccumulationModeComboBox();
- }
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- ImGui::Checkbox( ICON_FA_EYE_SLASH " Hide unknown", &m_statHideUnknown );
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- ImGui::Checkbox( ICON_FA_PUZZLE_PIECE " Show all", &m_showAllSymbols );
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- ImGui::Checkbox( ICON_FA_SITEMAP " Inlines", &m_statSeparateInlines );
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- ImGui::Checkbox( ICON_FA_AT " Address", &m_statShowAddress );
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- ImGui::TextUnformatted( "Location:" );
- ImGui::SameLine();
- const char* locationTable = "Entry\0Sample\0Smart\0";
- ImGui::SetNextItemWidth( ImGui::CalcTextSize( "Sample" ).x + ImGui::GetTextLineHeight() * 2 );
- ImGui::Combo( "##location", &m_statSampleLocation, locationTable );
- }
- else
- {
- assert( m_statMode == 2 );
- if( !m_worker.AreGpuSourceLocationZonesReady() )
- {
- ImGui::Spacing();
- ImGui::Separator();
- ImGui::PopStyleVar();
- ImGui::TextWrapped( "Please wait, computing data..." );
- DrawWaitingDots( s_time );
- ImGui::End();
- return;
- }
-
- const auto filterActive = m_statisticsFilter.IsActive();
- auto& slz = m_worker.GetGpuSourceLocationZones();
- srcloc.reserve( slz.size() );
- uint32_t slzcnt = 0;
- if( m_statRange.active )
- {
- const auto min = m_statRange.min;
- const auto max = m_statRange.max;
- const auto st = max - min;
- for( auto it = slz.begin(); it != slz.end(); ++it )
- {
- if( it->second.total != 0 && it->second.min <= st )
- {
- if( !filterActive )
- {
- auto cit = m_gpuStatCache.find( it->first );
- if( cit != m_gpuStatCache.end() && cit->second.range == m_statRange && cit->second.accumulationMode == m_statAccumulationMode && cit->second.sourceCount == it->second.zones.size() )
- {
- if( cit->second.count != 0 )
- {
- slzcnt++;
- srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total } );
- }
- }
- else
- {
- size_t cnt = 0;
- int64_t total = 0;
- for( auto& v : it->second.zones )
- {
- auto& z = *v.Zone();
- const auto start = z.GpuStart();
- const auto end = z.GpuEnd();
- if( start >= min && end <= max )
- {
- const auto zt = end - start;
- total += zt;
- cnt++;
- }
- }
- if( cnt != 0 )
- {
- slzcnt++;
- srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total } );
- }
- m_gpuStatCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total };
- }
- }
- else
- {
- slzcnt++;
- auto& sl = m_worker.GetSourceLocation( it->first );
- auto name = m_worker.GetString( sl.name.active ? sl.name : sl.function );
- if( m_statisticsFilter.PassFilter( name ) )
- {
- auto cit = m_gpuStatCache.find( it->first );
- if( cit != m_gpuStatCache.end() && cit->second.range == m_statRange && cit->second.accumulationMode == m_statAccumulationMode && cit->second.sourceCount == it->second.zones.size() )
- {
- if( cit->second.count != 0 )
- {
- srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total } );
- }
- }
- else
- {
- size_t cnt = 0;
- int64_t total = 0;
- for( auto& v : it->second.zones )
- {
- auto& z = *v.Zone();
- const auto start = z.GpuStart();
- const auto end = z.GpuEnd();
- if( start >= min && end <= max )
- {
- const auto zt = end - start;
- total += zt;
- cnt++;
- }
- }
- if( cnt != 0 )
- {
- srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total } );
- }
- m_gpuStatCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total };
- }
- }
- }
- }
- }
- }
- else
- {
- for( auto it = slz.begin(); it != slz.end(); ++it )
- {
- if( it->second.total != 0 )
- {
- slzcnt++;
- size_t count = it->second.zones.size();
- int64_t total = it->second.total;
- if( !filterActive )
- {
- srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, count, total } );
- }
- else
- {
- auto& sl = m_worker.GetSourceLocation( it->first );
- auto name = m_worker.GetString( sl.name.active ? sl.name : sl.function );
- if( m_statisticsFilter.PassFilter( name ) )
- {
- srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, count, total } );
- }
- }
- }
- }
- }
-
- TextFocused( "Total zone count:", RealToString( slzcnt ) );
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- TextFocused( "Visible zones:", RealToString( srcloc.size() ) );
- }
-
- ImGui::Separator();
- ImGui::AlignTextToFramePadding();
- TextDisabledUnformatted( "Filter results" );
- ImGui::SameLine();
- m_statisticsFilter.Draw( ICON_FA_FILTER "###resultFilter", 200 );
- ImGui::SameLine();
- if( ImGui::Button( ICON_FA_BACKSPACE " Clear" ) )
- {
- m_statisticsFilter.Clear();
- }
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- if( m_statMode == 1 )
- {
- TextDisabledUnformatted( "Image name" );
- ImGui::SameLine();
- m_statisticsImageFilter.Draw( ICON_FA_FILTER "###imageFilter", 200 );
- ImGui::SameLine();
- if( ImGui::BeginCombo( "###imageCombo", nullptr, ImGuiComboFlags_NoPreview | ImGuiComboFlags_HeightLarge ) )
- {
- unordered_flat_set set;
- std::vector imgNames;
- for( auto& v : m_worker.GetSymbolMap() )
- {
- auto it = set.find( v.second.imageName );
- if( it == set.end() )
- {
- set.emplace( v.second.imageName );
- }
- }
- imgNames.reserve( set.size() );
- for( auto& img : set )
- {
- imgNames.emplace_back( m_worker.GetString( img ) );
- }
- std::sort( imgNames.begin(), imgNames.end(), [] ( const auto& lhs, const auto& rhs ) { return strcmp( lhs, rhs ) < 0; } );
- for( auto& img : imgNames )
- {
- bool sel = false;
- if( ImGui::Selectable( img, &sel ) )
- {
- auto len = std::min( 255, strlen( img ) );
- memcpy( m_statisticsImageFilter.InputBuf, img, len );
- m_statisticsImageFilter.InputBuf[len] = 0;
- m_statisticsImageFilter.Build();
- }
- }
- ImGui::EndCombo();
- }
- ImGui::SameLine();
- if( ImGui::Button( ICON_FA_BACKSPACE " Clear###image" ) )
- {
- m_statisticsImageFilter.Clear();
- }
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- ImGui::Checkbox( ICON_FA_HAT_WIZARD " Include kernel", &m_statShowKernel );
- ImGui::SameLine();
- ImGui::Spacing();
- ImGui::SameLine();
- }
- if( m_statMode == 1 && !m_worker.AreSymbolSamplesReady() )
- {
- m_statRange.active = false;
- bool val = false;
- ImGui::PushItemFlag( ImGuiItemFlags_Disabled, true );
- ImGui::PushStyleVar( ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f );
- ImGui::Checkbox( "Limit range", &val );
- ImGui::PopItemFlag();
- ImGui::PopStyleVar();
- TooltipIfHovered( "Waiting for background tasks to finish" );
- }
- else
- {
- if( ImGui::Checkbox( "Limit range", &m_statRange.active ) )
- {
- if( m_statRange.active && m_statRange.min == 0 && m_statRange.max == 0 )
- {
- m_statRange.min = m_vd.zvStart;
- m_statRange.max = m_vd.zvEnd;
- }
- }
- if( m_statRange.active )
- {
- ImGui::SameLine();
- TextColoredUnformatted( 0xFF00FFFF, ICON_FA_EXCLAMATION_TRIANGLE );
- ImGui::SameLine();
- ToggleButton( ICON_FA_RULER " Limits", m_showRanges );
- }
- }
-
- ImGui::Separator();
- ImGui::PopStyleVar();
-
- int64_t timeRange;
- if( m_statRange.active )
- {
- const auto st = m_statRange.max - m_statRange.min;
- timeRange = st == 0 ? 1 : st;
- }
- else
- {
- timeRange = m_worker.GetLastTime();
- }
-
- if( m_statMode == 0 || m_statMode == 2 )
- {
- if( srcloc.empty() )
- {
- ImGui::TextUnformatted( "No entries to be displayed." );
- }
- else
- {
- ImGui::BeginChild( "##statistics" );
- if( ImGui::BeginTable( "##statistics", 5, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_ScrollY ) )
- {
- ImGui::TableSetupScrollFreeze( 0, 1 );
- ImGui::TableSetupColumn( "Name", ImGuiTableColumnFlags_NoHide );
- ImGui::TableSetupColumn( "Location", ImGuiTableColumnFlags_NoSort );
- ImGui::TableSetupColumn( "Total time", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
- ImGui::TableSetupColumn( "Counts", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
- ImGui::TableSetupColumn( "MTPC", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
- ImGui::TableHeadersRow();
-
- const auto& sortspec = *ImGui::TableGetSortSpecs()->Specs;
- switch( sortspec.ColumnIndex )
- {
- case 0:
- if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
- {
- pdqsort_branchless( srcloc.begin(), srcloc.end(), [this]( const auto& lhs, const auto& rhs ) { return strcmp( m_worker.GetZoneName( m_worker.GetSourceLocation( lhs.srcloc ) ), m_worker.GetZoneName( m_worker.GetSourceLocation( rhs.srcloc ) ) ) < 0; } );
- }
- else
- {
- pdqsort_branchless( srcloc.begin(), srcloc.end(), [this]( const auto& lhs, const auto& rhs ) { return strcmp( m_worker.GetZoneName( m_worker.GetSourceLocation( lhs.srcloc ) ), m_worker.GetZoneName( m_worker.GetSourceLocation( rhs.srcloc ) ) ) > 0; } );
- }
- break;
- case 2:
- if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
- {
- pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total < rhs.total; } );
- }
- else
- {
- pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total > rhs.total; } );
- }
- break;
- case 3:
- if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
- {
- pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.numZones < rhs.numZones; } );
- }
- else
- {
- pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.numZones > rhs.numZones; } );
- }
- break;
- case 4:
- if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
- {
- pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total / lhs.numZones < rhs.total / rhs.numZones; } );
- }
- else
- {
- pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total / lhs.numZones > rhs.total / rhs.numZones; } );
- }
- break;
- default:
- assert( false );
- break;
- }
-
- for( auto& v : srcloc )
- {
- ImGui::TableNextRow();
- ImGui::TableNextColumn();
-
- ImGui::PushID( v.srcloc );
- auto& srcloc = m_worker.GetSourceLocation( v.srcloc );
- auto name = m_worker.GetString( srcloc.name.active ? srcloc.name : srcloc.function );
- SmallColorBox( GetSrcLocColor( srcloc, 0 ) );
- ImGui::SameLine();
- if( m_statMode == 0 )
- {
- if( ImGui::Selectable( name, m_findZone.show && !m_findZone.match.empty() && m_findZone.match[m_findZone.selMatch] == v.srcloc, ImGuiSelectableFlags_SpanAllColumns ) )
- {
- m_findZone.ShowZone( v.srcloc, name );
- }
- }
- else
- {
- ImGui::TextUnformatted( name );
- }
- ImGui::TableNextColumn();
- float indentVal = 0.f;
- if( m_statBuzzAnim.Match( v.srcloc ) )
- {
- const auto time = m_statBuzzAnim.Time();
- indentVal = sin( time * 60.f ) * 10.f * time;
- ImGui::Indent( indentVal );
- }
- const auto file = m_worker.GetString( srcloc.file );
-
- TextDisabledUnformatted( LocationToString( file, srcloc.line ) );
- if( ImGui::IsItemHovered() )
- {
- DrawSourceTooltip( file, srcloc.line );
- if( ImGui::IsItemClicked( 1 ) )
- {
- if( SourceFileValid( file, m_worker.GetCaptureTime(), *this, m_worker ) )
- {
- ViewSource( file, srcloc.line );
- }
- else
- {
- m_statBuzzAnim.Enable( v.srcloc, 0.5f );
- }
- }
- }
- if( indentVal != 0.f )
- {
- ImGui::Unindent( indentVal );
- }
- ImGui::TableNextColumn();
- const auto time = v.total;
- ImGui::TextUnformatted( TimeToString( time ) );
- ImGui::SameLine();
- char buf[64];
- PrintStringPercent( buf, 100. * time / timeRange );
- TextDisabledUnformatted( buf );
- ImGui::TableNextColumn();
- ImGui::TextUnformatted( RealToString( v.numZones ) );
- ImGui::TableNextColumn();
- ImGui::TextUnformatted( TimeToString( time / v.numZones ) );
- ImGui::PopID();
- }
- ImGui::EndTable();
- }
- ImGui::EndChild();
- }
- }
- else
- {
- assert( m_statMode == 1 );
- const auto& symMap = m_worker.GetSymbolMap();
- const auto& symStat = m_worker.GetSymbolStats();
-
- Vector data;
- if( m_showAllSymbols )
- {
- data.reserve( symMap.size() );
- if( m_statisticsFilter.IsActive() || m_statisticsImageFilter.IsActive() || !m_statShowKernel )
- {
- for( auto& v : symMap )
- {
- const auto name = m_worker.GetString( v.second.name );
- const auto image = m_worker.GetString( v.second.imageName );
- bool pass = ( m_statShowKernel || ( v.first >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( name ) && m_statisticsImageFilter.PassFilter( image );
- if( !pass && v.second.size.Val() == 0 )
- {
- const auto parentAddr = m_worker.GetSymbolForAddress( v.first );
- if( parentAddr != 0 )
- {
- auto pit = symMap.find( parentAddr );
- if( pit != symMap.end() )
- {
- const auto parentName = m_worker.GetString( pit->second.name );
- pass = ( m_statShowKernel || ( parentAddr >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( parentName ) && m_statisticsImageFilter.PassFilter( image );
- }
- }
- }
- if( pass )
- {
- auto it = symStat.find( v.first );
- if( it == symStat.end() )
- {
- data.push_back_no_space_check( SymList { v.first, 0, 0 } );
- }
- else
- {
- if( m_statRange.active )
- {
- auto samples = m_worker.GetSamplesForSymbol( v.first );
- if( samples )
- {
- auto it = std::lower_bound( samples->begin(), samples->end(), m_statRange.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
- if( it != samples->end() )
- {
- auto end = std::lower_bound( it, samples->end(), m_statRange.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
- const auto count = uint32_t( end - it );
- data.push_back_no_space_check( SymList { v.first, 0, count } );
- }
- else
- {
- data.push_back_no_space_check( SymList { v.first, 0, 0 } );
- }
- }
- else
- {
- data.push_back_no_space_check( SymList { v.first, 0, 0 } );
- }
- }
- else
- {
- data.push_back_no_space_check( SymList { v.first, it->second.incl, it->second.excl } );
- }
- }
- }
- }
- }
- else
- {
- for( auto& v : symMap )
- {
- auto it = symStat.find( v.first );
- if( it == symStat.end() )
- {
- data.push_back_no_space_check( SymList { v.first, 0, 0 } );
- }
- else
- {
- if( m_statRange.active )
- {
- auto samples = m_worker.GetSamplesForSymbol( v.first );
- if( samples )
- {
- auto it = std::lower_bound( samples->begin(), samples->end(), m_statRange.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
- if( it != samples->end() )
- {
- auto end = std::lower_bound( it, samples->end(), m_statRange.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
- const auto count = uint32_t( end - it );
- data.push_back_no_space_check( SymList { v.first, 0, count } );
- }
- else
- {
- data.push_back_no_space_check( SymList { v.first, 0, 0 } );
- }
- }
- else
- {
- data.push_back_no_space_check( SymList { v.first, 0, 0 } );
- }
- }
- else
- {
- data.push_back_no_space_check( SymList { v.first, it->second.incl, it->second.excl } );
- }
- }
- }
- }
- }
- else
- {
- data.reserve( symStat.size() );
- if( m_statisticsFilter.IsActive() || m_statisticsImageFilter.IsActive() || !m_statShowKernel )
- {
- for( auto& v : symStat )
- {
- auto sit = symMap.find( v.first );
- if( sit != symMap.end() )
- {
- const auto name = m_worker.GetString( sit->second.name );
- const auto image = m_worker.GetString( sit->second.imageName );
- bool pass = ( m_statShowKernel || ( v.first >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( name ) && m_statisticsImageFilter.PassFilter( image );
- if( !pass && sit->second.size.Val() == 0 )
- {
- const auto parentAddr = m_worker.GetSymbolForAddress( v.first );
- if( parentAddr != 0 )
- {
- auto pit = symMap.find( parentAddr );
- if( pit != symMap.end() )
- {
- const auto parentName = m_worker.GetString( pit->second.name );
- pass = ( m_statShowKernel || ( parentAddr >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( parentName ) && m_statisticsImageFilter.PassFilter( image );
- }
- }
- }
- if( pass )
- {
- if( m_statRange.active )
- {
- auto samples = m_worker.GetSamplesForSymbol( v.first );
- if( samples )
- {
- auto it = std::lower_bound( samples->begin(), samples->end(), m_statRange.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
- if( it != samples->end() )
- {
- auto end = std::lower_bound( it, samples->end(), m_statRange.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
- const auto count = uint32_t( end - it );
- data.push_back_no_space_check( SymList { v.first, 0, count } );
- }
- }
- }
- else
- {
- data.push_back_no_space_check( SymList { v.first, v.second.incl, v.second.excl } );
- }
- }
- }
- }
- }
- else
- {
- if( m_statRange.active )
- {
- for( auto& v : symStat )
- {
- auto samples = m_worker.GetSamplesForSymbol( v.first );
- if( samples )
- {
- auto it = std::lower_bound( samples->begin(), samples->end(), m_statRange.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
- if( it != samples->end() )
- {
- auto end = std::lower_bound( it, samples->end(), m_statRange.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
- const auto count = uint32_t( end - it );
- data.push_back_no_space_check( SymList { v.first, 0, count } );
- }
- }
- }
- }
- else
- {
- for( auto& v : symStat )
- {
- data.push_back_no_space_check( SymList { v.first, v.second.incl, v.second.excl } );
- }
- }
- }
- }
-
- DrawSamplesStatistics(data, timeRange, m_statAccumulationMode);
- }
-#endif
- ImGui::End();
-}
-
-
void View::DrawSamplesStatistics(Vector& data, int64_t timeRange, AccumulationMode accumulationMode)
{
static unordered_flat_map inlineMap;
diff --git a/server/TracyView_Statistics.cpp b/server/TracyView_Statistics.cpp
new file mode 100644
index 00000000..ef9ce83f
--- /dev/null
+++ b/server/TracyView_Statistics.cpp
@@ -0,0 +1,873 @@
+#include "TracyFilesystem.hpp"
+#include "TracyPrint.hpp"
+#include "TracyView.hpp"
+
+namespace tracy
+{
+
+extern double s_time;
+
+struct SrcLocZonesSlim
+{
+ int16_t srcloc;
+ size_t numZones;
+ int64_t total;
+};
+
+void View::AccumulationModeComboBox()
+{
+ ImGui::TextUnformatted( "Timing" );
+ ImGui::SameLine();
+ const char* accumulationModeTable = m_statMode == 1 ? "Self only\0With children\0" : "Self only\0With children\0Non-reentrant\0";
+ ImGui::SetNextItemWidth( ImGui::CalcTextSize( "Non-reentrant" ).x + ImGui::GetTextLineHeight() * 2 );
+ if( m_statMode == 1 && m_statAccumulationMode == AccumulationMode::NonReentrantChildren )
+ {
+ m_statAccumulationMode = AccumulationMode::SelfOnly;
+ }
+ int accumulationMode = static_cast( m_statAccumulationMode );
+ ImGui::Combo( "##accumulationMode", &accumulationMode, accumulationModeTable );
+ m_statAccumulationMode = static_cast( accumulationMode );
+}
+
+void View::DrawStatistics()
+{
+ const auto scale = GetScale();
+ ImGui::SetNextWindowSize( ImVec2( 1400 * scale, 600 * scale ), ImGuiCond_FirstUseEver );
+ ImGui::Begin( "Statistics", &m_showStatistics, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse );
+ if( ImGui::GetCurrentWindowRead()->SkipItems ) { ImGui::End(); return; }
+#ifdef TRACY_NO_STATISTICS
+ ImGui::TextWrapped( "Collection of statistical data is disabled in this build." );
+ ImGui::TextWrapped( "Rebuild without the TRACY_NO_STATISTICS macro to enable statistics view." );
+#else
+ if( !m_worker.AreSourceLocationZonesReady() && ( !m_worker.AreCallstackSamplesReady() || m_worker.GetCallstackSampleCount() == 0 ) )
+ {
+ ImGui::TextWrapped( "Please wait, computing data..." );
+ DrawWaitingDots( s_time );
+ ImGui::End();
+ return;
+ }
+
+ ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 2, 2 ) );
+ ImGui::RadioButton( ICON_FA_SYRINGE " Instrumentation", &m_statMode, 0 );
+ if( m_worker.AreCallstackSamplesReady() )
+ {
+ ImGui::SameLine();
+ if( m_worker.GetCallstackSampleCount() > 0 )
+ {
+ ImGui::Spacing();
+ ImGui::SameLine();
+ ImGui::RadioButton( ICON_FA_EYE_DROPPER " Sampling", &m_statMode, 1 );
+ }
+ else if( m_worker.GetSymbolsCount() > 0 )
+ {
+ ImGui::Spacing();
+ ImGui::SameLine();
+ ImGui::RadioButton( ICON_FA_PUZZLE_PIECE " Symbols", &m_statMode, 1 );
+ }
+ }
+ if( m_worker.GetGpuZoneCount() > 0 )
+ {
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ ImGui::RadioButton( ICON_FA_EYE " GPU", &m_statMode, 2 );
+ }
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ ImGui::SeparatorEx( ImGuiSeparatorFlags_Vertical );
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+
+
+ Vector srcloc;
+
+ if( m_statMode == 0 )
+ {
+ if( !m_worker.AreSourceLocationZonesReady() )
+ {
+ ImGui::Spacing();
+ ImGui::Separator();
+ ImGui::PopStyleVar();
+ ImGui::TextWrapped( "Please wait, computing data..." );
+ DrawWaitingDots( s_time );
+ ImGui::End();
+ return;
+ }
+
+ const auto filterActive = m_statisticsFilter.IsActive();
+ auto& slz = m_worker.GetSourceLocationZones();
+ srcloc.reserve( slz.size() );
+ uint32_t slzcnt = 0;
+ if( m_statRange.active )
+ {
+ const auto min = m_statRange.min;
+ const auto max = m_statRange.max;
+ const auto st = max - min;
+ for( auto it = slz.begin(); it != slz.end(); ++it )
+ {
+ if( it->second.total != 0 && it->second.min <= st )
+ {
+ if( !filterActive )
+ {
+ auto cit = m_statCache.find( it->first );
+ if( cit != m_statCache.end() && cit->second.range == m_statRange && cit->second.accumulationMode == m_statAccumulationMode && cit->second.sourceCount == it->second.zones.size() )
+ {
+ if( cit->second.count != 0 )
+ {
+ slzcnt++;
+ srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total } );
+ }
+ }
+ else
+ {
+ size_t cnt = 0;
+ int64_t total = 0;
+ for( auto& v : it->second.zones )
+ {
+ auto& z = *v.Zone();
+ const auto start = z.Start();
+ const auto end = z.End();
+ if( start >= min && end <= max )
+ {
+ const auto zt = end - start;
+ if( m_statAccumulationMode == AccumulationMode::SelfOnly )
+ {
+ total += zt - GetZoneChildTimeFast( z );
+ cnt++;
+ }
+ else if( m_statAccumulationMode == AccumulationMode::AllChildren || !IsZoneReentry( z ) )
+ {
+ total += zt;
+ cnt++;
+ }
+ }
+ }
+ if( cnt != 0 )
+ {
+ slzcnt++;
+ srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total } );
+ }
+ m_statCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total };
+ }
+ }
+ else
+ {
+ slzcnt++;
+ auto& sl = m_worker.GetSourceLocation( it->first );
+ auto name = m_worker.GetString( sl.name.active ? sl.name : sl.function );
+ if( m_statisticsFilter.PassFilter( name ) )
+ {
+ auto cit = m_statCache.find( it->first );
+ if( cit != m_statCache.end() && cit->second.range == m_statRange && cit->second.accumulationMode == m_statAccumulationMode && cit->second.sourceCount == it->second.zones.size() )
+ {
+ if( cit->second.count != 0 )
+ {
+ srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total } );
+ }
+ }
+ else
+ {
+ size_t cnt = 0;
+ int64_t total = 0;
+ for( auto& v : it->second.zones )
+ {
+ auto& z = *v.Zone();
+ const auto start = z.Start();
+ const auto end = z.End();
+ if( start >= min && end <= max )
+ {
+ const auto zt = end - start;
+ if( m_statAccumulationMode == AccumulationMode::SelfOnly )
+ {
+ total += zt - GetZoneChildTimeFast( z );
+ cnt++;
+ }
+ else if( m_statAccumulationMode == AccumulationMode::AllChildren || !IsZoneReentry( z ) )
+ {
+ total += zt;
+ cnt++;
+ }
+ }
+ }
+ if( cnt != 0 )
+ {
+ srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total } );
+ }
+ m_statCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total };
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for( auto it = slz.begin(); it != slz.end(); ++it )
+ {
+ if( it->second.total != 0 )
+ {
+ slzcnt++;
+ size_t count;
+ int64_t total;
+ switch( m_statAccumulationMode )
+ {
+ case AccumulationMode::SelfOnly:
+ count = it->second.zones.size();
+ total = it->second.selfTotal;
+ break;
+ case AccumulationMode::AllChildren:
+ count = it->second.zones.size();
+ total = it->second.total;
+ break;
+ case AccumulationMode::NonReentrantChildren:
+ count = it->second.nonReentrantCount;
+ total = it->second.nonReentrantTotal;
+ break;
+ }
+ if( !filterActive )
+ {
+ srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, count, total } );
+ }
+ else
+ {
+ auto& sl = m_worker.GetSourceLocation( it->first );
+ auto name = m_worker.GetString( sl.name.active ? sl.name : sl.function );
+ if( m_statisticsFilter.PassFilter( name ) )
+ {
+ srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, count, total } );
+ }
+ }
+ }
+ }
+ }
+
+ TextFocused( "Total zone count:", RealToString( slzcnt ) );
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ TextFocused( "Visible zones:", RealToString( srcloc.size() ) );
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ AccumulationModeComboBox();
+ }
+ else if( m_statMode == 1 )
+ {
+ ImGui::Checkbox( ICON_FA_STOPWATCH " Show time", &m_statSampleTime );
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ if( m_statRange.active )
+ {
+ ImGui::BeginDisabled();
+ m_statAccumulationMode = AccumulationMode::SelfOnly;
+ AccumulationModeComboBox();
+ ImGui::EndDisabled();
+ }
+ else
+ {
+ AccumulationModeComboBox();
+ }
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ ImGui::Checkbox( ICON_FA_EYE_SLASH " Hide unknown", &m_statHideUnknown );
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ ImGui::Checkbox( ICON_FA_PUZZLE_PIECE " Show all", &m_showAllSymbols );
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ ImGui::Checkbox( ICON_FA_SITEMAP " Inlines", &m_statSeparateInlines );
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ ImGui::Checkbox( ICON_FA_AT " Address", &m_statShowAddress );
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ ImGui::TextUnformatted( "Location:" );
+ ImGui::SameLine();
+ const char* locationTable = "Entry\0Sample\0Smart\0";
+ ImGui::SetNextItemWidth( ImGui::CalcTextSize( "Sample" ).x + ImGui::GetTextLineHeight() * 2 );
+ ImGui::Combo( "##location", &m_statSampleLocation, locationTable );
+ }
+ else
+ {
+ assert( m_statMode == 2 );
+ if( !m_worker.AreGpuSourceLocationZonesReady() )
+ {
+ ImGui::Spacing();
+ ImGui::Separator();
+ ImGui::PopStyleVar();
+ ImGui::TextWrapped( "Please wait, computing data..." );
+ DrawWaitingDots( s_time );
+ ImGui::End();
+ return;
+ }
+
+ const auto filterActive = m_statisticsFilter.IsActive();
+ auto& slz = m_worker.GetGpuSourceLocationZones();
+ srcloc.reserve( slz.size() );
+ uint32_t slzcnt = 0;
+ if( m_statRange.active )
+ {
+ const auto min = m_statRange.min;
+ const auto max = m_statRange.max;
+ const auto st = max - min;
+ for( auto it = slz.begin(); it != slz.end(); ++it )
+ {
+ if( it->second.total != 0 && it->second.min <= st )
+ {
+ if( !filterActive )
+ {
+ auto cit = m_gpuStatCache.find( it->first );
+ if( cit != m_gpuStatCache.end() && cit->second.range == m_statRange && cit->second.accumulationMode == m_statAccumulationMode && cit->second.sourceCount == it->second.zones.size() )
+ {
+ if( cit->second.count != 0 )
+ {
+ slzcnt++;
+ srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total } );
+ }
+ }
+ else
+ {
+ size_t cnt = 0;
+ int64_t total = 0;
+ for( auto& v : it->second.zones )
+ {
+ auto& z = *v.Zone();
+ const auto start = z.GpuStart();
+ const auto end = z.GpuEnd();
+ if( start >= min && end <= max )
+ {
+ const auto zt = end - start;
+ total += zt;
+ cnt++;
+ }
+ }
+ if( cnt != 0 )
+ {
+ slzcnt++;
+ srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total } );
+ }
+ m_gpuStatCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total };
+ }
+ }
+ else
+ {
+ slzcnt++;
+ auto& sl = m_worker.GetSourceLocation( it->first );
+ auto name = m_worker.GetString( sl.name.active ? sl.name : sl.function );
+ if( m_statisticsFilter.PassFilter( name ) )
+ {
+ auto cit = m_gpuStatCache.find( it->first );
+ if( cit != m_gpuStatCache.end() && cit->second.range == m_statRange && cit->second.accumulationMode == m_statAccumulationMode && cit->second.sourceCount == it->second.zones.size() )
+ {
+ if( cit->second.count != 0 )
+ {
+ srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cit->second.count, cit->second.total } );
+ }
+ }
+ else
+ {
+ size_t cnt = 0;
+ int64_t total = 0;
+ for( auto& v : it->second.zones )
+ {
+ auto& z = *v.Zone();
+ const auto start = z.GpuStart();
+ const auto end = z.GpuEnd();
+ if( start >= min && end <= max )
+ {
+ const auto zt = end - start;
+ total += zt;
+ cnt++;
+ }
+ }
+ if( cnt != 0 )
+ {
+ srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, cnt, total } );
+ }
+ m_gpuStatCache[it->first] = StatisticsCache { RangeSlim { m_statRange.min, m_statRange.max, m_statRange.active }, m_statAccumulationMode, it->second.zones.size(), cnt, total };
+ }
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for( auto it = slz.begin(); it != slz.end(); ++it )
+ {
+ if( it->second.total != 0 )
+ {
+ slzcnt++;
+ size_t count = it->second.zones.size();
+ int64_t total = it->second.total;
+ if( !filterActive )
+ {
+ srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, count, total } );
+ }
+ else
+ {
+ auto& sl = m_worker.GetSourceLocation( it->first );
+ auto name = m_worker.GetString( sl.name.active ? sl.name : sl.function );
+ if( m_statisticsFilter.PassFilter( name ) )
+ {
+ srcloc.push_back_no_space_check( SrcLocZonesSlim { it->first, count, total } );
+ }
+ }
+ }
+ }
+ }
+
+ TextFocused( "Total zone count:", RealToString( slzcnt ) );
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ TextFocused( "Visible zones:", RealToString( srcloc.size() ) );
+ }
+
+ ImGui::Separator();
+ ImGui::AlignTextToFramePadding();
+ TextDisabledUnformatted( "Filter results" );
+ ImGui::SameLine();
+ m_statisticsFilter.Draw( ICON_FA_FILTER "###resultFilter", 200 );
+ ImGui::SameLine();
+ if( ImGui::Button( ICON_FA_BACKSPACE " Clear" ) )
+ {
+ m_statisticsFilter.Clear();
+ }
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ if( m_statMode == 1 )
+ {
+ TextDisabledUnformatted( "Image name" );
+ ImGui::SameLine();
+ m_statisticsImageFilter.Draw( ICON_FA_FILTER "###imageFilter", 200 );
+ ImGui::SameLine();
+ if( ImGui::BeginCombo( "###imageCombo", nullptr, ImGuiComboFlags_NoPreview | ImGuiComboFlags_HeightLarge ) )
+ {
+ unordered_flat_set set;
+ std::vector imgNames;
+ for( auto& v : m_worker.GetSymbolMap() )
+ {
+ auto it = set.find( v.second.imageName );
+ if( it == set.end() )
+ {
+ set.emplace( v.second.imageName );
+ }
+ }
+ imgNames.reserve( set.size() );
+ for( auto& img : set )
+ {
+ imgNames.emplace_back( m_worker.GetString( img ) );
+ }
+ std::sort( imgNames.begin(), imgNames.end(), [] ( const auto& lhs, const auto& rhs ) { return strcmp( lhs, rhs ) < 0; } );
+ for( auto& img : imgNames )
+ {
+ bool sel = false;
+ if( ImGui::Selectable( img, &sel ) )
+ {
+ auto len = std::min( 255, strlen( img ) );
+ memcpy( m_statisticsImageFilter.InputBuf, img, len );
+ m_statisticsImageFilter.InputBuf[len] = 0;
+ m_statisticsImageFilter.Build();
+ }
+ }
+ ImGui::EndCombo();
+ }
+ ImGui::SameLine();
+ if( ImGui::Button( ICON_FA_BACKSPACE " Clear###image" ) )
+ {
+ m_statisticsImageFilter.Clear();
+ }
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ ImGui::Checkbox( ICON_FA_HAT_WIZARD " Include kernel", &m_statShowKernel );
+ ImGui::SameLine();
+ ImGui::Spacing();
+ ImGui::SameLine();
+ }
+ if( m_statMode == 1 && !m_worker.AreSymbolSamplesReady() )
+ {
+ m_statRange.active = false;
+ bool val = false;
+ ImGui::PushItemFlag( ImGuiItemFlags_Disabled, true );
+ ImGui::PushStyleVar( ImGuiStyleVar_Alpha, ImGui::GetStyle().Alpha * 0.5f );
+ ImGui::Checkbox( "Limit range", &val );
+ ImGui::PopItemFlag();
+ ImGui::PopStyleVar();
+ TooltipIfHovered( "Waiting for background tasks to finish" );
+ }
+ else
+ {
+ if( ImGui::Checkbox( "Limit range", &m_statRange.active ) )
+ {
+ if( m_statRange.active && m_statRange.min == 0 && m_statRange.max == 0 )
+ {
+ m_statRange.min = m_vd.zvStart;
+ m_statRange.max = m_vd.zvEnd;
+ }
+ }
+ if( m_statRange.active )
+ {
+ ImGui::SameLine();
+ TextColoredUnformatted( 0xFF00FFFF, ICON_FA_EXCLAMATION_TRIANGLE );
+ ImGui::SameLine();
+ ToggleButton( ICON_FA_RULER " Limits", m_showRanges );
+ }
+ }
+
+ ImGui::Separator();
+ ImGui::PopStyleVar();
+
+ int64_t timeRange;
+ if( m_statRange.active )
+ {
+ const auto st = m_statRange.max - m_statRange.min;
+ timeRange = st == 0 ? 1 : st;
+ }
+ else
+ {
+ timeRange = m_worker.GetLastTime();
+ }
+
+ if( m_statMode == 0 || m_statMode == 2 )
+ {
+ if( srcloc.empty() )
+ {
+ ImGui::TextUnformatted( "No entries to be displayed." );
+ }
+ else
+ {
+ ImGui::BeginChild( "##statistics" );
+ if( ImGui::BeginTable( "##statistics", 5, ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable | ImGuiTableFlags_Hideable | ImGuiTableFlags_Sortable | ImGuiTableFlags_BordersInnerV | ImGuiTableFlags_ScrollY ) )
+ {
+ ImGui::TableSetupScrollFreeze( 0, 1 );
+ ImGui::TableSetupColumn( "Name", ImGuiTableColumnFlags_NoHide );
+ ImGui::TableSetupColumn( "Location", ImGuiTableColumnFlags_NoSort );
+ ImGui::TableSetupColumn( "Total time", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
+ ImGui::TableSetupColumn( "Counts", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
+ ImGui::TableSetupColumn( "MTPC", ImGuiTableColumnFlags_PreferSortDescending | ImGuiTableColumnFlags_WidthFixed | ImGuiTableColumnFlags_NoResize );
+ ImGui::TableHeadersRow();
+
+ const auto& sortspec = *ImGui::TableGetSortSpecs()->Specs;
+ switch( sortspec.ColumnIndex )
+ {
+ case 0:
+ if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
+ {
+ pdqsort_branchless( srcloc.begin(), srcloc.end(), [this]( const auto& lhs, const auto& rhs ) { return strcmp( m_worker.GetZoneName( m_worker.GetSourceLocation( lhs.srcloc ) ), m_worker.GetZoneName( m_worker.GetSourceLocation( rhs.srcloc ) ) ) < 0; } );
+ }
+ else
+ {
+ pdqsort_branchless( srcloc.begin(), srcloc.end(), [this]( const auto& lhs, const auto& rhs ) { return strcmp( m_worker.GetZoneName( m_worker.GetSourceLocation( lhs.srcloc ) ), m_worker.GetZoneName( m_worker.GetSourceLocation( rhs.srcloc ) ) ) > 0; } );
+ }
+ break;
+ case 2:
+ if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
+ {
+ pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total < rhs.total; } );
+ }
+ else
+ {
+ pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total > rhs.total; } );
+ }
+ break;
+ case 3:
+ if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
+ {
+ pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.numZones < rhs.numZones; } );
+ }
+ else
+ {
+ pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.numZones > rhs.numZones; } );
+ }
+ break;
+ case 4:
+ if( sortspec.SortDirection == ImGuiSortDirection_Ascending )
+ {
+ pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total / lhs.numZones < rhs.total / rhs.numZones; } );
+ }
+ else
+ {
+ pdqsort_branchless( srcloc.begin(), srcloc.end(), []( const auto& lhs, const auto& rhs ) { return lhs.total / lhs.numZones > rhs.total / rhs.numZones; } );
+ }
+ break;
+ default:
+ assert( false );
+ break;
+ }
+
+ for( auto& v : srcloc )
+ {
+ ImGui::TableNextRow();
+ ImGui::TableNextColumn();
+
+ ImGui::PushID( v.srcloc );
+ auto& srcloc = m_worker.GetSourceLocation( v.srcloc );
+ auto name = m_worker.GetString( srcloc.name.active ? srcloc.name : srcloc.function );
+ SmallColorBox( GetSrcLocColor( srcloc, 0 ) );
+ ImGui::SameLine();
+ if( m_statMode == 0 )
+ {
+ if( ImGui::Selectable( name, m_findZone.show && !m_findZone.match.empty() && m_findZone.match[m_findZone.selMatch] == v.srcloc, ImGuiSelectableFlags_SpanAllColumns ) )
+ {
+ m_findZone.ShowZone( v.srcloc, name );
+ }
+ }
+ else
+ {
+ ImGui::TextUnformatted( name );
+ }
+ ImGui::TableNextColumn();
+ float indentVal = 0.f;
+ if( m_statBuzzAnim.Match( v.srcloc ) )
+ {
+ const auto time = m_statBuzzAnim.Time();
+ indentVal = sin( time * 60.f ) * 10.f * time;
+ ImGui::Indent( indentVal );
+ }
+ const auto file = m_worker.GetString( srcloc.file );
+
+ TextDisabledUnformatted( LocationToString( file, srcloc.line ) );
+ if( ImGui::IsItemHovered() )
+ {
+ DrawSourceTooltip( file, srcloc.line );
+ if( ImGui::IsItemClicked( 1 ) )
+ {
+ if( SourceFileValid( file, m_worker.GetCaptureTime(), *this, m_worker ) )
+ {
+ ViewSource( file, srcloc.line );
+ }
+ else
+ {
+ m_statBuzzAnim.Enable( v.srcloc, 0.5f );
+ }
+ }
+ }
+ if( indentVal != 0.f )
+ {
+ ImGui::Unindent( indentVal );
+ }
+ ImGui::TableNextColumn();
+ const auto time = v.total;
+ ImGui::TextUnformatted( TimeToString( time ) );
+ ImGui::SameLine();
+ char buf[64];
+ PrintStringPercent( buf, 100. * time / timeRange );
+ TextDisabledUnformatted( buf );
+ ImGui::TableNextColumn();
+ ImGui::TextUnformatted( RealToString( v.numZones ) );
+ ImGui::TableNextColumn();
+ ImGui::TextUnformatted( TimeToString( time / v.numZones ) );
+ ImGui::PopID();
+ }
+ ImGui::EndTable();
+ }
+ ImGui::EndChild();
+ }
+ }
+ else
+ {
+ assert( m_statMode == 1 );
+ const auto& symMap = m_worker.GetSymbolMap();
+ const auto& symStat = m_worker.GetSymbolStats();
+
+ Vector data;
+ if( m_showAllSymbols )
+ {
+ data.reserve( symMap.size() );
+ if( m_statisticsFilter.IsActive() || m_statisticsImageFilter.IsActive() || !m_statShowKernel )
+ {
+ for( auto& v : symMap )
+ {
+ const auto name = m_worker.GetString( v.second.name );
+ const auto image = m_worker.GetString( v.second.imageName );
+ bool pass = ( m_statShowKernel || ( v.first >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( name ) && m_statisticsImageFilter.PassFilter( image );
+ if( !pass && v.second.size.Val() == 0 )
+ {
+ const auto parentAddr = m_worker.GetSymbolForAddress( v.first );
+ if( parentAddr != 0 )
+ {
+ auto pit = symMap.find( parentAddr );
+ if( pit != symMap.end() )
+ {
+ const auto parentName = m_worker.GetString( pit->second.name );
+ pass = ( m_statShowKernel || ( parentAddr >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( parentName ) && m_statisticsImageFilter.PassFilter( image );
+ }
+ }
+ }
+ if( pass )
+ {
+ auto it = symStat.find( v.first );
+ if( it == symStat.end() )
+ {
+ data.push_back_no_space_check( SymList { v.first, 0, 0 } );
+ }
+ else
+ {
+ if( m_statRange.active )
+ {
+ auto samples = m_worker.GetSamplesForSymbol( v.first );
+ if( samples )
+ {
+ auto it = std::lower_bound( samples->begin(), samples->end(), m_statRange.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
+ if( it != samples->end() )
+ {
+ auto end = std::lower_bound( it, samples->end(), m_statRange.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
+ const auto count = uint32_t( end - it );
+ data.push_back_no_space_check( SymList { v.first, 0, count } );
+ }
+ else
+ {
+ data.push_back_no_space_check( SymList { v.first, 0, 0 } );
+ }
+ }
+ else
+ {
+ data.push_back_no_space_check( SymList { v.first, 0, 0 } );
+ }
+ }
+ else
+ {
+ data.push_back_no_space_check( SymList { v.first, it->second.incl, it->second.excl } );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ for( auto& v : symMap )
+ {
+ auto it = symStat.find( v.first );
+ if( it == symStat.end() )
+ {
+ data.push_back_no_space_check( SymList { v.first, 0, 0 } );
+ }
+ else
+ {
+ if( m_statRange.active )
+ {
+ auto samples = m_worker.GetSamplesForSymbol( v.first );
+ if( samples )
+ {
+ auto it = std::lower_bound( samples->begin(), samples->end(), m_statRange.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
+ if( it != samples->end() )
+ {
+ auto end = std::lower_bound( it, samples->end(), m_statRange.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
+ const auto count = uint32_t( end - it );
+ data.push_back_no_space_check( SymList { v.first, 0, count } );
+ }
+ else
+ {
+ data.push_back_no_space_check( SymList { v.first, 0, 0 } );
+ }
+ }
+ else
+ {
+ data.push_back_no_space_check( SymList { v.first, 0, 0 } );
+ }
+ }
+ else
+ {
+ data.push_back_no_space_check( SymList { v.first, it->second.incl, it->second.excl } );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ data.reserve( symStat.size() );
+ if( m_statisticsFilter.IsActive() || m_statisticsImageFilter.IsActive() || !m_statShowKernel )
+ {
+ for( auto& v : symStat )
+ {
+ auto sit = symMap.find( v.first );
+ if( sit != symMap.end() )
+ {
+ const auto name = m_worker.GetString( sit->second.name );
+ const auto image = m_worker.GetString( sit->second.imageName );
+ bool pass = ( m_statShowKernel || ( v.first >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( name ) && m_statisticsImageFilter.PassFilter( image );
+ if( !pass && sit->second.size.Val() == 0 )
+ {
+ const auto parentAddr = m_worker.GetSymbolForAddress( v.first );
+ if( parentAddr != 0 )
+ {
+ auto pit = symMap.find( parentAddr );
+ if( pit != symMap.end() )
+ {
+ const auto parentName = m_worker.GetString( pit->second.name );
+ pass = ( m_statShowKernel || ( parentAddr >> 63 ) == 0 ) && m_statisticsFilter.PassFilter( parentName ) && m_statisticsImageFilter.PassFilter( image );
+ }
+ }
+ }
+ if( pass )
+ {
+ if( m_statRange.active )
+ {
+ auto samples = m_worker.GetSamplesForSymbol( v.first );
+ if( samples )
+ {
+ auto it = std::lower_bound( samples->begin(), samples->end(), m_statRange.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
+ if( it != samples->end() )
+ {
+ auto end = std::lower_bound( it, samples->end(), m_statRange.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
+ const auto count = uint32_t( end - it );
+ data.push_back_no_space_check( SymList { v.first, 0, count } );
+ }
+ }
+ }
+ else
+ {
+ data.push_back_no_space_check( SymList { v.first, v.second.incl, v.second.excl } );
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ if( m_statRange.active )
+ {
+ for( auto& v : symStat )
+ {
+ auto samples = m_worker.GetSamplesForSymbol( v.first );
+ if( samples )
+ {
+ auto it = std::lower_bound( samples->begin(), samples->end(), m_statRange.min, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
+ if( it != samples->end() )
+ {
+ auto end = std::lower_bound( it, samples->end(), m_statRange.max, [] ( const auto& lhs, const auto& rhs ) { return lhs.time.Val() < rhs; } );
+ const auto count = uint32_t( end - it );
+ data.push_back_no_space_check( SymList { v.first, 0, count } );
+ }
+ }
+ }
+ }
+ else
+ {
+ for( auto& v : symStat )
+ {
+ data.push_back_no_space_check( SymList { v.first, v.second.incl, v.second.excl } );
+ }
+ }
+ }
+ }
+
+ DrawSamplesStatistics(data, timeRange, m_statAccumulationMode);
+ }
+#endif
+ ImGui::End();
+}
+
+}