[PATCH xserver 3/6] modesetting: add DRI2 page flip support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Qiang Yu <Qiang.Yu at amd.com>
---
 hw/xfree86/drivers/modesetting/dri2.c | 260 ++++++++++++++++++++++++++++++++--
 1 file changed, 251 insertions(+), 9 deletions(-)

diff --git a/hw/xfree86/drivers/modesetting/dri2.c b/hw/xfree86/drivers/modesetting/dri2.c
index 83cb3e0..11e0e6f 100644
--- a/hw/xfree86/drivers/modesetting/dri2.c
+++ b/hw/xfree86/drivers/modesetting/dri2.c
@@ -46,6 +46,7 @@
 
 enum ms_dri2_frame_event_type {
     MS_DRI2_QUEUE_SWAP,
+    MS_DRI2_QUEUE_FLIP,
     MS_DRI2_WAIT_MSC,
 };
 
@@ -399,6 +400,220 @@ ms_dri2_blit_swap(DrawablePtr drawable,
     ms_dri2_copy_region(drawable, &region, dst, src);
 }
 
+struct ms_dri2_vblank_event {
+    XID drawable_id;
+    ClientPtr client;
+    DRI2SwapEventPtr event_complete;
+    void *event_data;
+};
+
+static void
+ms_dri2_flip_free(struct ms_crtc_pageflip *flip)
+{
+    struct ms_flipdata *flipdata = flip->flipdata;
+
+    free(flip);
+    if (--flipdata->flip_count > 0)
+        return;
+    free(flipdata);
+}
+
+static void
+ms_dri2_flip_abort(void *data)
+{
+    struct ms_crtc_pageflip *flip = data;
+    struct ms_flipdata *flipdata = flip->flipdata;
+
+    if (flipdata->flip_count == 1)
+        free(flipdata->event);
+
+    ms_dri2_flip_free(flip);
+}
+
+static void
+ms_dri2_flip_handler(uint64_t msc, uint64_t ust, void *data)
+{
+    struct ms_crtc_pageflip *flip = data;
+    ScreenPtr screen = flip->flipdata->screen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+    struct ms_flipdata *flipdata = flip->flipdata;
+    struct ms_dri2_vblank_event *event = flipdata->event;
+
+    if (flip->on_reference_crtc) {
+        flipdata->fe_msc = msc;
+        flipdata->fe_usec = ust;
+    }
+
+    if (flipdata->flip_count == 1) {
+        uint32_t frame = flipdata->fe_msc;
+        uint32_t tv_sec = flipdata->fe_usec / 1000000;
+        uint32_t tv_usec = flipdata->fe_usec % 1000000;
+        DrawablePtr drawable;
+        int status;
+
+        status = dixLookupDrawable(&drawable, event->drawable_id, serverClient,
+                                   M_ANY, DixWriteAccess);
+        if (status == Success)
+            DRI2SwapComplete(event->client, drawable, frame, tv_sec, tv_usec,
+                             DRI2_FLIP_COMPLETE, event->event_complete,
+                             event->event_data);
+        drmModeRmFB(ms->fd, flipdata->old_fb_id);
+    }
+
+    ms_dri2_flip_free(flip);
+}
+
+static Bool
+ms_dri2_schedule_flip(ms_dri2_frame_event_ptr info)
+{
+    DrawablePtr draw = info->drawable;
+    ScreenPtr screen = draw->pScreen;
+    ms_dri2_buffer_private_ptr back_priv = info->back->driverPrivate;
+    struct ms_dri2_vblank_event *event;
+    drmmode_crtc_private_ptr drmmode_crtc = info->crtc->driver_private;
+
+    event = calloc(1, sizeof(struct ms_dri2_vblank_event));
+    if (!event)
+        return FALSE;
+
+    event->drawable_id = draw->id;
+    event->client = info->client;
+    event->event_complete = info->event_complete;
+    event->event_data = info->event_data;
+
+    if (ms_do_pageflip(screen, back_priv->pixmap, event,
+                       drmmode_crtc->vblank_pipe, FALSE,
+                       ms_dri2_flip_handler,
+                       ms_dri2_flip_abort)) {
+        return TRUE;
+    }
+    return FALSE;
+}
+
+static Bool
+update_front(DrawablePtr draw, DRI2BufferPtr front)
+{
+    ScreenPtr screen = draw->pScreen;
+    PixmapPtr pixmap = get_drawable_pixmap(draw);
+    ms_dri2_buffer_private_ptr priv = front->driverPrivate;
+    CARD32 size;
+    CARD16 pitch;
+
+    front->name = glamor_name_from_pixmap(pixmap, &pitch, &size);
+    if (front->name < 0)
+        return FALSE;
+
+    (*screen->DestroyPixmap) (priv->pixmap);
+    front->pitch = pixmap->devKind;
+    front->cpp = pixmap->drawable.bitsPerPixel / 8;
+    priv->pixmap = pixmap;
+    pixmap->refcnt++;
+
+    return TRUE;
+}
+
+static Bool
+can_exchange(ScrnInfoPtr scrn, DrawablePtr draw,
+	     DRI2BufferPtr front, DRI2BufferPtr back)
+{
+    ms_dri2_buffer_private_ptr front_priv = front->driverPrivate;
+    ms_dri2_buffer_private_ptr back_priv = back->driverPrivate;
+    PixmapPtr front_pixmap;
+    PixmapPtr back_pixmap = back_priv->pixmap;
+    xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn);
+    int num_crtcs_on = 0;
+    int i;
+
+    for (i = 0; i < config->num_crtc; i++) {
+        drmmode_crtc_private_ptr drmmode_crtc = config->crtc[i]->driver_private;
+
+        /* Don't do pageflipping if CRTCs are rotated. */
+#ifdef GLAMOR_HAS_GBM
+        if (drmmode_crtc->rotate_bo.gbm)
+            return FALSE;
+#endif
+
+        if (ms_crtc_on(config->crtc[i]))
+            num_crtcs_on++;
+    }
+
+    /* We can't do pageflipping if all the CRTCs are off. */
+    if (num_crtcs_on == 0)
+        return FALSE;
+
+    if (!update_front(draw, front))
+        return FALSE;
+
+    front_pixmap = front_priv->pixmap;
+
+    if (front_pixmap->drawable.width != back_pixmap->drawable.width)
+        return FALSE;
+
+    if (front_pixmap->drawable.height != back_pixmap->drawable.height)
+        return FALSE;
+
+    if (front_pixmap->drawable.bitsPerPixel !=
+        back_pixmap->drawable.bitsPerPixel)
+        return FALSE;
+
+    if (front_pixmap->devKind != back_pixmap->devKind)
+        return FALSE;
+
+    return TRUE;
+}
+
+static Bool
+can_flip(ScrnInfoPtr scrn, DrawablePtr draw,
+	 DRI2BufferPtr front, DRI2BufferPtr back)
+{
+    modesettingPtr ms = modesettingPTR(scrn);
+
+    return draw->type == DRAWABLE_WINDOW &&
+        ms->drmmode.pageflip &&
+        scrn->vtSema &&
+        DRI2CanFlip(draw) && can_exchange(scrn, draw, front, back);
+}
+
+static void
+ms_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front,
+                         DRI2BufferPtr back)
+{
+    ms_dri2_buffer_private_ptr front_priv = front->driverPrivate;
+    ms_dri2_buffer_private_ptr back_priv = back->driverPrivate;
+    ScreenPtr screen = draw->pScreen;
+    ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
+    modesettingPtr ms = modesettingPTR(scrn);
+    msPixmapPrivPtr front_pix = msGetPixmapPriv(&ms->drmmode, front_priv->pixmap);
+    msPixmapPrivPtr back_pix = msGetPixmapPriv(&ms->drmmode, back_priv->pixmap);
+    msPixmapPrivRec tmp_pix;
+    RegionRec region;
+    int tmp;
+
+    /* Swap BO names so DRI works */
+    tmp = front->name;
+    front->name = back->name;
+    back->name = tmp;
+
+    /* Swap pixmap privates */
+    tmp_pix = *front_pix;
+    *front_pix = *back_pix;
+    *back_pix = tmp_pix;
+
+    glamor_egl_exchange_buffers(front_priv->pixmap, back_priv->pixmap);
+
+    /* Post damage on the front buffer so that listeners, such
+     * as DisplayLink know take a copy and shove it over the USB.
+     * also for sw cursors.
+     */
+    region.extents.x1 = region.extents.y1 = 0;
+    region.extents.x2 = front_priv->pixmap->drawable.width;
+    region.extents.y2 = front_priv->pixmap->drawable.width;
+    region.data = NULL;
+    DamageRegionAppend(&front_priv->pixmap->drawable, &region);
+    DamageRegionProcessPending(&front_priv->pixmap->drawable);
+}
+
 static void
 ms_dri2_frame_event_handler(uint64_t msc,
                             uint64_t usec,
@@ -417,6 +632,13 @@ ms_dri2_frame_event_handler(uint64_t msc,
     }
 
     switch (frame_info->type) {
+    case MS_DRI2_QUEUE_FLIP:
+        if (can_flip(scrn, drawable, frame_info->front, frame_info->back) &&
+            ms_dri2_schedule_flip(frame_info)) {
+            ms_dri2_exchange_buffers(drawable, frame_info->front, frame_info->back);
+            break;
+        }
+        /* else fall through to blit */
     case MS_DRI2_QUEUE_SWAP:
         ms_dri2_blit_swap(drawable, frame_info->front, frame_info->back);
         DRI2SwapComplete(frame_info->client, drawable, msc, tv_sec, tv_usec,
@@ -607,7 +829,7 @@ ms_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
     ScrnInfoPtr scrn = xf86ScreenToScrn(screen);
     modesettingPtr ms = modesettingPTR(scrn);
     drmVBlank vbl;
-    int ret;
+    int ret, flip = 0;
     xf86CrtcPtr crtc = ms_dri2_crtc_covering_drawable(draw);
     drmmode_crtc_private_ptr drmmode_crtc;
     ms_dri2_frame_event_ptr frame_info = NULL;
@@ -645,20 +867,36 @@ ms_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
 
     ret = ms_get_crtc_ust_msc(crtc, &current_ust, &current_msc);
 
+    /* Flips need to be submitted one frame before */
+    if (can_flip(scrn, draw, front, back)) {
+        frame_info->type = MS_DRI2_QUEUE_FLIP;
+        flip = 1;
+    }
+
+    /* Correct target_msc by 'flip' if frame_info->type == MS_DRI2_QUEUE_FLIP.
+     * Do it early, so handling of different timing constraints
+     * for divisor, remainder and msc vs. target_msc works.
+     */
+    if (*target_msc > 0)
+        *target_msc -= flip;
+
     /*
      * If divisor is zero, or current_msc is smaller than target_msc
      * we just need to make sure target_msc passes before initiating
      * the swap.
      */
     if (divisor == 0 || current_msc < *target_msc) {
-        /* We need to use DRM_VBLANK_NEXTONMISS to avoid unreliable
-         * timestamping later on.
-         */
         vbl.request.type = (DRM_VBLANK_ABSOLUTE |
-                            DRM_VBLANK_NEXTONMISS |
                             DRM_VBLANK_EVENT |
                             drmmode_crtc->vblank_pipe);
 
+        /* If non-pageflipping, but blitting/exchanging, we need to use
+         * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later
+         * on.
+         */
+        if (flip == 0)
+            vbl.request.type |= DRM_VBLANK_NEXTONMISS;
+
         /* If target_msc already reached or passed, set it to
          * current_msc to ensure we return a reasonable value back
          * to the caller. This makes swap_interval logic more robust.
@@ -683,7 +921,8 @@ ms_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
             goto blit_fallback;
         }
 
-        *target_msc = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence);
+        *target_msc = ms_kernel_msc_to_crtc_msc(crtc,
+                                                vbl.reply.sequence + flip);
         frame_info->frame = *target_msc;
 
         return TRUE;
@@ -695,9 +934,10 @@ ms_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
      * equation.
      */
     vbl.request.type = (DRM_VBLANK_ABSOLUTE |
-                        DRM_VBLANK_NEXTONMISS |
                         DRM_VBLANK_EVENT |
                         drmmode_crtc->vblank_pipe);
+    if (flip == 0)
+        vbl.request.type |= DRM_VBLANK_NEXTONMISS;
 
     request_msc = current_msc - (current_msc % divisor) +
         remainder;
@@ -721,7 +961,8 @@ ms_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
     if (!seq)
         goto blit_fallback;
 
-    vbl.request.sequence = ms_crtc_msc_to_kernel_msc(crtc, request_msc);
+    /* Account for 1 frame extra pageflip delay if flip > 0 */
+    vbl.request.sequence = ms_crtc_msc_to_kernel_msc(crtc, request_msc) - flip;
     vbl.request.signal = (unsigned long)seq;
 
     ret = drmWaitVBlank(ms->fd, &vbl);
@@ -732,7 +973,8 @@ ms_dri2_schedule_swap(ClientPtr client, DrawablePtr draw,
         goto blit_fallback;
     }
 
-    *target_msc = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence);
+    /* Adjust returned value for 1 fame pageflip offset of flip > 0 */
+    *target_msc = ms_kernel_msc_to_crtc_msc(crtc, vbl.reply.sequence + flip);
     frame_info->frame = *target_msc;
 
     return TRUE;
-- 
2.7.4



[Index of Archives]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux