[PATCH 09/11] modesetting: Implement PRIME syncing as a sink

Alex Goins agoins at nvidia.com
Wed Nov 25 18:39:33 PST 2015


Implements (Enable/Disable)SharedPixmapFlipping, the sink functions for
PRIME synchronization and double buffering. Allows modesetting driver to be
used as a sink with PRIME synchronization.

driver.c:
    Add plumbing for (Enable/Disable)SharedPixmapFlipping.

drmmode_display.h:
    Add flipSeq field to msPixmapPrivRec to keep track of the event handler
    associated with a given pixmap, if any.

    Add enable_flipping field to drmmode_crtc_private_rec to keep track if
    flipping is enabled or disabled.

    Add declarations for drmmode_SetupPageFlipFence(),
    drmmode_EnableSharedPixmapFlipping(),
    drmmode_DisableSharedPixmapFlipping, and drmmode_SharedPixmapFlip().

drmmode_display.c:
    Add definitions for functions drmmode_SharedPixmapFlip(),
    drmmode_SharedPixmapPageFlipEventHandler(),
    drmmode_SharedPixmapPageFlipEventAbort(),
    drmmode_EnableSharedPixmapFlipping(), and
    drmmode_DisableSharedPixmapFlipping,
    drmmode_InitSharedPixmapFlipping(), and
    drmmode_FiniSharedPixmapFlipping, along with struct
    page_flip_event_args.

    The control flow is as follows:
        pScreen->EnableSharedPixmapFlipping() makes its way to
        drmmode_EnableSharedPixmapFlipping(), which sets enable_flipping to
        TRUE.

        When setting a mode, if scanout_pixmap_back is defined, modesetting
        driver will call drmmode_InitSharedPixmapFlipping(), which if
        flipping is enabled will call drmmode_SharedPixmapFlip(noflip =
        TRUE) to raise an event on the next vblank.

        drmmode_SharedPixmapFlip() sets up page flip event handler by
        packing struct page_flip_event_args with the necessary parameters,
        and registering drmmode_SharedPixmapPageFlipEventHandler() and
        drmmode_SharedPixmapPageFlipEventAbort() with the modesetting DRM
        event handler queue. Then, it uses the drmModePageFlip() to flip on
        the next vblank and raise an event, or drmWaitVBlank to just raise
        an event on the next vblank.

        When the flip finally occurs on the next vblank, DRM will raise an
        event that will ultimately be handled by
        drmmode_SharedPixmapPageFlipEventHandler().  This handler updates
        scanout_pixmap and scanout_pixmap_back to reflect that frontTarget
        is now being displayed, calls into the source driver with
        PresentTrackedFlippingPixmap() to present on the now-hidden
        backTarget, and uses drmmode_SharedPixmapFlip() to either flip to
        the target if the present succeeded, or wait for the next vblank to
        try again if it failed. Note that presenting 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 to attaching the
        fences and signaling them when presenting is finished.

        Note that because presenting is requested in response to a vblank,
        PRIME sources will now conform to the sink's refresh rate.

        At teardown, pScreen->DisableSharedPixmapFlipping() will be called,
        making its way to drmmode_FiniSharedPixmapFlipping(). There, the
        event handlers for scanout_pixmap and scanout_pixmap_back are
        aborted, freeing the left over parameter structure.
        drmmode_FiniSharedPixmapFlipping() is also called when detaching
        scanout_pixmap_back.

Signed-off-by: Alex Goins <agoins at nvidia.com>
---
 hw/xfree86/drivers/modesetting/driver.c          |  35 ++++++
 hw/xfree86/drivers/modesetting/drmmode_display.c | 149 +++++++++++++++++++++++
 hw/xfree86/drivers/modesetting/drmmode_display.h |   7 ++
 3 files changed, 191 insertions(+)

diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c
index 80abcdf..e458f97 100644
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -1078,6 +1078,38 @@ msSetSharedPixmapBacking(PixmapPtr ppix, void *fd_handle)
 }
 
 static Bool
+msEnableSharedPixmapFlipping(void *crtcDevPrivate)
+{
+    xf86CrtcPtr crtc = crtcDevPrivate;
+
+    if (crtc->randr_crtc->scanout_pixmap &&
+         crtc->randr_crtc->scanout_pixmap_back) {
+        ScreenPtr screen = crtc->randr_crtc->scanout_pixmap->drawable.pScreen;
+        ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+        modesettingPtr ms = modesettingPTR(scrn);
+
+        return drmmode_EnableSharedPixmapFlipping(crtc, &ms->drmmode);
+    }
+
+    return FALSE;
+}
+
+static void
+msDisableSharedPixmapFlipping(void *crtcDevPrivate)
+{
+    xf86CrtcPtr crtc = crtcDevPrivate;
+
+    if (crtc->randr_crtc->scanout_pixmap &&
+         crtc->randr_crtc->scanout_pixmap_back) {
+        ScreenPtr screen = crtc->randr_crtc->scanout_pixmap->drawable.pScreen;
+        ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+        modesettingPtr ms = modesettingPTR(scrn);
+
+        drmmode_DisableSharedPixmapFlipping(crtc, &ms->drmmode);
+    }
+}
+
+static Bool
 SetMaster(ScrnInfoPtr pScrn)
 {
     modesettingPtr ms = modesettingPTR(pScrn);
@@ -1217,6 +1249,9 @@ ScreenInit(ScreenPtr pScreen, int argc, char **argv)
     pScreen->StartPixmapTracking = PixmapStartDirtyTracking;
     pScreen->StopPixmapTracking = PixmapStopDirtyTracking;
 
+    pScreen->EnableSharedPixmapFlipping = msEnableSharedPixmapFlipping;
+    pScreen->DisableSharedPixmapFlipping = msDisableSharedPixmapFlipping;
+
     if (!xf86CrtcScreenInit(pScreen))
         return FALSE;
 
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c
index 3bbf0d3..64d5e4e 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
@@ -221,6 +221,151 @@ drmmode_SetSlaveBO(PixmapPtr ppix,
     return TRUE;
 }
 
+struct page_flip_event_args {
+    PixmapPtr frontTarget;
+    PixmapPtr backTarget;
+    xf86CrtcPtr crtc;
+    drmmode_ptr drmmode;
+};
+static void
+drmmode_SharedPixmapPageFlipEventHandler(uint64_t frame, uint64_t usec,
+                                         void *data)
+{
+    struct page_flip_event_args *args = data;
+
+    RRCrtcPtr randr_crtc = args->crtc->randr_crtc;
+
+    ScreenPtr slave  = randr_crtc->pScreen,
+              master = slave->current_master;
+
+    if (randr_crtc->scanout_pixmap && randr_crtc->scanout_pixmap_back) {
+        /* frontTarget is being displayed, update crtc to reflect */
+        randr_crtc->scanout_pixmap = args->frontTarget;
+        randr_crtc->scanout_pixmap_back = args->backTarget;
+
+        /* Safe to present on backTarget, no longer displayed */
+        if (master->PresentTrackedFlippingPixmap(args->backTarget)) {
+            /* Queue flip to back target */
+            drmmode_SharedPixmapFlip(args->backTarget, args->frontTarget,
+                                     args->crtc, args->drmmode, FALSE);
+        } else {
+            /* Failed to present, try again on next vblank */
+            drmmode_SharedPixmapFlip(args->frontTarget, args->backTarget,
+                                     args->crtc, args->drmmode, TRUE);
+        }
+    }
+
+    free(args);
+}
+
+static void
+drmmode_SharedPixmapPageFlipEventAbort(void *data)
+{
+    struct page_flip_event_args *args = data;
+
+    msGetPixmapPriv(args->drmmode, args->frontTarget)->flipSeq = 0;
+
+    free(args);
+}
+
+Bool
+drmmode_SharedPixmapFlip(PixmapPtr frontTarget, PixmapPtr backTarget,
+                         xf86CrtcPtr crtc, drmmode_ptr drmmode, Bool noflip)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+    msPixmapPrivPtr ppriv_front = msGetPixmapPriv(drmmode, frontTarget);
+
+    struct page_flip_event_args *event_args;
+
+    event_args = calloc(1, sizeof(*event_args));
+    if (!event_args)
+        return FALSE;
+
+    event_args->frontTarget = frontTarget;
+    event_args->backTarget = backTarget;
+    event_args->crtc = crtc;
+    event_args->drmmode = drmmode;
+
+    ppriv_front->flipSeq =
+        ms_drm_queue_alloc(crtc, event_args,
+                           drmmode_SharedPixmapPageFlipEventHandler,
+                           drmmode_SharedPixmapPageFlipEventAbort);
+
+    if (!noflip &&
+        drmModePageFlip(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
+                        ppriv_front->fb_id, DRM_MODE_PAGE_FLIP_EVENT,
+                        (void *)(intptr_t) ppriv_front->flipSeq) >= 0) {
+        return TRUE;
+    }
+
+    /* Didn't actually flip, just wait for vblank instead */
+    {
+        drmVBlank vbl;
+
+        vbl.request.type =
+            DRM_VBLANK_RELATIVE | DRM_VBLANK_EVENT | drmmode_crtc->vblank_pipe;
+        vbl.request.sequence = 1;
+        vbl.request.signal = (unsigned long) ppriv_front->flipSeq;
+
+        if (drmWaitVBlank(drmmode->fd, &vbl) >= 0)
+            return TRUE;
+    }
+
+    ms_drm_abort_seq(crtc->scrn, ppriv_front->flipSeq);
+    return FALSE;
+}
+
+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;
+
+    return drmmode_SharedPixmapFlip(crtc->randr_crtc->scanout_pixmap_back,
+                                    crtc->randr_crtc->scanout_pixmap,
+                                    crtc, drmmode, TRUE);
+}
+
+static void
+drmmode_FiniSharedPixmapFlipping(xf86CrtcPtr crtc, drmmode_ptr drmmode)
+{
+    uint32_t seq;
+
+    /* Abort page flip event handler on scanout_pixmap */
+    seq = msGetPixmapPriv(drmmode, crtc->randr_crtc->scanout_pixmap)->flipSeq;
+    if (seq)
+        ms_drm_abort_seq(crtc->scrn, seq);
+
+    /* Abort page flip event handler on scanout_pixmap_back */
+    seq = msGetPixmapPriv(drmmode,
+                          crtc->randr_crtc->scanout_pixmap_back)->flipSeq;
+    if (seq)
+        ms_drm_abort_seq(crtc->scrn, seq);
+}
+
+Bool
+drmmode_EnableSharedPixmapFlipping(xf86CrtcPtr crtc, drmmode_ptr drmmode)
+{
+    drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private;
+
+    drmmode_crtc->enable_flipping = TRUE;
+
+    return drmmode_crtc->enable_flipping;
+}
+
+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);
+}
+
 static void
 drmmode_ConvertFromKMode(ScrnInfoPtr scrn,
                          drmModeModeInfo * kmode, DisplayModePtr mode)
@@ -436,6 +581,9 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
         drmmode_crtc->need_modeset = FALSE;
         crtc->funcs->dpms(crtc, DPMSModeOn);
 
+        if (crtc->randr_crtc->scanout_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];
@@ -632,6 +780,7 @@ drmmode_set_scanout_pixmap_cpu(xf86CrtcPtr crtc, PixmapPtr ppix)
             drmModeRmFB(drmmode->fd, ppriv->fb_id);
 
             if (crtc->randr_crtc->scanout_pixmap_back) {
+                drmmode_FiniSharedPixmapFlipping(crtc, drmmode);
                 ppriv = msGetPixmapPriv(drmmode,
                                         crtc->randr_crtc->scanout_pixmap_back);
                 drmModeRmFB(drmmode->fd, ppriv->fb_id);
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.h b/hw/xfree86/drivers/modesetting/drmmode_display.h
index fca68a6..b0f6084 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.h
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.h
@@ -115,6 +115,8 @@ typedef struct {
     /** @} */
 
     Bool need_modeset;
+
+    Bool enable_flipping;
 } drmmode_crtc_private_rec, *drmmode_crtc_private_ptr;
 
 typedef struct {
@@ -140,6 +142,7 @@ typedef struct {
 
 typedef struct _msPixmapPriv {
     uint32_t fb_id;
+    int flipSeq; /* seq of current page flip event handler */
     struct dumb_bo *backing_bo; /* if this pixmap is backed by a dumb bo */
 } msPixmapPrivRec, *msPixmapPrivPtr;
 
@@ -158,6 +161,10 @@ void *drmmode_map_slave_bo(drmmode_ptr drmmode, msPixmapPrivPtr ppriv);
 Bool drmmode_SetSlaveBO(PixmapPtr ppix,
                         drmmode_ptr drmmode,
                         int fd_handle, int pitch, int size);
+Bool drmmode_SharedPixmapFlip(PixmapPtr frontTarget, PixmapPtr backTarget,
+                              xf86CrtcPtr crtc, drmmode_ptr drmmode, Bool noflip);
+Bool drmmode_EnableSharedPixmapFlipping(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);
-- 
1.9.1



More information about the xorg-devel mailing list