Skip to content

Commit

Permalink
vblankmanager: Port to timerfd
Browse files Browse the repository at this point in the history
Ports vblankmanager to be timerfd based, and also fxies issues
with re-arming at higher refresh rates.

The old nudge thread method still exists (and is needed for VR), and
it has also been improved to fix the re-arming issue.

The old method can be enabled with GAMESCOPE_DISABLE_TIMERFD.
  • Loading branch information
misyltoad committed Dec 7, 2023
1 parent 46bbf72 commit ecccd77
Show file tree
Hide file tree
Showing 6 changed files with 538 additions and 235 deletions.
6 changes: 3 additions & 3 deletions src/drm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ static void page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsi

// This is the last vblank time
uint64_t vblanktime = sec * 1'000'000'000lu + usec * 1'000lu;
vblank_mark_possible_vblank(vblanktime);
g_VBlankTimer.MarkVBlank( vblanktime, true );

// TODO: get the fbids_queued instance from data if we ever have more than one in flight

Expand Down Expand Up @@ -1351,7 +1351,7 @@ void load_pnps(void)
fclose(f);
}

static bool env_to_bool(const char *env)
bool env_to_bool(const char *env)
{
if (!env || !*env)
return false;
Expand Down Expand Up @@ -1758,7 +1758,7 @@ int drm_commit(struct drm_t *drm, const struct FrameInfo_t *frameInfo )
// is queued and would end up being the new page flip, rather than here.
// However, the page flip handler is called when the page flip occurs,
// not when it is successfully queued.
g_uVblankDrawTimeNS = get_time_in_nanos() - g_SteamCompMgrVBlankTime.pipe_write_time;
g_VBlankTimer.UpdateLastDrawTime( get_time_in_nanos() - g_SteamCompMgrVBlankTime.ulWakeupTime );

if ( isPageFlip ) {
// Wait for flip handler to unlock
Expand Down
2 changes: 1 addition & 1 deletion src/rendervulkan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2600,7 +2600,7 @@ static void present_wait_thread_func( void )
{
g_device.vk.WaitForPresentKHR( g_device.device(), g_output.swapChain, present_wait_id, 1'000'000'000lu );
uint64_t vblanktime = get_time_in_nanos();
vblank_mark_possible_vblank( vblanktime );
g_VBlankTimer.MarkVBlank( vblanktime, true );
mangoapp_output_update( vblanktime );
}
}
Expand Down
98 changes: 40 additions & 58 deletions src/steamcompmgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,6 @@ extern float g_flHDRItmTargetNits;

uint64_t g_lastWinSeq = 0;

extern std::atomic<uint64_t> g_lastVblank;

static std::shared_ptr<wlserver_ctm> s_scRGB709To2020Matrix;

std::string clipboard;
Expand All @@ -156,6 +154,16 @@ uint64_t timespec_to_nanos(struct timespec& spec)
return spec.tv_sec * 1'000'000'000ul + spec.tv_nsec;
}

timespec nanos_to_timespec( uint64_t ulNanos )
{
timespec ts =
{
.tv_sec = time_t( ulNanos / 1'000'000'000ul ),
.tv_nsec = long( ulNanos % 1'000'000'000ul ),
};
return ts;
}

static void
update_runtime_info();

Expand Down Expand Up @@ -882,7 +890,7 @@ bool synchronize;

std::mutex g_SteamCompMgrXWaylandServerMutex;

VBlankTimeInfo_t g_SteamCompMgrVBlankTime = {};
gamescope::VBlankTime g_SteamCompMgrVBlankTime = {};

uint64_t g_uCurrentBasePlaneCommitID = 0;
bool g_bCurrentBasePlaneIsFifo = false;
Expand Down Expand Up @@ -1196,9 +1204,7 @@ uint64_t get_time_in_nanos()

void sleep_for_nanos(uint64_t nanos)
{
timespec ts;
ts.tv_sec = time_t(nanos / 1'000'000'000ul);
ts.tv_nsec = long(nanos % 1'000'000'000ul);
timespec ts = nanos_to_timespec( nanos );
nanosleep(&ts, nullptr);
}

Expand Down Expand Up @@ -2738,7 +2744,7 @@ paint_all(bool async)
}

// Update to let the vblank manager know we are currently compositing.
g_bCurrentlyCompositing = bDoComposite;
g_VBlankTimer.UpdateWasCompositing( bDoComposite );

if ( bDoComposite == true )
{
Expand Down Expand Up @@ -2861,7 +2867,7 @@ paint_all(bool async)
}

// Update the time it took us to commit
g_uVblankDrawTimeNS = get_time_in_nanos() - g_SteamCompMgrVBlankTime.pipe_write_time;
g_VBlankTimer.UpdateLastDrawTime( get_time_in_nanos() - g_SteamCompMgrVBlankTime.ulWakeupTime );
}
else
{
Expand Down Expand Up @@ -3141,7 +3147,7 @@ paint_all(bool async)
int ret = system(cmd);

/* Above call may fail, ffmpeg returns 0 on success */
if (ret) {
if (ret) {
xwm_log.infof("Ffmpeg call return status %i", ret);
xwm_log.errorf( "Failed to save screenshot to %s", pTimeBuffer );
} else {
Expand Down Expand Up @@ -5589,6 +5595,7 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev)
focusDirty = true;
}
}
#if 0
if ( ev->atom == ctx->atoms.gamescopeTuneableVBlankRedZone )
{
g_uVblankDrawBufferRedZoneNS = (uint64_t)get_prop( ctx, ctx->root, ctx->atoms.gamescopeTuneableVBlankRedZone, g_uDefaultVBlankRedZone );
Expand All @@ -5597,6 +5604,7 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev)
{
g_uVBlankRateOfDecayPercentage = (uint64_t)get_prop( ctx, ctx->root, ctx->atoms.gamescopeTuneableRateOfDecay, g_uDefaultVBlankRateOfDecayPercentage );
}
#endif
if ( ev->atom == ctx->atoms.gamescopeScalingFilter )
{
int nScalingMode = get_prop( ctx, ctx->root, ctx->atoms.gamescopeScalingFilter, 0 );
Expand Down Expand Up @@ -6399,7 +6407,7 @@ void handle_done_commits_xwayland( xwayland_ctx_t *ctx, bool vblank, uint64_t vb
{
std::lock_guard<std::mutex> lock( ctx->doneCommits.listCommitsDoneLock );

uint64_t next_refresh_time = g_SteamCompMgrVBlankTime.target_vblank_time;
uint64_t next_refresh_time = g_SteamCompMgrVBlankTime.schedule.ulTargetVBlank;

// commits that were not ready to be presented based on their display timing.
static std::vector< CommitDoneEntry_t > commits_before_their_time;
Expand Down Expand Up @@ -6456,7 +6464,7 @@ void handle_done_commits_xdg()
{
std::lock_guard<std::mutex> lock( g_steamcompmgr_xdg_done_commits.listCommitsDoneLock );

uint64_t next_refresh_time = g_SteamCompMgrVBlankTime.target_vblank_time;
uint64_t next_refresh_time = g_SteamCompMgrVBlankTime.schedule.ulTargetVBlank;

// commits that were not ready to be presented based on their display timing.
std::vector< CommitDoneEntry_t > commits_before_their_time;
Expand Down Expand Up @@ -6490,7 +6498,7 @@ void handle_done_commits_xdg()

void handle_presented_for_window( steamcompmgr_win_t* w )
{
uint64_t next_refresh_time = g_SteamCompMgrVBlankTime.target_vblank_time;
uint64_t next_refresh_time = g_SteamCompMgrVBlankTime.schedule.ulTargetVBlank;

uint64_t refresh_cycle = g_nSteamCompMgrTargetFPS && steamcompmgr_window_should_limit_fps( w )
? g_SteamCompMgrLimitedAppRefreshCycle
Expand Down Expand Up @@ -7015,41 +7023,6 @@ void xwayland_ctx_t::Dispatch()
}
}

static bool
dispatch_vblank( int fd )
{
bool vblank = false;
for (;;)
{
VBlankTimeInfo_t vblanktime = {};
ssize_t ret = read( fd, &vblanktime, sizeof( vblanktime ) );
if ( ret < 0 )
{
if ( errno == EAGAIN )
break;

xwm_log.errorf_errno( "steamcompmgr: dispatch_vblank: read failed" );
break;
}

g_SteamCompMgrVBlankTime = vblanktime;

uint64_t diff = get_time_in_nanos() - vblanktime.pipe_write_time;

// give it 1 ms of slack from pipe to steamcompmgr... maybe too long
if ( diff > 1'000'000ul )
{
gpuvis_trace_printf( "ignored stale vblank" );
}
else
{
gpuvis_trace_printf( "got vblank" );
vblank = true;
}
}
return vblank;
}

struct rgba_t
{
uint8_t r,g,b,a;
Expand Down Expand Up @@ -7699,9 +7672,6 @@ steamcompmgr_main(int argc, char **argv)
vrsession_steam_mode( steamMode );
#endif

int vblankFD = vblank_init();
assert( vblankFD >= 0 );

std::unique_lock<std::mutex> xwayland_server_guard(g_SteamCompMgrXWaylandServerMutex);

// Initialize any xwayland ctxs we have
Expand Down Expand Up @@ -7735,12 +7705,8 @@ steamcompmgr_main(int argc, char **argv)
}

bool vblank = false;
g_SteamCompMgrWaiter.AddWaitable(
new gamescope::CFunctionWaitable{ vblankFD, [ vblankFD, &vblank ]()
{
vblank = dispatch_vblank( vblankFD );
}}
);
g_SteamCompMgrWaiter.AddWaitable( &g_VBlankTimer );
g_VBlankTimer.RearmTimer( true );

{
gamescope_xwayland_server_t *pServer = NULL;
Expand Down Expand Up @@ -7786,6 +7752,12 @@ steamcompmgr_main(int argc, char **argv)

g_SteamCompMgrWaiter.PollEvents();

if ( std::optional<gamescope::VBlankTime> pendingVBlank = g_VBlankTimer.ProcessVBlank() )
{
g_SteamCompMgrVBlankTime = *pendingVBlank;
vblank = true;
}

if ( g_bRun == false )
{
break;
Expand Down Expand Up @@ -8077,13 +8049,13 @@ steamcompmgr_main(int argc, char **argv)
// If we are compositing, always force sync flips because we currently wait
// for composition to finish before submitting.
// If we want to do async + composite, we should set up syncfile stuff and have DRM wait on it.
const bool bNeedsSyncFlip = bForceSyncFlip || g_bCurrentlyCompositing || nIgnoredOverlayRepaints;
const bool bNeedsSyncFlip = bForceSyncFlip || g_VBlankTimer.WasCompositing() || nIgnoredOverlayRepaints;
const bool bDoAsyncFlip = ( ((g_nAsyncFlipsEnabled >= 1) && g_bSupportsAsyncFlips && bSurfaceWantsAsync && !bHasOverlay) || bVRR ) && !bSteamOverlayOpen && !bNeedsSyncFlip;

bool bShouldPaint = false;
if ( bDoAsyncFlip )
{
if ( hasRepaint && !g_bCurrentlyCompositing )
if ( hasRepaint && !g_VBlankTimer.WasCompositing() )
bShouldPaint = true;
}
else
Expand Down Expand Up @@ -8119,6 +8091,16 @@ steamcompmgr_main(int argc, char **argv)
}
}

if ( vblank )
{
// Pre-emptively re-arm the vblank timer if it
// isn't already re-armed.
//
// Juuust in case pageflip handler doesn't happen
// so we don't stop vblanking forever.
g_VBlankTimer.RearmTimer( true );
}

update_vrr_atoms(root_ctx, false, &flush_root);

if (global_focus.cursor)
Expand Down
3 changes: 2 additions & 1 deletion src/steamcompmgr.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ unsigned int get_time_in_milliseconds(void);
uint64_t get_time_in_nanos();
void sleep_for_nanos(uint64_t nanos);
void sleep_until_nanos(uint64_t nanos);
timespec nanos_to_timespec( uint64_t ulNanos );

void steamcompmgr_main(int argc, char **argv);

Expand Down Expand Up @@ -155,7 +156,7 @@ wlserver_vk_swapchain_feedback* steamcompmgr_get_base_layer_swapchain_feedback()

struct wlserver_x11_surface_info *lookup_x11_surface_info_from_xid( gamescope_xwayland_server_t *xwayland_server, uint32_t xid );

extern VBlankTimeInfo_t g_SteamCompMgrVBlankTime;
extern gamescope::VBlankTime g_SteamCompMgrVBlankTime;
extern pid_t focusWindow_pid;

void init_xwayland_ctx(uint32_t serverId, gamescope_xwayland_server_t *xwayland_server);
Expand Down
Loading

0 comments on commit ecccd77

Please sign in to comment.