[PATCH xserver] WIP : Add support for variable refresh rate in modesetting DDX

pichika.uday.kiran at intel.com pichika.uday.kiran at intel.com
Wed Jun 10 12:05:25 UTC 2020


From: Uday Kiran Pichika <pichika.uday.kiran at intel.com>

These changes have been ported from AMD GPU DDX driver.

This patch adds support for setting the CRTC variable refresh property
for suitable windows flipping via the Present extension.

The "VariableRefresh" Option is added to modesetting backend in this patch. This
option defaults to false, and must be set to "true" in an X conf
file for variable refresh support in the driver. Also DRM connector should
be VRR capable.

In order for a window to be suitable for variable refresh it must have
the _VARIABLE_REFRESH property with a 32-bit CARDINAL value of 1.

Then the window must pass the checks required to be suitable for
Present extension flips - it must cover the entire X screen and no
other window may already be flipping.

With these conditions met every CRTC for the X screen will have their
variable refresh property set to true.

Kernel Changes to support this feature is under development.

What is Tested ?
Verified with DOTA2, Xonotic gaming apps, private glxapp runs in
Fullscreen mode. When these apps are running in fullscreen mode,
able to call the set_vrr on all the CRTCs with VRR_ENABLED property.

Signed-off-by: Uday Kiran Pichika <pichika.uday.kiran at intel.com>
---
 hw/xfree86/drivers/modesetting/driver.c       | 156 ++++++++++++++++++
 hw/xfree86/drivers/modesetting/driver.h       |  14 ++
 .../drivers/modesetting/drmmode_display.c     |  99 +++++++++++
 .../drivers/modesetting/drmmode_display.h     |   6 +
 hw/xfree86/drivers/modesetting/present.c      |  39 ++++-
 5 files changed, 312 insertions(+), 2 deletions(-)
 mode change 100644 => 100755 hw/xfree86/drivers/modesetting/driver.c
 mode change 100644 => 100755 hw/xfree86/drivers/modesetting/driver.h
 mode change 100644 => 100755 hw/xfree86/drivers/modesetting/drmmode_display.c
 mode change 100644 => 100755 hw/xfree86/drivers/modesetting/drmmode_display.h
 mode change 100644 => 100755 hw/xfree86/drivers/modesetting/present.c

diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c
old mode 100644
new mode 100755
index 0c1867f02..eed6d71d2
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -80,6 +80,12 @@ static Bool ms_pci_probe(DriverPtr driver,
                          intptr_t match_data);
 static Bool ms_driver_func(ScrnInfoPtr scrn, xorgDriverFuncOp op, void *data);
 
+static Atom vrr_atom;
+static Bool property_vectors_wrapped;
+static Bool restore_property_vector;
+static int (*saved_change_property) (ClientPtr client);
+static int (*saved_delete_property) (ClientPtr client);
+
 #ifdef XSERVER_LIBPCIACCESS
 static const struct pci_id_match ms_device_match[] = {
     {
@@ -131,6 +137,7 @@ static const OptionInfoRec Options[] = {
     {OPTION_ZAPHOD_HEADS, "ZaphodHeads", OPTV_STRING, {0}, FALSE},
     {OPTION_DOUBLE_SHADOW, "DoubleShadow", OPTV_BOOLEAN, {0}, FALSE},
     {OPTION_ATOMIC, "Atomic", OPTV_BOOLEAN, {0}, FALSE},
+    {OPTION_VARIABLE_REFRESH, "VariableRefresh", OPTV_BOOLEAN, {0}, FALSE},
     {-1, NULL, OPTV_NONE, {0}, FALSE}
 };
 
@@ -706,6 +713,129 @@ msBlockHandler_oneshot(ScreenPtr pScreen, void *pTimeout)
     drmmode_set_desired_modes(pScrn, &ms->drmmode, TRUE);
 }
 
+Bool
+ms_window_has_variable_refresh(modesettingPtr ms, WindowPtr win) {
+    struct ms_vrr_priv *priv = dixLookupPrivate(&win->devPrivates, &ms->drmmode.vrrPrivateKeyRec);
+
+    return priv->variable_refresh;
+}
+
+static void
+ms_vrr_property_update(WindowPtr window, Bool variable_refresh)
+{
+    ScrnInfoPtr scrn = xf86ScreenToScrn(window->drawable.pScreen);
+    modesettingPtr ms = modesettingPTR(scrn);
+
+    struct ms_vrr_priv *priv = dixLookupPrivate(&window->devPrivates, &ms->drmmode.vrrPrivateKeyRec);
+    priv->variable_refresh = variable_refresh;
+
+    if (ms->flip_window == window && ms->drmmode.present_flipping)
+        ms_present_set_screen_vrr(scrn, variable_refresh);
+}
+
+static int
+ms_change_property(ClientPtr client)
+{
+    WindowPtr window = NULL;
+    int ret = 0;
+
+    REQUEST(xChangePropertyReq);
+
+    client->requestVector[X_ChangeProperty] = saved_change_property;
+    ret = saved_change_property(client);
+    if (ret != Success)
+        return ret;
+
+    if (restore_property_vector)
+        return ret;
+
+    client->requestVector[X_ChangeProperty] = ms_change_property;
+
+    ret = dixLookupWindow(&window, stuff->window, client, DixSetPropAccess);
+    if (ret != Success)
+        return ret;
+
+    if (stuff->property == vrr_atom &&
+        xf86ScreenToScrn(window->drawable.pScreen)->PreInit == PreInit &&
+        stuff->format == 32 && stuff->nUnits == 1) {
+        uint32_t *value = (uint32_t *)(stuff + 1);
+        ms_vrr_property_update(window, *value != 0);
+    }
+
+    return ret;
+}
+
+/* Wrapper for xserver/dix/property.c:ProcDeleteProperty */
+static int
+ms_delete_property(ClientPtr client)
+{
+    WindowPtr window;
+    int ret;
+
+    REQUEST(xDeletePropertyReq);
+
+    client->requestVector[X_DeleteProperty] = saved_delete_property;
+    ret = saved_delete_property(client);
+
+    if (restore_property_vector)
+        return ret;
+
+    client->requestVector[X_DeleteProperty] = ms_delete_property;
+
+    if (ret != Success)
+        return ret;
+
+    ret = dixLookupWindow(&window, stuff->window, client, DixSetPropAccess);
+    if (ret != Success)
+        return ret;
+
+    if (stuff->property == vrr_atom &&
+        xf86ScreenToScrn(window->drawable.pScreen)->PreInit == PreInit)
+        ms_vrr_property_update(window, FALSE);
+
+    return ret;
+}
+
+static void
+ms_unwrap_property_requests(ScrnInfoPtr scrn)
+{
+    int i;
+
+    if (!property_vectors_wrapped)
+        return;
+
+    if (ProcVector[X_ChangeProperty] == ms_change_property)
+        ProcVector[X_ChangeProperty] = saved_change_property;
+    else
+        restore_property_vector = TRUE;
+
+    if (ProcVector[X_DeleteProperty] == ms_delete_property)
+        ProcVector[X_DeleteProperty] = saved_delete_property;
+    else
+        restore_property_vector = TRUE;
+
+    for (i = 0; i < currentMaxClients; i++) {
+        if (clients[i]->requestVector[X_ChangeProperty] == ms_change_property) {
+            clients[i]->requestVector[X_ChangeProperty] = saved_change_property;
+        } else {
+            restore_property_vector = TRUE;
+        }
+
+        if (clients[i]->requestVector[X_DeleteProperty] == ms_delete_property) {
+            clients[i]->requestVector[X_DeleteProperty] = saved_delete_property;
+        } else {
+            restore_property_vector = TRUE;
+        }
+    }
+
+    if (restore_property_vector) {
+        xf86DrvMsg(scrn->scrnIndex, X_WARNING,
+                   "Couldn't unwrap some window property request vectors\n");
+    }
+
+    property_vectors_wrapped = FALSE;
+}
+
 static void
 FreeRec(ScrnInfoPtr pScrn)
 {
@@ -725,6 +855,7 @@ FreeRec(ScrnInfoPtr pScrn)
         ms_ent = ms_ent_priv(pScrn);
         ms_ent->fd_ref--;
         if (!ms_ent->fd_ref) {
+            ms_unwrap_property_requests(pScrn);
             if (ms->pEnt->location.type == BUS_PCI)
                 ret = drmClose(ms->fd);
             else
@@ -1053,6 +1184,13 @@ PreInit(ScrnInfoPtr pScrn, int flags)
                    ms->drmmode.shadow_enable ? "YES" : "NO");
 
         ms->drmmode.shadow_enable2 = msShouldDoubleShadow(pScrn, ms);
+    } else {
+        if (!pScrn->is_gpu) {
+            MessageType from = xf86GetOptValBool(ms->drmmode.Options, OPTION_VARIABLE_REFRESH,
+                                                 &ms->vrr_support) ? X_CONFIG : X_DEFAULT;
+            xf86DrvMsg(pScrn->scrnIndex, from, "VariableRefresh: %sabled\n",
+                       ms->vrr_support ? "en" : "dis");
+        }
     }
 
     ms->drmmode.pageflip =
@@ -1451,6 +1589,12 @@ CreateScreenResources(ScreenPtr pScreen)
         pScrPriv->rrStartFlippingPixmapTracking = msStartFlippingPixmapTracking;
     }
 
+    if (ms->vrr_support &&
+        !dixRegisterPrivateKey(&ms->drmmode.vrrPrivateKeyRec,
+                               PRIVATE_WINDOW,
+                               sizeof(struct ms_vrr_priv)))
+        return FALSE;
+
     return ret;
 }
 
@@ -1805,6 +1949,18 @@ ScreenInit(ScreenPtr pScreen, int argc, char **argv)
 
     pScrn->vtSema = TRUE;
 
+    if (ms->vrr_support) {
+        if (!property_vectors_wrapped) {
+            saved_change_property = ProcVector[X_ChangeProperty];
+            ProcVector[X_ChangeProperty] = ms_change_property;
+            saved_delete_property = ProcVector[X_DeleteProperty];
+            ProcVector[X_DeleteProperty] = ms_delete_property;
+            property_vectors_wrapped = TRUE;
+        }
+        vrr_atom = MakeAtom("_VARIABLE_REFRESH",
+                             strlen("_VARIABLE_REFRESH"), TRUE);
+    }
+
     return TRUE;
 }
 
diff --git a/hw/xfree86/drivers/modesetting/driver.h b/hw/xfree86/drivers/modesetting/driver.h
old mode 100644
new mode 100755
index 7ee9f6827..0998d3908
--- a/hw/xfree86/drivers/modesetting/driver.h
+++ b/hw/xfree86/drivers/modesetting/driver.h
@@ -43,6 +43,10 @@
 #include "drmmode_display.h"
 #define MS_LOGLEVEL_DEBUG 4
 
+struct ms_vrr_priv {
+    Bool variable_refresh;
+};
+
 typedef enum {
     OPTION_SW_CURSOR,
     OPTION_DEVICE_PATH,
@@ -52,6 +56,7 @@ typedef enum {
     OPTION_ZAPHOD_HEADS,
     OPTION_DOUBLE_SHADOW,
     OPTION_ATOMIC,
+    OPTION_VARIABLE_REFRESH,
 } modesettingOpts;
 
 typedef struct
@@ -122,6 +127,13 @@ typedef struct _modesettingRec {
 
     Bool kms_has_modifiers;
 
+    /* VRR stuff */
+    Bool vrr_support;
+    WindowPtr flip_window;
+
+    Bool is_connector_vrr_capable;
+    uint32_t connector_prop_id;
+
     /* shadow API */
     struct {
         Bool (*Setup)(ScreenPtr);
@@ -224,3 +236,5 @@ Bool ms_do_pageflip(ScreenPtr screen,
 #endif
 
 int ms_flush_drm_events(ScreenPtr screen);
+Bool ms_window_has_variable_refresh(modesettingPtr ms, WindowPtr win);
+void ms_present_set_screen_vrr(ScrnInfoPtr scrn, Bool vrr_enabled);
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c
old mode 100644
new mode 100755
index 8e6b697c4..a14a3341e
--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
@@ -2208,6 +2208,53 @@ drmmode_crtc_create_planes(xf86CrtcPtr crtc, int num)
     drmModeFreePlaneResources(kplane_res);
 }
 
+static uint32_t
+drmmode_crtc_get_prop_id(uint32_t drm_fd,
+                         drmModeObjectPropertiesPtr props,
+                         char const* name)
+{
+    uint32_t i, prop_id = 0;
+
+    for (i = 0; !prop_id && i < props->count_props; ++i) {
+        drmModePropertyPtr drm_prop =
+                     drmModeGetProperty(drm_fd, props->props[i]);
+
+        if (!drm_prop)
+            continue;
+
+        if (strcmp(drm_prop->name, name) == 0)
+            prop_id = drm_prop->prop_id;
+
+        drmModeFreeProperty(drm_prop);
+    }
+
+    return prop_id;
+}
+
+static void
+drmmode_crtc_vrr_init(int drm_fd, xf86CrtcPtr crtc)
+{
+    drmModeObjectPropertiesPtr drm_props;
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+    drmmode_ptr drmmode = drmmode_crtc->drmmode;
+
+    if (drmmode->vrr_prop_id)
+        return;
+
+    drm_props = drmModeObjectGetProperties(drm_fd,
+                                           drmmode_crtc->mode_crtc->crtc_id,
+                                           DRM_MODE_OBJECT_CRTC);
+
+    if (!drm_props)
+        return;
+
+    drmmode->vrr_prop_id = drmmode_crtc_get_prop_id(drm_fd,
+                                                    drm_props,
+                                                    "VRR_ENABLED");
+
+    drmModeFreeObjectProperties(drm_props);
+}
+
 static unsigned int
 drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num)
 {
@@ -2250,6 +2297,8 @@ drmmode_crtc_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res
     /* Hide any cursors which may be active from previous users */
     drmModeSetCursor(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, 0, 0, 0);
 
+    drmmode_crtc_vrr_init(drmmode->fd, crtc);
+
     /* Mark num'th crtc as in use on this device. */
     ms_ent->assigned_crtcs |= (1 << num);
     xf86DrvMsgVerb(pScrn->scrnIndex, X_INFO, MS_LOGLEVEL_DEBUG,
@@ -2918,6 +2967,34 @@ drmmode_create_name(ScrnInfoPtr pScrn, drmModeConnectorPtr koutput, char *name,
         snprintf(name, 32, "%s-%d", output_names[koutput->connector_type], koutput->connector_type_id);
 }
 
+static Bool
+drmmode_connector_check_vrr_capable(uint32_t drm_fd,
+                         drmModeObjectPropertiesPtr props,
+                         char const* name)
+{
+    uint32_t i;
+    Bool found = FALSE;
+    uint64_t prop_value = 0;
+
+    for (i = 0; !found && i < props->count_props; ++i) {
+        drmModePropertyPtr drm_prop = drmModeGetProperty(drm_fd, props->props[i]);
+
+        if (!drm_prop)
+            continue;
+
+        if (strcasecmp(drm_prop->name, name) == 0) {
+            prop_value = props->prop_values[i];
+            found = TRUE;
+        }
+
+        drmModeFreeProperty(drm_prop);
+    }
+    if(found)
+        return prop_value ? TRUE : FALSE;
+
+    return FALSE;
+}
+
 static unsigned int
 drmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_res, int num, Bool dynamic, int crtcshift)
 {
@@ -3023,6 +3100,9 @@ drmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_r
     }
     /* work out the possible clones later */
     output->possible_clones = 0;
+    props = drmModeObjectGetProperties(drmmode->fd,
+                                       drmmode_output->output_id,
+                                       DRM_MODE_OBJECT_CONNECTOR);
 
     if (ms->atomic_modeset) {
         if (!drmmode_prop_info_copy(drmmode_output->props_connector,
@@ -3048,6 +3128,7 @@ drmmode_output_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, drmModeResPtr mode_r
             RRPostPendingProperties(output->randr_output);
         }
     }
+    ms->is_connector_vrr_capable = drmmode_connector_check_vrr_capable(drmmode->fd, props, "VRR_CAPABLE");
 
     return 1;
 
@@ -3968,6 +4049,24 @@ drmmode_get_default_bpp(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int *depth,
     return;
 }
 
+void
+drmmode_crtc_set_vrr(xf86CrtcPtr crtc, Bool enabled)
+{
+    ScrnInfoPtr pScrn = crtc->scrn;
+    modesettingPtr ms = modesettingPTR(pScrn);
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+    drmmode_ptr drmmode = drmmode_crtc->drmmode;
+
+    //TODO(pichika) check if ms->fd == drmmode->fd
+    if (drmmode->vrr_prop_id && drmmode_crtc->vrr_enabled != enabled &&
+        drmModeObjectSetProperty(ms->fd,
+                                 drmmode_crtc->mode_crtc->crtc_id,
+                                 DRM_MODE_OBJECT_CRTC,
+                                 drmmode->vrr_prop_id,
+                                 enabled) == 0)
+        drmmode_crtc->vrr_enabled = enabled;
+}
+
 /*
  * We hook the screen's cursor-sprite (swcursor) functions to see if a swcursor
  * is active. When a swcursor is active we disabe page-flipping.
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.h b/hw/xfree86/drivers/modesetting/drmmode_display.h
old mode 100644
new mode 100755
index 2711a5776..4d5b0d30a
--- a/hw/xfree86/drivers/modesetting/drmmode_display.h
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.h
@@ -112,6 +112,7 @@ typedef struct {
 
     DevPrivateKeyRec pixmapPrivateKeyRec;
     DevScreenPrivateKeyRec spritePrivateKeyRec;
+    DevPrivateKeyRec vrrPrivateKeyRec;
     /* Number of SW cursors currently visible on this screen */
     int sprites_visible;
 
@@ -127,6 +128,8 @@ typedef struct {
 
     Bool dri2_enable;
     Bool present_enable;
+
+    uint32_t vrr_prop_id;
 } drmmode_rec, *drmmode_ptr;
 
 typedef struct {
@@ -193,6 +196,8 @@ typedef struct {
 
     Bool enable_flipping;
     Bool flipping_active;
+
+    Bool vrr_enabled;
 } drmmode_crtc_private_rec, *drmmode_crtc_private_ptr;
 
 typedef struct {
@@ -293,5 +298,6 @@ void drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode);
 int drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, uint32_t flags, void *data);
 
 void drmmode_set_dpms(ScrnInfoPtr scrn, int PowerManagementMode, int flags);
+void drmmode_crtc_set_vrr(xf86CrtcPtr crtc, Bool enabled);
 
 #endif
diff --git a/hw/xfree86/drivers/modesetting/present.c b/hw/xfree86/drivers/modesetting/present.c
old mode 100644
new mode 100755
index fea2d663e..9ba8cea5f
--- a/hw/xfree86/drivers/modesetting/present.c
+++ b/hw/xfree86/drivers/modesetting/present.c
@@ -70,6 +70,22 @@ ms_present_get_ust_msc(RRCrtcPtr crtc, CARD64 *ust, CARD64 *msc)
     return ms_get_crtc_ust_msc(xf86_crtc, ust, msc);
 }
 
+/*
+ * Changes the variable refresh state for every CRTC on the screen.
+ */
+void
+ms_present_set_screen_vrr(ScrnInfoPtr scrn, Bool vrr_enabled)
+{
+    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
+    xf86CrtcPtr crtc;
+    int i;
+
+    for (i = 0; i < config->num_crtc; i++) {
+        crtc = config->crtc[i];
+        drmmode_crtc_set_vrr(crtc, vrr_enabled);
+    }
+}
+
 /*
  * Called when the queued vblank event has occurred
  */
@@ -295,11 +311,17 @@ ms_present_check_flip(RRCrtcPtr crtc,
     ScreenPtr screen = window->drawable.pScreen;
     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
     modesettingPtr ms = modesettingPTR(scrn);
+    Bool ret;
 
     if (ms->drmmode.sprites_visible > 0)
         return FALSE;
 
-    return ms_present_check_unflip(crtc, window, pixmap, sync_flip, reason);
+    if(ret = ms_present_check_unflip(crtc, window, pixmap, sync_flip, reason))
+    {
+        ms->flip_window = window;
+    }
+
+    return ret;
 }
 
 /*
@@ -321,7 +343,7 @@ ms_present_flip(RRCrtcPtr crtc,
     Bool ret;
     struct ms_present_vblank_event *event;
 
-    if (!ms_present_check_flip(crtc, screen->root, pixmap, sync_flip, NULL))
+    if (!ms_present_check_flip(crtc, ms->flip_window, pixmap, sync_flip, NULL))
         return FALSE;
 
     event = calloc(1, sizeof(struct ms_present_vblank_event));
@@ -334,6 +356,17 @@ ms_present_flip(RRCrtcPtr crtc,
     event->event_id = event_id;
     event->unflip = FALSE;
 
+    /* A window can only flip if it covers the entire X screen.
+     * Only one window can flip at a time.
+     *
+     * If the window also has the variable refresh property then
+     * variable refresh supported can be enabled on every CRTC.
+     */
+    if (ms->vrr_support && ms->is_connector_vrr_capable &&
+          ms_window_has_variable_refresh(ms, ms->flip_window)) {
+        ms_present_set_screen_vrr(scrn, TRUE);
+    }
+
     ret = ms_do_pageflip(screen, pixmap, event, drmmode_crtc->vblank_pipe, !sync_flip,
                          ms_present_flip_handler, ms_present_flip_abort,
                          "Present-flip");
@@ -356,6 +389,8 @@ ms_present_unflip(ScreenPtr screen, uint64_t event_id)
     int i;
     struct ms_present_vblank_event *event;
 
+    ms_present_set_screen_vrr(scrn, FALSE);
+
     event = calloc(1, sizeof(struct ms_present_vblank_event));
     if (!event)
         return;
-- 
2.25.1



More information about the xorg-devel mailing list