diff --git a/src/drmmode_display.c b/src/drmmode_display.c index 37010cd..f22ae0b 100644 --- a/src/drmmode_display.c +++ b/src/drmmode_display.c @@ -29,6 +29,7 @@ #include "config.h" #endif +#include #ifdef XF86DRM_MODE #include #include "micmap.h" @@ -1195,6 +1196,25 @@ drmmode_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec, } static void +drmmode_flip_handler(int fd, unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + drmmode_ptr drmmode = event_data; + + drmmode->flip_count--; + if (drmmode->flip_count > 0) + return; + + drmModeRmFB(drmmode->fd, drmmode->old_fb_id); + + if (drmmode->event_data == NULL) + return; + + radeon_dri2_flip_event_handler(frame, tv_sec, tv_usec, drmmode->event_data); +} + + +static void drm_wakeup_handler(pointer data, int err, pointer p) { drmmode_ptr drmmode = data; @@ -1236,7 +1256,7 @@ Bool drmmode_pre_init(ScrnInfoPtr pScrn, drmmode_ptr drmmode, int cpp) drmmode->flip_count = 0; drmmode->event_context.version = DRM_EVENT_CONTEXT_VERSION; drmmode->event_context.vblank_handler = drmmode_vblank_handler; - drmmode->event_context.page_flip_handler = NULL; + drmmode->event_context.page_flip_handler = drmmode_flip_handler; if (info->dri->pKernelDRMVersion->version_minor >= 4) { AddGeneralSocket(drmmode->fd); RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA, @@ -1476,4 +1496,59 @@ void drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode) #endif } +Bool radeon_do_pageflip(ScrnInfoPtr scrn, struct radeon_bo *new_front, void *data) +{ + RADEONInfoPtr info = RADEONPTR(scrn); + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(scrn); + drmmode_crtc_private_ptr drmmode_crtc = config->crtc[0]->driver_private; + drmmode_ptr drmmode = drmmode_crtc->drmmode; + unsigned int pitch = scrn->displayWidth * info->CurrentLayout.pixel_bytes; + int i, old_fb_id; + + /* + * Create a new handle for the back buffer + */ + old_fb_id = drmmode->fb_id; + if (drmModeAddFB(drmmode->fd, scrn->virtualX, scrn->virtualY, + scrn->depth, scrn->bitsPerPixel, pitch, + new_front->handle, &drmmode->fb_id)) + goto error_out; + + /* + * Queue flips on all enabled CRTCs + * Note that if/when we get per-CRTC buffers, we'll have to update this. + * Right now it assumes a single shared fb across all CRTCs, with the + * kernel fixing up the offset of each CRTC as necessary. + * + * Also, flips queued on disabled or incorrectly configured displays + * may never complete; this is a configuration error. + */ + for (i = 0; i < config->num_crtc; i++) { + if (!config->crtc[i]->enabled) + continue; + + drmmode->event_data = data; + drmmode->flip_count++; + drmmode_crtc = config->crtc[i]->driver_private; + if (drmModePageFlip(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, + drmmode->fb_id, DRM_MODE_PAGE_FLIP_EVENT, drmmode)) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "flip queue failed: %s\n", strerror(errno)); + goto error_undo; + } + } + + drmmode->old_fb_id = old_fb_id; + return TRUE; + +error_undo: + drmModeRmFB(drmmode->fd, drmmode->fb_id); + drmmode->fb_id = old_fb_id; + +error_out: + xf86DrvMsg(scrn->scrnIndex, X_WARNING, "Page flip failed: %s\n", + strerror(errno)); + return FALSE; +} + #endif diff --git a/src/drmmode_display.h b/src/drmmode_display.h index a9891b2..2226075 100644 --- a/src/drmmode_display.h +++ b/src/drmmode_display.h @@ -39,6 +39,7 @@ typedef struct { int fd; unsigned fb_id; + unsigned old_fb_id; drmModeResPtr mode_res; drmModeFBPtr mode_fb; int cpp; @@ -50,6 +51,7 @@ typedef struct { #endif drmEventContext event_context; int flip_count; + void *event_data; } drmmode_rec, *drmmode_ptr; typedef struct { @@ -95,6 +97,8 @@ extern Bool drmmode_setup_colormap(ScreenPtr pScreen, ScrnInfoPtr pScrn); extern void drmmode_uevent_init(ScrnInfoPtr scrn, drmmode_ptr drmmode); extern void drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode); +Bool radeon_do_pageflip(ScrnInfoPtr scrn, struct radeon_bo *new_front, void *data); + #endif #endif diff --git a/src/radeon_dri2.c b/src/radeon_dri2.c index 2668812..b74d172 100644 --- a/src/radeon_dri2.c +++ b/src/radeon_dri2.c @@ -549,6 +549,103 @@ radeon_dri2_unref_buffer(BufferPtr buffer) } } +static Bool +radeon_dri2_schedule_flip(ScrnInfoPtr scrn, ClientPtr client, + DrawablePtr draw, DRI2BufferPtr front, + DRI2BufferPtr back, DRI2SwapEventPtr func, + void *data) +{ + struct dri2_buffer_priv *back_priv; + struct radeon_exa_pixmap_priv *exa_priv; + DRI2FrameEventPtr flip_info; + + flip_info = calloc(1, sizeof(DRI2FrameEventRec)); + if (!flip_info) + return FALSE; + + flip_info->drawable_id = draw->id; + flip_info->client = client; + flip_info->type = DRI2_SWAP; + flip_info->event_complete = func; + flip_info->event_data = data; + xf86DrvMsg(scrn->scrnIndex, X_INFO, "%s:%d fevent[%p]\n", __func__, __LINE__, flip_info); + + /* Page flip the full screen buffer */ + back_priv = back->driverPrivate; + exa_priv = exaGetPixmapDriverPrivate(back_priv->pixmap); + return radeon_do_pageflip(scrn, exa_priv->bo, flip_info); +} + +static Bool +can_exchange(DRI2BufferPtr front, DRI2BufferPtr back) +{ + struct dri2_buffer_priv *front_priv = front->driverPrivate; + struct dri2_buffer_priv *back_priv = back->driverPrivate; + PixmapPtr front_pixmap = front_priv->pixmap; + PixmapPtr back_pixmap = back_priv->pixmap; + + if (front_pixmap->drawable.width != back_pixmap->drawable.width) { + ErrorF("can't exchange - width\n"); + return FALSE; + } + + if (front_pixmap->drawable.height != back_pixmap->drawable.height) { + ErrorF("can't exchange - height\n"); + return FALSE; + } + + /* XXX should we be checking depth instead of bpp? */ +#if 0 + if (front_pixmap->drawable.depth != back_pixmap->drawable.depth) + return FALSE; +#else + if (front_pixmap->drawable.bitsPerPixel != back_pixmap->drawable.bitsPerPixel) { + ErrorF("can't exchange - bpp\n"); + return FALSE; + } +#endif + + return TRUE; +} + +static void +radeon_dri2_exchange_buffers(DrawablePtr draw, DRI2BufferPtr front, DRI2BufferPtr back) +{ + struct dri2_buffer_priv *front_priv = front->driverPrivate; + struct dri2_buffer_priv *back_priv = back->driverPrivate; + struct radeon_exa_pixmap_priv *front_radeon, *back_radeon; + ScreenPtr screen; + RADEONInfoPtr info; + struct radeon_bo *bo; + int tmp; + + /* Swap BO names so DRI works */ + tmp = front->name; + front->name = back->name; + back->name = tmp; + + /* Swap pixmap bos */ + front_radeon = exaGetPixmapDriverPrivate(front_priv->pixmap); + back_radeon = exaGetPixmapDriverPrivate(back_priv->pixmap); + //exaSetPixmapDriverPrivate(front_priv->pixmap, back_radeon); + //exaSetPixmapDriverPrivate(back_priv->pixmap, front_radeon); + bo = back_radeon->bo; + back_radeon->bo = front_radeon->bo; + front_radeon->bo = bo; + + /* Do we need to update the Screen? */ + screen = draw->pScreen; + info = RADEONPTR(xf86Screens[screen->myNum]); + if (front_radeon->bo == info->front_bo) { + radeon_bo_unref(info->front_bo); + info->front_bo = back_radeon->bo; + radeon_bo_ref(info->front_bo); + //exaSetPixmapDriverPrivate(screen->GetScreenPixmap(screen), back_radeon); + front_radeon = exaGetPixmapDriverPrivate(screen->GetScreenPixmap(screen)); + front_radeon->bo = bo; + } +} + void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec, unsigned int tv_usec, void *event_data) { @@ -576,6 +673,19 @@ void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec, switch (event->type) { case DRI2_FLIP: + if (DRI2CanFlip(drawable) && + can_exchange(event->front, event->back) && + radeon_dri2_schedule_flip(scrn, + event->client, + drawable, + event->front, + event->back, + event->event_complete, + event->event_data)) { + radeon_dri2_exchange_buffers(drawable, event->front, event->back); + break; + } + /* else fall through to exchange/blit */ case DRI2_SWAP: box.x1 = 0; box.y1 = 0; @@ -794,6 +904,47 @@ out_complete: return TRUE; } +void radeon_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + DRI2FrameEventPtr flip = event_data; + DrawablePtr drawable; + ScreenPtr screen; + ScrnInfoPtr scrn; + int status; + PixmapPtr pixmap; + + status = dixLookupDrawable(&drawable, flip->drawable_id, serverClient, + M_ANY, DixWriteAccess); + if (status != Success) { + ErrorF("%s - status failed\n", __func__); + free(flip); + return; + } + + screen = drawable->pScreen; + scrn = xf86Screens[screen->myNum]; + + pixmap = screen->GetScreenPixmap(screen); + xf86DrvMsg(scrn->scrnIndex, X_INFO, "%s:%d fevent[%p] width %d pitch %d (/4 %d)\n", + __func__, __LINE__, flip, pixmap->drawable.width, pixmap->devKind, pixmap->devKind/4); + + /* We assume our flips arrive in order, so we don't check the frame */ + switch (flip->type) { + case DRI2_SWAP: + DRI2SwapComplete(flip->client, drawable, frame, tv_sec, tv_usec, + DRI2_FLIP_COMPLETE, flip->event_complete, + flip->event_data); + break; + default: + xf86DrvMsg(scrn->scrnIndex, X_WARNING, "%s: unknown vblank event received\n", __func__); + /* Unknown type */ + break; + } + + free(flip); +} + /* * ScheduleSwap is responsible for requesting a DRM vblank event for the * appropriate frame. @@ -877,6 +1028,13 @@ static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, } current_msc = vbl.reply.sequence; + + /* Flips need to be submitted one frame before */ + if (DRI2CanFlip(draw) && can_exchange(front, back)) { + swap_type = DRI2_FLIP; + flip = 1; + } + swap_info->type = swap_type; /* Correct target_msc by 'flip' if swap_type == DRI2_FLIP. diff --git a/src/radeon_dri2.h b/src/radeon_dri2.h index 688530f..7995286 100644 --- a/src/radeon_dri2.h +++ b/src/radeon_dri2.h @@ -44,5 +44,7 @@ xf86CrtcPtr radeon_covering_crtc(ScrnInfoPtr pScrn, BoxPtr box, xf86CrtcPtr desired, BoxPtr crtc_box_ret); void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec, unsigned int tv_usec, void *event_data); +void radeon_dri2_flip_event_handler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data); #endif