vblankmanager: Track latent frames

This commit is contained in:
Joshua Ashton 2022-02-14 19:15:52 +00:00
parent 765ddd74b1
commit 7ec498af0a
3 changed files with 44 additions and 25 deletions

View file

@ -253,7 +253,7 @@ void steamcompmgr_fpslimit_add_commit( std::shared_ptr<commit_t> commit )
g_FrameLimitCommits.push( commit ); g_FrameLimitCommits.push( commit );
} }
void steamcompmgr_fpslimit_release_commit( int consecutive_missed_frame_count ) bool steamcompmgr_fpslimit_release_commit( int consecutive_missed_frame_count )
{ {
std::unique_lock<std::mutex> lock(g_FrameLimitCommitsMutex); std::unique_lock<std::mutex> lock(g_FrameLimitCommitsMutex);
@ -265,19 +265,23 @@ void steamcompmgr_fpslimit_release_commit( int consecutive_missed_frame_count )
g_FrameLimitCommits = std::queue< std::shared_ptr<commit_t> >(); g_FrameLimitCommits = std::queue< std::shared_ptr<commit_t> >();
g_nAppBufferCount = 0; g_nAppBufferCount = 0;
g_nMaxAppBufferCount = 0; g_nMaxAppBufferCount = 0;
return; return true;
} }
// Only allow 1 latent buffer -- essentially go to only "double // Only allow 1 latent buffer -- essentially go to only "double
// buffering" when we are falling behind. // buffering" when we are falling behind.
if ( g_nAppBufferCount >= g_nMaxAppBufferCount - 1 ) if ( g_nAppBufferCount == g_nMaxAppBufferCount )
{ {
if ( !g_FrameLimitCommits.empty() ) if ( !g_FrameLimitCommits.empty() )
{ {
g_FrameLimitCommits.pop(); g_FrameLimitCommits.pop();
g_nAppBufferCount--; g_nAppBufferCount--;
} }
return false;
} }
return true;
} }
@ -494,13 +498,17 @@ retry:
close( entry.fence ); close( entry.fence );
uint64_t now = get_time_in_nanos(); uint64_t frametime;
static uint64_t lastFrameTime = now; if ( entry.fps_nudge || entry.mangoapp_nudge )
uint64_t frametime = now - lastFrameTime; {
lastFrameTime = now; uint64_t now = get_time_in_nanos();
static uint64_t lastFrameTime = now;
frametime = now - lastFrameTime;
lastFrameTime = now;
}
if ( entry.fps_nudge ) if ( entry.fps_nudge )
fpslimit_mark_frame(); fpslimit_mark_frame( frametime );
{ {
std::unique_lock< std::mutex > lock( entry.ctx->listCommitsDoneLock ); std::unique_lock< std::mutex > lock( entry.ctx->listCommitsDoneLock );

View file

@ -168,12 +168,12 @@ static std::mutex g_TargetFPSMutex;
static std::condition_variable g_TargetFPSCondition; static std::condition_variable g_TargetFPSCondition;
static int g_nFpsLimitTargetFPS = 0; static int g_nFpsLimitTargetFPS = 0;
void steamcompmgr_fpslimit_release_commit( int consecutive_missed_frame_count ); bool steamcompmgr_fpslimit_release_commit( int consecutive_missed_frame_count );
void steamcompmgr_fpslimit_release_all(); void steamcompmgr_fpslimit_release_all();
void steamcompmgr_send_frame_done_to_focus_window(); void steamcompmgr_send_frame_done_to_focus_window();
// Dump some stats. // Dump some stats.
//#define FPS_LIMIT_DEBUG #define FPS_LIMIT_DEBUG
// 1.80ms for the app's deadzone to account for varying GPU clocks, other variances, etc // 1.80ms for the app's deadzone to account for varying GPU clocks, other variances, etc
uint64_t g_uFPSLimiterRedZoneNS = 1'800'000; uint64_t g_uFPSLimiterRedZoneNS = 1'800'000;
@ -187,6 +187,9 @@ bool g_bFPSLimitThreadRun = true;
extern bool g_bLowLatency; extern bool g_bLowLatency;
uint64_t g_uFPSLimitLastFullFrameTime = 0;
uint64_t g_uFPSLimitDoneToDoneTime = 0;
void fpslimitThreadRun( void ) void fpslimitThreadRun( void )
{ {
pthread_setname_np( pthread_self(), "gamescope-fps" ); pthread_setname_np( pthread_self(), "gamescope-fps" );
@ -197,6 +200,10 @@ void fpslimitThreadRun( void )
uint64_t vblank = 0; uint64_t vblank = 0;
int consecutive_missed_frame_count = 0; int consecutive_missed_frame_count = 0;
bool last_frame_was_late = false; bool last_frame_was_late = false;
g_uFPSLimitLastFullFrameTime = get_time_in_nanos();
uint64_t lastFullFrameTime = 0;
uint64_t donetodonetime = 0;
bool isLatent = false;
while ( true ) while ( true )
{ {
int nTargetFPS; int nTargetFPS;
@ -228,6 +235,8 @@ void fpslimitThreadRun( void )
} }
} }
nTargetFPS = g_nFpsLimitTargetFPS; nTargetFPS = g_nFpsLimitTargetFPS;
lastFullFrameTime = g_uFPSLimitLastFullFrameTime;
donetodonetime = g_uFPSLimitDoneToDoneTime;
} }
const int refresh = g_nNestedRefresh ? g_nNestedRefresh : g_nOutputRefresh; const int refresh = g_nNestedRefresh ? g_nNestedRefresh : g_nOutputRefresh;
@ -252,7 +261,7 @@ void fpslimitThreadRun( void )
bool useFrameCallbacks = fpslimit_use_frame_callbacks_for_focus_window( nTargetFPS, 0 ); bool useFrameCallbacks = fpslimit_use_frame_callbacks_for_focus_window( nTargetFPS, 0 );
uint64_t t0 = lastCommitReleased; uint64_t t0 = lastCommitReleased;
uint64_t t1 = get_time_in_nanos(); uint64_t t1 = lastFullFrameTime;
// Not the actual frame time of the game // Not the actual frame time of the game
// this is the time of the amount of work a 'frame' has done. // this is the time of the amount of work a 'frame' has done.
@ -333,7 +342,7 @@ void fpslimitThreadRun( void )
if ( !no_frame ) if ( !no_frame )
{ {
mangoapp_update( targetInterval, frameTime, latency ); mangoapp_update( isLatent ? donetodonetime : targetInterval, frameTime, latency );
} }
#ifdef FPS_LIMIT_DEBUG #ifdef FPS_LIMIT_DEBUG
@ -343,7 +352,7 @@ void fpslimitThreadRun( void )
sleep_until_nanos( targetPoint ); sleep_until_nanos( targetPoint );
lastCommitReleased = get_time_in_nanos(); lastCommitReleased = get_time_in_nanos();
steamcompmgr_fpslimit_release_commit( consecutive_missed_frame_count ); isLatent = steamcompmgr_fpslimit_release_commit( consecutive_missed_frame_count );
// If we aren't vblank aligned, nudge ourselves to process done commits now. // If we aren't vblank aligned, nudge ourselves to process done commits now.
if ( !useFrameCallbacks ) if ( !useFrameCallbacks )
@ -357,16 +366,7 @@ void fpslimitThreadRun( void )
{ {
if ( nTargetFPS ) if ( nTargetFPS )
{ {
uint64_t t0 = lastCommitReleased; mangoapp_update( donetodonetime, donetodonetime, uint64_t(~0ull) );
uint64_t t1 = get_time_in_nanos();
uint64_t frametime = t1 - t0 + targetInterval;
uint64_t latency = uint64_t(~0ull);
if ( refresh % nTargetFPS == 0 )
latency = frametime;
mangoapp_update( frametime, frametime, latency );
} }
} }
@ -390,13 +390,23 @@ void fpslimit_shutdown( void )
g_TargetFPSCondition.notify_all(); g_TargetFPSCondition.notify_all();
} }
void fpslimit_mark_frame( void ) void fpslimit_mark_frame( uint64_t frametime )
{ {
uint64_t now = get_time_in_nanos();
{
std::unique_lock<std::mutex> lock(g_TargetFPSMutex);
g_uFPSLimitLastFullFrameTime = now;
g_uFPSLimitDoneToDoneTime = frametime;
}
g_TargetFPSCondition.notify_all(); g_TargetFPSCondition.notify_all();
} }
bool fpslimit_use_frame_callbacks_for_focus_window( int nTargetFPS, int nVBlankCount ) bool fpslimit_use_frame_callbacks_for_focus_window( int nTargetFPS, int nVBlankCount )
{ {
// Avoids a race incase the surface changes
// We don't use this anymore since we force no-fifo
return true;
#if 0
if ( !nTargetFPS ) if ( !nTargetFPS )
return true; return true;
@ -412,6 +422,7 @@ bool fpslimit_use_frame_callbacks_for_focus_window( int nTargetFPS, int nVBlankC
// call them from fpslimit // call them from fpslimit
return false; return false;
} }
#endif
} }
// Called from steamcompmgr thread // Called from steamcompmgr thread

View file

@ -15,7 +15,7 @@ extern uint64_t g_uVBlankRateOfDecayPercentage;
void fpslimit_init( void ); void fpslimit_init( void );
void fpslimit_mark_frame( void ); void fpslimit_mark_frame( uint64_t frametime );
bool fpslimit_use_frame_callbacks_for_focus_window( int nTargetFPS, int nVBlankCount ); bool fpslimit_use_frame_callbacks_for_focus_window( int nTargetFPS, int nVBlankCount );