drm, steamcompmgr: Allow async flips if supported with GAMESCOPE_ALLOW_TEARING
Reworks some logic re. frame pacing and presentation to flip directly when an app asks when this is enabled. The next step for us would be to make this respect whether the app has vsync enabled or not using the tearing protocol too. This is a good starting point though.
This commit is contained in:
parent
c40c8aa482
commit
a4ab1b0459
4 changed files with 64 additions and 13 deletions
15
src/drm.cpp
15
src/drm.cpp
|
@ -41,6 +41,12 @@ bool g_bUseLayers = true;
|
|||
bool g_bDebugLayers = false;
|
||||
const char *g_sOutputName = nullptr;
|
||||
|
||||
#ifndef DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP
|
||||
#define DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP 0x15
|
||||
#endif
|
||||
|
||||
bool g_bSupportsAsyncFlips = false;
|
||||
|
||||
enum drm_mode_generation g_drmModeGeneration = DRM_MODE_GENERATE_CVT;
|
||||
|
||||
static LogScope drm_log("drm");
|
||||
|
@ -800,6 +806,10 @@ bool init_drm(struct drm_t *drm, int width, int height, int refresh)
|
|||
drm->allow_modifiers = true;
|
||||
}
|
||||
|
||||
g_bSupportsAsyncFlips = drmGetCap(drm->fd, DRM_CAP_ATOMIC_ASYNC_PAGE_FLIP, &cap) == 0 && cap != 0;
|
||||
if (!g_bSupportsAsyncFlips)
|
||||
drm_log.errorf("Immediate flips are not supported by the KMS driver");
|
||||
|
||||
if (!get_resources(drm)) {
|
||||
return false;
|
||||
}
|
||||
|
@ -1536,7 +1546,7 @@ drm_prepare_liftoff( struct drm_t *drm, const struct FrameInfo_t *frameInfo, boo
|
|||
|
||||
/* Prepares an atomic commit for the provided scene-graph. Returns 0 on success,
|
||||
* negative errno on failure or if the scene-graph can't be presented directly. */
|
||||
int drm_prepare( struct drm_t *drm, const struct FrameInfo_t *frameInfo )
|
||||
int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameInfo )
|
||||
{
|
||||
drm->pending.screen_type = drm_get_screen_type(drm);
|
||||
|
||||
|
@ -1556,6 +1566,9 @@ int drm_prepare( struct drm_t *drm, const struct FrameInfo_t *frameInfo )
|
|||
// We do internal refcounting with these events
|
||||
flags |= DRM_MODE_PAGE_FLIP_EVENT;
|
||||
|
||||
if ( async )
|
||||
flags |= DRM_MODE_PAGE_FLIP_ASYNC;
|
||||
|
||||
if ( needs_modeset ) {
|
||||
flags |= DRM_MODE_ATOMIC_ALLOW_MODESET;
|
||||
|
||||
|
|
|
@ -197,7 +197,7 @@ extern enum drm_mode_generation g_drmModeGeneration;
|
|||
bool init_drm(struct drm_t *drm, int width, int height, int refresh);
|
||||
void finish_drm(struct drm_t *drm);
|
||||
int drm_commit(struct drm_t *drm, const struct FrameInfo_t *frameInfo );
|
||||
int drm_prepare( struct drm_t *drm, const struct FrameInfo_t *frameInfo );
|
||||
int drm_prepare( struct drm_t *drm, bool async, const struct FrameInfo_t *frameInfo );
|
||||
void drm_rollback( struct drm_t *drm );
|
||||
bool drm_poll_state(struct drm_t *drm);
|
||||
uint32_t drm_fbid_from_dmabuf( struct drm_t *drm, struct wlr_buffer *buf, struct wlr_dmabuf_attributes *dma_buf );
|
||||
|
@ -221,3 +221,5 @@ 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);
|
||||
|
||||
extern bool g_bSupportsAsyncFlips;
|
||||
|
|
|
@ -208,6 +208,8 @@ struct commit_t
|
|||
|
||||
#define MWM_TEAROFF_WINDOW 1
|
||||
|
||||
static bool g_bAsyncFlipsEnabled = false;
|
||||
|
||||
struct motif_hints_t
|
||||
{
|
||||
unsigned long flags;
|
||||
|
@ -307,6 +309,7 @@ uint32_t lastPublishedInputCounter;
|
|||
|
||||
bool focusDirty = false;
|
||||
bool hasRepaint = false;
|
||||
bool hasRepaintNonBasePlane = false;
|
||||
|
||||
unsigned long damageSequence = 0;
|
||||
|
||||
|
@ -1569,7 +1572,7 @@ static void update_touch_scaling( const struct FrameInfo_t *frameInfo )
|
|||
}
|
||||
|
||||
static void
|
||||
paint_all()
|
||||
paint_all(bool async)
|
||||
{
|
||||
gamescope_xwayland_server_t *root_server = wlserver_get_xwayland_server(0);
|
||||
xwayland_ctx_t *root_ctx = root_server->ctx.get();
|
||||
|
@ -1823,7 +1826,7 @@ paint_all()
|
|||
|
||||
if ( !bNeedsComposite )
|
||||
{
|
||||
int ret = drm_prepare( &g_DRM, &frameInfo );
|
||||
int ret = drm_prepare( &g_DRM, async, &frameInfo );
|
||||
if ( ret == 0 )
|
||||
bDoComposite = false;
|
||||
else if ( ret == -EACCES )
|
||||
|
@ -1877,7 +1880,7 @@ paint_all()
|
|||
|
||||
layer->linearFilter = false;
|
||||
|
||||
int ret = drm_prepare( &g_DRM, &frameInfo );
|
||||
int ret = drm_prepare( &g_DRM, async, &frameInfo );
|
||||
|
||||
// Happens when we're VT-switched away
|
||||
if ( ret == -EACCES )
|
||||
|
@ -1896,7 +1899,7 @@ paint_all()
|
|||
drm_rollback( &g_DRM );
|
||||
|
||||
// Try once again to in case we need to fall back to another mode.
|
||||
ret = drm_prepare( &g_DRM, &frameInfo );
|
||||
ret = drm_prepare( &g_DRM, async, &frameInfo );
|
||||
|
||||
// Happens when we're VT-switched away
|
||||
if ( ret == -EACCES )
|
||||
|
@ -3753,7 +3756,7 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev)
|
|||
|
||||
if ( gameFocused && ( w == ctx->focus.overlayWindow || w == ctx->focus.notificationWindow ) )
|
||||
{
|
||||
hasRepaint = true;
|
||||
hasRepaintNonBasePlane = true;
|
||||
}
|
||||
if ( w == ctx->focus.externalOverlayWindow )
|
||||
{
|
||||
|
@ -4181,6 +4184,10 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev)
|
|||
g_bIsCompositeDebug = !!get_prop( ctx, ctx->root, ctx->atoms.gamescopeCompositeDebug, 0 );
|
||||
hasRepaint = true;
|
||||
}
|
||||
if ( ev->atom == ctx->atoms.gamescopeAllowTearing )
|
||||
{
|
||||
g_bAsyncFlipsEnabled = !!get_prop( ctx, ctx->root, ctx->atoms.gamescopeAllowTearing, 0 );
|
||||
}
|
||||
if (ev->atom == ctx->atoms.wineHwndStyle)
|
||||
{
|
||||
win * w = find_win(ctx, ev->window);
|
||||
|
@ -4416,12 +4423,12 @@ void handle_done_commits( xwayland_ctx_t *ctx )
|
|||
{
|
||||
if ( w == global_focus.overlayWindow && w->opacity != TRANSLUCENT )
|
||||
{
|
||||
hasRepaint = true;
|
||||
hasRepaintNonBasePlane = true;
|
||||
}
|
||||
|
||||
if ( w == global_focus.notificationWindow && w->opacity != TRANSLUCENT )
|
||||
{
|
||||
hasRepaint = true;
|
||||
hasRepaintNonBasePlane = true;
|
||||
}
|
||||
}
|
||||
if ( ctx->focus.outdatedInteractiveFocus )
|
||||
|
@ -4432,7 +4439,7 @@ void handle_done_commits( xwayland_ctx_t *ctx )
|
|||
// If this is an external overlay, repaint
|
||||
if ( w == ctx->focus.externalOverlayWindow && w->opacity != TRANSLUCENT )
|
||||
{
|
||||
hasRepaint = true;
|
||||
hasRepaintNonBasePlane = true;
|
||||
}
|
||||
// If this is the main plane, repaint
|
||||
if ( w == global_focus.focusWindow && !w->isSteamStreamingClient )
|
||||
|
@ -4443,7 +4450,7 @@ void handle_done_commits( xwayland_ctx_t *ctx )
|
|||
|
||||
if ( w == global_focus.overrideWindow )
|
||||
{
|
||||
hasRepaint = true;
|
||||
hasRepaintNonBasePlane = true;
|
||||
}
|
||||
|
||||
if ( w->isSteamStreamingClientVideo && global_focus.focusWindow && global_focus.focusWindow->isSteamStreamingClient )
|
||||
|
@ -5094,6 +5101,8 @@ void init_xwayland_ctx(gamescope_xwayland_server_t *xwayland_server)
|
|||
ctx->atoms.gamescopeCompositeForce = XInternAtom( ctx->dpy, "GAMESCOPE_COMPOSITE_FORCE", false );
|
||||
ctx->atoms.gamescopeCompositeDebug = XInternAtom( ctx->dpy, "GAMESCOPE_COMPOSITE_DEBUG", false );
|
||||
|
||||
ctx->atoms.gamescopeAllowTearing = XInternAtom( ctx->dpy, "GAMESCOPE_ALLOW_TEARING", false );
|
||||
|
||||
ctx->atoms.wineHwndStyle = XInternAtom( ctx->dpy, "_WINE_HWND_STYLE", false );
|
||||
ctx->atoms.wineHwndStyleEx = XInternAtom( ctx->dpy, "_WINE_HWND_EXSTYLE", false );
|
||||
|
||||
|
@ -5424,12 +5433,37 @@ steamcompmgr_main(int argc, char **argv)
|
|||
if (focusDirty)
|
||||
determine_and_apply_focus();
|
||||
|
||||
if ( ( g_bTakeScreenshot == true || hasRepaint == true || is_fading_out() ) && vblank == true )
|
||||
static int nMissedOverlayPaints = 0;
|
||||
|
||||
const bool bForceSyncFlip = 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 || nMissedOverlayPaints;
|
||||
const bool bDoAsyncFlip = g_bAsyncFlipsEnabled && g_bSupportsAsyncFlips && !bNeedsSyncFlip;
|
||||
|
||||
bool bShouldPaint = false;
|
||||
if ( bDoAsyncFlip )
|
||||
{
|
||||
paint_all();
|
||||
if ( hasRepaint && !g_bCurrentlyCompositing )
|
||||
bShouldPaint = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
bShouldPaint = vblank && ( hasRepaint || hasRepaintNonBasePlane || bForceSyncFlip );
|
||||
}
|
||||
|
||||
if ( !bShouldPaint && hasRepaintNonBasePlane && vblank )
|
||||
nMissedOverlayPaints++;
|
||||
|
||||
if ( bShouldPaint )
|
||||
{
|
||||
paint_all( !vblank );
|
||||
|
||||
// Consumed the need to repaint here
|
||||
hasRepaint = false;
|
||||
hasRepaintNonBasePlane = false;
|
||||
nMissedOverlayPaints = 0;
|
||||
|
||||
// If we're in the middle of a fade, pump an event into the loop to
|
||||
// make sure we keep pushing frames even if the app isn't updating.
|
||||
|
|
|
@ -146,6 +146,8 @@ struct xwayland_ctx_t
|
|||
Atom gamescopeCompositeForce;
|
||||
Atom gamescopeCompositeDebug;
|
||||
|
||||
Atom gamescopeAllowTearing;
|
||||
|
||||
Atom wineHwndStyle;
|
||||
Atom wineHwndStyleEx;
|
||||
} atoms;
|
||||
|
|
Loading…
Reference in a new issue