[PATCH xserver 3/6] modesetting: add DRI2 page flip support
Qiang Yu
Qiang.Yu at amd.com
Wed Aug 17 10:29:08 UTC 2016
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, ®ion, 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, ®ion);
+ 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, ¤t_ust, ¤t_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
More information about the xorg-devel
mailing list