[PATCH v2 6/8] modesetting: Implement PRIME syncing as a sink

Alex Goins agoins at nvidia.com
Tue Jan 5 13:56:04 PST 2016


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 and
    SharedPixmapNotifyDamage.

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 declarations for drmmode_SetupPageFlipFence(),
    drmmode_EnableSharedPixmapFlipping(),
    drmmode_DisableSharedPixmapFlipping, drmmode_SharedPixmapFlip(), and
    drmmode_SharedPixmapPresentOnVBlank().

drmmode_display.c:
    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:
        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_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 scanout_pixmap and scanout_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 to 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, 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.

v1: Initial commit
v2: Renamed PresentTrackedFlippingPixmap to PresentSharedPixmap
    Renamed flipSeq to flip_seq
    Renamed page_flip_event_args to vblank_event_args
    Warn if flip failed
    Use SharedPixmapNotifyDamage to retry on next vblank after damage

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

diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c
index e53d3d4..fd292e8 100644
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -1075,6 +1075,71 @@ 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
+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];
+
+        if (!crtc->randr_crtc)
+            continue;
+        if (!(crtc->randr_crtc->scanout_pixmap &&
+              crtc->randr_crtc->scanout_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);
@@ -1214,6 +1279,10 @@ ScreenInit(ScreenPtr pScreen, int argc, char **argv)
     pScreen->StartPixmapTracking = PixmapStartDirtyTracking;
     pScreen->StopPixmapTracking = PixmapStopDirtyTracking;
 
+    pScreen->EnableSharedPixmapFlipping = msEnableSharedPixmapFlipping;
+    pScreen->SharedPixmapNotifyDamage = msSharedPixmapNotifyDamage;
+    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..46d9694 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
@@ -221,6 +221,201 @@ 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;
+
+    RRCrtcPtr randr_crtc = args->crtc->randr_crtc;
+
+    if (args->flip) {
+        /* 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 */
+        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 == crtc->randr_crtc->scanout_pixmap)
+        return FALSE; /* Already flipped to this pixmap */
+    if (ppix != crtc->randr_crtc->scanout_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 = crtc->randr_crtc->scanout_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 = crtc->randr_crtc->scanout_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;
+
+    return drmmode_SharedPixmapPresent(crtc->randr_crtc->scanout_pixmap_back,
+                                       crtc, drmmode);
+}
+
+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)->flip_seq;
+    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)->flip_seq;
+    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 +631,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 +830,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..2ba0297 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 {
@@ -141,6 +143,10 @@ typedef struct {
 typedef struct _msPixmapPriv {
     uint32_t fb_id;
     struct dumb_bo *backing_bo; /* if this pixmap is backed by a dumb bo */
+
+    /** 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;
@@ -159,6 +165,13 @@ Bool drmmode_SetSlaveBO(PixmapPtr ppix,
                         drmmode_ptr drmmode,
                         int fd_handle, int pitch, int size);
 
+Bool drmmode_EnableSharedPixmapFlipping(xf86CrtcPtr crtc, drmmode_ptr drmmode);
+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);
-- 
1.9.1



More information about the xorg-devel mailing list