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(); +} + +}