From f0fce23bb65b8a6c14cab099a1e2d60949125b50 Mon Sep 17 00:00:00 2001 From: Bartosz Taudul Date: Thu, 20 Aug 2026 22:41:27 +0200 Subject: [PATCH] Allow sorting child zones by name in the zone info window. The child zone list was always sorted by time. It is now a sortable table with Zone and Time columns, like the statistics view and the time distribution table in the same window: Time is the default sort (descending, as before), Zone sorts by name with time as the tiebreak, and groups plus their members follow the same selection. The sort choice persists across zones and the group toggle. Group and child names are looked up once per draw only when name-sorting. --- profiler/src/profiler/TracyView_ZoneInfo.cpp | 310 +++++++++++++------ 1 file changed, 217 insertions(+), 93 deletions(-) diff --git a/profiler/src/profiler/TracyView_ZoneInfo.cpp b/profiler/src/profiler/TracyView_ZoneInfo.cpp index 60deca0d..0a6526d6 100644 --- a/profiler/src/profiler/TracyView_ZoneInfo.cpp +++ b/profiler/src/profiler/TracyView_ZoneInfo.cpp @@ -28,6 +28,80 @@ inline uint32_t GetZoneCallstack( const GpuEvent& ev, const Worker& wo return ev.callstack.Val(); } +static void SortChildIndices( uint32_t* cti, size_t count, const uint64_t* ctt, const Vector* ctn, const ImGuiTableColumnSortSpecs& sortspec ) +{ + const bool asc = sortspec.SortDirection == ImGuiSortDirection_Ascending; + if( sortspec.ColumnIndex == 0 ) + { + const Vector& names = *ctn; + if( asc ) + { + pdqsort_branchless( cti, cti + count, [&names, ctt]( const auto& lhs, const auto& rhs ) { + const int cmp = strcmp( names[lhs], names[rhs] ); + if( cmp != 0 ) return cmp < 0; + return ctt[lhs] > ctt[rhs]; + } ); + } + else + { + pdqsort_branchless( cti, cti + count, [&names, ctt]( const auto& lhs, const auto& rhs ) { + const int cmp = strcmp( names[lhs], names[rhs] ); + if( cmp != 0 ) return cmp > 0; + return ctt[lhs] > ctt[rhs]; + } ); + } + } + else if( asc ) + { + pdqsort_branchless( cti, cti + count, [ctt]( const auto& lhs, const auto& rhs ) { return ctt[lhs] < ctt[rhs]; } ); + } + else + { + pdqsort_branchless( cti, cti + count, [ctt]( const auto& lhs, const auto& rhs ) { return ctt[lhs] > ctt[rhs]; } ); + } +} + +struct ChildGroup +{ + int16_t srcloc; + uint64_t t; + Vector v; + const char* name; +}; + +static void SortChildGroups( Vector& cgvec, const ImGuiTableColumnSortSpecs& sortspec, const Worker& worker ) +{ + const bool asc = sortspec.SortDirection == ImGuiSortDirection_Ascending; + if( sortspec.ColumnIndex == 0 ) + { + for( auto& it : cgvec ) it->name = worker.GetZoneName( worker.GetSourceLocation( it->srcloc ) ); + if( asc ) + { + pdqsort_branchless( cgvec.begin(), cgvec.end(), []( const auto& lhs, const auto& rhs ) { + const int cmp = strcmp( lhs->name, rhs->name ); + if( cmp != 0 ) return cmp < 0; + return lhs->t > rhs->t; + } ); + } + else + { + pdqsort_branchless( cgvec.begin(), cgvec.end(), []( const auto& lhs, const auto& rhs ) { + const int cmp = strcmp( lhs->name, rhs->name ); + if( cmp != 0 ) return cmp > 0; + return lhs->t > rhs->t; + } ); + } + } + else if( asc ) + { + pdqsort_branchless( cgvec.begin(), cgvec.end(), []( const auto& lhs, const auto& rhs ) { return lhs->t < rhs->t; } ); + } + else + { + pdqsort_branchless( cgvec.begin(), cgvec.end(), []( const auto& lhs, const auto& rhs ) { return lhs->t > rhs->t; } ); + } +} + void View::CalcZoneTimeData( unordered_flat_map& data, int64_t& ztime, const ZoneEvent& zone ) { assert( zone.HasChildren() ); @@ -1145,12 +1219,6 @@ void View::DrawZoneInfoChildren( const V& children, int64_t ztime ) if( m_groupChildrenLocations ) { - struct ChildGroup - { - int16_t srcloc; - uint64_t t; - Vector v; - }; uint64_t ctime = 0; unordered_flat_map cmap; cmap.reserve( 128 ); @@ -1178,23 +1246,30 @@ void View::DrawZoneInfoChildren( const V& children, int64_t ztime ) cgvec[idx++] = &it.second; } - pdqsort_branchless( cgvec.begin(), cgvec.end(), []( const auto& lhs, const auto& rhs ) { return lhs->t > rhs->t; } ); + if( !ImGui::BeginTable( "##childzones", 2, ImGuiTableFlags_Sortable | ImGuiTableFlags_BordersInnerV ) ) return; + ImGui::TableSetupColumn( "Zone" ); + ImGui::TableSetupColumn( "Time", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_PreferSortDescending ); + ImGui::TableHeadersRow(); - ImGui::Columns( 2 ); - ImGui::Indent( ImGui::GetTreeNodeToLabelSpacing() * 2 ); + const auto& sortspec = *ImGui::TableGetSortSpecs()->Specs; + SortChildGroups( cgvec, sortspec, m_worker ); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); TextColoredUnformatted( ImVec4( 1.0f, 1.0f, 0.4f, 1.0f ), "Self time" ); - ImGui::Unindent( ImGui::GetTreeNodeToLabelSpacing() * 2 ); - ImGui::NextColumn(); + ImGui::TableNextColumn(); char buf[128]; PrintStringPercent( buf, TimeToString( ztime - ctime ), double( ztime - ctime ) / ztime * 100 ); ImGui::ProgressBar( double( ztime - ctime ) * rztime, ImVec2( -1, ty ), buf ); - ImGui::NextColumn(); + for( size_t i=0; i( new uint64_t[cgr.v.size()] ); @@ -1259,7 +1333,13 @@ void View::DrawZoneInfoChildren( const V& children, int64_t ztime ) cti[i] = uint32_t( i ); } - pdqsort_branchless( cti.get(), cti.get() + cgr.v.size(), [&ctt] ( const auto& lhs, const auto& rhs ) { return ctt[lhs] > ctt[rhs]; } ); + Vector ctn; + if( sortspec.ColumnIndex == 0 ) + { + ctn.reserve_and_use( cgr.v.size() ); + for( size_t i=0; i ctt[rhs]; } ); + if( !ImGui::BeginTable( "##childzones", 2, ImGuiTableFlags_Sortable | ImGuiTableFlags_BordersInnerV ) ) return; + ImGui::TableSetupColumn( "Zone" ); + ImGui::TableSetupColumn( "Time", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_PreferSortDescending ); + ImGui::TableHeadersRow(); - ImGui::Columns( 2 ); - ImGui::Indent( ImGui::GetTreeNodeToLabelSpacing() ); + const auto& sortspec = *ImGui::TableGetSortSpecs()->Specs; + Vector ctn; + if( sortspec.ColumnIndex == 0 ) + { + ctn.reserve_and_use( children.size() ); + for( size_t i=0; i v; - }; uint64_t ctime = 0; unordered_flat_map cmap; cmap.reserve( 128 ); @@ -1661,23 +1748,30 @@ void View::DrawGpuInfoChildren( const V& children, int64_t ztime ) cgvec[idx++] = &it.second; } - pdqsort_branchless( cgvec.begin(), cgvec.end(), []( const auto& lhs, const auto& rhs ) { return lhs->t > rhs->t; } ); + if( !ImGui::BeginTable( "##childzones", 2, ImGuiTableFlags_Sortable | ImGuiTableFlags_BordersInnerV ) ) return; + ImGui::TableSetupColumn( "Zone" ); + ImGui::TableSetupColumn( "Time", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_PreferSortDescending ); + ImGui::TableHeadersRow(); - ImGui::Columns( 2 ); - ImGui::Indent( ImGui::GetTreeNodeToLabelSpacing() ); + const auto& sortspec = *ImGui::TableGetSortSpecs()->Specs; + SortChildGroups( cgvec, sortspec, m_worker ); + + ImGui::TableNextRow(); + ImGui::TableNextColumn(); TextColoredUnformatted( ImVec4( 1.0f, 1.0f, 0.4f, 1.0f ), "Self time" ); - ImGui::Unindent( ImGui::GetTreeNodeToLabelSpacing() ); - ImGui::NextColumn(); + ImGui::TableNextColumn(); char buf[128]; PrintStringPercent( buf, TimeToString( ztime - ctime ), double( ztime - ctime ) / ztime * 100 ); ImGui::ProgressBar( double( ztime - ctime ) * rztime, ImVec2( -1, ty ), buf ); - ImGui::NextColumn(); + for( size_t i=0; i( new uint64_t[cgr.v.size()] ); @@ -1738,41 +1831,53 @@ void View::DrawGpuInfoChildren( const V& children, int64_t ztime ) cti[i] = uint32_t( i ); } - pdqsort_branchless( cti.get(), cti.get() + cgr.v.size(), [&ctt] ( const auto& lhs, const auto& rhs ) { return ctt[lhs] > ctt[rhs]; } ); - - for( size_t i=0; i ctn; + if( sortspec.ColumnIndex == 0 ) { - auto& cev = a(children[cgr.v[cti[i]]]); - const auto txt = m_worker.GetZoneName( cev ); - bool b = false; - ImGui::Indent(); - ImGui::PushID( (int)cgr.v[cti[i]] ); - if( ImGui::Selectable( txt, &b, ImGuiSelectableFlags_SpanAllColumns ) ) + ctn.reserve_and_use( cgr.v.size() ); + for( size_t i=0; i ctt[rhs]; } ); + if( !ImGui::BeginTable( "##childzones", 2, ImGuiTableFlags_Sortable | ImGuiTableFlags_BordersInnerV ) ) return; + ImGui::TableSetupColumn( "Zone" ); + ImGui::TableSetupColumn( "Time", ImGuiTableColumnFlags_DefaultSort | ImGuiTableColumnFlags_PreferSortDescending ); + ImGui::TableHeadersRow(); - ImGui::Columns( 2 ); + const auto& sortspec = *ImGui::TableGetSortSpecs()->Specs; + Vector ctn; + if( sortspec.ColumnIndex == 0 ) + { + ctn.reserve_and_use( children.size() ); + for( size_t i=0; i