Skip to content

Commit

Permalink
drm, steamcompmgr: Support for VRR
Browse files Browse the repository at this point in the history
Exposes GAMESCOPE_VRR_ENABLED, GAMESCOPE_VRR_CAPABLE and GAMESCOPE_VRR_FEEDBACK
  • Loading branch information
misyltoad committed Oct 19, 2022
1 parent 4e748ba commit ec6bd30
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 41 deletions.
67 changes: 67 additions & 0 deletions src/drm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -205,9 +205,12 @@ static struct plane *find_primary_plane(struct drm_t *drm)

static void drm_unlock_fb_internal( struct drm_t *drm, struct fb *fb );

std::atomic<uint64_t> g_nCompletedPageFlipCount = { 0u };

static void page_flip_handler(int fd, unsigned int frame, unsigned int sec, unsigned int usec, unsigned int crtc_id, void *data)
{
uint64_t flipcount = (uint64_t)data;
g_nCompletedPageFlipCount = flipcount;

if ( g_DRM.crtc->id != crtc_id )
return;
Expand Down Expand Up @@ -506,6 +509,8 @@ static bool refresh_state( drm_t *drm )

conn->target_refresh = 0;

conn->vrr_capable = !!conn->initial_prop_values["vrr_capable"];

drm_log.debugf("found new connector '%s'", conn->name);
}

Expand All @@ -524,8 +529,13 @@ static bool refresh_state( drm_t *drm )
crtc->has_ctm = (crtc->props.find( "CTM" ) != crtc->props.end());
if (!crtc->has_ctm)
drm_log.infof("CRTC %" PRIu32 " has no CTM support", crtc->id);
crtc->has_vrr_enabled = (crtc->props.find( "VRR_ENABLED" ) != crtc->props.end());
if (!crtc->has_vrr_enabled)
drm_log.infof("CRTC %" PRIu32 " has no VRR_ENABLED support", crtc->id);

crtc->current.active = crtc->initial_prop_values["ACTIVE"];
if (crtc->has_vrr_enabled)
drm->current.vrr_enabled = crtc->initial_prop_values["VRR_ENABLED"];
}

for (size_t i = 0; i < drm->planes.size(); i++) {
Expand Down Expand Up @@ -977,6 +987,8 @@ void finish_drm(struct drm_t *drm)
add_crtc_property(req, &drm->crtcs[i], "DEGAMMA_LUT", 0);
if ( drm->crtcs[i].has_ctm )
add_crtc_property(req, &drm->crtcs[i], "CTM", 0);
if ( drm->crtcs[i].has_vrr_enabled )
add_crtc_property(req, &drm->crtcs[i], "VRR_ENABLED", 0);
add_crtc_property(req, &drm->crtcs[i], "ACTIVE", 0);
}
for ( size_t i = 0; i < drm->planes.size(); i++ ) {
Expand Down Expand Up @@ -1672,6 +1684,7 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI
drm_update_gamma_lut(drm);
drm_update_degamma_lut(drm);
drm_update_color_mtx(drm);
drm_update_vrr_state(drm);

drm->fbids_in_req.clear();

Expand Down Expand Up @@ -1732,6 +1745,12 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI
if (ret < 0)
return ret;
}
if (crtc->has_vrr_enabled)
{
int ret = add_crtc_property(drm->req, crtc, "VRR_ENABLED", 0);
if (ret < 0)
return ret;
}

int ret = add_crtc_property(drm->req, crtc, "ACTIVE", 0);
if (ret < 0)
Expand Down Expand Up @@ -1775,6 +1794,13 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI
return false;
}

if (drm->crtc->has_vrr_enabled)
{
ret = add_crtc_property(drm->req, drm->crtc, "VRR_ENABLED", drm->pending.vrr_enabled);
if (ret < 0)
return false;
}

ret = add_crtc_property(drm->req, drm->crtc, "ACTIVE", 1);
if (ret < 0)
return false;
Expand Down Expand Up @@ -1802,6 +1828,13 @@ int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameI
if (ret < 0)
return ret;
}

if ( drm->crtc->has_vrr_enabled && drm->pending.vrr_enabled != drm->current.vrr_enabled )
{
int ret = add_crtc_property(drm->req, drm->crtc, "VRR_ENABLED", drm->pending.vrr_enabled );
if (ret < 0)
return ret;
}
}

drm->flags = flags;
Expand Down Expand Up @@ -1967,6 +2000,16 @@ bool drm_set_color_mtx(struct drm_t *drm, float *mtx, enum drm_screen_type scree
return false;
}

void drm_set_vrr_enabled(struct drm_t *drm, bool enabled)
{
drm->wants_vrr_enabled = enabled;
}

bool drm_get_vrr_in_use(struct drm_t *drm)
{
return drm->current.vrr_enabled;
}

bool drm_set_color_gain_blend(struct drm_t *drm, float blend)
{
drm->pending.gain_blend = blend;
Expand Down Expand Up @@ -2120,6 +2163,22 @@ bool drm_update_color_mtx(struct drm_t *drm)
return true;
}

bool drm_update_vrr_state(struct drm_t *drm)
{
drm->pending.vrr_enabled = false;

if ( drm->connector && drm->crtc && drm->crtc->has_vrr_enabled )
{
if ( drm->wants_vrr_enabled && drm->connector->vrr_capable )
drm->pending.vrr_enabled = true;
}

if (drm->pending.vrr_enabled != drm->current.vrr_enabled)
drm->needs_modeset = true;

return true;
}

static float safe_pow(float x, float y)
{
// Avoids pow(x, 1.0f) != x.
Expand Down Expand Up @@ -2363,3 +2422,11 @@ int drm_get_default_refresh(struct drm_t *drm)

return 60;
}

bool drm_get_vrr_capable(struct drm_t *drm)
{
if ( drm->connector )
return drm->connector->vrr_capable;

return false;
}
8 changes: 8 additions & 0 deletions src/drm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ struct crtc {
bool has_gamma_lut;
bool has_degamma_lut;
bool has_ctm;
bool has_vrr_enabled;

struct {
bool active;
Expand All @@ -88,6 +89,7 @@ struct connector {
char *model;

int target_refresh;
bool vrr_capable;

struct {
uint32_t crtc_id;
Expand Down Expand Up @@ -162,7 +164,9 @@ struct drm_t {
};
float gain_blend = 0.0f;
enum drm_screen_type screen_type = DRM_SCREEN_TYPE_INTERNAL;
bool vrr_enabled = false;
} current, pending;
bool wants_vrr_enabled = false;

/* FBs in the atomic request, but not yet submitted to KMS */
std::vector < uint32_t > fbids_in_req;
Expand Down Expand Up @@ -239,11 +243,15 @@ bool drm_set_color_gain_blend(struct drm_t *drm, float blend);
bool drm_update_gamma_lut(struct drm_t *drm);
bool drm_update_degamma_lut(struct drm_t *drm);
bool drm_update_color_mtx(struct drm_t *drm);
bool drm_update_vrr_state(struct drm_t *drm);
bool drm_set_gamma_exponent(struct drm_t *drm, float *vec, enum drm_screen_type screen_type);
bool drm_set_degamma_exponent(struct drm_t *drm, float *vec, enum drm_screen_type screen_type);
drm_screen_type drm_get_screen_type(struct drm_t *drm);

char *find_drm_node_by_devid(dev_t devid);
int drm_get_default_refresh(struct drm_t *drm);
bool drm_get_vrr_capable(struct drm_t *drm);
void drm_set_vrr_enabled(struct drm_t *drm, bool enabled);
bool drm_get_vrr_in_use(struct drm_t *drm);

extern bool g_bSupportsAsyncFlips;
67 changes: 54 additions & 13 deletions src/steamcompmgr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,8 @@ struct commit_t

int g_nAsyncFlipsEnabled = 0;
int g_nSteamMaxHeight = 0;
bool g_bVRRCapable_CachedValue = false;
bool g_bVRRInUse_CachedValue = false;

struct motif_hints_t
{
Expand Down Expand Up @@ -4252,6 +4254,11 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev)
g_nSteamMaxHeight = get_prop( ctx, ctx->root, ctx->atoms.gamescopeSteamMaxHeight, 0 );
focusDirty = true;
}
if ( ev->atom == ctx->atoms.gamescopeVRREnabled )
{
bool enabled = !!get_prop( ctx, ctx->root, ctx->atoms.gamescopeVRREnabled, 0 );
drm_set_vrr_enabled( &g_DRM, enabled );
}
if ( ev->atom == ctx->atoms.gamescopeDisplayForceInternal )
{
if ( !BIsNested() )
Expand Down Expand Up @@ -5214,6 +5221,9 @@ void init_xwayland_ctx(gamescope_xwayland_server_t *xwayland_server)
ctx->atoms.gamescopeCursorVisibleFeedback = XInternAtom( ctx->dpy, "GAMESCOPE_CURSOR_VISIBLE_FEEDBACK", false );

ctx->atoms.gamescopeSteamMaxHeight = XInternAtom( ctx->dpy, "GAMESCOPE_STEAM_MAX_HEIGHT", false );
ctx->atoms.gamescopeVRREnabled = XInternAtom( ctx->dpy, "GAMESCOPE_VRR_ENABLED", false );
ctx->atoms.gamescopeVRRCapable = XInternAtom( ctx->dpy, "GAMESCOPE_VRR_CAPABLE", false );
ctx->atoms.gamescopeVRRInUse = XInternAtom( ctx->dpy, "GAMESCOPE_VRR_FEEDBACK", false );

ctx->atoms.wineHwndStyle = XInternAtom( ctx->dpy, "_WINE_HWND_STYLE", false );
ctx->atoms.wineHwndStyleEx = XInternAtom( ctx->dpy, "_WINE_HWND_EXSTYLE", false );
Expand Down Expand Up @@ -5260,6 +5270,36 @@ void init_xwayland_ctx(gamescope_xwayland_server_t *xwayland_server)
}
}

void update_vrr_atoms(xwayland_ctx_t *root_ctx, bool force)
{
bool capable = drm_get_vrr_capable( &g_DRM );
if ( capable != g_bVRRCapable_CachedValue || force )
{
uint32_t capable_value = capable ? 1 : 0;
XChangeProperty(root_ctx->dpy, root_ctx->root, root_ctx->atoms.gamescopeVRRCapable, XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)&capable_value, 1 );
g_bVRRCapable_CachedValue = capable;
}

bool in_use = drm_get_vrr_in_use( &g_DRM );
if ( in_use != g_bVRRInUse_CachedValue || force )
{
uint32_t in_use_value = in_use ? 1 : 0;
XChangeProperty(root_ctx->dpy, root_ctx->root, root_ctx->atoms.gamescopeVRRInUse, XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)&in_use_value, 1 );
g_bVRRInUse_CachedValue = in_use;
}

// Don't update this in-sync with DRM vrr usage.
// Keep this as a preference, starting with off.
if ( force )
{
uint32_t enabled_value = 0;
XChangeProperty(root_ctx->dpy, root_ctx->root, root_ctx->atoms.gamescopeVRREnabled, XA_CARDINAL, 32, PropModeReplace,
(unsigned char *)&enabled_value, 1 );
}
}

void update_mode_atoms(xwayland_ctx_t *root_ctx)
{
if ( drm_get_screen_type(&g_DRM) == DRM_SCREEN_TYPE_INTERNAL )
Expand Down Expand Up @@ -5297,6 +5337,8 @@ extern int g_nPreferredOutputHeight;

static bool g_bWasFSRActive = false;

extern std::atomic<uint64_t> g_nCompletedPageFlipCount;

void
steamcompmgr_main(int argc, char **argv)
{
Expand Down Expand Up @@ -5442,6 +5484,7 @@ steamcompmgr_main(int argc, char **argv)
}
}

update_vrr_atoms(root_ctx, true);
update_mode_atoms(root_ctx);

for (;;)
Expand Down Expand Up @@ -5583,22 +5626,21 @@ steamcompmgr_main(int argc, char **argv)
if (focusDirty)
determine_and_apply_focus();

const bool bAllowRelaxedVsync = g_nAsyncFlipsEnabled >= 2;

static int nIgnoredOverlayRepaints = 0;
static int nBasePlaneMissedVBlankCount = 0;

const bool bVRR = drm_get_vrr_in_use( &g_DRM );

const bool bSteamOverlayOpen = global_focus.overlayWindow && global_focus.overlayWindow->opacity;
// If we are running behind, allow tearing.
const bool bSurfaceWantsAsync = (g_HeldCommits[HELD_COMMIT_BASE] && g_HeldCommits[HELD_COMMIT_BASE]->async) || (nBasePlaneMissedVBlankCount && bAllowRelaxedVsync);
const bool bSurfaceWantsAsync = (g_HeldCommits[HELD_COMMIT_BASE] && g_HeldCommits[HELD_COMMIT_BASE]->async);

const bool bForceRepaint = g_bForceRepaint.exchange(false);
const bool bForceSyncFlip = bForceRepaint || g_bTakeScreenshot || is_fading_out();
// 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 bDoAsyncFlip = (g_nAsyncFlipsEnabled >= 1) && g_bSupportsAsyncFlips && bSurfaceWantsAsync && !bSteamOverlayOpen && !bNeedsSyncFlip;
const bool bDoAsyncFlip = ( ((g_nAsyncFlipsEnabled >= 1) && g_bSupportsAsyncFlips && bSurfaceWantsAsync) || bVRR ) && !bSteamOverlayOpen && !bNeedsSyncFlip;

bool bShouldPaint = false;
if ( bDoAsyncFlip )
Expand All @@ -5611,19 +5653,16 @@ steamcompmgr_main(int argc, char **argv)
bShouldPaint = vblank && ( hasRepaint || hasRepaintNonBasePlane || bForceSyncFlip );
}

// If we have a pending page flip and doing VRR, lets not do another...
if ( bVRR && g_nCompletedPageFlipCount != g_DRM.flipcount )
bShouldPaint = false;

if ( !bShouldPaint && hasRepaintNonBasePlane && vblank )
nIgnoredOverlayRepaints++;

if ( !hasRepaint && vblank )
nBasePlaneMissedVBlankCount++;

if ( bShouldPaint )
{
paint_all( !vblank );

// Consumed the need to repaint here
if (hasRepaint)
nBasePlaneMissedVBlankCount = 0;
paint_all( !vblank && !bVRR );

hasRepaint = false;
hasRepaintNonBasePlane = false;
Expand All @@ -5637,6 +5676,8 @@ steamcompmgr_main(int argc, char **argv)
}
}

update_vrr_atoms(root_ctx, false);

// TODO: Look into making this _RAW
// wlroots, seems to just use normal MONOTONIC
// all over so this may be problematic to just change.
Expand Down
Loading

0 comments on commit ec6bd30

Please sign in to comment.