xserver: Branch 'master' - 10 commits

Adam Jackson ajax at kemper.freedesktop.org
Tue Jun 28 16:57:26 UTC 2016


 hw/xfree86/drivers/modesetting/driver.c          |  277 +++++++++++++++++-
 hw/xfree86/drivers/modesetting/drmmode_display.c |  338 ++++++++++++++++++++---
 hw/xfree86/drivers/modesetting/drmmode_display.h |   28 +
 hw/xfree86/modes/xf86Crtc.h                      |    4 
 hw/xfree86/modes/xf86RandR12.c                   |    4 
 include/scrnintstr.h                             |   14 
 randr/randrstr.h                                 |   18 +
 randr/rrcrtc.c                                   |  131 +++++++-
 8 files changed, 726 insertions(+), 88 deletions(-)

New commits:
commit b83dede9cb930cf55249ad8e935f3c4d4328e2d9
Author: Alex Goins <agoins at nvidia.com>
Date:   Thu Jun 16 20:06:56 2016 -0700

    modesetting: Implement PRIME syncing as a source
    
    Implements (Start/Stop)FlippingPixmapTracking, PresentSharedPixmap, and
    RequestSharedPixmapNotifyDamage, the source functions for PRIME
    synchronization and double buffering. Allows modesetting driver to be used
    as a source with PRIME synchronization.
    
    v1: N/A
    v2: N/A
    v3: N/A
    v4: Initial commit
    v5: Move disabling of reverse PRIME on sink to sink commit
    v6: Rebase onto ToT
    v7: Unchanged
    
    Reviewed-by: Dave Airlie <airlied at redhat.com>
    Signed-off-by: Alex Goins <agoins at nvidia.com>

diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c
index 160cbc9..256ca95 100644
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -577,6 +577,8 @@ redisplay_dirty(ScreenPtr screen, PixmapDirtyUpdatePtr dirty)
 static void
 ms_dirty_update(ScreenPtr screen)
 {
+    modesettingPtr ms = modesettingPTR(xf86ScreenToScrn(screen));
+
     RegionPtr region;
     PixmapDirtyUpdatePtr ent;
 
@@ -586,12 +588,42 @@ ms_dirty_update(ScreenPtr screen)
     xorg_list_for_each_entry(ent, &screen->pixmap_dirty_list, ent) {
         region = DamageRegion(ent->damage);
         if (RegionNotEmpty(region)) {
+            msPixmapPrivPtr ppriv =
+                msGetPixmapPriv(&ms->drmmode, ent->slave_dst);
+
+            if (ppriv->notify_on_damage) {
+                ppriv->notify_on_damage = FALSE;
+
+                ent->slave_dst->drawable.pScreen->
+                    SharedPixmapNotifyDamage(ent->slave_dst);
+            }
+
+            /* Requested manual updating */
+            if (ppriv->defer_dirty_update)
+                continue;
+
             redisplay_dirty(screen, ent);
             DamageEmpty(ent->damage);
         }
     }
 }
 
+static PixmapDirtyUpdatePtr
+ms_dirty_get_ent(ScreenPtr screen, PixmapPtr slave_dst)
+{
+    PixmapDirtyUpdatePtr ent;
+
+    if (xorg_list_is_empty(&screen->pixmap_dirty_list))
+        return NULL;
+
+    xorg_list_for_each_entry(ent, &screen->pixmap_dirty_list, ent) {
+        if (ent->slave_dst == slave_dst)
+            return ent;
+    }
+
+    return NULL;
+}
+
 static void
 msBlockHandler(ScreenPtr pScreen, void *pTimeout, void *pReadmask)
 {
@@ -1017,6 +1049,90 @@ msDisableSharedPixmapFlipping(RRCrtcPtr crtc)
 }
 
 static Bool
+msStartFlippingPixmapTracking(RRCrtcPtr crtc, PixmapPtr src,
+                              PixmapPtr slave_dst1, PixmapPtr slave_dst2,
+                              int x, int y, int dst_x, int dst_y,
+                              Rotation rotation)
+{
+    ScreenPtr pScreen = src->drawable.pScreen;
+    modesettingPtr ms = modesettingPTR(xf86ScreenToScrn(pScreen));
+
+    msPixmapPrivPtr ppriv1 = msGetPixmapPriv(&ms->drmmode, slave_dst1),
+                    ppriv2 = msGetPixmapPriv(&ms->drmmode, slave_dst2);
+
+    if (!PixmapStartDirtyTracking(src, slave_dst1, x, y,
+                                  dst_x, dst_y, rotation)) {
+        return FALSE;
+    }
+
+    if (!PixmapStartDirtyTracking(src, slave_dst2, x, y,
+                                  dst_x, dst_y, rotation)) {
+        PixmapStopDirtyTracking(src, slave_dst1);
+        return FALSE;
+    }
+
+    ppriv1->slave_src = src;
+    ppriv2->slave_src = src;
+
+    ppriv1->dirty = ms_dirty_get_ent(pScreen, slave_dst1);
+    ppriv2->dirty = ms_dirty_get_ent(pScreen, slave_dst2);
+
+    ppriv1->defer_dirty_update = TRUE;
+    ppriv2->defer_dirty_update = TRUE;
+
+    return TRUE;
+}
+
+static Bool
+msPresentSharedPixmap(PixmapPtr slave_dst)
+{
+    ScreenPtr pScreen = slave_dst->drawable.pScreen;
+    modesettingPtr ms = modesettingPTR(xf86ScreenToScrn(pScreen));
+
+    msPixmapPrivPtr ppriv = msGetPixmapPriv(&ms->drmmode, slave_dst);
+
+    RegionPtr region = DamageRegion(ppriv->dirty->damage);
+
+    if (RegionNotEmpty(region)) {
+        redisplay_dirty(ppriv->slave_src->drawable.pScreen, ppriv->dirty);
+        DamageEmpty(ppriv->dirty->damage);
+
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+static Bool
+msStopFlippingPixmapTracking(PixmapPtr src,
+                             PixmapPtr slave_dst1, PixmapPtr slave_dst2)
+{
+    ScreenPtr pScreen = src->drawable.pScreen;
+    modesettingPtr ms = modesettingPTR(xf86ScreenToScrn(pScreen));
+
+    msPixmapPrivPtr ppriv1 = msGetPixmapPriv(&ms->drmmode, slave_dst1),
+                    ppriv2 = msGetPixmapPriv(&ms->drmmode, slave_dst2);
+
+    Bool ret = TRUE;
+
+    ret &= PixmapStopDirtyTracking(src, slave_dst1);
+    ret &= PixmapStopDirtyTracking(src, slave_dst2);
+
+    if (ret) {
+        ppriv1->slave_src = NULL;
+        ppriv2->slave_src = NULL;
+
+        ppriv1->dirty = NULL;
+        ppriv2->dirty = NULL;
+
+        ppriv1->defer_dirty_update = FALSE;
+        ppriv2->defer_dirty_update = FALSE;
+    }
+
+    return ret;
+}
+
+static Bool
 CreateScreenResources(ScreenPtr pScreen)
 {
     ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
@@ -1083,6 +1199,8 @@ CreateScreenResources(ScreenPtr pScreen)
     pScrPriv->rrEnableSharedPixmapFlipping = msEnableSharedPixmapFlipping;
     pScrPriv->rrDisableSharedPixmapFlipping = msDisableSharedPixmapFlipping;
 
+    pScrPriv->rrStartFlippingPixmapTracking = msStartFlippingPixmapTracking;
+
     return ret;
 }
 
@@ -1146,6 +1264,20 @@ msSetSharedPixmapBacking(PixmapPtr ppix, void *fd_handle)
 }
 
 static Bool
+msRequestSharedPixmapNotifyDamage(PixmapPtr ppix)
+{
+    ScreenPtr screen = ppix->drawable.pScreen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+
+    msPixmapPrivPtr ppriv = msGetPixmapPriv(&ms->drmmode, ppix);
+
+    ppriv->notify_on_damage = TRUE;
+
+    return TRUE;
+}
+
+static Bool
 msSharedPixmapNotifyDamage(PixmapPtr ppix)
 {
     Bool ret = FALSE;
@@ -1343,6 +1475,11 @@ ScreenInit(ScreenPtr pScreen, int argc, char **argv)
     pScreen->StopPixmapTracking = PixmapStopDirtyTracking;
 
     pScreen->SharedPixmapNotifyDamage = msSharedPixmapNotifyDamage;
+    pScreen->RequestSharedPixmapNotifyDamage =
+        msRequestSharedPixmapNotifyDamage;
+
+    pScreen->PresentSharedPixmap = msPresentSharedPixmap;
+    pScreen->StopFlippingPixmapTracking = msStopFlippingPixmapTracking;
 
     if (!xf86CrtcScreenInit(pScreen))
         return FALSE;
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.h b/hw/xfree86/drivers/modesetting/drmmode_display.h
index 3161bba..6d2c0a4 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.h
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.h
@@ -152,9 +152,15 @@ typedef struct _msPixmapPriv {
 
     DamagePtr slave_damage;
 
-    /** Fields for flipping shared pixmaps */
+    /** Sink fields for flipping shared pixmaps */
     int flip_seq; /* seq of current page flip event handler */
     Bool wait_for_damage; /* if we have requested damage notification from source */
+
+    /** Source fields for flipping shared pixmaps */
+    Bool defer_dirty_update; /* if we want to manually update */
+    PixmapDirtyUpdatePtr dirty; /* cached dirty ent to avoid searching list */
+    PixmapPtr slave_src; /* if we exported shared pixmap, dirty tracking src */
+    Bool notify_on_damage; /* if sink has requested damage notification */
 } msPixmapPrivRec, *msPixmapPrivPtr;
 
 extern DevPrivateKeyRec msPixmapPrivateKeyRec;
commit 44cb9578c0e5e10568826bc3ecbed97d358bba3c
Author: Alex Goins <agoins at nvidia.com>
Date:   Thu Jun 16 20:06:55 2016 -0700

    modesetting: Disable Reverse PRIME for i915
    
    Reverse PRIME seems to be designed with discrete graphics as a sink in
    mind, designed to do an extra copy from sysmem to vidmem to prevent a
    discrete chip from needing to scan out from sysmem.
    
    The criteria it used to detect this case is if we are a GPU screen and
    Glamor accelerated. It's possible for i915 to fulfill these conditions,
    despite the fact that the additional copy doesn't make sense for i915.
    
    Normally, you could just set AccelMethod = none as an option for the device
    and call it a day. However, when running with modesetting as both the sink
    and the source, Glamor must be enabled.
    
    Ideally, you would be able to set AccelMethod individually for devices
    using the same driver, but there seems to be a bug in X option parsing that
    makes all devices on a driver inherit the options from the first detected
    device. Thus, glamor needs to be enabled for all or for none until that bug
    (if it's even a bug) is fixed.
    
    Nonetheless, it probably doesn't make sense to do the extra copy on i915
    even if Glamor is enabled for the device, so this is more user friendly by
    not requiring users to disable acceleration for i915.
    
    v1: N/A
    v2: N/A
    v3: N/A
    v4: Initial commit
    v5: Unchanged
    v6: Rebase onto ToT
    v7: NULL check and free drmVersionPtr
    
    Reviewed-by: Dave Airlie <airlied at redhat.com>
    Signed-off-by: Alex Goins <agoins at nvidia.com>

diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c
index 2d66a4b..160cbc9 100644
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -1385,9 +1385,22 @@ ScreenInit(ScreenPtr pScreen, int argc, char **argv)
             xf86DrvMsg(pScrn->scrnIndex, X_ERROR,
                        "Failed to initialize the Present extension.\n");
         }
-        /* enable reverse prime if we are a GPU screen, and accelerated */
-        if (pScreen->isGPU)
+        /* enable reverse prime if we are a GPU screen, and accelerated, and not
+         * i915. i915 is happy scanning out from sysmem. */
+        if (pScreen->isGPU) {
+            drmVersionPtr version;
+
+            /* enable if we are an accelerated GPU screen */
             ms->drmmode.reverse_prime_offload_mode = TRUE;
+
+            /* disable if we detect i915 */
+            if ((version = drmGetVersion(ms->drmmode.fd))) {
+                if (!strncmp("i915", version->name, version->name_len)) {
+                    ms->drmmode.reverse_prime_offload_mode = FALSE;
+                }
+                drmFreeVersion(version);
+            }
+        }
     }
 #endif
 
commit f6fef2a171366156c4c6807de7fe086f04f41b7b
Author: Alex Goins <agoins at nvidia.com>
Date:   Thu Jun 16 20:06:54 2016 -0700

    modesetting: Blacklist USB transport devices from PRIME sync
    
    UDL (USB 2.0 DisplayLink DRM driver) and other drivers for USB transport devices
    have strange semantics when it comes to vblank events, due to their inability to
    get the actual vblank info.
    
    When doing a page flip, UDL instantly raises a vblank event without waiting for
    vblank. It also has no support for DRM_IOCTL_WAIT_VBLANK, and has some strange
    behavior with how it handles damage when page flipping.
    
    It's possible to get something semi-working by hacking around these issues,
    but even then there isn't much value-add vs single buffered PRIME, and it
    reduces maintainability and adds additional risks to the modesetting driver
    when running with more well-behaved DRM drivers.
    
    Work needs to be done on UDL in order to properly support synchronized
    PRIME. For now, just blacklist it, causing RandR to fall back to
    unsynchronized PRIME.
    
    This patch originally blacklisted UDL by name, but it was pointed out that there
    are other USB transport device drivers with similar limitations, so it was
    expanded to blacklist all USB transport devices.
    
    v1: N/A
    v2: N/A
    v3: Initial commit
    v4: Move check to driver.c for consistency/visibility
    v5: Refactor to accomodate earlier changes
    v6: Rebase onto ToT
    v7: Expand to blacklist all USB transport devices, not just UDL
    
    Signed-off-by: Alex Goins <agoins at nvidia.com>
    Reviewed-by: Hans de Goede <hdegoede at redhat.com>
    Reviewed-by: Dave Airlie <airlied at redhat.com>

diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c
index ab3b028..2d66a4b 100644
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -973,6 +973,7 @@ msEnableSharedPixmapFlipping(RRCrtcPtr crtc, PixmapPtr front, PixmapPtr back)
     ScreenPtr screen = crtc->pScreen;
     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
     modesettingPtr ms = modesettingPTR(scrn);
+    EntityInfoPtr pEnt = ms->pEnt;
     xf86CrtcPtr xf86Crtc = crtc->devPrivate;
 
     if (!xf86Crtc)
@@ -986,6 +987,19 @@ msEnableSharedPixmapFlipping(RRCrtcPtr crtc, PixmapPtr front, PixmapPtr back)
     if (ms->drmmode.reverse_prime_offload_mode)
         return FALSE;
 
+#if XSERVER_PLATFORM_BUS
+    if (pEnt->location.type == BUS_PLATFORM) {
+        char *syspath =
+            xf86_platform_device_odev_attributes(pEnt->location.id.plat)->
+            syspath;
+
+        /* Not supported for devices using USB transport due to misbehaved
+         * vblank events */
+        if (syspath && strstr(syspath, "usb"))
+            return FALSE;
+    }
+#endif
+
     return drmmode_EnableSharedPixmapFlipping(xf86Crtc, &ms->drmmode,
                                               front, back);
 }
commit 500853086dd5fbfe6d2b3e30923fdc4d8c262cf0
Author: Alex Goins <agoins at nvidia.com>
Date:   Thu Jun 16 20:06:53 2016 -0700

    modesetting: Suspend and resume flipping with DPMS
    
    DPMS would prevent page flip / vblank events from being raised, freezing
    the screen until PRIME flipping was reinitialized. To handle DPMS cleanly,
    suspend PRIME page flipping when DPMS mode is not on, and resume it when
    DPMS mode is on.
    
    v1: Initial commit
    v2: Moved flipping_active check from previous commit to here
    v3: Unchanged
    v4: Unchanged
    v5: Move flipping_active check to sink support commit
    v6: Rebase onto ToT
    v7: Unchanged
    
    Reviewed-by: Dave Airlie <airlied at redhat.com>
    Signed-off-by: Alex Goins <agoins at nvidia.com>

diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c
index 2915b78..fb93348 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
@@ -1405,12 +1405,22 @@ drmmode_output_dpms(xf86OutputPtr output, int mode)
     drmModeConnectorSetProperty(drmmode->fd, koutput->connector_id,
                                 drmmode_output->dpms_enum_id, mode);
 
-    if (mode == DPMSModeOn && crtc) {
+    if (crtc) {
         drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
-        if (drmmode_crtc->need_modeset)
-            drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
-                                   crtc->x, crtc->y);
+
+        if (mode == DPMSModeOn) {
+            if (drmmode_crtc->need_modeset)
+                drmmode_set_mode_major(crtc, &crtc->mode, crtc->rotation,
+                                       crtc->x, crtc->y);
+
+            if (drmmode_crtc->enable_flipping)
+                drmmode_InitSharedPixmapFlipping(crtc, drmmode_crtc->drmmode);
+        } else {
+            if (drmmode_crtc->enable_flipping)
+                drmmode_FiniSharedPixmapFlipping(crtc, drmmode_crtc->drmmode);
+        }
     }
+
     return;
 }
 
commit 80e64dae8af1eb3bb225b00fd800c6924883cf46
Author: Alex Goins <agoins at nvidia.com>
Date:   Thu Jun 16 20:06:52 2016 -0700

    modesetting: Implement PRIME syncing as a sink
    
    Implements (Enable/Disable)SharedPixmapFlipping and
    SharedPixmapNotifyDamage, the sink functions for PRIME synchronization and
    double buffering. Allows modesetting driver to be used as a sink with PRIME
    synchronization.
    
    Changes dispatch_slave_dirty to flush damage from both scanout pixmaps.
    
    Changes drmmode_set_scanout_pixmap*() functions to
    drmmode_set_target_scanout_pixmap*() that take an additional parameter
    PixmapPtr *target. Then, treat *target as it did prime_pixmap. This allows
    me to use it to explicitly set both prime_pixmap and prime_pixmap_back
    individually. drmmode_set_scanout_pixmap() without the extra parameter
    remains to cover the single-buffered case, but only works if we aren't
    already double buffered.
    
    driver.c:
        Add plumbing for rr(Enable/Disable)SharedPixmapFlipping and
        SharedPixmapNotifyDamage.
    
        Change dispatch_dirty_crtc to dispatch_dirty_pixmap, which functions the
        same but flushes damage associated with a ppriv instead of the crtc, and
        chanage dispatch_slave_dirty to use it on both scanout pixmaps if
        applicable.
    
    drmmode_display.h:
        Add flip_seq field to msPixmapPrivRec to keep track of the event handler
        associated with a given pixmap, if any.
    
        Add wait_for_damage field to msPixmapPrivRec to keep track if we have
        requested a damage notification from the source.
    
        Add enable_flipping field to drmmode_crtc_private_rec to keep track if
        flipping is enabled or disabled.
    
        Add prime_pixmap_back to drmmode_crtc_private_rec to keep track of back
        buffer internally.
    
        Add declarations for drmmode_SetupPageFlipFence(),
        drmmode_EnableSharedPixmapFlipping(),
        drmmode_DisableSharedPixmapFlipping, drmmode_SharedPixmapFlip(), and
        drmmode_SharedPixmapPresentOnVBlank().
    
        Move slave damage from crtc to ppriv.
    
    drmmode_display.c:
        Change drmmode_set_scanout_pixmap*() functions to
        drmmode_set_target_scanout_pixmap*() that take an additional parameter
        PixmapPtr *target for explicitly setting different scanout pixmaps.
    
        Add definitions for functions drmmode_SharedPixmapFlip(),
        drmmode_SharedPixmapPresentOnVBlank(),
        drmmode_SharedPixmapPresent(),
        drmmode_SharedPixmapVBlankEventHandler(),
        drmmode_SharedPixmapVBlankEventAbort(),
        drmmode_EnableSharedPixmapFlipping(), and
        drmmode_DisableSharedPixmapFlipping,
        drmmode_InitSharedPixmapFlipping(), and
        drmmode_FiniSharedPixmapFlipping, along with struct
        vblank_event_args.
    
        The control flow is as follows:
            pScrPriv->rrEnableSharedPixmapFlipping() makes its way to
            drmmode_EnableSharedPixmapFlipping(), which sets enable_flipping to
            TRUE and sets both scanout pixmaps prime_pixmap and
            prime_pixmap_back.
    
            When setting a mode, if prime_pixmap is defined, modesetting
            driver will call drmmode_InitSharedPixmapFlipping(), which if
            flipping is enabled will call drmmode_SharedPixmapPresent() on
            scanout_pixmap_back.
    
            drmmode_SharedPixmapPresent() requests that for the source to
            present on the given buffer using master->PresentSharedPixmap(). If
            it succeeds, it will then attempt to flip to that buffer using
            drmmode_SharedPixmapFlip(). Flipping shouldn't fail, but if it
            does, it will raise a warning and try drmmode_SharedPixmapPresent()
            again on the next vblank using
            drmmode_SharedPixmapPresentOnVBlank().
    
            master->PresentSharedPixmap() could fail, in most cases because
            there is no outstanding damage on the mscreenpix tracked by the
            shared pixmap. In this case, drmmode_SharedPixmapPresent() will
            attempt to use master->RequestSharedPixmapNotifyDamage() to request
            for the source driver to call slave->SharedPixmapNotifyDamage() in
            response to damage on mscreenpix. This will ultimately call
            into drmmode_SharedPixmapPresentOnVBlank() to retry
            drmmode_SharedPixmapPresent() on the next vblank after
            accumulating damage.
    
            drmmode_SharedPixmapFlip() sets up page flip event handler by
            packing struct vblank_event_args with the necessary parameters, and
            registering drmmode_SharedPixmapVBlankEventHandler() and
            drmmode_SharedPixmapVBlankEventAbort() with the modesetting DRM
            event handler queue. Then, it uses the drmModePageFlip() to flip on
            the next vblank and raise an event.
    
            drmmode_SharedPixmapPresentOnVBlank() operates similarly to
            drmmode_SharedPixmapFlip(), but uses drmWaitVBlank() instead of
            drmModePageFlip() to raise the event without flipping.
    
            On the next vblank, DRM will raise an event that will ultimately be
            handled by drmmode_SharedPixmapVBlankEventHandler(). If we flipped,
            it will update prime_pixmap and prime_pixmap_back to reflect that
            frontTarget is now being displayed, and use
            drmmode_SharedPixmapPresent(backTarget) to start the process again
            on the now-hidden shared pixmap. If we didn't flip, it will just
            use drmmode_SharedPixmapPresent(frontTarget) to start the process
            again on the still-hidden shared pixmap.
    
            Note that presentation generally happens asynchronously, so with
            these changes alone tearing is reduced, but we can't always
            guarantee that the present will finish before the flip. These
            changes are meant to be paired with changes to the sink DRM driver
            that makes flips wait on fences attached to dmabuf backed buffers.
            The source driver is responsible for attaching the fences and
            signaling them when presentation is finished.
    
            Note that because presentation is requested in response to a
            vblank, PRIME sources will now conform to the sink's refresh rate.
    
            At teardown, pScrPriv->rrDisableSharedPixmapFlipping() will be
            called, making its way to drmmode_FiniSharedPixmapFlipping().
            There, the event handlers for prime_pixmap and prime_pixmap_back
            are aborted, freeing the left over parameter structure. Then,
            prime_pixmap and prime_pixmap back are unset as scanout pixmaps.
    
        Register and tear down slave damage per-scanout pixmap instead of
        per-crtc.
    
    v1: Initial commit
    v2: Renamed PresentTrackedFlippingPixmap to PresentSharedPixmap
        Renamed flipSeq to flip_seq
        Warn if flip failed
        Use SharedPixmapNotifyDamage to retry on next vblank after damage
    v3: Refactor to accomodate moving (rr)StartFlippingPixmapTracking and
        (rr)(Enable/Disable)SharedPixmapFlipping to rrScrPrivRec from ScreenRec
        Do damage tracking on both scanout pixmaps
    v4: Tweaks to commit message
    v5: Revise for internal storage of prime pixmap ptrs
        Move disabling for reverse PRIME from source commit to here
        Use drmmode_set_target_scanout_pixmap*() to set scanout pixmaps
        internally to EnableSharedPixmapFlipping().
        Don't support flipping if ms->drmmode.pageflip == FALSE.
        Move flipping_active check to this commit
    v6: Rebase onto ToT
    v7: Unchanged
    
    Reviewed-by: Dave Airlie <airlied at redhat.com>
    Signed-off-by: Alex Goins <agoins at nvidia.com>

diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c
index 97a7404..ab3b028 100644
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -529,20 +529,14 @@ dispatch_dirty(ScreenPtr pScreen)
 }
 
 static void
-dispatch_dirty_crtc(ScrnInfoPtr scrn, xf86CrtcPtr crtc)
+dispatch_dirty_pixmap(ScrnInfoPtr scrn, xf86CrtcPtr crtc, PixmapPtr ppix)
 {
     modesettingPtr ms = modesettingPTR(scrn);
-    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
-    PixmapPtr pixmap = drmmode_crtc->prime_pixmap;
-    DamagePtr damage = drmmode_crtc->slave_damage;
-    msPixmapPrivPtr ppriv = msGetPixmapPriv(&ms->drmmode, pixmap);
+    msPixmapPrivPtr ppriv = msGetPixmapPriv(&ms->drmmode, ppix);
+    DamagePtr damage = ppriv->slave_damage;
     int fb_id = ppriv->fb_id;
-    int ret;
-
-    ret = dispatch_dirty_region(scrn, pixmap, damage, fb_id);
-    if (ret) {
 
-    }
+    dispatch_dirty_region(scrn, ppix, damage, fb_id);
 }
 
 static void
@@ -558,10 +552,11 @@ dispatch_slave_dirty(ScreenPtr pScreen)
 
         if (!drmmode_crtc)
             continue;
-        if (!drmmode_crtc->prime_pixmap)
-            continue;
 
-        dispatch_dirty_crtc(scrn, crtc);
+        if (drmmode_crtc->prime_pixmap)
+            dispatch_dirty_pixmap(scrn, crtc, drmmode_crtc->prime_pixmap);
+        if (drmmode_crtc->prime_pixmap_back)
+            dispatch_dirty_pixmap(scrn, crtc, drmmode_crtc->prime_pixmap_back);
     }
 }
 
@@ -973,9 +968,45 @@ msUpdatePacked(ScreenPtr pScreen, shadowBufPtr pBuf)
 }
 
 static Bool
+msEnableSharedPixmapFlipping(RRCrtcPtr crtc, PixmapPtr front, PixmapPtr back)
+{
+    ScreenPtr screen = crtc->pScreen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+    xf86CrtcPtr xf86Crtc = crtc->devPrivate;
+
+    if (!xf86Crtc)
+        return FALSE;
+
+    /* Not supported if we can't flip */
+    if (!ms->drmmode.pageflip)
+        return FALSE;
+
+    /* Not currently supported with reverse PRIME */
+    if (ms->drmmode.reverse_prime_offload_mode)
+        return FALSE;
+
+    return drmmode_EnableSharedPixmapFlipping(xf86Crtc, &ms->drmmode,
+                                              front, back);
+}
+
+static void
+msDisableSharedPixmapFlipping(RRCrtcPtr crtc)
+{
+    ScreenPtr screen = crtc->pScreen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+    xf86CrtcPtr xf86Crtc = crtc->devPrivate;
+
+    if (xf86Crtc)
+        drmmode_DisableSharedPixmapFlipping(xf86Crtc, &ms->drmmode);
+}
+
+static Bool
 CreateScreenResources(ScreenPtr pScreen)
 {
     ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+    rrScrPrivPtr pScrPriv = rrGetScrPriv(pScreen);
     modesettingPtr ms = modesettingPTR(pScrn);
     PixmapPtr rootPixmap;
     Bool ret;
@@ -1034,6 +1065,10 @@ CreateScreenResources(ScreenPtr pScreen)
             return FALSE;
         }
     }
+
+    pScrPriv->rrEnableSharedPixmapFlipping = msEnableSharedPixmapFlipping;
+    pScrPriv->rrDisableSharedPixmapFlipping = msDisableSharedPixmapFlipping;
+
     return ret;
 }
 
@@ -1097,6 +1132,39 @@ msSetSharedPixmapBacking(PixmapPtr ppix, void *fd_handle)
 }
 
 static Bool
+msSharedPixmapNotifyDamage(PixmapPtr ppix)
+{
+    Bool ret = FALSE;
+    int c;
+
+    ScreenPtr screen = ppix->drawable.pScreen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+    xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn);
+
+    msPixmapPrivPtr ppriv = msGetPixmapPriv(&ms->drmmode, ppix);
+
+    if (!ppriv->wait_for_damage)
+        return ret;
+    ppriv->wait_for_damage = FALSE;
+
+    for (c = 0; c < xf86_config->num_crtc; c++) {
+        xf86CrtcPtr crtc = xf86_config->crtc[c];
+        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+        if (!drmmode_crtc)
+            continue;
+        if (!(drmmode_crtc->prime_pixmap && drmmode_crtc->prime_pixmap_back))
+            continue;
+
+        // Received damage on master screen pixmap, schedule present on vblank
+        ret |= drmmode_SharedPixmapPresentOnVBlank(ppix, crtc, &ms->drmmode);
+    }
+
+    return ret;
+}
+
+static Bool
 SetMaster(ScrnInfoPtr pScrn)
 {
     modesettingPtr ms = modesettingPTR(pScrn);
@@ -1260,6 +1328,8 @@ ScreenInit(ScreenPtr pScreen, int argc, char **argv)
     pScreen->StartPixmapTracking = PixmapStartDirtyTracking;
     pScreen->StopPixmapTracking = PixmapStopDirtyTracking;
 
+    pScreen->SharedPixmapNotifyDamage = msSharedPixmapNotifyDamage;
+
     if (!xf86CrtcScreenInit(pScreen))
         return FALSE;
 
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c
index cce9a33..2915b78 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
@@ -229,6 +229,239 @@ drmmode_SetSlaveBO(PixmapPtr ppix,
     return TRUE;
 }
 
+static Bool
+drmmode_SharedPixmapPresent(PixmapPtr ppix, xf86CrtcPtr crtc,
+                            drmmode_ptr drmmode)
+{
+    ScreenPtr master = crtc->randr_crtc->pScreen->current_master;
+
+    if (master->PresentSharedPixmap(ppix)) {
+        /* Success, queue flip to back target */
+        if (drmmode_SharedPixmapFlip(ppix, crtc, drmmode))
+            return TRUE;
+
+        xf86DrvMsg(drmmode->scrn->scrnIndex, X_WARNING,
+                   "drmmode_SharedPixmapFlip() failed, trying again next vblank\n");
+
+        return drmmode_SharedPixmapPresentOnVBlank(ppix, crtc, drmmode);
+    }
+
+    /* Failed to present, try again on next vblank after damage */
+    if (master->RequestSharedPixmapNotifyDamage) {
+        msPixmapPrivPtr ppriv = msGetPixmapPriv(drmmode, ppix);
+
+        /* Set flag first in case we are immediately notified */
+        ppriv->wait_for_damage = TRUE;
+
+        if (master->RequestSharedPixmapNotifyDamage(ppix))
+            return TRUE;
+        else
+            ppriv->wait_for_damage = FALSE;
+    }
+
+    /* Damage notification not available, just try again on vblank */
+    return drmmode_SharedPixmapPresentOnVBlank(ppix, crtc, drmmode);
+}
+
+struct vblank_event_args {
+    PixmapPtr frontTarget;
+    PixmapPtr backTarget;
+    xf86CrtcPtr crtc;
+    drmmode_ptr drmmode;
+    Bool flip;
+};
+static void
+drmmode_SharedPixmapVBlankEventHandler(uint64_t frame, uint64_t usec,
+                                       void *data)
+{
+    struct vblank_event_args *args = data;
+
+    drmmode_crtc_private_ptr drmmode_crtc = args->crtc->driver_private;
+
+    if (args->flip) {
+        /* frontTarget is being displayed, update crtc to reflect */
+        drmmode_crtc->prime_pixmap = args->frontTarget;
+        drmmode_crtc->prime_pixmap_back = args->backTarget;
+
+        /* Safe to present on backTarget, no longer displayed */
+        drmmode_SharedPixmapPresent(args->backTarget, args->crtc, args->drmmode);
+    } else {
+        /* backTarget is still being displayed, present on frontTarget */
+        drmmode_SharedPixmapPresent(args->frontTarget, args->crtc, args->drmmode);
+    }
+
+    free(args);
+}
+
+static void
+drmmode_SharedPixmapVBlankEventAbort(void *data)
+{
+    struct vblank_event_args *args = data;
+
+    msGetPixmapPriv(args->drmmode, args->frontTarget)->flip_seq = 0;
+
+    free(args);
+}
+
+Bool
+drmmode_SharedPixmapPresentOnVBlank(PixmapPtr ppix, xf86CrtcPtr crtc,
+                                    drmmode_ptr drmmode)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+    msPixmapPrivPtr ppriv = msGetPixmapPriv(drmmode, ppix);
+
+    drmVBlank vbl;
+    struct vblank_event_args *event_args;
+
+    if (ppix == drmmode_crtc->prime_pixmap)
+        return FALSE; /* Already flipped to this pixmap */
+    if (ppix != drmmode_crtc->prime_pixmap_back)
+        return FALSE; /* Pixmap is not a scanout pixmap for CRTC */
+
+    event_args = calloc(1, sizeof(*event_args));
+    if (!event_args)
+        return FALSE;
+
+    event_args->frontTarget = ppix;
+    event_args->backTarget = drmmode_crtc->prime_pixmap;
+    event_args->crtc = crtc;
+    event_args->drmmode = drmmode;
+    event_args->flip = FALSE;
+
+    ppriv->flip_seq =
+        ms_drm_queue_alloc(crtc, event_args,
+                           drmmode_SharedPixmapVBlankEventHandler,
+                           drmmode_SharedPixmapVBlankEventAbort);
+
+    vbl.request.type =
+        DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT | drmmode_crtc->vblank_pipe;
+    vbl.request.sequence = 1;
+    vbl.request.signal = (unsigned long) ppriv->flip_seq;
+
+    return drmWaitVBlank(drmmode->fd, &vbl) >= 0;
+}
+
+Bool
+drmmode_SharedPixmapFlip(PixmapPtr frontTarget, xf86CrtcPtr crtc,
+                         drmmode_ptr drmmode)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+    msPixmapPrivPtr ppriv_front = msGetPixmapPriv(drmmode, frontTarget);
+
+    struct vblank_event_args *event_args;
+
+    event_args = calloc(1, sizeof(*event_args));
+    if (!event_args)
+        return FALSE;
+
+    event_args->frontTarget = frontTarget;
+    event_args->backTarget = drmmode_crtc->prime_pixmap;
+    event_args->crtc = crtc;
+    event_args->drmmode = drmmode;
+    event_args->flip = TRUE;
+
+    ppriv_front->flip_seq =
+        ms_drm_queue_alloc(crtc, event_args,
+                           drmmode_SharedPixmapVBlankEventHandler,
+                           drmmode_SharedPixmapVBlankEventAbort);
+
+    if (drmModePageFlip(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
+                        ppriv_front->fb_id, DRM_MODE_PAGE_FLIP_EVENT,
+                        (void *)(intptr_t) ppriv_front->flip_seq) < 0) {
+        ms_drm_abort_seq(crtc->scrn, ppriv_front->flip_seq);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static Bool
+drmmode_InitSharedPixmapFlipping(xf86CrtcPtr crtc, drmmode_ptr drmmode)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+    if (!drmmode_crtc->enable_flipping)
+        return FALSE;
+
+    if (drmmode_crtc->flipping_active)
+        return TRUE;
+
+    drmmode_crtc->flipping_active =
+        drmmode_SharedPixmapPresent(drmmode_crtc->prime_pixmap_back,
+                                    crtc, drmmode);
+
+    return drmmode_crtc->flipping_active;
+}
+
+static void
+drmmode_FiniSharedPixmapFlipping(xf86CrtcPtr crtc, drmmode_ptr drmmode)
+{
+    uint32_t seq;
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+    if (!drmmode_crtc->flipping_active)
+        return;
+
+    drmmode_crtc->flipping_active = FALSE;
+
+    /* Abort page flip event handler on prime_pixmap */
+    seq = msGetPixmapPriv(drmmode, drmmode_crtc->prime_pixmap)->flip_seq;
+    if (seq)
+        ms_drm_abort_seq(crtc->scrn, seq);
+
+    /* Abort page flip event handler on prime_pixmap_back */
+    seq = msGetPixmapPriv(drmmode,
+                          drmmode_crtc->prime_pixmap_back)->flip_seq;
+    if (seq)
+        ms_drm_abort_seq(crtc->scrn, seq);
+}
+
+static Bool drmmode_set_target_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix,
+                                              PixmapPtr *target);
+
+Bool
+drmmode_EnableSharedPixmapFlipping(xf86CrtcPtr crtc, drmmode_ptr drmmode,
+                                   PixmapPtr front, PixmapPtr back)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+    drmmode_crtc->enable_flipping = TRUE;
+
+    /* Set front scanout pixmap */
+    drmmode_crtc->enable_flipping &=
+        drmmode_set_target_scanout_pixmap(crtc, front,
+                                          &drmmode_crtc->prime_pixmap);
+    if (!drmmode_crtc->enable_flipping)
+        return FALSE;
+
+    /* Set back scanout pixmap */
+    drmmode_crtc->enable_flipping &=
+        drmmode_set_target_scanout_pixmap(crtc, back,
+                                          &drmmode_crtc->prime_pixmap_back);
+    if (!drmmode_crtc->enable_flipping) {
+        drmmode_set_target_scanout_pixmap(crtc, NULL,
+                                          &drmmode_crtc->prime_pixmap);
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+void
+drmmode_DisableSharedPixmapFlipping(xf86CrtcPtr crtc, drmmode_ptr drmmode)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+    drmmode_crtc->enable_flipping = FALSE;
+
+    drmmode_FiniSharedPixmapFlipping(crtc, drmmode);
+
+    drmmode_set_target_scanout_pixmap(crtc, NULL, &drmmode_crtc->prime_pixmap);
+
+    drmmode_set_target_scanout_pixmap(crtc, NULL,
+                                      &drmmode_crtc->prime_pixmap_back);
+}
+
 static void
 drmmode_ConvertFromKMode(ScrnInfoPtr scrn,
                          drmModeModeInfo * kmode, DisplayModePtr mode)
@@ -499,6 +732,9 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
         drmmode_crtc->need_modeset = FALSE;
         crtc->funcs->dpms(crtc, DPMSModeOn);
 
+        if (drmmode_crtc->prime_pixmap_back)
+            drmmode_InitSharedPixmapFlipping(crtc, drmmode);
+
         /* go through all the outputs and force DPMS them back on? */
         for (i = 0; i < xf86_config->num_output; i++) {
             xf86OutputPtr output = xf86_config->output[i];
@@ -629,7 +865,8 @@ drmmode_crtc_gamma_set(xf86CrtcPtr crtc, uint16_t * red, uint16_t * green,
 }
 
 static Bool
-drmmode_set_scanout_pixmap_gpu(xf86CrtcPtr crtc, PixmapPtr ppix)
+drmmode_set_target_scanout_pixmap_gpu(xf86CrtcPtr crtc, PixmapPtr ppix,
+                                      PixmapPtr *target)
 {
     ScreenPtr screen = xf86ScrnToScreen(crtc->scrn);
     PixmapPtr screenpix = screen->GetScreenPixmap(screen);
@@ -638,13 +875,14 @@ drmmode_set_scanout_pixmap_gpu(xf86CrtcPtr crtc, PixmapPtr ppix)
     drmmode_ptr drmmode = drmmode_crtc->drmmode;
     int c, total_width = 0, max_height = 0, this_x = 0;
 
-    if (drmmode_crtc->prime_pixmap) {
-        PixmapStopDirtyTracking(drmmode_crtc->prime_pixmap, screenpix);
+    if (*target) {
+        PixmapStopDirtyTracking(*target, screenpix);
         if (drmmode->fb_id) {
             drmModeRmFB(drmmode->fd, drmmode->fb_id);
             drmmode->fb_id = 0;
         }
         drmmode_crtc->prime_pixmap_x = 0;
+        *target = NULL;
     }
 
     if (!ppix)
@@ -679,41 +917,43 @@ drmmode_set_scanout_pixmap_gpu(xf86CrtcPtr crtc, PixmapPtr ppix)
     }
     drmmode_crtc->prime_pixmap_x = this_x;
     PixmapStartDirtyTracking(ppix, screenpix, 0, 0, this_x, 0, RR_Rotate_0);
+    *target = ppix;
     return TRUE;
 }
 
 static Bool
-drmmode_set_scanout_pixmap_cpu(xf86CrtcPtr crtc, PixmapPtr ppix)
+drmmode_set_target_scanout_pixmap_cpu(xf86CrtcPtr crtc, PixmapPtr ppix,
+                                      PixmapPtr *target)
 {
     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
     drmmode_ptr drmmode = drmmode_crtc->drmmode;
     msPixmapPrivPtr ppriv;
     void *ptr;
 
-    if (drmmode_crtc->prime_pixmap) {
-        ppriv = msGetPixmapPriv(drmmode, drmmode_crtc->prime_pixmap);
+    if (*target) {
+        ppriv = msGetPixmapPriv(drmmode, *target);
         drmModeRmFB(drmmode->fd, ppriv->fb_id);
         ppriv->fb_id = 0;
-    }
-    if (drmmode_crtc->slave_damage) {
-        DamageUnregister(drmmode_crtc->slave_damage);
-        drmmode_crtc->slave_damage = NULL;
+        if (ppriv->slave_damage) {
+            DamageUnregister(ppriv->slave_damage);
+            ppriv->slave_damage = NULL;
+        }
     }
 
     if (!ppix)
         return TRUE;
 
     ppriv = msGetPixmapPriv(drmmode, ppix);
-    if (!drmmode_crtc->slave_damage) {
-        drmmode_crtc->slave_damage = DamageCreate(NULL, NULL,
-                                                  DamageReportNone,
-                                                  TRUE,
-                                                  crtc->randr_crtc->pScreen,
-                                                  NULL);
+    if (!ppriv->slave_damage) {
+        ppriv->slave_damage = DamageCreate(NULL, NULL,
+                                           DamageReportNone,
+                                           TRUE,
+                                           crtc->randr_crtc->pScreen,
+                                           NULL);
     }
     ptr = drmmode_map_slave_bo(drmmode, ppriv);
     ppix->devPrivate.ptr = ptr;
-    DamageRegister(&ppix->drawable, drmmode_crtc->slave_damage);
+    DamageRegister(&ppix->drawable, ppriv->slave_damage);
 
     if (ppriv->fb_id == 0) {
         drmModeAddFB(drmmode->fd, ppix->drawable.width,
@@ -722,25 +962,34 @@ drmmode_set_scanout_pixmap_cpu(xf86CrtcPtr crtc, PixmapPtr ppix)
                      ppix->drawable.bitsPerPixel,
                      ppix->devKind, ppriv->backing_bo->handle, &ppriv->fb_id);
     }
+    *target = ppix;
     return TRUE;
 }
 
 static Bool
-drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix)
+drmmode_set_target_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix,
+                                  PixmapPtr *target)
 {
-    Bool ret;
     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
     drmmode_ptr drmmode = drmmode_crtc->drmmode;
 
     if (drmmode->reverse_prime_offload_mode)
-        ret = drmmode_set_scanout_pixmap_gpu(crtc, ppix);
+        return drmmode_set_target_scanout_pixmap_gpu(crtc, ppix, target);
     else
-        ret = drmmode_set_scanout_pixmap_cpu(crtc, ppix);
+        return drmmode_set_target_scanout_pixmap_cpu(crtc, ppix, target);
+}
 
-    if (ret)
-        drmmode_crtc->prime_pixmap = ppix;
+static Bool
+drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
 
-    return ret;
+    /* Use DisableSharedPixmapFlipping before switching to single buf */
+    if (drmmode_crtc->enable_flipping)
+        return FALSE;
+
+    return drmmode_set_target_scanout_pixmap(crtc, ppix,
+                                             &drmmode_crtc->prime_pixmap);
 }
 
 static void *
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.h b/hw/xfree86/drivers/modesetting/drmmode_display.h
index 36c6e91..3161bba 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.h
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.h
@@ -99,12 +99,12 @@ typedef struct {
     struct dumb_bo *cursor_bo;
     Bool cursor_up;
     uint16_t lut_r[256], lut_g[256], lut_b[256];
-    DamagePtr slave_damage;
 
     drmmode_bo rotate_bo;
     unsigned rotate_fb_id;
 
     PixmapPtr prime_pixmap;
+    PixmapPtr prime_pixmap_back;
     unsigned prime_pixmap_x;
 
     /**
@@ -120,6 +120,9 @@ typedef struct {
     /** @} */
 
     Bool need_modeset;
+
+    Bool enable_flipping;
+    Bool flipping_active;
 } drmmode_crtc_private_rec, *drmmode_crtc_private_ptr;
 
 typedef struct {
@@ -146,6 +149,12 @@ typedef struct {
 typedef struct _msPixmapPriv {
     uint32_t fb_id;
     struct dumb_bo *backing_bo; /* if this pixmap is backed by a dumb bo */
+
+    DamagePtr slave_damage;
+
+    /** Fields for flipping shared pixmaps */
+    int flip_seq; /* seq of current page flip event handler */
+    Bool wait_for_damage; /* if we have requested damage notification from source */
 } msPixmapPrivRec, *msPixmapPrivPtr;
 
 extern DevPrivateKeyRec msPixmapPrivateKeyRec;
@@ -164,6 +173,14 @@ Bool drmmode_SetSlaveBO(PixmapPtr ppix,
                         drmmode_ptr drmmode,
                         int fd_handle, int pitch, int size);
 
+Bool drmmode_EnableSharedPixmapFlipping(xf86CrtcPtr crtc, drmmode_ptr drmmode,
+                                        PixmapPtr front, PixmapPtr back);
+Bool drmmode_SharedPixmapPresentOnVBlank(PixmapPtr frontTarget, xf86CrtcPtr crtc,
+                                         drmmode_ptr drmmode);
+Bool drmmode_SharedPixmapFlip(PixmapPtr frontTarget, xf86CrtcPtr crtc,
+                              drmmode_ptr drmmode);
+void drmmode_DisableSharedPixmapFlipping(xf86CrtcPtr crtc, drmmode_ptr drmmode);
+
 extern Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp);
 void drmmode_adjust_frame(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int x, int y);
 extern Bool drmmode_set_desired_modes(ScrnInfoPtr pScrn, drmmode_ptr drmmode, Bool set_hw);
commit 378c85a8848679eaa3c2881f3ba8b686e59df25e
Author: Alex Goins <agoins at nvidia.com>
Date:   Thu Jun 16 20:06:51 2016 -0700

    modesetting: Always load ms->drmmode.pageflip
    
    ms->drmmode.pageflip was only loaded from options if ms->drmmode.glamor was
    defined, otherwise it would always assume FALSE.
    
    PRIME Synchronization requires ms->drmmode.pageflip even if we aren't using
    glamor, so load it unconditionally.
    
    v1: N/A
    v2: N/A
    v3: N/A
    v4: N/A
    v5: Initial commit
    v6: Rebase onto ToT
    v7: Unchanged
    
    Reviewed-by: Dave Airlie <airlied at redhat.com>
    Signed-off-by: Alex Goins <agoins at nvidia.com>

diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c
index 0059e56..97a7404 100644
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -873,10 +873,7 @@ PreInit(ScrnInfoPtr pScrn, int flags)
 
     try_enable_glamor(pScrn);
 
-    if (ms->drmmode.glamor) {
-        ms->drmmode.pageflip =
-            xf86ReturnOptValBool(ms->drmmode.Options, OPTION_PAGEFLIP, TRUE);
-    } else {
+    if (!ms->drmmode.glamor) {
         Bool prefer_shadow = TRUE;
 
         ret = drmGetCap(ms->fd, DRM_CAP_DUMB_PREFER_SHADOW, &value);
@@ -892,10 +889,11 @@ PreInit(ScrnInfoPtr pScrn, int flags)
                    "ShadowFB: preferred %s, enabled %s\n",
                    prefer_shadow ? "YES" : "NO",
                    ms->drmmode.shadow_enable ? "YES" : "NO");
-
-        ms->drmmode.pageflip = FALSE;
     }
 
+    ms->drmmode.pageflip =
+        xf86ReturnOptValBool(ms->drmmode.Options, OPTION_PAGEFLIP, TRUE);
+
     pScrn->capabilities = 0;
 #ifdef DRM_CAP_PRIME
     ret = drmGetCap(ms->fd, DRM_CAP_PRIME, &value);
commit b773a9c8126222e5fed2904d012fbf917a9f22fd
Author: Alex Goins <agoins at nvidia.com>
Date:   Thu Jun 16 20:06:50 2016 -0700

    modesetting: Always tear down scanout pixmap
    
    drmmode_set_scanout_pixmap_(cpu/gpu) would only do teardown if ppix ==
    NULL. This meant that if there were consecutive calls to
    SetScanoutPixmap(ppix != NULL) without calls to SetScanoutPixmap(ppix ==
    NULL) in between, earlier calls would be leaked.  RRReplaceScanoutPixmap()
    does this today.
    
    Instead, when setting a scanout pixmap, always do teardown of the existing
    scanout pixmap before setting up the new one. Then, if there is no new one
    to set up, stop there.
    
    This maintains the previous behavior in all cases except those with
    multiple consecutive calls to SetScanoutPixmap(ppix != NULL).
    
    v1: N/A
    v2: N/A
    v3: N/A
    v4: N/A
    v5: Initial commit
    v6: Rebase onto ToT
    v7: Unchanged
    
    Reviewed-by: Dave Airlie <airlied at redhat.com>
    Signed-off-by: Alex Goins <agoins at nvidia.com>

diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c
index 89c468b..cce9a33 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
@@ -638,17 +638,18 @@ drmmode_set_scanout_pixmap_gpu(xf86CrtcPtr crtc, PixmapPtr ppix)
     drmmode_ptr drmmode = drmmode_crtc->drmmode;
     int c, total_width = 0, max_height = 0, this_x = 0;
 
-    if (!ppix) {
-        if (drmmode_crtc->prime_pixmap) {
-            PixmapStopDirtyTracking(drmmode_crtc->prime_pixmap, screenpix);
-            if (drmmode->fb_id) {
-                drmModeRmFB(drmmode->fd, drmmode->fb_id);
-                drmmode->fb_id = 0;
-            }
+    if (drmmode_crtc->prime_pixmap) {
+        PixmapStopDirtyTracking(drmmode_crtc->prime_pixmap, screenpix);
+        if (drmmode->fb_id) {
+            drmModeRmFB(drmmode->fd, drmmode->fb_id);
+            drmmode->fb_id = 0;
         }
         drmmode_crtc->prime_pixmap_x = 0;
-        return TRUE;
     }
+
+    if (!ppix)
+        return TRUE;
+
     /* iterate over all the attached crtcs to work out the bounding box */
     for (c = 0; c < xf86_config->num_crtc; c++) {
         xf86CrtcPtr iter = xf86_config->crtc[c];
@@ -689,18 +690,18 @@ drmmode_set_scanout_pixmap_cpu(xf86CrtcPtr crtc, PixmapPtr ppix)
     msPixmapPrivPtr ppriv;
     void *ptr;
 
-    if (!ppix) {
-        if (drmmode_crtc->prime_pixmap) {
-            ppriv = msGetPixmapPriv(drmmode, drmmode_crtc->prime_pixmap);
-            drmModeRmFB(drmmode->fd, ppriv->fb_id);
-            ppriv->fb_id = 0;
-        }
-        if (drmmode_crtc->slave_damage) {
-            DamageUnregister(drmmode_crtc->slave_damage);
-            drmmode_crtc->slave_damage = NULL;
-        }
-        return TRUE;
+    if (drmmode_crtc->prime_pixmap) {
+        ppriv = msGetPixmapPriv(drmmode, drmmode_crtc->prime_pixmap);
+        drmModeRmFB(drmmode->fd, ppriv->fb_id);
+        ppriv->fb_id = 0;
     }
+    if (drmmode_crtc->slave_damage) {
+        DamageUnregister(drmmode_crtc->slave_damage);
+        drmmode_crtc->slave_damage = NULL;
+    }
+
+    if (!ppix)
+        return TRUE;
 
     ppriv = msGetPixmapPriv(drmmode, ppix);
     if (!drmmode_crtc->slave_damage) {
commit f4c37eeee7953df1fe0e3196eda452acf0078e61
Author: Alex Goins <agoins at nvidia.com>
Date:   Thu Jun 16 20:06:49 2016 -0700

    modesetting: Internal storage of scanout pixmaps
    
    modesetting relied on randr_crtc->scanout_pixmap being consistent with
    calls to SetScanoutPixmap, which is very fragile and makes a lot of
    assumptions about the caller's behavior.
    
    For example, RRReplaceScanoutPixmap(), when dropping off with !size_fits,
    will set randr_crtc->scanout_pixmap = NULL and then call SetScanoutPixmap.
    Without this patch, drmmode_set_scanout_pixmap_(cpu/gpu) will think that
    there is no scanout pixmap to tear down, because it's already been set to
    NULL.
    
    By keeping track of the scanout pixmap in its internal state, modesetting
    can avoid these types of bugs and reduce constraints on calling
    conventions.
    
    v1: N/A
    v2: N/A
    v3: N/A
    v4: N/A
    v5: Initial commit
    v6: Rebase onto ToT
    v7: Unchanged
    
    Reviewed-by: Dave Airlie <airlied at redhat.com>
    Signed-off-by: Alex Goins <agoins at nvidia.com>

diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c
index 1604044..0059e56 100644
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -532,10 +532,10 @@ static void
 dispatch_dirty_crtc(ScrnInfoPtr scrn, xf86CrtcPtr crtc)
 {
     modesettingPtr ms = modesettingPTR(scrn);
-    PixmapPtr pixmap = crtc->randr_crtc->scanout_pixmap;
-    msPixmapPrivPtr ppriv = msGetPixmapPriv(&ms->drmmode, pixmap);
     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+    PixmapPtr pixmap = drmmode_crtc->prime_pixmap;
     DamagePtr damage = drmmode_crtc->slave_damage;
+    msPixmapPrivPtr ppriv = msGetPixmapPriv(&ms->drmmode, pixmap);
     int fb_id = ppriv->fb_id;
     int ret;
 
@@ -554,10 +554,11 @@ dispatch_slave_dirty(ScreenPtr pScreen)
 
     for (c = 0; c < xf86_config->num_crtc; c++) {
         xf86CrtcPtr crtc = xf86_config->crtc[c];
+        drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
 
-        if (!crtc->randr_crtc)
+        if (!drmmode_crtc)
             continue;
-        if (!crtc->randr_crtc->scanout_pixmap)
+        if (!drmmode_crtc->prime_pixmap)
             continue;
 
         dispatch_dirty_crtc(scrn, crtc);
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c
index 7e5901a..89c468b 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
@@ -454,10 +454,10 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
         drmmode_ConvertToKMode(crtc->scrn, &kmode, mode);
 
         fb_id = drmmode->fb_id;
-        if (crtc->randr_crtc->scanout_pixmap) {
+        if (drmmode_crtc->prime_pixmap) {
             if (!drmmode->reverse_prime_offload_mode) {
                 msPixmapPrivPtr ppriv =
-                    msGetPixmapPriv(drmmode, crtc->randr_crtc->scanout_pixmap);
+                    msGetPixmapPriv(drmmode, drmmode_crtc->prime_pixmap);
                 fb_id = ppriv->fb_id;
                 x = 0;
             } else
@@ -639,8 +639,8 @@ drmmode_set_scanout_pixmap_gpu(xf86CrtcPtr crtc, PixmapPtr ppix)
     int c, total_width = 0, max_height = 0, this_x = 0;
 
     if (!ppix) {
-        if (crtc->randr_crtc->scanout_pixmap) {
-            PixmapStopDirtyTracking(crtc->randr_crtc->scanout_pixmap, screenpix);
+        if (drmmode_crtc->prime_pixmap) {
+            PixmapStopDirtyTracking(drmmode_crtc->prime_pixmap, screenpix);
             if (drmmode->fb_id) {
                 drmModeRmFB(drmmode->fd, drmmode->fb_id);
                 drmmode->fb_id = 0;
@@ -690,8 +690,8 @@ drmmode_set_scanout_pixmap_cpu(xf86CrtcPtr crtc, PixmapPtr ppix)
     void *ptr;
 
     if (!ppix) {
-        if (crtc->randr_crtc->scanout_pixmap) {
-            ppriv = msGetPixmapPriv(drmmode, crtc->randr_crtc->scanout_pixmap);
+        if (drmmode_crtc->prime_pixmap) {
+            ppriv = msGetPixmapPriv(drmmode, drmmode_crtc->prime_pixmap);
             drmModeRmFB(drmmode->fd, ppriv->fb_id);
             ppriv->fb_id = 0;
         }
@@ -727,13 +727,19 @@ drmmode_set_scanout_pixmap_cpu(xf86CrtcPtr crtc, PixmapPtr ppix)
 static Bool
 drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix)
 {
+    Bool ret;
     drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
     drmmode_ptr drmmode = drmmode_crtc->drmmode;
 
     if (drmmode->reverse_prime_offload_mode)
-        return drmmode_set_scanout_pixmap_gpu(crtc, ppix);
+        ret = drmmode_set_scanout_pixmap_gpu(crtc, ppix);
     else
-        return drmmode_set_scanout_pixmap_cpu(crtc, ppix);
+        ret = drmmode_set_scanout_pixmap_cpu(crtc, ppix);
+
+    if (ret)
+        drmmode_crtc->prime_pixmap = ppix;
+
+    return ret;
 }
 
 static void *
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.h b/hw/xfree86/drivers/modesetting/drmmode_display.h
index 6b94641..36c6e91 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.h
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.h
@@ -103,7 +103,10 @@ typedef struct {
 
     drmmode_bo rotate_bo;
     unsigned rotate_fb_id;
+
+    PixmapPtr prime_pixmap;
     unsigned prime_pixmap_x;
+
     /**
      * @{ MSC (vblank count) handling for the PRESENT extension.
      *
commit 1bdbc7e764ed7bf7c1ae46287dec368aa7c7e80d
Author: Alex Goins <agoins at nvidia.com>
Date:   Thu Jun 16 20:06:47 2016 -0700

    randr/xf86: Add PRIME Synchronization / Double Buffer
    
    Changes PRIME to use double buffering and synchronization if all required
    driver functions are available.
    
    rrcrtc.c:
        Changes rrSetupPixmapSharing() to use double buffering and
        synchronization in the case that all required driver functions are
        available. Otherwise, falls back to unsynchronized single buffer.
    
        Changes RRCrtcDetachScanoutPixmap() to properly clean up in the case of
        double buffering.
    
        Moves StopPixmapTracking() from rrDestroySharedPixmap() to
        RRCrtcDetachScanoutPixmap().
    
        Changes RRReplaceScanoutPixmap() to fail if we are using double buffering,
        as it would need a second ppix parameter to function with double buffering,
        and AFAICT no driver I've implemented double buffered source support in uses
        RRReplaceScanoutPixmap().
    
    randrstr.h:
        Adds scanout_pixmap_back to struct _rrCrtc to facilitate PRIME
        double buffering.
    
    xf86Crtc.h:
        Adds current_scanout_back to _xf86Crtc to facilitate detection
        of changes to it in xf86RandR12CrtcSet().
    
    xf86RandR12.c:
        Changes xf86RandR12CrtcSet() to detect changes in
        scanout_pixmap_back.
    
        Adds scanout_pixmap_back to struct _rrCrtc to facilitate PRIME double
        buffering.
    
    v1: Initial commit
    v2: Rename PresentTrackedFlippingPixmap to PresentSharedPixmap
    v3: Refactor to accomodate moving (rr)StartFlippingPixmapTracking and
        (rr)(Enable/Disable)SharedPixmapFlipping to rrScrPrivRec from ScreenRec
        Add fallback if flipping funcs fail
    v4: Detach scanout pixmap when destroying scanout_pixmap_back, to avoid
        dangling pointers in some drivers
    v5: Disable RRReplaceScanoutPixmap for double-buffered PRIME, it would need an
        ABI change with support for 2 pixmaps if it were to be supported, but AFAICT
        no driver that actually supports double-buffered PRIME uses it.
        Refactor to use rrEnableSharedPixmapFlipping() as a substitute for
        rrCrtcSetScanoutPixmap() in the flipping case.
        Remove extraneous pSlaveScrPriv from DetachScanoutPixmap()
        Remove extraneous protopix and pScrPriv from rrSetupPixmapSharing()
    v6: Rebase onto ToT
    v7: Unchanged
    
    Reviewed-by: Dave Airlie <airlied at redhat.com>
    Signed-off-by: Alex Goins <agoins at nvidia.com>

diff --git a/hw/xfree86/modes/xf86Crtc.h b/hw/xfree86/modes/xf86Crtc.h
index fb1dd4f..14ba9d7 100644
--- a/hw/xfree86/modes/xf86Crtc.h
+++ b/hw/xfree86/modes/xf86Crtc.h
@@ -405,6 +405,10 @@ struct _xf86Crtc {
     /* Added in ABI version 5
      */
     PixmapPtr current_scanout;
+
+    /* Added in ABI version 6
+     */
+    PixmapPtr current_scanout_back;
 };
 
 typedef struct _xf86OutputFuncs {
diff --git a/hw/xfree86/modes/xf86RandR12.c b/hw/xfree86/modes/xf86RandR12.c
index 4a21766..9f93270 100644
--- a/hw/xfree86/modes/xf86RandR12.c
+++ b/hw/xfree86/modes/xf86RandR12.c
@@ -1154,7 +1154,8 @@ xf86RandR12CrtcSet(ScreenPtr pScreen,
     if (rotation != crtc->rotation)
         changed = TRUE;
 
-    if (crtc->current_scanout != randr_crtc->scanout_pixmap)
+    if (crtc->current_scanout != randr_crtc->scanout_pixmap ||
+        crtc->current_scanout_back != randr_crtc->scanout_pixmap_back)
         changed = TRUE;
 
     transform = RRCrtcGetTransform(randr_crtc);
@@ -1219,6 +1220,7 @@ xf86RandR12CrtcSet(ScreenPtr pScreen,
             xf86SaveModeContents(&crtc->desiredMode, &mode);
             crtc->desiredRotation = rotation;
             crtc->current_scanout = randr_crtc->scanout_pixmap;
+            crtc->current_scanout_back = randr_crtc->scanout_pixmap_back;
             if (transform) {
                 crtc->desiredTransform = *transform;
                 crtc->desiredTransformPresent = TRUE;
diff --git a/randr/randrstr.h b/randr/randrstr.h
index 3e37df7..ada1348 100644
--- a/randr/randrstr.h
+++ b/randr/randrstr.h
@@ -130,6 +130,7 @@ struct _rrCrtc {
     struct pict_f_transform f_inverse;
 
     PixmapPtr scanout_pixmap;
+    PixmapPtr scanout_pixmap_back;
 };
 
 struct _rrOutput {
diff --git a/randr/rrcrtc.c b/randr/rrcrtc.c
index ea91ab7..089fc1a 100644
--- a/randr/rrcrtc.c
+++ b/randr/rrcrtc.c
@@ -366,9 +366,6 @@ rrDestroySharedPixmap(RRCrtcPtr crtc, PixmapPtr pPixmap) {
     ScreenPtr master = crtc->pScreen->current_master;
 
     if (master && pPixmap->master_pixmap) {
-        PixmapPtr mscreenpix = master->GetScreenPixmap(master);
-
-        master->StopPixmapTracking(mscreenpix, pPixmap);
         /*
          * Unref the pixmap twice: once for the original reference, and once
          * for the reference implicitly added by PixmapShareToSlave.
@@ -387,11 +384,29 @@ RRCrtcDetachScanoutPixmap(RRCrtcPtr crtc)
 {
     rrScrPriv(crtc->pScreen);
 
-    pScrPriv->rrCrtcSetScanoutPixmap(crtc, NULL);
     if (crtc->scanout_pixmap) {
+        ScreenPtr master = crtc->pScreen->current_master;
+        PixmapPtr mscreenpix = master->GetScreenPixmap(master);
+
+        if (crtc->scanout_pixmap_back) {
+            pScrPriv->rrDisableSharedPixmapFlipping(crtc);
+
+            master->StopFlippingPixmapTracking(mscreenpix,
+                                               crtc->scanout_pixmap,
+                                               crtc->scanout_pixmap_back);
+
+            rrDestroySharedPixmap(crtc, crtc->scanout_pixmap_back);
+            crtc->scanout_pixmap_back = NULL;
+        }
+        else {
+            pScrPriv->rrCrtcSetScanoutPixmap(crtc, NULL);
+            master->StopPixmapTracking(mscreenpix, crtc->scanout_pixmap);
+        }
+
         rrDestroySharedPixmap(crtc, crtc->scanout_pixmap);
+        crtc->scanout_pixmap = NULL;
     }
-    crtc->scanout_pixmap = NULL;
+
     RRCrtcChanged(crtc, TRUE);
 }
 
@@ -400,9 +415,7 @@ rrCreateSharedPixmap(RRCrtcPtr crtc, ScreenPtr master,
                      int width, int height, int depth,
                      int x, int y, Rotation rotation)
 {
-    Bool ret;
     PixmapPtr mpix, spix;
-    rrScrPriv(crtc->pScreen);
 
     mpix = master->CreatePixmap(master, width, height, depth,
                                 CREATE_PIXMAP_USAGE_SHARED);
@@ -415,13 +428,6 @@ rrCreateSharedPixmap(RRCrtcPtr crtc, ScreenPtr master,
         return NULL;
     }
 
-    ret = pScrPriv->rrCrtcSetScanoutPixmap(crtc, spix);
-    if (ret == FALSE) {
-        rrDestroySharedPixmap(crtc, spix);
-        ErrorF("randr: failed to set shadow slave pixmap\n");
-        return NULL;
-    }
-
     return spix;
 }
 
@@ -430,16 +436,30 @@ rrSetupPixmapSharing(RRCrtcPtr crtc, int width, int height,
                      int x, int y, Rotation rotation)
 {
     ScreenPtr master = crtc->pScreen->current_master;
+    rrScrPrivPtr pMasterScrPriv = rrGetScrPriv(master);
+    rrScrPrivPtr pSlaveScrPriv = rrGetScrPriv(crtc->pScreen);
+
     int depth;
     PixmapPtr mscreenpix;
-    PixmapPtr spix;
-
-    /* create a pixmap on the master screen,
-       then get a shared handle for it
-       create a shared pixmap on the slave screen using the handle
-       set the master screen to do dirty updates to the shared pixmap
-       from the screen pixmap.
-       set slave screen to scanout shared linear pixmap
+    PixmapPtr spix_front;
+
+    /* Create a pixmap on the master screen, then get a shared handle for it.
+       Create a shared pixmap on the slave screen using the handle.
+
+       If sync == FALSE --
+       Set slave screen to scanout shared linear pixmap.
+       Set the master screen to do dirty updates to the shared pixmap
+       from the screen pixmap on its own accord.
+
+       If sync == TRUE --
+       If any of the below steps fail, clean up and fall back to sync == FALSE.
+       Create another shared pixmap on the slave screen using the handle.
+       Set slave screen to prepare for scanout and flipping between shared
+       linear pixmaps.
+       Set the master screen to do dirty updates to the shared pixmaps from the
+       screen pixmap when prompted to by us or the slave.
+       Prompt the master to do a dirty update on the first shared pixmap, then
+       defer to the slave.
     */
 
     mscreenpix = master->GetScreenPixmap(master);
@@ -452,16 +472,65 @@ rrSetupPixmapSharing(RRCrtcPtr crtc, int width, int height,
         return TRUE;
     }
 
-    spix = rrCreateSharedPixmap(crtc, master,
-                                width, height, depth,
-                                x, y, rotation);
-    if (spix == NULL) {
+    spix_front = rrCreateSharedPixmap(crtc, master,
+                                      width, height, depth,
+                                      x, y, rotation);
+    if (spix_front == NULL) {
         return FALSE;
     }
 
-    crtc->scanout_pixmap = spix;
+    /* Both source and sink must support required ABI funcs for flipping */
+    if (pSlaveScrPriv->rrEnableSharedPixmapFlipping &&
+        pSlaveScrPriv->rrDisableSharedPixmapFlipping &&
+        pMasterScrPriv->rrStartFlippingPixmapTracking &&
+        master->PresentSharedPixmap &&
+        master->StopFlippingPixmapTracking) {
+
+        PixmapPtr spix_back = rrCreateSharedPixmap(crtc, master,
+                                                   width, height, depth,
+                                                   x, y, rotation);
+        if (spix_back == NULL)
+            goto fail;
+
+        if (!pSlaveScrPriv->rrEnableSharedPixmapFlipping(crtc,
+                                                         spix_front, spix_back))
+            goto fail;
+
+        crtc->scanout_pixmap = spix_front;
+        crtc->scanout_pixmap_back = spix_back;
+
+        if (!pMasterScrPriv->rrStartFlippingPixmapTracking(crtc, mscreenpix,
+                                                           spix_front,
+                                                           spix_back,
+                                                           x, y, 0, 0,
+                                                           rotation)) {
+            pSlaveScrPriv->rrDisableSharedPixmapFlipping(crtc);
+            goto fail;
+        }
+
+        master->PresentSharedPixmap(spix_front);
+
+        return TRUE;
+
+fail: /* If flipping funcs fail, just fall back to unsynchronized */
+        if (spix_back)
+            rrDestroySharedPixmap(crtc, spix_back);
+
+        crtc->scanout_pixmap = NULL;
+        crtc->scanout_pixmap_back = NULL;
+    }
+
+    ErrorF("randr: falling back to unsynchronized pixmap sharing\n");
+
+    if (!pSlaveScrPriv->rrCrtcSetScanoutPixmap(crtc, spix_front)) {
+        rrDestroySharedPixmap(crtc, spix_front);
+        ErrorF("randr: failed to set shadow slave pixmap\n");
+        return FALSE;
+    }
+    crtc->scanout_pixmap = spix_front;
+
+    master->StartPixmapTracking(mscreenpix, spix_front, x, y, 0, 0, rotation);
 
-    master->StartPixmapTracking(mscreenpix, spix, x, y, 0, 0, rotation);
     return TRUE;
 }
 
@@ -1751,6 +1820,12 @@ RRReplaceScanoutPixmap(DrawablePtr pDrawable, PixmapPtr pPixmap, Bool enable)
         if (!crtc->scanout_pixmap && !enable)
             continue;
 
+        /* not supported with double buffering, needs ABI change for 2 ppix */
+        if (crtc->scanout_pixmap_back) {
+            ret = FALSE;
+            continue;
+        }
+
         size_fits = (crtc->mode &&
                      crtc->x == pDrawable->x &&
                      crtc->y == pDrawable->y &&
commit b601f96a5915a2c486b389483b291797e6fdf617
Author: Alex Goins <agoins at nvidia.com>
Date:   Thu Jun 16 20:06:46 2016 -0700

    xf86: Add PRIME flipping functions to Screen
    
    Adds typedefs for (*RRStartFlippingPixmapTrackingProcPtr),
    (*RREnableSharedPixmapFlippingProcPtr),
    and (*RRDisableSharedPixmapFlippingProcPtr) in randrstr.h.
    
    Adds typedefs for (*PresentSharedPixmapProcPtr),
    (*SharedPixmapNotifyDamageProcPtr),
    (*RequestSharedPixmapNotifyDamageProcPtr), and
    (*StopFlippingPixmapTrackingProcPtr) in scrnintstr.h.
    
    Adds RR(Enable/Disable)SharedPixmapFlipping, and
    RRStartFlippingPixmapTracking to rrScrnPrivRec.
    
    Adds StopFlippingPixmapTracking, PresentSharedPixmap,
    SharedPixmapNotifyDamage, and RequestSharedPixmapNotifyDamage to ScreenRec.
    
    rrScrnPrivRec used for functions that use RandR-private data types, and
    ScreenRec used for the rest.
    
    RREnableSharedPixmapFlipping will allow the sink driver to setup for
    flipping between two shared pixmaps.
    
    RRDisableSharedPixmapFlipping will allow the sink driver to do teardown
    associated with flipping between two shared pixmaps.
    
    (RRStart/Stop)FlippingPixmapTracking are merely the double-buffered
    equivalents of (Start/Stop)PixmapTracking, allowing the source driver to do
    whatever setup and teardown necessary for presenting on the two shared
    pixmaps.
    
    PresentSharedPixmap is a function exposed by the source driver for the X
    server or sink driver to call to request a present on a given shared
    pixmap.  This way, presents can be driven by the sink's vblank instead of a
    timer or similar mechanism.
    
    SharedPixmapNotifyDamage and RequestSharedPixmapNotifyDamage are OPTIONAL
    (even for double-buffered PRIME) functions exposed by the sink driver and
    the source driver, respectively. By calling
    master->RequestSharedPixmapNotifyDamage(ppix), the sink driver can request
    for the source driver to call slave->SharedPixmapNotifyDamage(ppix) in
    response to damage on the master screen pixmap tracked by ppix.
    
    v1: Initial commit
    v2: Rename PresentTrackedFlippingPixmap to PresentSharedPixmap
        Add SharedPixmapNotifyDamage / RequestSharedPixmapNotifyDamage
    v3: Add RRCrtcPtr as a parameter to StartFlippingPixmapTracking
        Move functions that use RandR-private data types to rrScrnPrivRec.
    v4: Unchanged
    v5: Add front and back parameters to RREnableSharedPixmapFlippingProcPtr
    v6: Rebase onto ToT
    v7: Unchanged
    
    Reviewed-by: Dave Airlie <airlied at redhat.com>
    Signed-off-by: Alex Goins <agoins at nvidia.com>

diff --git a/include/scrnintstr.h b/include/scrnintstr.h
index 63ef55c..c5dadc7 100644
--- a/include/scrnintstr.h
+++ b/include/scrnintstr.h
@@ -349,8 +349,17 @@ typedef Bool (*StartPixmapTrackingProcPtr)(PixmapPtr, PixmapPtr,
                                            int dst_x, int dst_y,
                                            Rotation rotation);
 
+typedef Bool (*PresentSharedPixmapProcPtr)(PixmapPtr);
+
+typedef Bool (*RequestSharedPixmapNotifyDamageProcPtr)(PixmapPtr);
+
 typedef Bool (*StopPixmapTrackingProcPtr)(PixmapPtr, PixmapPtr);
 
+typedef Bool (*StopFlippingPixmapTrackingProcPtr)(PixmapPtr,
+                                                  PixmapPtr, PixmapPtr);
+
+typedef Bool (*SharedPixmapNotifyDamageProcPtr)(PixmapPtr);
+
 typedef Bool (*ReplaceScanoutPixmapProcPtr)(DrawablePtr, PixmapPtr, Bool);
 
 typedef WindowPtr (*XYToWindowProcPtr)(ScreenPtr pScreen,
@@ -605,6 +614,11 @@ typedef struct _Screen {
     StartPixmapTrackingProcPtr StartPixmapTracking;
     StopPixmapTrackingProcPtr StopPixmapTracking;
 
+    SharedPixmapNotifyDamageProcPtr SharedPixmapNotifyDamage;
+    RequestSharedPixmapNotifyDamageProcPtr RequestSharedPixmapNotifyDamage;
+    PresentSharedPixmapProcPtr PresentSharedPixmap;
+    StopFlippingPixmapTrackingProcPtr StopFlippingPixmapTracking;
+
     struct xorg_list pixmap_dirty_list;
 
     ReplaceScanoutPixmapProcPtr ReplaceScanoutPixmap;
diff --git a/randr/randrstr.h b/randr/randrstr.h
index 472721a..3e37df7 100644
--- a/randr/randrstr.h
+++ b/randr/randrstr.h
@@ -278,6 +278,19 @@ typedef Bool (*RRSetConfigProcPtr) (ScreenPtr pScreen,
 
 typedef Bool (*RRCrtcSetScanoutPixmapProcPtr)(RRCrtcPtr crtc, PixmapPtr pixmap);
 
+typedef Bool (*RRStartFlippingPixmapTrackingProcPtr)(RRCrtcPtr, PixmapPtr,
+                                                     PixmapPtr, PixmapPtr,
+                                                     int x, int y,
+                                                     int dst_x, int dst_y,
+                                                     Rotation rotation);
+
+typedef Bool (*RREnableSharedPixmapFlippingProcPtr)(RRCrtcPtr,
+                                                    PixmapPtr front,
+                                                    PixmapPtr back);
+
+typedef void (*RRDisableSharedPixmapFlippingProcPtr)(RRCrtcPtr);
+
+
 typedef struct _rrScrPriv {
     /*
      * 'public' part of the structure; DDXen fill this in
@@ -304,6 +317,10 @@ typedef struct _rrScrPriv {
     /* TODO #if RANDR_15_INTERFACE */
     RRCrtcSetScanoutPixmapProcPtr rrCrtcSetScanoutPixmap;
 
+    RRStartFlippingPixmapTrackingProcPtr rrStartFlippingPixmapTracking;
+    RREnableSharedPixmapFlippingProcPtr rrEnableSharedPixmapFlipping;
+    RRDisableSharedPixmapFlippingProcPtr rrDisableSharedPixmapFlipping;
+
     RRProviderSetOutputSourceProcPtr rrProviderSetOutputSource;
     RRProviderSetOffloadSinkProcPtr rrProviderSetOffloadSink;
     RRProviderGetPropertyProcPtr rrProviderGetProperty;


More information about the xorg-commit mailing list