demotool/profiling.cc

231 lines
5.8 KiB
C++

#include "externals.hh"
#include "profiling.hh"
#include <sys/time.h>
#include <time.h>
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;
}