diff --git a/server/TracyView_Compare.cpp b/server/TracyView_Compare.cpp index e64337c3..3621e2c8 100644 --- a/server/TracyView_Compare.cpp +++ b/server/TracyView_Compare.cpp @@ -243,829 +243,839 @@ void View::DrawCompare() ImGui::RadioButton( "Zones", &m_compare.compareMode, 0 ); ImGui::SameLine(); ImGui::RadioButton( "Frames", &m_compare.compareMode, 1 ); + ImGui::SameLine(); + ImGui::RadioButton( "Source diff", &m_compare.compareMode, 2 ); if( oldMode != m_compare.compareMode ) { m_compare.Reset(); } - bool findClicked = false; - - if( m_compare.compareMode == 0 ) + if( m_compare.compareMode == 2 ) { - ImGui::PushItemWidth( -0.01f ); - findClicked |= ImGui::InputTextWithHint( "###compare", "Enter zone name to search for", m_compare.pattern, 1024, ImGuiInputTextFlags_EnterReturnsTrue ); - ImGui::PopItemWidth(); - - findClicked |= ImGui::Button( ICON_FA_MAGNIFYING_GLASS " Find" ); - ImGui::SameLine(); - - if( ImGui::Button( ICON_FA_BAN " Clear" ) ) - { - m_compare.Reset(); - } - ImGui::SameLine(); - ImGui::Checkbox( "Ignore case", &m_compare.ignoreCase ); - - if( findClicked ) - { - m_compare.Reset(); - FindZonesCompare(); - } - - if( m_compare.match[0].empty() && m_compare.match[1].empty() ) - { - ImGui::End(); - return; - } - ImGui::Separator(); ImGui::BeginChild( "##compare" ); - - if( ImGui::TreeNodeEx( "Matched source locations", ImGuiTreeNodeFlags_DefaultOpen ) ) - { - ImGui::SameLine(); - SmallCheckbox( "Link selection", &m_compare.link ); - - ImGui::Separator(); - ImGui::Columns( 2 ); - TextColoredUnformatted( ImVec4( 0xDD/255.f, 0xDD/255.f, 0x22/255.f, 1.f ), ICON_FA_LEMON ); - ImGui::SameLine(); - ImGui::TextUnformatted( "This trace" ); - ImGui::SameLine(); - ImGui::TextDisabled( "(%zu)", m_compare.match[0].size() ); - ImGui::NextColumn(); - TextColoredUnformatted( ImVec4( 0xDD/255.f, 0x22/255.f, 0x22/255.f, 1.f ), ICON_FA_GEM ); - ImGui::SameLine(); - ImGui::TextUnformatted( "External trace" ); - ImGui::SameLine(); - ImGui::TextDisabled( "(%zu)", m_compare.match[1].size() ); - ImGui::Separator(); - ImGui::NextColumn(); - - const auto prev0 = m_compare.selMatch[0]; - int idx = 0; - for( auto& v : m_compare.match[0] ) - { - auto& srcloc = m_worker.GetSourceLocation( v ); - auto& zones = m_worker.GetZonesForSourceLocation( v ).zones; - SmallColorBox( GetSrcLocColor( srcloc, 0 ) ); - ImGui::SameLine(); - ImGui::PushID( idx ); - ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) ); - ImGui::RadioButton( m_worker.GetString( srcloc.name.active ? srcloc.name : srcloc.function ), &m_compare.selMatch[0], idx++ ); - ImGui::PopStyleVar(); - ImGui::SameLine(); - ImGui::TextColored( ImVec4( 0.5, 0.5, 0.5, 1 ), "(%s) %s", RealToString( zones.size() ), LocationToString( m_worker.GetString( srcloc.file ), srcloc.line ) ); - ImGui::PopID(); - } - ImGui::NextColumn(); - - const auto prev1 = m_compare.selMatch[1]; - idx = 0; - for( auto& v : m_compare.match[1] ) - { - auto& srcloc = m_compare.second->GetSourceLocation( v ); - auto& zones = m_compare.second->GetZonesForSourceLocation( v ).zones; - ImGui::PushID( -1 - idx ); - ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) ); - ImGui::RadioButton( m_compare.second->GetString( srcloc.name.active ? srcloc.name : srcloc.function ), &m_compare.selMatch[1], idx++ ); - ImGui::PopStyleVar(); - ImGui::SameLine(); - ImGui::TextColored( ImVec4( 0.5, 0.5, 0.5, 1 ), "(%s) %s", RealToString( zones.size() ), LocationToString( m_compare.second->GetString( srcloc.file ), srcloc.line ) ); - ImGui::PopID(); - } - ImGui::NextColumn(); - ImGui::EndColumns(); - ImGui::TreePop(); - - if( prev0 != m_compare.selMatch[0] || prev1 != m_compare.selMatch[1] ) - { - m_compare.ResetSelection(); - - if( m_compare.link ) - { - if( !FindMatchingZone( prev0, prev1, FindMatchingZoneFlagSourceFile | FindMatchingZoneFlagLineNum ) ) - { - if( !FindMatchingZone( prev0, prev1, FindMatchingZoneFlagSourceFile ) ) - { - FindMatchingZone( prev0, prev1, FindMatchingZoneFlagDefault ); - } - } - } - } - } - - if( m_compare.match[0].empty() || m_compare.match[1].empty() ) - { - ImGui::Separator(); - ImGui::TextWrapped( "Both traces must have matches." ); - ImGui::End(); - return; - } } else { - assert( m_compare.compareMode == 1 ); - - ImGui::Separator(); - ImGui::BeginChild( "##compare" ); - if( ImGui::TreeNodeEx( "Frame sets", ImGuiTreeNodeFlags_DefaultOpen ) ) - { - const auto& f0 = m_worker.GetFrames(); - const auto& f1 = m_compare.second->GetFrames(); - - ImGui::SameLine(); - SmallCheckbox( "Link selection", &m_compare.link ); - - ImGui::Separator(); - ImGui::Columns( 2 ); - TextColoredUnformatted( ImVec4( 0xDD/255.f, 0xDD/255.f, 0x22/255.f, 1.f ), ICON_FA_LEMON ); - ImGui::SameLine(); - ImGui::TextUnformatted( "This trace" ); - ImGui::SameLine(); - ImGui::TextDisabled( "(%zu)", f0.size() ); - ImGui::NextColumn(); - TextColoredUnformatted( ImVec4( 0xDD/255.f, 0x22/255.f, 0x22/255.f, 1.f ), ICON_FA_GEM ); - ImGui::SameLine(); - ImGui::TextUnformatted( "External trace" ); - ImGui::SameLine(); - ImGui::TextDisabled( "(%zu)", f1.size() ); - ImGui::Separator(); - ImGui::NextColumn(); - - const auto prev0 = m_compare.selMatch[0]; - int idx = 0; - for( auto& v : f0 ) - { - const auto name = GetFrameSetName( *v ); - ImGui::PushID( -1 - idx ); - ImGui::RadioButton( name, &m_compare.selMatch[0], idx++ ); - ImGui::SameLine(); - ImGui::TextDisabled( "(%s)", RealToString( v->frames.size() ) ); - ImGui::PopID(); - } - ImGui::NextColumn(); - - const auto prev1 = m_compare.selMatch[1]; - idx = 0; - for( auto& v : f1 ) - { - const auto name = GetFrameSetName( *v, *m_compare.second ); - ImGui::PushID( idx ); - ImGui::RadioButton( name, &m_compare.selMatch[1], idx++ ); - ImGui::SameLine(); - ImGui::TextDisabled( "(%s)", RealToString( v->frames.size() ) ); - ImGui::PopID(); - } - ImGui::NextColumn(); - ImGui::EndColumns(); - ImGui::TreePop(); - - if( prev0 != m_compare.selMatch[0] || prev1 != m_compare.selMatch[1] ) - { - m_compare.ResetSelection(); - - if( m_compare.link ) - { - auto string0 = GetFrameSetName( *f0[m_compare.selMatch[0]] ); - auto string1 = GetFrameSetName( *f1[m_compare.selMatch[1]], *m_compare.second ); - - if( strcmp( string0, string1 ) != 0 ) - { - idx = 0; - if( prev0 != m_compare.selMatch[0] ) - { - for( auto& v : f1 ) - { - auto string = GetFrameSetName( *v, *m_compare.second ); - if( strcmp( string0, string ) == 0 ) - { - m_compare.selMatch[1] = idx; - break; - } - idx++; - } - } - else - { - assert( prev1 != m_compare.selMatch[1] ); - for( auto& v : f0 ) - { - auto string = GetFrameSetName( *v ); - if( strcmp( string1, string ) == 0 ) - { - m_compare.selMatch[0] = idx; - break; - } - idx++; - } - } - } - } - } - } - } - - ImGui::Separator(); - if( ImGui::TreeNodeEx( "Histogram", ImGuiTreeNodeFlags_DefaultOpen ) ) - { - const auto ty = ImGui::GetTextLineHeight(); - - int64_t tmin, tmax; - size_t size0, size1; - int64_t total0, total1; - double sumSq0, sumSq1; + bool findClicked = false; if( m_compare.compareMode == 0 ) { - auto& zoneData0 = m_worker.GetZonesForSourceLocation( m_compare.match[0][m_compare.selMatch[0]] ); - auto& zoneData1 = m_compare.second->GetZonesForSourceLocation( m_compare.match[1][m_compare.selMatch[1]] ); - auto& zones0 = zoneData0.zones; - auto& zones1 = zoneData1.zones; - zones0.ensure_sorted(); - zones1.ensure_sorted(); + ImGui::PushItemWidth( -0.01f ); + findClicked |= ImGui::InputTextWithHint( "###compare", "Enter zone name to search for", m_compare.pattern, 1024, ImGuiInputTextFlags_EnterReturnsTrue ); + ImGui::PopItemWidth(); - tmin = std::min( zoneData0.min, zoneData1.min ); - tmax = std::max( zoneData0.max, zoneData1.max ); + findClicked |= ImGui::Button( ICON_FA_MAGNIFYING_GLASS " Find" ); + ImGui::SameLine(); - size0 = zones0.size(); - size1 = zones1.size(); - total0 = zoneData0.total; - total1 = zoneData1.total; - sumSq0 = zoneData0.sumSq; - sumSq1 = zoneData1.sumSq; - - const size_t zsz[2] = { size0, size1 }; - for( int k=0; k<2; k++ ) + if( ImGui::Button( ICON_FA_BAN " Clear" ) ) { - if( m_compare.sortedNum[k] != zsz[k] ) - { - auto& zones = k == 0 ? zones0 : zones1; - auto& vec = m_compare.sorted[k]; - vec.reserve( zsz[k] ); - int64_t total = m_compare.total[k]; - size_t i; - for( i=m_compare.sortedNum[k]; iGetSourceLocation( v ); + auto& zones = m_compare.second->GetZonesForSourceLocation( v ).zones; + ImGui::PushID( -1 - idx ); + ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 0, 0 ) ); + ImGui::RadioButton( m_compare.second->GetString( srcloc.name.active ? srcloc.name : srcloc.function ), &m_compare.selMatch[1], idx++ ); + ImGui::PopStyleVar(); + ImGui::SameLine(); + ImGui::TextColored( ImVec4( 0.5, 0.5, 0.5, 1 ), "(%s) %s", RealToString( zones.size() ), LocationToString( m_compare.second->GetString( srcloc.file ), srcloc.line ) ); + ImGui::PopID(); + } + ImGui::NextColumn(); + ImGui::EndColumns(); + ImGui::TreePop(); + + if( prev0 != m_compare.selMatch[0] || prev1 != m_compare.selMatch[1] ) + { + m_compare.ResetSelection(); + + if( m_compare.link ) + { + if( !FindMatchingZone( prev0, prev1, FindMatchingZoneFlagSourceFile | FindMatchingZoneFlagLineNum ) ) + { + if( !FindMatchingZone( prev0, prev1, FindMatchingZoneFlagSourceFile ) ) + { + FindMatchingZone( prev0, prev1, FindMatchingZoneFlagDefault ); + } + } + } + } + } + + if( m_compare.match[0].empty() || m_compare.match[1].empty() ) + { + ImGui::Separator(); + ImGui::TextWrapped( "Both traces must have matches." ); + ImGui::End(); + return; } } else { assert( m_compare.compareMode == 1 ); - const auto& f0 = m_worker.GetFrames()[m_compare.selMatch[0]]; - const auto& f1 = m_compare.second->GetFrames()[m_compare.selMatch[1]]; - - tmin = std::min( f0->min, f1->min ); - tmax = std::max( f0->max, f1->max ); - - size0 = f0->frames.size(); - size1 = f1->frames.size(); - total0 = f0->total; - total1 = f1->total; - sumSq0 = f0->sumSq; - sumSq1 = f1->sumSq; - - const size_t zsz[2] = { size0, size1 }; - for( int k=0; k<2; k++ ) + ImGui::Separator(); + ImGui::BeginChild( "##compare" ); + if( ImGui::TreeNodeEx( "Frame sets", ImGuiTreeNodeFlags_DefaultOpen ) ) { - if( m_compare.sortedNum[k] != zsz[k] ) - { - auto& frameSet = k == 0 ? f0 : f1; - auto worker = k == 0 ? &m_worker : m_compare.second.get(); - auto& vec = m_compare.sorted[k]; - vec.reserve( zsz[k] ); - int64_t total = m_compare.total[k]; - size_t i; - for( i=m_compare.sortedNum[k]; iGetFrameEnd( *frameSet, i ) == worker->GetLastTime() ) break; - const auto t = worker->GetFrameTime( *frameSet, i ); - vec.emplace_back( t ); - total += t; - } - auto mid = vec.begin() + m_compare.sortedNum[k]; - pdqsort_branchless( mid, vec.end() ); - std::inplace_merge( vec.begin(), mid, vec.end() ); + const auto& f0 = m_worker.GetFrames(); + const auto& f1 = m_compare.second->GetFrames(); - m_compare.average[k] = float( total ) / i; - m_compare.median[k] = vec[i/2]; - m_compare.total[k] = total; - m_compare.sortedNum[k] = i; + ImGui::SameLine(); + SmallCheckbox( "Link selection", &m_compare.link ); + + ImGui::Separator(); + ImGui::Columns( 2 ); + TextColoredUnformatted( ImVec4( 0xDD/255.f, 0xDD/255.f, 0x22/255.f, 1.f ), ICON_FA_LEMON ); + ImGui::SameLine(); + ImGui::TextUnformatted( "This trace" ); + ImGui::SameLine(); + ImGui::TextDisabled( "(%zu)", f0.size() ); + ImGui::NextColumn(); + TextColoredUnformatted( ImVec4( 0xDD/255.f, 0x22/255.f, 0x22/255.f, 1.f ), ICON_FA_GEM ); + ImGui::SameLine(); + ImGui::TextUnformatted( "External trace" ); + ImGui::SameLine(); + ImGui::TextDisabled( "(%zu)", f1.size() ); + ImGui::Separator(); + ImGui::NextColumn(); + + const auto prev0 = m_compare.selMatch[0]; + int idx = 0; + for( auto& v : f0 ) + { + const auto name = GetFrameSetName( *v ); + ImGui::PushID( -1 - idx ); + ImGui::RadioButton( name, &m_compare.selMatch[0], idx++ ); + ImGui::SameLine(); + ImGui::TextDisabled( "(%s)", RealToString( v->frames.size() ) ); + ImGui::PopID(); + } + ImGui::NextColumn(); + + const auto prev1 = m_compare.selMatch[1]; + idx = 0; + for( auto& v : f1 ) + { + const auto name = GetFrameSetName( *v, *m_compare.second ); + ImGui::PushID( idx ); + ImGui::RadioButton( name, &m_compare.selMatch[1], idx++ ); + ImGui::SameLine(); + ImGui::TextDisabled( "(%s)", RealToString( v->frames.size() ) ); + ImGui::PopID(); + } + ImGui::NextColumn(); + ImGui::EndColumns(); + ImGui::TreePop(); + + if( prev0 != m_compare.selMatch[0] || prev1 != m_compare.selMatch[1] ) + { + m_compare.ResetSelection(); + + if( m_compare.link ) + { + auto string0 = GetFrameSetName( *f0[m_compare.selMatch[0]] ); + auto string1 = GetFrameSetName( *f1[m_compare.selMatch[1]], *m_compare.second ); + + if( strcmp( string0, string1 ) != 0 ) + { + idx = 0; + if( prev0 != m_compare.selMatch[0] ) + { + for( auto& v : f1 ) + { + auto string = GetFrameSetName( *v, *m_compare.second ); + if( strcmp( string0, string ) == 0 ) + { + m_compare.selMatch[1] = idx; + break; + } + idx++; + } + } + else + { + assert( prev1 != m_compare.selMatch[1] ); + for( auto& v : f0 ) + { + auto string = GetFrameSetName( *v ); + if( strcmp( string1, string ) == 0 ) + { + m_compare.selMatch[0] = idx; + break; + } + idx++; + } + } + } + } } } } - if( tmin != std::numeric_limits::max() ) + ImGui::Separator(); + if( ImGui::TreeNodeEx( "Histogram", ImGuiTreeNodeFlags_DefaultOpen ) ) { - TextDisabledUnformatted( "Minimum values in bin:" ); - ImGui::SameLine(); - ImGui::SetNextItemWidth( ImGui::CalcTextSize( "123456890123456" ).x ); - ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 1, 1 ) ); - ImGui::InputInt( "##minBinVal", &m_compare.minBinVal ); - if( m_compare.minBinVal < 1 ) m_compare.minBinVal = 1; - ImGui::SameLine(); - if( ImGui::Button( "Reset" ) ) m_compare.minBinVal = 1; - ImGui::PopStyleVar(); + const auto ty = ImGui::GetTextLineHeight(); - SmallCheckbox( "Log values", &m_compare.logVal ); - ImGui::SameLine(); - SmallCheckbox( "Log time", &m_compare.logTime ); - ImGui::SameLine(); - SmallCheckbox( "Cumulate time", &m_compare.cumulateTime ); - ImGui::SameLine(); - DrawHelpMarker( "Show total time taken by calls in each bin instead of call counts." ); - ImGui::SameLine(); - SmallCheckbox( "Normalize values", &m_compare.normalize ); - ImGui::SameLine(); - DrawHelpMarker( "Normalization will fudge reported data values!" ); + int64_t tmin, tmax; + size_t size0, size1; + int64_t total0, total1; + double sumSq0, sumSq1; - const auto cumulateTime = m_compare.cumulateTime; - - if( tmax - tmin > 0 ) + if( m_compare.compareMode == 0 ) { - const auto w = ImGui::GetContentRegionAvail().x; + auto& zoneData0 = m_worker.GetZonesForSourceLocation( m_compare.match[0][m_compare.selMatch[0]] ); + auto& zoneData1 = m_compare.second->GetZonesForSourceLocation( m_compare.match[1][m_compare.selMatch[1]] ); + auto& zones0 = zoneData0.zones; + auto& zones1 = zoneData1.zones; + zones0.ensure_sorted(); + zones1.ensure_sorted(); - const auto numBins = int64_t( w - 4 ); - if( numBins > 1 ) + tmin = std::min( zoneData0.min, zoneData1.min ); + tmax = std::max( zoneData0.max, zoneData1.max ); + + size0 = zones0.size(); + size1 = zones1.size(); + total0 = zoneData0.total; + total1 = zoneData1.total; + sumSq0 = zoneData0.sumSq; + sumSq1 = zoneData1.sumSq; + + const size_t zsz[2] = { size0, size1 }; + for( int k=0; k<2; k++ ) { - if( numBins > m_compare.numBins ) + if( m_compare.sortedNum[k] != zsz[k] ) { - m_compare.numBins = numBins; - m_compare.bins = std::make_unique( numBins ); - m_compare.binTime = std::make_unique( numBins ); - } - - const auto& bins = m_compare.bins; - const auto& binTime = m_compare.binTime; - - memset( bins.get(), 0, sizeof( CompVal ) * numBins ); - memset( binTime.get(), 0, sizeof( CompVal ) * numBins ); - - double adj0 = 1; - double adj1 = 1; - if( m_compare.normalize ) - { - if( size0 > size1 ) + auto& zones = k == 0 ? zones0 : zones1; + auto& vec = m_compare.sorted[k]; + vec.reserve( zsz[k] ); + int64_t total = m_compare.total[k]; + size_t i; + for( i=m_compare.sortedNum[k]; iGetFrames()[m_compare.selMatch[1]]; - if( m_compare.minBinVal > 1 ) + tmin = std::min( f0->min, f1->min ); + tmax = std::max( f0->max, f1->max ); + + size0 = f0->frames.size(); + size1 = f1->frames.size(); + total0 = f0->total; + total1 = f1->total; + sumSq0 = f0->sumSq; + sumSq1 = f1->sumSq; + + const size_t zsz[2] = { size0, size1 }; + for( int k=0; k<2; k++ ) + { + if( m_compare.sortedNum[k] != zsz[k] ) { + auto& frameSet = k == 0 ? f0 : f1; + auto worker = k == 0 ? &m_worker : m_compare.second.get(); + auto& vec = m_compare.sorted[k]; + vec.reserve( zsz[k] ); + int64_t total = m_compare.total[k]; + size_t i; + for( i=m_compare.sortedNum[k]; iGetFrameEnd( *frameSet, i ) == worker->GetLastTime() ) break; + const auto t = worker->GetFrameTime( *frameSet, i ); + vec.emplace_back( t ); + total += t; + } + auto mid = vec.begin() + m_compare.sortedNum[k]; + pdqsort_branchless( mid, vec.end() ); + std::inplace_merge( vec.begin(), mid, vec.end() ); + + m_compare.average[k] = float( total ) / i; + m_compare.median[k] = vec[i/2]; + m_compare.total[k] = total; + m_compare.sortedNum[k] = i; + } + } + } + + if( tmin != std::numeric_limits::max() ) + { + TextDisabledUnformatted( "Minimum values in bin:" ); + ImGui::SameLine(); + ImGui::SetNextItemWidth( ImGui::CalcTextSize( "123456890123456" ).x ); + ImGui::PushStyleVar( ImGuiStyleVar_FramePadding, ImVec2( 1, 1 ) ); + ImGui::InputInt( "##minBinVal", &m_compare.minBinVal ); + if( m_compare.minBinVal < 1 ) m_compare.minBinVal = 1; + ImGui::SameLine(); + if( ImGui::Button( "Reset" ) ) m_compare.minBinVal = 1; + ImGui::PopStyleVar(); + + SmallCheckbox( "Log values", &m_compare.logVal ); + ImGui::SameLine(); + SmallCheckbox( "Log time", &m_compare.logTime ); + ImGui::SameLine(); + SmallCheckbox( "Cumulate time", &m_compare.cumulateTime ); + ImGui::SameLine(); + DrawHelpMarker( "Show total time taken by calls in each bin instead of call counts." ); + ImGui::SameLine(); + SmallCheckbox( "Normalize values", &m_compare.normalize ); + ImGui::SameLine(); + DrawHelpMarker( "Normalization will fudge reported data values!" ); + + const auto cumulateTime = m_compare.cumulateTime; + + if( tmax - tmin > 0 ) + { + const auto w = ImGui::GetContentRegionAvail().x; + + const auto numBins = int64_t( w - 4 ); + if( numBins > 1 ) + { + if( numBins > m_compare.numBins ) + { + m_compare.numBins = numBins; + m_compare.bins = std::make_unique( numBins ); + m_compare.binTime = std::make_unique( numBins ); + } + + const auto& bins = m_compare.bins; + const auto& binTime = m_compare.binTime; + + memset( bins.get(), 0, sizeof( CompVal ) * numBins ); + memset( binTime.get(), 0, sizeof( CompVal ) * numBins ); + + double adj0 = 1; + double adj1 = 1; + if( m_compare.normalize ) + { + if( size0 > size1 ) + { + adj1 = double( size0 ) / size1; + } + else + { + adj0 = double( size1 ) / size0; + } + } + + const auto& sorted = m_compare.sorted; + auto sBegin0 = sorted[0].begin(); + auto sBegin1 = sorted[1].begin(); + auto sEnd0 = sorted[0].end(); + auto sEnd1 = sorted[1].end(); + + if( m_compare.minBinVal > 1 ) + { + if( m_compare.logTime ) + { + const auto tMinLog = log10( tmin ); + const auto zmax = ( log10( tmax ) - tMinLog ) / numBins; + int64_t i; + for( i=0; i= m_compare.minBinVal || distance1 >= m_compare.minBinVal ) break; + sBegin0 = nit0; + sBegin1 = nit1; + } + for( int64_t j=numBins-1; j>i; j-- ) + { + const auto nextBinVal = int64_t( pow( 10.0, tMinLog + ( j-1 ) * zmax ) ); + auto nit0 = std::lower_bound( sBegin0, sEnd0, nextBinVal ); + auto nit1 = std::lower_bound( sBegin1, sEnd1, nextBinVal ); + const auto distance0 = std::distance( nit0, sEnd0 ); + const auto distance1 = std::distance( nit1, sEnd1 ); + if( distance0 >= m_compare.minBinVal || distance1 >= m_compare.minBinVal ) break; + sEnd0 = nit0; + sEnd1 = nit1; + } + } + else + { + const auto zmax = tmax - tmin; + int64_t i; + for( i=0; i= m_compare.minBinVal || distance1 >= m_compare.minBinVal ) break; + sBegin0 = nit0; + sBegin1 = nit1; + } + for( int64_t j=numBins-1; j>i; j-- ) + { + const auto nextBinVal = tmin + ( j-1 ) * zmax / numBins; + auto nit0 = std::lower_bound( sBegin0, sEnd0, nextBinVal ); + auto nit1 = std::lower_bound( sBegin1, sEnd1, nextBinVal ); + const auto distance0 = std::distance( nit0, sEnd0 ); + const auto distance1 = std::distance( nit1, sEnd1 ); + if( distance0 >= m_compare.minBinVal || distance1 >= m_compare.minBinVal ) break; + sEnd0 = nit0; + sEnd1 = nit1; + } + } + + tmin = std::min( *sBegin0, *sBegin1 ); + tmax = std::max( *(sEnd0-1), *(sEnd1-1) ); + } + + auto zit0 = sBegin0; + auto zit1 = sBegin1; if( m_compare.logTime ) { const auto tMinLog = log10( tmin ); const auto zmax = ( log10( tmax ) - tMinLog ) / numBins; - int64_t i; - for( i=0; i= m_compare.minBinVal || distance1 >= m_compare.minBinVal ) break; - sBegin0 = nit0; - sBegin1 = nit1; - } - for( int64_t j=numBins-1; j>i; j-- ) - { - const auto nextBinVal = int64_t( pow( 10.0, tMinLog + ( j-1 ) * zmax ) ); - auto nit0 = std::lower_bound( sBegin0, sEnd0, nextBinVal ); - auto nit1 = std::lower_bound( sBegin1, sEnd1, nextBinVal ); - const auto distance0 = std::distance( nit0, sEnd0 ); - const auto distance1 = std::distance( nit1, sEnd1 ); - if( distance0 >= m_compare.minBinVal || distance1 >= m_compare.minBinVal ) break; - sEnd0 = nit0; - sEnd1 = nit1; + auto nit0 = std::lower_bound( zit0, sEnd0, nextBinVal ); + auto nit1 = std::lower_bound( zit1, sEnd1, nextBinVal ); + bins[i].v0 += adj0 * std::distance( zit0, nit0 ); + bins[i].v1 += adj1 * std::distance( zit1, nit1 ); + binTime[i].v0 += adj0 * std::accumulate( zit0, nit0, int64_t( 0 ) ); + binTime[i].v1 += adj1 * std::accumulate( zit1, nit1, int64_t( 0 ) ); + zit0 = nit0; + zit1 = nit1; } } else { const auto zmax = tmax - tmin; - int64_t i; - for( i=0; i= m_compare.minBinVal || distance1 >= m_compare.minBinVal ) break; - sBegin0 = nit0; - sBegin1 = nit1; + auto nit0 = std::lower_bound( zit0, sEnd0, nextBinVal ); + auto nit1 = std::lower_bound( zit1, sEnd1, nextBinVal ); + bins[i].v0 += adj0 * std::distance( zit0, nit0 ); + bins[i].v1 += adj1 * std::distance( zit1, nit1 ); + binTime[i].v0 += adj0 * std::accumulate( zit0, nit0, int64_t( 0 ) ); + binTime[i].v1 += adj1 * std::accumulate( zit1, nit1, int64_t( 0 ) ); + zit0 = nit0; + zit1 = nit1; } - for( int64_t j=numBins-1; j>i; j-- ) + } + + double maxVal; + if( cumulateTime ) + { + maxVal = std::max( binTime[0].v0, binTime[0].v1 ); + for( int i=1; i= m_compare.minBinVal || distance1 >= m_compare.minBinVal ) break; - sEnd0 = nit0; - sEnd1 = nit1; + maxVal = std::max( { maxVal, binTime[i].v0, binTime[i].v1 } ); } } - - tmin = std::min( *sBegin0, *sBegin1 ); - tmax = std::max( *(sEnd0-1), *(sEnd1-1) ); - } - - auto zit0 = sBegin0; - auto zit1 = sBegin1; - if( m_compare.logTime ) - { - const auto tMinLog = log10( tmin ); - const auto zmax = ( log10( tmax ) - tMinLog ) / numBins; - for( int64_t i=0; i 1 ) - { - const auto sz = sorted[0].size(); - const auto avg = m_compare.average[0]; - const auto ss = sumSq0 - 2. * total0 * avg + avg * avg * sz; - const auto sd = sqrt( ss / ( sz - 1 ) ); - - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - TextColoredUnformatted( ImVec4( 0xDD/511.f, 0xDD/511.f, 0x22/511.f, 1.f ), ICON_FA_LEMON ); - ImGui::SameLine(); - TextFocused( "\xcf\x83 (this):", TimeToString( sd ) ); - TooltipIfHovered( "Standard deviation" ); - } - - - TextColoredUnformatted( ImVec4( 0xDD/511.f, 0x22/511.f, 0x22/511.f, 1.f ), ICON_FA_GEM ); - ImGui::SameLine(); - TextFocused( "Mean time (ext.):", TimeToString( m_compare.average[1] ) ); - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - TextColoredUnformatted( ImVec4( 0xDD/511.f, 0x22/511.f, 0x22/511.f, 1.f ), ICON_FA_GEM ); - ImGui::SameLine(); - TextFocused( "Median time (ext.):", TimeToString( m_compare.median[1] ) ); - if( sorted[1].size() > 1 ) - { - const auto sz = sorted[1].size(); - const auto avg = m_compare.average[1]; - const auto ss = sumSq1 - 2. * total1 * avg + avg * avg * sz; - const auto sd = sqrt( ss / ( sz - 1 ) ); - - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - TextColoredUnformatted( ImVec4( 0xDD/511.f, 0x22/511.f, 0x22/511.f, 1.f ), ICON_FA_GEM ); - ImGui::SameLine(); - TextFocused( "\xcf\x83 (ext.):", TimeToString( sd ) ); - TooltipIfHovered( "Standard deviation" ); - } - - ImGui::PushStyleColor( ImGuiCol_Text, ImVec4( 0xDD/511.f, 0xDD/511.f, 0x22/511.f, 1.f ) ); - ImGui::PushStyleColor( ImGuiCol_Button, ImVec4( 0xDD/255.f, 0xDD/255.f, 0x22/255.f, 1.f ) ); - ImGui::PushStyleColor( ImGuiCol_ButtonHovered, ImVec4( 0xDD/255.f, 0xDD/255.f, 0x22/255.f, 1.f ) ); - ImGui::PushStyleColor( ImGuiCol_ButtonActive, ImVec4( 0xDD/255.f, 0xDD/255.f, 0x22/255.f, 1.f ) ); - ImGui::Button( ICON_FA_LEMON ); - ImGui::PopStyleColor( 4 ); - ImGui::SameLine(); - ImGui::TextUnformatted( "This trace" ); - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - - ImGui::PushStyleColor( ImGuiCol_Text, ImVec4( 0xDD/511.f, 0x22/511.f, 0x22/511.f, 1.f ) ); - ImGui::PushStyleColor( ImGuiCol_Button, ImVec4( 0xDD/255.f, 0x22/255.f, 0x22/255.f, 1.f ) ); - ImGui::PushStyleColor( ImGuiCol_ButtonHovered, ImVec4( 0xDD/255.f, 0x22/255.f, 0x22/255.f, 1.f ) ); - ImGui::PushStyleColor( ImGuiCol_ButtonActive, ImVec4( 0xDD/255.f, 0x22/255.f, 0x22/255.f, 1.f ) ); - ImGui::Button( ICON_FA_GEM ); - ImGui::PopStyleColor( 4 ); - ImGui::SameLine(); - ImGui::TextUnformatted( "External trace" ); - ImGui::SameLine(); - ImGui::Spacing(); - ImGui::SameLine(); - - ImGui::ColorButton( "c3", ImVec4( 0x44/255.f, 0xBB/255.f, 0xBB/255.f, 1.f ), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop ); - ImGui::SameLine(); - ImGui::TextUnformatted( "Overlap" ); - - const auto Height = 200 * scale; - const auto wpos = ImGui::GetCursorScreenPos(); - const auto dpos = wpos + ImVec2( 0.5f, 0.5f ); - - ImGui::InvisibleButton( "##histogram", ImVec2( w, Height + round( ty * 2.5 ) ) ); - const bool hover = ImGui::IsItemHovered(); - - auto draw = ImGui::GetWindowDrawList(); - draw->AddRectFilled( wpos, wpos + ImVec2( w, Height ), 0x22FFFFFF ); - draw->AddRect( wpos, wpos + ImVec2( w, Height ), 0x88FFFFFF ); - - if( m_compare.logVal ) - { - const auto hAdj = double( Height - 4 ) / log10( maxVal + 1 ); - for( int i=0; i 0 || val1 > 0 ) - { - const auto val = std::min( val0, val1 ); - if( val > 0 ) - { - DrawLine( draw, dpos + ImVec2( 2+i, Height-3 ), dpos + ImVec2( 2+i, Height-3 - log10( val + 1 ) * hAdj ), 0xFFBBBB44 ); - } - if( val1 == val ) - { - DrawLine( draw, dpos + ImVec2( 2+i, Height-3 - log10( val + 1 ) * hAdj ), dpos + ImVec2( 2+i, Height-3 - log10( val0 + 1 ) * hAdj ), 0xFF22DDDD ); - } - else - { - DrawLine( draw, dpos + ImVec2( 2+i, Height-3 - log10( val + 1 ) * hAdj ), dpos + ImVec2( 2+i, Height-3 - log10( val1 + 1 ) * hAdj ), 0xFF2222DD ); - } - } - } - } - else - { - const auto hAdj = double( Height - 4 ) / maxVal; - for( int i=0; i 0 || val1 > 0 ) - { - const auto val = std::min( val0, val1 ); - if( val > 0 ) - { - DrawLine( draw, dpos + ImVec2( 2+i, Height-3 ), dpos + ImVec2( 2+i, Height-3 - val * hAdj ), 0xFFBBBB44 ); - } - if( val1 == val ) - { - DrawLine( draw, dpos + ImVec2( 2+i, Height-3 - val * hAdj ), dpos + ImVec2( 2+i, Height-3 - val0 * hAdj ), 0xFF22DDDD ); - } - else - { - DrawLine( draw, dpos + ImVec2( 2+i, Height-3 - val * hAdj ), dpos + ImVec2( 2+i, Height-3 - val1 * hAdj ), 0xFF2222DD ); - } - } - } - } - - const auto xoff = 2; - const auto yoff = Height + 1; - - DrawHistogramMinMaxLabel( draw, tmin, tmax, wpos + ImVec2( 0, yoff ), w, ty ); - - const auto ty05 = round( ty * 0.5f ); - const auto ty025 = round( ty * 0.25f ); - if( m_compare.logTime ) - { - const auto ltmin = log10( tmin ); - const auto ltmax = log10( tmax ); - const auto start = int( floor( ltmin ) ); - const auto end = int( ceil( ltmax ) ); - - const auto range = ltmax - ltmin; - const auto step = w / range; - auto offset = start - ltmin; - int tw = 0; - int tx = 0; - - auto tt = int64_t( pow( 10, start ) ); - - static const double logticks[] = { log10( 2 ), log10( 3 ), log10( 4 ), log10( 5 ), log10( 6 ), log10( 7 ), log10( 8 ), log10( 9 ) }; - - for( int i=start; i<=end; i++ ) - { - const auto x = ( i - start + offset ) * step; - - if( x >= 0 ) - { - DrawLine( draw, dpos + ImVec2( x, yoff ), dpos + ImVec2( x, yoff + ty05 ), 0x66FFFFFF ); - if( tw == 0 || x > tx + tw + ty * 1.1 ) - { - tx = x; - auto txt = TimeToString( tt ); - draw->AddText( wpos + ImVec2( x, yoff + ty05 ), 0x66FFFFFF, txt ); - tw = ImGui::CalcTextSize( txt ).x; - } - } - - for( int j=0; j<8; j++ ) - { - const auto xoff = x + logticks[j] * step; - if( xoff >= 0 ) - { - DrawLine( draw, dpos + ImVec2( xoff, yoff ), dpos + ImVec2( xoff, yoff + ty025 ), 0x66FFFFFF ); - } - } - - tt *= 10; - } - } - else - { - const auto pxns = numBins / double( tmax - tmin ); - const auto nspx = 1.0 / pxns; - const auto scale = std::max( 0.0f, round( log10( nspx ) + 2 ) ); - const auto step = pow( 10, scale ); - - const auto dx = step * pxns; - double x = 0; - int tw = 0; - int tx = 0; - - const auto sstep = step / 10.0; - const auto sdx = dx / 10.0; - - static const double linelen[] = { 0.5, 0.25, 0.25, 0.25, 0.25, 0.375, 0.25, 0.25, 0.25, 0.25 }; - - int64_t tt = int64_t( ceil( tmin / sstep ) * sstep ); - const auto diff = tmin / sstep - int64_t( tmin / sstep ); - const auto xo = ( diff == 0 ? 0 : ( ( 1 - diff ) * sstep * pxns ) ) + xoff; - int iter = int( ceil( ( tmin - int64_t( tmin / step ) * step ) / sstep ) ); - - while( x < numBins ) - { - DrawLine( draw, dpos + ImVec2( xo + x, yoff ), dpos + ImVec2( xo + x, yoff + round( ty * linelen[iter] ) ), 0x66FFFFFF ); - if( iter == 0 && ( tw == 0 || x > tx + tw + ty * 1.1 ) ) - { - tx = x; - auto txt = TimeToString( tt ); - draw->AddText( wpos + ImVec2( xo + x, yoff + ty05 ), 0x66FFFFFF, txt ); - tw = ImGui::CalcTextSize( txt ).x; - } - - iter = ( iter + 1 ) % 10; - x += sdx; - tt += sstep; - } - } - - if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( 2, 2 ), wpos + ImVec2( w-2, Height + round( ty * 1.5 ) ) ) ) - { - const auto ltmin = log10( tmin ); - const auto ltmax = log10( tmax ); - - auto& io = ImGui::GetIO(); - DrawLine( draw, ImVec2( io.MousePos.x + 0.5f, dpos.y ), ImVec2( io.MousePos.x + 0.5f, dpos.y+Height-2 ), 0x33FFFFFF ); - - const auto bin = int64_t( io.MousePos.x - wpos.x - 2 ); - int64_t t0, t1; - if( m_compare.logTime ) - { - t0 = int64_t( pow( 10, ltmin + double( bin ) / numBins * ( ltmax - ltmin ) ) ); - t1 = int64_t( pow( 10, ltmin + double( bin+1 ) / numBins * ( ltmax - ltmin ) ) ); - } else { - t0 = int64_t( tmin + double( bin ) / numBins * ( tmax - tmin ) ); - t1 = int64_t( tmin + double( bin+1 ) / numBins * ( tmax - tmin ) ); + maxVal = std::max( bins[0].v0, bins[0].v1 ); + for( int i=1; i 1 ) + { + const auto sz = sorted[0].size(); + const auto avg = m_compare.average[0]; + const auto ss = sumSq0 - 2. * total0 * avg + avg * avg * sz; + const auto sd = sqrt( ss / ( sz - 1 ) ); + + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + TextColoredUnformatted( ImVec4( 0xDD/511.f, 0xDD/511.f, 0x22/511.f, 1.f ), ICON_FA_LEMON ); + ImGui::SameLine(); + TextFocused( "\xcf\x83 (this):", TimeToString( sd ) ); + TooltipIfHovered( "Standard deviation" ); + } + + + TextColoredUnformatted( ImVec4( 0xDD/511.f, 0x22/511.f, 0x22/511.f, 1.f ), ICON_FA_GEM ); + ImGui::SameLine(); + TextFocused( "Mean time (ext.):", TimeToString( m_compare.average[1] ) ); + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + TextColoredUnformatted( ImVec4( 0xDD/511.f, 0x22/511.f, 0x22/511.f, 1.f ), ICON_FA_GEM ); + ImGui::SameLine(); + TextFocused( "Median time (ext.):", TimeToString( m_compare.median[1] ) ); + if( sorted[1].size() > 1 ) + { + const auto sz = sorted[1].size(); + const auto avg = m_compare.average[1]; + const auto ss = sumSq1 - 2. * total1 * avg + avg * avg * sz; + const auto sd = sqrt( ss / ( sz - 1 ) ); + + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + TextColoredUnformatted( ImVec4( 0xDD/511.f, 0x22/511.f, 0x22/511.f, 1.f ), ICON_FA_GEM ); + ImGui::SameLine(); + TextFocused( "\xcf\x83 (ext.):", TimeToString( sd ) ); + TooltipIfHovered( "Standard deviation" ); + } + + ImGui::PushStyleColor( ImGuiCol_Text, ImVec4( 0xDD/511.f, 0xDD/511.f, 0x22/511.f, 1.f ) ); + ImGui::PushStyleColor( ImGuiCol_Button, ImVec4( 0xDD/255.f, 0xDD/255.f, 0x22/255.f, 1.f ) ); + ImGui::PushStyleColor( ImGuiCol_ButtonHovered, ImVec4( 0xDD/255.f, 0xDD/255.f, 0x22/255.f, 1.f ) ); + ImGui::PushStyleColor( ImGuiCol_ButtonActive, ImVec4( 0xDD/255.f, 0xDD/255.f, 0x22/255.f, 1.f ) ); + ImGui::Button( ICON_FA_LEMON ); + ImGui::PopStyleColor( 4 ); + ImGui::SameLine(); + ImGui::TextUnformatted( "This trace" ); + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + + ImGui::PushStyleColor( ImGuiCol_Text, ImVec4( 0xDD/511.f, 0x22/511.f, 0x22/511.f, 1.f ) ); + ImGui::PushStyleColor( ImGuiCol_Button, ImVec4( 0xDD/255.f, 0x22/255.f, 0x22/255.f, 1.f ) ); + ImGui::PushStyleColor( ImGuiCol_ButtonHovered, ImVec4( 0xDD/255.f, 0x22/255.f, 0x22/255.f, 1.f ) ); + ImGui::PushStyleColor( ImGuiCol_ButtonActive, ImVec4( 0xDD/255.f, 0x22/255.f, 0x22/255.f, 1.f ) ); + ImGui::Button( ICON_FA_GEM ); + ImGui::PopStyleColor( 4 ); + ImGui::SameLine(); + ImGui::TextUnformatted( "External trace" ); + ImGui::SameLine(); + ImGui::Spacing(); + ImGui::SameLine(); + + ImGui::ColorButton( "c3", ImVec4( 0x44/255.f, 0xBB/255.f, 0xBB/255.f, 1.f ), ImGuiColorEditFlags_NoTooltip | ImGuiColorEditFlags_NoDragDrop ); + ImGui::SameLine(); + ImGui::TextUnformatted( "Overlap" ); + + const auto Height = 200 * scale; + const auto wpos = ImGui::GetCursorScreenPos(); + const auto dpos = wpos + ImVec2( 0.5f, 0.5f ); + + ImGui::InvisibleButton( "##histogram", ImVec2( w, Height + round( ty * 2.5 ) ) ); + const bool hover = ImGui::IsItemHovered(); + + auto draw = ImGui::GetWindowDrawList(); + draw->AddRectFilled( wpos, wpos + ImVec2( w, Height ), 0x22FFFFFF ); + draw->AddRect( wpos, wpos + ImVec2( w, Height ), 0x88FFFFFF ); + + if( m_compare.logVal ) + { + const auto hAdj = double( Height - 4 ) / log10( maxVal + 1 ); + for( int i=0; i 0 || val1 > 0 ) + { + const auto val = std::min( val0, val1 ); + if( val > 0 ) + { + DrawLine( draw, dpos + ImVec2( 2+i, Height-3 ), dpos + ImVec2( 2+i, Height-3 - log10( val + 1 ) * hAdj ), 0xFFBBBB44 ); + } + if( val1 == val ) + { + DrawLine( draw, dpos + ImVec2( 2+i, Height-3 - log10( val + 1 ) * hAdj ), dpos + ImVec2( 2+i, Height-3 - log10( val0 + 1 ) * hAdj ), 0xFF22DDDD ); + } + else + { + DrawLine( draw, dpos + ImVec2( 2+i, Height-3 - log10( val + 1 ) * hAdj ), dpos + ImVec2( 2+i, Height-3 - log10( val1 + 1 ) * hAdj ), 0xFF2222DD ); + } + } + } + } + else + { + const auto hAdj = double( Height - 4 ) / maxVal; + for( int i=0; i 0 || val1 > 0 ) + { + const auto val = std::min( val0, val1 ); + if( val > 0 ) + { + DrawLine( draw, dpos + ImVec2( 2+i, Height-3 ), dpos + ImVec2( 2+i, Height-3 - val * hAdj ), 0xFFBBBB44 ); + } + if( val1 == val ) + { + DrawLine( draw, dpos + ImVec2( 2+i, Height-3 - val * hAdj ), dpos + ImVec2( 2+i, Height-3 - val0 * hAdj ), 0xFF22DDDD ); + } + else + { + DrawLine( draw, dpos + ImVec2( 2+i, Height-3 - val * hAdj ), dpos + ImVec2( 2+i, Height-3 - val1 * hAdj ), 0xFF2222DD ); + } + } + } + } + + const auto xoff = 2; + const auto yoff = Height + 1; + + DrawHistogramMinMaxLabel( draw, tmin, tmax, wpos + ImVec2( 0, yoff ), w, ty ); + + const auto ty05 = round( ty * 0.5f ); + const auto ty025 = round( ty * 0.25f ); + if( m_compare.logTime ) + { + const auto ltmin = log10( tmin ); + const auto ltmax = log10( tmax ); + const auto start = int( floor( ltmin ) ); + const auto end = int( ceil( ltmax ) ); + + const auto range = ltmax - ltmin; + const auto step = w / range; + auto offset = start - ltmin; + int tw = 0; + int tx = 0; + + auto tt = int64_t( pow( 10, start ) ); + + static const double logticks[] = { log10( 2 ), log10( 3 ), log10( 4 ), log10( 5 ), log10( 6 ), log10( 7 ), log10( 8 ), log10( 9 ) }; + + for( int i=start; i<=end; i++ ) + { + const auto x = ( i - start + offset ) * step; + + if( x >= 0 ) + { + DrawLine( draw, dpos + ImVec2( x, yoff ), dpos + ImVec2( x, yoff + ty05 ), 0x66FFFFFF ); + if( tw == 0 || x > tx + tw + ty * 1.1 ) + { + tx = x; + auto txt = TimeToString( tt ); + draw->AddText( wpos + ImVec2( x, yoff + ty05 ), 0x66FFFFFF, txt ); + tw = ImGui::CalcTextSize( txt ).x; + } + } + + for( int j=0; j<8; j++ ) + { + const auto xoff = x + logticks[j] * step; + if( xoff >= 0 ) + { + DrawLine( draw, dpos + ImVec2( xoff, yoff ), dpos + ImVec2( xoff, yoff + ty025 ), 0x66FFFFFF ); + } + } + + tt *= 10; + } + } + else + { + const auto pxns = numBins / double( tmax - tmin ); + const auto nspx = 1.0 / pxns; + const auto scale = std::max( 0.0f, round( log10( nspx ) + 2 ) ); + const auto step = pow( 10, scale ); + + const auto dx = step * pxns; + double x = 0; + int tw = 0; + int tx = 0; + + const auto sstep = step / 10.0; + const auto sdx = dx / 10.0; + + static const double linelen[] = { 0.5, 0.25, 0.25, 0.25, 0.25, 0.375, 0.25, 0.25, 0.25, 0.25 }; + + int64_t tt = int64_t( ceil( tmin / sstep ) * sstep ); + const auto diff = tmin / sstep - int64_t( tmin / sstep ); + const auto xo = ( diff == 0 ? 0 : ( ( 1 - diff ) * sstep * pxns ) ) + xoff; + int iter = int( ceil( ( tmin - int64_t( tmin / step ) * step ) / sstep ) ); + + while( x < numBins ) + { + DrawLine( draw, dpos + ImVec2( xo + x, yoff ), dpos + ImVec2( xo + x, yoff + round( ty * linelen[iter] ) ), 0x66FFFFFF ); + if( iter == 0 && ( tw == 0 || x > tx + tw + ty * 1.1 ) ) + { + tx = x; + auto txt = TimeToString( tt ); + draw->AddText( wpos + ImVec2( xo + x, yoff + ty05 ), 0x66FFFFFF, txt ); + tw = ImGui::CalcTextSize( txt ).x; + } + + iter = ( iter + 1 ) % 10; + x += sdx; + tt += sstep; + } + } + + if( hover && ImGui::IsMouseHoveringRect( wpos + ImVec2( 2, 2 ), wpos + ImVec2( w-2, Height + round( ty * 1.5 ) ) ) ) + { + const auto ltmin = log10( tmin ); + const auto ltmax = log10( tmax ); + + auto& io = ImGui::GetIO(); + DrawLine( draw, ImVec2( io.MousePos.x + 0.5f, dpos.y ), ImVec2( io.MousePos.x + 0.5f, dpos.y+Height-2 ), 0x33FFFFFF ); + + const auto bin = int64_t( io.MousePos.x - wpos.x - 2 ); + int64_t t0, t1; + if( m_compare.logTime ) + { + t0 = int64_t( pow( 10, ltmin + double( bin ) / numBins * ( ltmax - ltmin ) ) ); + t1 = int64_t( pow( 10, ltmin + double( bin+1 ) / numBins * ( ltmax - ltmin ) ) ); + } + else + { + t0 = int64_t( tmin + double( bin ) / numBins * ( tmax - tmin ) ); + t1 = int64_t( tmin + double( bin+1 ) / numBins * ( tmax - tmin ) ); + } + + int64_t tBefore[2] = { 0, 0 }; + for( int i=0; i