#include "externals.hh" #include "profiling.hh" #include #include constexpr uint32_t T_Profiler::Samples; constexpr uint32_t T_Profiler::History; constexpr uint32_t T_Profiler::Invalid; void T_Profiler::clear( ) { sections_.clear( ); samples_.clear( ); starts_.clear( ); } void T_Profiler::startFrame( ) { previous_ = Invalid; current_ = Invalid; } void T_Profiler::endFrame( ) { const auto n( sections_.size( ) ); while ( secDurations_.size( ) < n ) { secDurations_.add( 0 ); secStarts_.add( 0 ); } for ( auto i = 0u ; i < n ; i ++ ) { const float d = computeDuration( i ); secDurations_[ i ] = d; if ( parents_[ i ] != Invalid ) { assert( parents_[ i ] < i ); secStarts_[ i ] = secStarts_[ parents_[ i ] ]; } else if ( chain_[ i ] != Invalid ) { assert( chain_[ i ] < i ); secStarts_[ i ] = secStarts_[ chain_[ i ] ] + secDurations_[ chain_[ i ] ]; } else { secStarts_[ i ] = 0; } } } void T_Profiler::start( T_String const& section ) { const auto n( sections_.size( ) ); const auto pos( find( section ) ); if ( pos == n ) { sections_.add( section ); samples_.add( T_SamplesList_{ } ); starts_.add( 0u ); chain_.add( Invalid ); parents_.add( Invalid ); } chain_[ pos ] = previous_; if ( current_ != Invalid ) { parents_[ pos ] = current_; previous_ = Invalid; } else { parents_[ pos ] = Invalid; } current_ = pos; struct timespec ts; clock_gettime( CLOCK_MONOTONIC , &ts ); starts_[ pos ] = ts.tv_sec * 1000000000 + ts.tv_nsec; } void T_Profiler::end( T_String const& section ) { const auto pos( find( section ) ); const auto n( sections_.size( ) ); if ( pos == n ) { return; } struct timespec ts; clock_gettime( CLOCK_MONOTONIC , &ts ); const uint64_t ended = ts.tv_sec * 1000000000 + ts.tv_nsec; const uint64_t duration = ended - starts_[ pos ]; auto& samples( samples_[ pos ] ); if ( samples.size( ) == 0 || ( samples.size( ) < History && samples[ 0 ].nSamples == Samples ) ) { samples.insert( 0 , T_ProfilerSamples{ 0 , 0 } ); } else if ( samples.size( ) == History && samples[ 0 ].nSamples == Samples ) { for ( auto i = 1u ; i < History ; i ++ ) { samples[ i ] = samples[ i - 1 ]; } samples[ 0 ].sum = 0; samples[ 0 ].nSamples = 0; } samples[ 0 ].sum += float( duration ) * 1e-6; samples[ 0 ].nSamples ++; previous_ = pos; current_ = Invalid; } void T_Profiler::makeUI( ) { if ( !uiEnabled_ ) { return; } auto const n( sections_.size( ) ); if ( n != secDurations_.size( ) ) { return; } auto const& dspSize( ImGui::GetIO( ).DisplaySize ); ImGui::SetNextWindowSize( ImVec2( dspSize.x , dspSize.y * .34f ) , ImGuiSetCond_Appearing ); ImGui::SetNextWindowPos( ImVec2( 0 , dspSize.y * .66f ) , ImGuiSetCond_Appearing ); ImGui::Begin( "Profiler" , &uiEnabled_ , ImGuiWindowFlags_NoCollapse ); ImGui::BeginChild( "left" , ImVec2( 180 , 0 ) , true ); float angle( 0 ); for ( auto i = 0u ; i < n ; i ++ ) { if ( displayed_.size( ) == i ) { displayed_.add( true ); } angle = fmod( angle + 137 , 360 ); ImVec4 color( 0 , 0 , 0 , 1 ); ImGui::ColorConvertHSVtoRGB( angle / 360. , .5 , 1 , color.x , color.y , color.z ); ImGui::PushStyleColor( ImGuiCol_Text , color ); ebcl::T_StringBuilder sb; sb << sections_[ i ] << " (" << int( round( secDurations_[ i ] ) ) << "ms)" << '\0'; ImGui::Checkbox( sb.data( ) , (bool*) &displayed_[ i ] ); ImGui::PopStyleColor( ); } ImGui::EndChild( ); ImGui::SameLine( ); auto* const dl( ImGui::GetWindowDrawList( ) ); const ImVec2 wp( ImGui::GetWindowPos( ) ); const ImVec2 pos( ImGui::GetCursorPos( ) ); const ImVec2 ws( ImGui::GetWindowContentRegionMax( ) ); const ImVec2 start( wp.x + pos.x , wp.y + pos.y ); auto& style( ImGui::GetStyle( ) ); const ImVec2 end( wp.x + ws.x + 1 - style.FramePadding.x , wp.y + ws.y + 1 - style.FramePadding.y ); dl->AddRectFilled( start , end , ImGui::GetColorU32( ImVec4( .5 , .5 , .5 , .8 ) ) ); // Ms marks static constexpr float msWidth = 20; float lp( 0 ); while ( lp < ws.x ) { dl->AddLine( ImVec2( start.x + lp , start.y ) , ImVec2( start.x + lp , end.y ) , ImGui::GetColorU32( ImVec4( 0 , 0 , 0 , .8 ) ) ); lp += msWidth; } // 60/30FPS line dl->AddLine( ImVec2( start.x + msWidth * 1000. / 60. , start.y ) , ImVec2( start.x + msWidth * 1000. / 60. , end.y ) , ImGui::GetColorU32( ImVec4( 0 , 1 , 0 , 1 ) ) ); dl->AddLine( ImVec2( start.x + msWidth * 1000. / 30. , start.y ) , ImVec2( start.x + msWidth * 1000. / 30. , end.y ) , ImGui::GetColorU32( ImVec4( 1 , 0 , 0 , 1 ) ) ); // Plot static constexpr float plHeight = 5; uint32_t dLine[ n ]; uint32_t cLine = 0; angle = 0; for ( auto i = 0u ; i < n ; i ++ ) { angle = fmod( angle + 137 , 360 ); if ( i != 0 ) { if ( chain_[ i ] == Invalid ) { cLine ++; } else { cLine = dLine[ chain_[ i ] ]; } } dLine[ i ] = cLine; if ( !displayed_[ i ] ) { continue; } const float lx0 = msWidth * secStarts_[ i ]; const float lx1 = msWidth * ( secStarts_[ i ] + secDurations_[ i ] ); const float lh = cLine * ( plHeight + 1 ); const float ly1 = lh + plHeight; ImVec4 color( 0 , 0 , 0 , 1 ); ImGui::ColorConvertHSVtoRGB( angle / 360. , .5 , 1 , color.x , color.y , color.z ); dl->AddRectFilled( ImVec2( start.x + lx0 , start.y + lh ) , ImVec2( start.x + lx1 , start.y + ly1 ) , ImGui::GetColorU32( color ) ); } ImGui::End( ); } float T_Profiler::computeDuration( const uint32_t section ) const { auto const& samples( samples_[ section ] ); float total = 0; float nSamples = 0; for ( auto const& entry : samples ) { total += entry.sum; nSamples += entry.nSamples; } return total / nSamples; } uint32_t T_Profiler::find( T_String const& section ) const { const auto n( sections_.size( ) ); auto pos( 0u ); while ( pos < n && sections_[ pos ] != section ) { pos ++; } return pos; }