demotool/ui-profiling.cc
Emmanuel BENOîT a266120761 Profiler - "Expected order" workaround
So the profiler apparently expects starts and ends to be consistent in
ordering. Whenever the order changes, it will likely crash. Modified the
main loop so it doesn't add the debug output's timings if the profiler
has been reset due to reloading as a workaround. This needs to be fixed,
though.
2018-03-30 17:34:22 +02:00

307 lines
7.8 KiB
C++

#include "externals.hh"
#include "ui-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;
T_Profiler::~T_Profiler( )
{
const auto iSize{ gpuQueries_.size( ) };
if ( iSize ) {
glDeleteQueries( iSize , &gpuQueries_[ 0 ] );
}
}
void T_Profiler::clear( )
{
sections_.clear( );
samples_.clear( );
cpuStarts_.clear( );
}
void T_Profiler::startFrame( )
{
previous_ = Invalid;
current_ = Invalid;
}
void T_Profiler::endFrame( )
{
const auto n{ sections_.size( ) };
if ( n ) {
int32_t done{ 0 };
while ( !done ) {
glGetQueryObjectiv( gpuQueries_[ n * 2 - 1 ] ,
GL_QUERY_RESULT_AVAILABLE ,
&done );
}
if ( gpuSamples_.size( ) < n ) {
gpuSamples_.resize( n );
}
for ( auto i = 0u ; i < n ; i ++ ) {
uint64_t a , b;
glGetQueryObjectui64v( gpuQueries_[ i * 2 ] ,
GL_QUERY_RESULT , &a );
glGetQueryObjectui64v( gpuQueries_[ i * 2 + 1 ] ,
GL_QUERY_RESULT , &b );
addSample( gpuSamples_[ i ] , b - a );
}
}
while ( secDurations_.size( ) < n ) {
secCPUDurations_.add( 0 );
secGPUDurations_.add( 0 );
secDurations_.add( 0 );
secStarts_.add( 0 );
}
for ( auto i = 0u ; i < n ; i ++ ) {
const float cpuD = computeDuration( samples_[ i ] );
const float gpuD = computeDuration( gpuSamples_[ i ] );
secCPUDurations_[ i ] = cpuD;
secGPUDurations_[ i ] = gpuD;
secDurations_[ i ] = std::max( cpuD , gpuD );
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_{ } );
cpuStarts_.add( 0u );
chain_.add( Invalid );
parents_.add( Invalid );
}
assert( previous_ == Invalid || previous_ < pos );
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 );
cpuStarts_[ pos ] = ts.tv_sec * 1000000000 + ts.tv_nsec;
if ( gpuQueries_.size( ) <= pos * 2 + 1 ) {
extendGPUQueries( );
}
glQueryCounter( gpuQueries_[ pos * 2 ] , GL_TIMESTAMP );
}
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;
glQueryCounter( gpuQueries_[ pos * 2 + 1 ] , GL_TIMESTAMP );
clock_gettime( CLOCK_MONOTONIC , &ts );
const uint64_t ended = ts.tv_sec * 1000000000 + ts.tv_nsec;
addSample( samples_[ pos ] , ended - cpuStarts_[ pos ] );
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( 240 , 0 ) , true );
float angle( 0 );
displayed_.resize( n , true );
for ( auto i = 0u ; i < n ; i ++ ) {
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 ] << '\0';
ImGui::Checkbox( sb.data( ) , &displayed_[ i ] );
ImGui::PopStyleColor( );
if ( ImGui::IsItemHovered( ) ) {
ImGui::BeginTooltip( );
char tms[ 12 ];
snprintf( tms , 12 , "%.3f" , secDurations_[ i ] );
sb.clear( ) << sections_[ i ] << '\0';
ImGui::PushStyleColor( ImGuiCol_Text , color );
ImGui::Text( "%s" , sb.data( ) );
ImGui::PopStyleColor( );
sb.clear( ) << "\nTime: " << tms << "ms\n\nCPU: ";
snprintf( tms , 12 , "%.3f" , secCPUDurations_[ i ] );
sb << tms << "ms\nGPU: ";
snprintf( tms , 12 , "%.3f" , secGPUDurations_[ i ] );
sb << tms << "ms" << '\0';
ImGui::Text( "%s" , sb.data( ) );
ImGui::EndTooltip( );
}
ImGui::PushStyleColor( ImGuiCol_Text , color );
ImGui::SameLine( 180 );
ImGui::Text( "%.3fms" , secDurations_[ 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( );
}
void T_Profiler::extendGPUQueries( ) noexcept
{
const auto iSize{ gpuQueries_.size( ) };
const auto iGrowth{ gpuQueries_.growth( ) };
gpuQueries_.resize( iSize + iGrowth , 0 );
glGenQueries( iGrowth , &gpuQueries_[ iSize ] );
}
void T_Profiler::addSample(
T_SamplesList_& list ,
const uint64_t duration
) noexcept
{
if ( list.size( ) == 0 || ( list.size( ) < History
&& list[ 0 ].nSamples == Samples ) ) {
list.insert( 0 , T_ProfilerSamples{ 0 , 0 } );
} else if ( list.size( ) == History && list[ 0 ].nSamples == Samples ) {
for ( auto i = 1u ; i < History ; i ++ ) {
list[ i ] = list[ i - 1 ];
}
list[ 0 ].sum = 0;
list[ 0 ].nSamples = 0;
}
list[ 0 ].sum += float( duration ) * 1e-6;
list[ 0 ].nSamples ++;
}
float T_Profiler::computeDuration(
T_SamplesList_ const& section
) noexcept
{
float total = 0;
float nSamples = 0;
const auto ns{ section.size( ) };
for ( auto i = 0u ; i < ns ; i ++ ) {
total += section[ i ].sum;
nSamples += section[ i ].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;
}