2020-01-26 22:59:51 +00:00
|
|
|
// Try to figure out when vblank is and notify steamcompmgr to render some time before it
|
|
|
|
|
2022-02-14 10:19:10 +00:00
|
|
|
#include <cstdint>
|
2022-01-25 07:14:18 +00:00
|
|
|
#include <mutex>
|
2020-01-26 22:59:51 +00:00
|
|
|
#include <thread>
|
|
|
|
#include <vector>
|
|
|
|
#include <chrono>
|
2020-01-27 00:28:35 +00:00
|
|
|
#include <atomic>
|
2022-01-25 07:14:18 +00:00
|
|
|
#include <condition_variable>
|
2020-01-26 22:59:51 +00:00
|
|
|
|
2021-06-09 16:42:38 +00:00
|
|
|
#include <assert.h>
|
|
|
|
#include <fcntl.h>
|
|
|
|
#include <unistd.h>
|
2020-01-26 22:59:51 +00:00
|
|
|
|
2020-01-27 00:28:35 +00:00
|
|
|
#include "gpuvis_trace_utils.h"
|
|
|
|
|
2020-01-26 22:59:51 +00:00
|
|
|
#include "vblankmanager.hpp"
|
|
|
|
#include "steamcompmgr.hpp"
|
2020-06-11 10:42:27 +00:00
|
|
|
#include "wlserver.hpp"
|
2020-01-26 22:59:51 +00:00
|
|
|
#include "main.hpp"
|
2022-07-27 15:44:17 +00:00
|
|
|
#include "drm.hpp"
|
2020-01-26 22:59:51 +00:00
|
|
|
|
2021-06-09 16:42:38 +00:00
|
|
|
static int g_vblankPipe[2];
|
2020-01-26 22:59:51 +00:00
|
|
|
|
2020-09-05 16:53:27 +00:00
|
|
|
std::atomic<uint64_t> g_lastVblank;
|
2020-01-27 00:28:35 +00:00
|
|
|
|
2022-01-25 13:04:32 +00:00
|
|
|
// 3ms by default -- a good starting value.
|
|
|
|
const uint64_t g_uStartingDrawTime = 3'000'000;
|
|
|
|
|
|
|
|
// This is the last time a draw took.
|
|
|
|
std::atomic<uint64_t> g_uVblankDrawTimeNS = { g_uStartingDrawTime };
|
|
|
|
|
2022-02-13 18:35:59 +00:00
|
|
|
// 1.3ms by default. (g_uDefaultMinVBlankTime)
|
|
|
|
// This accounts for some time we cannot account for (which (I think) is the drm_commit -> triggering the pageflip)
|
2022-01-25 13:04:32 +00:00
|
|
|
// It would be nice to make this lower if we can find a way to track that effectively
|
|
|
|
// Perhaps the missing time is spent elsewhere, but given we track from the pipe write
|
|
|
|
// to after the return from `drm_commit` -- I am very doubtful.
|
2022-02-13 18:35:59 +00:00
|
|
|
uint64_t g_uMinVblankTime = g_uDefaultMinVBlankTime;
|
|
|
|
|
|
|
|
// Tuneable
|
|
|
|
// 0.3ms by default. (g_uDefaultVBlankRedZone)
|
|
|
|
// This is the leeway we always apply to our buffer.
|
2022-01-28 23:44:21 +00:00
|
|
|
uint64_t g_uVblankDrawBufferRedZoneNS = g_uDefaultVBlankRedZone;
|
2022-01-25 13:04:32 +00:00
|
|
|
|
|
|
|
// Tuneable
|
2022-01-28 23:44:21 +00:00
|
|
|
// 93% by default. (g_uVBlankRateOfDecayPercentage)
|
2022-01-25 13:04:32 +00:00
|
|
|
// The rate of decay (as a percentage) of the rolling average -> current draw time
|
2022-01-28 23:44:21 +00:00
|
|
|
uint64_t g_uVBlankRateOfDecayPercentage = g_uDefaultVBlankRateOfDecayPercentage;
|
2022-01-25 13:04:32 +00:00
|
|
|
|
2022-02-11 05:28:03 +00:00
|
|
|
const uint64_t g_uVBlankRateOfDecayMax = 1000;
|
2022-01-25 13:04:32 +00:00
|
|
|
|
2022-01-25 07:14:18 +00:00
|
|
|
static std::atomic<uint64_t> g_uRollingMaxDrawTime = { g_uStartingDrawTime };
|
|
|
|
|
2022-01-25 13:04:32 +00:00
|
|
|
//#define VBLANK_DEBUG
|
2020-01-27 00:28:35 +00:00
|
|
|
|
2020-01-26 22:59:51 +00:00
|
|
|
void vblankThreadRun( void )
|
|
|
|
{
|
2021-10-18 16:46:31 +00:00
|
|
|
pthread_setname_np( pthread_self(), "gamescope-vblk" );
|
|
|
|
|
2022-01-25 13:04:32 +00:00
|
|
|
// Start off our average with our starting draw time.
|
|
|
|
uint64_t rollingMaxDrawTime = g_uStartingDrawTime;
|
|
|
|
|
|
|
|
const uint64_t range = g_uVBlankRateOfDecayMax;
|
2020-01-26 22:59:51 +00:00
|
|
|
while ( true )
|
|
|
|
{
|
2022-01-25 13:04:32 +00:00
|
|
|
const uint64_t alpha = g_uVBlankRateOfDecayPercentage;
|
|
|
|
const int refresh = g_nNestedRefresh ? g_nNestedRefresh : g_nOutputRefresh;
|
|
|
|
|
|
|
|
const uint64_t nsecInterval = 1'000'000'000ul / refresh;
|
|
|
|
const uint64_t drawTime = g_uVblankDrawTimeNS;
|
2020-01-27 00:28:35 +00:00
|
|
|
|
2022-07-27 10:46:37 +00:00
|
|
|
// The redzone is relative to 60Hz, scale it by our
|
|
|
|
// target refresh so we don't miss submitting for vblank in DRM.
|
|
|
|
// (This fixes 4K@30Hz screens)
|
|
|
|
const uint64_t nsecToSec = 1'000'000'000ul;
|
2022-07-27 15:58:56 +00:00
|
|
|
const drm_screen_type screen_type = drm_get_screen_type( &g_DRM );
|
2022-07-27 15:44:17 +00:00
|
|
|
const uint64_t redZone = screen_type == DRM_SCREEN_TYPE_INTERNAL
|
|
|
|
? g_uVblankDrawBufferRedZoneNS
|
|
|
|
: ( g_uVblankDrawBufferRedZoneNS * 60 * nsecToSec ) / ( refresh * nsecToSec );
|
2022-07-27 10:46:37 +00:00
|
|
|
|
2022-01-25 13:04:32 +00:00
|
|
|
// This is a rolling average when drawTime < rollingMaxDrawTime,
|
|
|
|
// and a a max when drawTime > rollingMaxDrawTime.
|
|
|
|
// This allows us to deal with spikes in the draw buffer time very easily.
|
|
|
|
// eg. if we suddenly spike up (eg. because of test commits taking a stupid long time),
|
|
|
|
// we will then be able to deal with spikes in the long term, even if several commits after
|
|
|
|
// we get back into a good state and then regress again.
|
2022-02-11 05:28:03 +00:00
|
|
|
|
|
|
|
// If we go over half of our deadzone, be more defensive about things.
|
2022-07-27 10:46:37 +00:00
|
|
|
if ( int64_t(drawTime) - int64_t(redZone / 2) > int64_t(rollingMaxDrawTime) )
|
2022-02-11 05:28:03 +00:00
|
|
|
rollingMaxDrawTime = drawTime;
|
|
|
|
else
|
|
|
|
rollingMaxDrawTime = ( ( alpha * rollingMaxDrawTime ) + ( range - alpha ) * drawTime ) / range;
|
2022-01-25 13:04:32 +00:00
|
|
|
|
|
|
|
// If we need to offset for our draw more than half of our vblank, something is very wrong.
|
|
|
|
// Clamp our max time to half of the vblank if we can.
|
2022-07-27 10:46:37 +00:00
|
|
|
rollingMaxDrawTime = std::min( rollingMaxDrawTime, nsecInterval - redZone );
|
2022-01-25 13:04:32 +00:00
|
|
|
|
2022-02-14 13:39:51 +00:00
|
|
|
g_uRollingMaxDrawTime = rollingMaxDrawTime;
|
2022-02-13 18:35:59 +00:00
|
|
|
|
2022-07-27 10:46:37 +00:00
|
|
|
uint64_t offset = rollingMaxDrawTime + redZone;
|
2022-01-25 13:04:32 +00:00
|
|
|
|
|
|
|
#ifdef VBLANK_DEBUG
|
|
|
|
// Debug stuff for logging missed vblanks
|
|
|
|
static uint64_t vblankIdx = 0;
|
|
|
|
static uint64_t lastDrawTime = g_uVblankDrawTimeNS;
|
2022-07-27 10:46:37 +00:00
|
|
|
static uint64_t lastOffset = g_uVblankDrawTimeNS + redZone;
|
2022-01-25 13:04:32 +00:00
|
|
|
|
|
|
|
if ( vblankIdx++ % 300 == 0 || drawTime > lastOffset )
|
2020-09-15 21:46:12 +00:00
|
|
|
{
|
2022-01-25 13:04:32 +00:00
|
|
|
if ( drawTime > lastOffset )
|
|
|
|
fprintf( stderr, " !! missed vblank " );
|
|
|
|
|
2022-02-14 13:39:51 +00:00
|
|
|
fprintf( stderr, "redZone: %.2fms decayRate: %lu%% - rollingMaxDrawTime: %.2fms lastDrawTime: %.2fms lastOffset: %.2fms - drawTime: %.2fms offset: %.2fms\n",
|
2022-07-27 10:46:37 +00:00
|
|
|
redZone / 1'000'000.0,
|
2022-01-25 13:04:32 +00:00
|
|
|
g_uVBlankRateOfDecayPercentage,
|
2022-02-14 13:39:51 +00:00
|
|
|
rollingMaxDrawTime / 1'000'000.0,
|
2022-01-25 13:04:32 +00:00
|
|
|
lastDrawTime / 1'000'000.0,
|
|
|
|
lastOffset / 1'000'000.0,
|
|
|
|
drawTime / 1'000'000.0,
|
|
|
|
offset / 1'000'000.0 );
|
2020-09-15 21:46:12 +00:00
|
|
|
}
|
|
|
|
|
2022-01-25 13:04:32 +00:00
|
|
|
lastDrawTime = drawTime;
|
|
|
|
lastOffset = offset;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
uint64_t lastVblank = g_lastVblank - offset;
|
|
|
|
|
2020-09-05 16:53:27 +00:00
|
|
|
uint64_t now = get_time_in_nanos();
|
|
|
|
uint64_t targetPoint = lastVblank + nsecInterval;
|
2020-01-27 00:28:35 +00:00
|
|
|
while ( targetPoint < now )
|
2020-09-05 16:53:27 +00:00
|
|
|
targetPoint += nsecInterval;
|
|
|
|
|
|
|
|
sleep_until_nanos( targetPoint );
|
2020-09-02 01:17:08 +00:00
|
|
|
|
|
|
|
// give the time of vblank to steamcompmgr
|
2020-09-05 16:24:20 +00:00
|
|
|
uint64_t vblanktime = get_time_in_nanos();
|
2021-06-09 16:42:38 +00:00
|
|
|
|
|
|
|
ssize_t ret = write( g_vblankPipe[ 1 ], &vblanktime, sizeof( vblanktime ) );
|
|
|
|
if ( ret <= 0 )
|
|
|
|
{
|
|
|
|
perror( "vblankmanager: write failed" );
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
gpuvis_trace_printf( "sent vblank" );
|
|
|
|
}
|
2020-01-27 00:28:35 +00:00
|
|
|
|
|
|
|
// Get on the other side of it now
|
2022-01-25 13:04:32 +00:00
|
|
|
sleep_for_nanos( offset + 1'000'000 );
|
2020-01-26 22:59:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-09 16:42:38 +00:00
|
|
|
int vblank_init( void )
|
2020-01-26 22:59:51 +00:00
|
|
|
{
|
2021-06-09 16:42:38 +00:00
|
|
|
if ( pipe2( g_vblankPipe, O_CLOEXEC | O_NONBLOCK ) != 0 )
|
|
|
|
{
|
|
|
|
perror( "vblankmanager: pipe failed" );
|
|
|
|
return -1;
|
|
|
|
}
|
2020-01-27 00:28:35 +00:00
|
|
|
|
2020-09-05 16:53:27 +00:00
|
|
|
g_lastVblank = get_time_in_nanos();
|
2020-01-26 22:59:51 +00:00
|
|
|
|
|
|
|
std::thread vblankThread( vblankThreadRun );
|
|
|
|
vblankThread.detach();
|
2021-06-09 16:42:38 +00:00
|
|
|
|
|
|
|
return g_vblankPipe[ 0 ];
|
2020-01-26 22:59:51 +00:00
|
|
|
}
|
|
|
|
|
2022-01-25 14:35:55 +00:00
|
|
|
void vblank_mark_possible_vblank( uint64_t nanos )
|
2020-01-26 22:59:51 +00:00
|
|
|
{
|
2022-01-25 14:35:55 +00:00
|
|
|
g_lastVblank = nanos;
|
2020-01-26 22:59:51 +00:00
|
|
|
}
|