Signed-off-by: Qiang Yu <Qiang.Yu at amd.com> Reviewed-by: Michel Dänzer <michel.daenzer at amd.com> --- hw/xfree86/drivers/modesetting/dri2.c | 237 ++++++++++++++++++++++- hw/xfree86/drivers/modesetting/driver.h | 5 +- hw/xfree86/drivers/modesetting/drmmode_display.h | 3 + hw/xfree86/drivers/modesetting/pageflip.c | 7 +- hw/xfree86/drivers/modesetting/present.c | 20 +- 5 files changed, 257 insertions(+), 15 deletions(-) diff --git a/hw/xfree86/drivers/modesetting/dri2.c b/hw/xfree86/drivers/modesetting/dri2.c index 83cb3e0..d9d4de8 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,197 @@ 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_abort(modesettingPtr ms, void *data) +{ + struct ms_present_vblank_event *event = data; + + ms->drmmode.dri2_flipping = FALSE; + free(event); +} + +static void +ms_dri2_flip_handler(modesettingPtr ms, uint64_t msc, + uint64_t ust, void *data) +{ + struct ms_dri2_vblank_event *event = data; + uint32_t frame = msc; + uint32_t tv_sec = ust / 1000000; + uint32_t tv_usec = ust % 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); + + ms->drmmode.dri2_flipping = FALSE; + free(event); +} + +static Bool +ms_dri2_schedule_flip(ms_dri2_frame_event_ptr info) +{ + DrawablePtr draw = info->drawable; + ScreenPtr screen = draw->pScreen; + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + modesettingPtr ms = modesettingPTR(scrn); + 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)) { + ms->drmmode.dri2_flipping = TRUE; + 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 && + !ms->drmmode.present_flipping && + 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. + */ + region.extents.x1 = region.extents.y1 = 0; + region.extents.x2 = front_priv->pixmap->drawable.width; + region.extents.y2 = front_priv->pixmap->drawable.height; + 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 +609,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 +806,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 +844,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 +898,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 +911,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 +938,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 +950,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; diff --git a/hw/xfree86/drivers/modesetting/driver.h b/hw/xfree86/drivers/modesetting/driver.h index 38f0ec0..761490a 100644 --- a/hw/xfree86/drivers/modesetting/driver.h +++ b/hw/xfree86/drivers/modesetting/driver.h @@ -156,11 +156,12 @@ Bool ms_present_screen_init(ScreenPtr screen); #ifdef GLAMOR -typedef void (*ms_pageflip_handler_proc)(uint64_t frame, +typedef void (*ms_pageflip_handler_proc)(modesettingPtr ms, + uint64_t frame, uint64_t usec, void *data); -typedef void (*ms_pageflip_abort_proc)(void *data); +typedef void (*ms_pageflip_abort_proc)(modesettingPtr ms, void *data); int ms_flush_drm_events(ScreenPtr screen); diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.h b/hw/xfree86/drivers/modesetting/drmmode_display.h index 5499c16..f774250 100644 --- a/hw/xfree86/drivers/modesetting/drmmode_display.h +++ b/hw/xfree86/drivers/modesetting/drmmode_display.h @@ -80,6 +80,9 @@ typedef struct { Bool is_secondary; PixmapPtr fbcon_pixmap; + + Bool dri2_flipping; + Bool present_flipping; } drmmode_rec, *drmmode_ptr; typedef struct { diff --git a/hw/xfree86/drivers/modesetting/pageflip.c b/hw/xfree86/drivers/modesetting/pageflip.c index 5f35999..a82e0d6 100644 --- a/hw/xfree86/drivers/modesetting/pageflip.c +++ b/hw/xfree86/drivers/modesetting/pageflip.c @@ -98,7 +98,7 @@ ms_pageflip_handler(uint64_t msc, uint64_t ust, void *data) } if (flipdata->flip_count == 1) { - flipdata->event_handler(flipdata->fe_msc, + flipdata->event_handler(ms, flipdata->fe_msc, flipdata->fe_usec, flipdata->event); @@ -115,9 +115,12 @@ ms_pageflip_abort(void *data) { struct ms_crtc_pageflip *flip = data; struct ms_flipdata *flipdata = flip->flipdata; + ScreenPtr screen = flipdata->screen; + ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + modesettingPtr ms = modesettingPTR(scrn); if (flipdata->flip_count == 1) - flipdata->abort_handler(flipdata->event); + flipdata->abort_handler(ms, flipdata->event); ms_pageflip_free(flip); } diff --git a/hw/xfree86/drivers/modesetting/present.c b/hw/xfree86/drivers/modesetting/present.c index 0a7e75a..92130c0 100644 --- a/hw/xfree86/drivers/modesetting/present.c +++ b/hw/xfree86/drivers/modesetting/present.c @@ -53,6 +53,7 @@ struct ms_present_vblank_event { uint64_t event_id; + Bool unflip; }; static RRCrtcPtr @@ -197,7 +198,8 @@ ms_present_flush(WindowPtr window) * Notify the extension code */ static void -ms_present_flip_handler(uint64_t msc, uint64_t ust, void *data) +ms_present_flip_handler(modesettingPtr ms, uint64_t msc, + uint64_t ust, void *data) { struct ms_present_vblank_event *event = data; @@ -205,6 +207,9 @@ ms_present_flip_handler(uint64_t msc, uint64_t ust, void *data) (long long) event->event_id, (long long) msc, (long long) ust)); + if (event->unflip) + ms->drmmode.present_flipping = FALSE; + ms_present_vblank_handler(msc, ust, event); } @@ -212,7 +217,7 @@ ms_present_flip_handler(uint64_t msc, uint64_t ust, void *data) * Callback for the DRM queue abort code. A flip has been aborted. */ static void -ms_present_flip_abort(void *data) +ms_present_flip_abort(modesettingPtr ms, void *data) { struct ms_present_vblank_event *event = data; @@ -240,6 +245,9 @@ ms_present_check_flip(RRCrtcPtr crtc, if (!ms->drmmode.pageflip) return FALSE; + if (ms->drmmode.dri2_flipping) + return FALSE; + if (!scrn->vtSema) return FALSE; @@ -286,6 +294,7 @@ ms_present_flip(RRCrtcPtr crtc, { ScreenPtr screen = crtc->pScreen; ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + modesettingPtr ms = modesettingPTR(scrn); xf86CrtcPtr xf86_crtc = crtc->devPrivate; drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private; Bool ret; @@ -302,10 +311,14 @@ ms_present_flip(RRCrtcPtr crtc, (long long) event_id, (long long) target_msc)); event->event_id = event_id; + event->unflip = FALSE; + ret = ms_do_pageflip(screen, pixmap, event, drmmode_crtc->vblank_pipe, !sync_flip, ms_present_flip_handler, ms_present_flip_abort); if (!ret) xf86DrvMsg(scrn->scrnIndex, X_ERROR, "present flip failed\n"); + else + ms->drmmode.present_flipping = TRUE; return ret; } @@ -317,6 +330,7 @@ static void ms_present_unflip(ScreenPtr screen, uint64_t event_id) { ScrnInfoPtr scrn = xf86ScreenToScrn(screen); + modesettingPtr ms = modesettingPTR(scrn); PixmapPtr pixmap = screen->GetScreenPixmap(screen); xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); int i; @@ -327,6 +341,7 @@ ms_present_unflip(ScreenPtr screen, uint64_t event_id) return; event->event_id = event_id; + event->unflip = TRUE; if (ms_present_check_flip(NULL, screen->root, pixmap, TRUE) && ms_do_pageflip(screen, pixmap, event, -1, FALSE, @@ -358,6 +373,7 @@ ms_present_unflip(ScreenPtr screen, uint64_t event_id) } present_event_notify(event_id, 0, 0); + ms->drmmode.present_flipping = FALSE; } #endif -- 2.7.4