From patchwork Mon Jan 11 20:54:45 2010 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jesse Barnes X-Patchwork-Id: 72207 Received: from gabe.freedesktop.org (gabe.freedesktop.org [131.252.210.177]) by demeter.kernel.org (8.14.3/8.14.2) with ESMTP id o0BKsgRF029083 for ; Mon, 11 Jan 2010 20:54:42 GMT Received: from gabe.freedesktop.org (localhost [127.0.0.1]) by gabe.freedesktop.org (Postfix) with ESMTP id 523589ECBD; Mon, 11 Jan 2010 12:54:42 -0800 (PST) X-Original-To: intel-gfx@lists.freedesktop.org Delivered-To: intel-gfx@lists.freedesktop.org Received: from outbound-mail-10.bluehost.com (outbound-mail-10.bluehost.com [69.89.17.210]) by gabe.freedesktop.org (Postfix) with SMTP id 6B4489E733 for ; Mon, 11 Jan 2010 12:54:38 -0800 (PST) Received: (qmail 12942 invoked by uid 0); 11 Jan 2010 20:54:38 -0000 Received: from unknown (HELO box514.bluehost.com) (74.220.219.114) by outboundproxy1.bluehost.com with SMTP; 11 Jan 2010 20:54:38 -0000 Received: from [75.111.28.251] (helo=jbarnes-piketon) by box514.bluehost.com with esmtpsa (TLSv1:AES128-SHA:128) (Exim 4.69) (envelope-from ) id 1NURHZ-00045w-Pl for intel-gfx@lists.freedesktop.org; Mon, 11 Jan 2010 13:54:38 -0700 Date: Mon, 11 Jan 2010 12:54:45 -0800 From: Jesse Barnes To: intel-gfx@lists.freedesktop.org Message-ID: <20100111125445.2b322e8a@jbarnes-piketon> X-Mailer: Claws Mail 3.7.2 (GTK+ 2.18.3; x86_64-pc-linux-gnu) Mime-Version: 1.0 X-Identified-User: {10642:box514.bluehost.com:virtuous:virtuousgeek.org} {sentby:smtp auth 75.111.28.251 authed with jbarnes@virtuousgeek.org} Subject: [Intel-gfx] [PATCH] DRI2: support new DRI2 APIs X-BeenThere: intel-gfx@lists.freedesktop.org X-Mailman-Version: 2.1.9 Precedence: list List-Id: Intel graphics driver community testing & development List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: intel-gfx-bounces@lists.freedesktop.org Errors-To: intel-gfx-bounces@lists.freedesktop.org diff --git a/src/drmmode_display.c b/src/drmmode_display.c index a469f6c..4c6c78b 100644 --- a/src/drmmode_display.c +++ b/src/drmmode_display.c @@ -34,6 +34,7 @@ #include #include #include +#include #include "xorgVersion.h" @@ -47,6 +48,11 @@ typedef struct { uint32_t fb_id; drmModeResPtr mode_res; int cpp; + + drmEventContext event_context; + void *event_data; + int old_fb_id; + int flip_count; } drmmode_rec, *drmmode_ptr; typedef struct { @@ -501,6 +507,7 @@ static PixmapPtr drmmode_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height) { ScrnInfoPtr scrn = crtc->scrn; + intel_screen_private *intel = intel_get_screen_private(scrn); drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; unsigned long rotate_pitch; @@ -533,12 +540,16 @@ drmmode_crtc_shadow_create(xf86CrtcPtr crtc, void *data, int width, int height) if (drmmode_crtc->rotate_bo) i830_set_pixmap_bo(rotate_pixmap, drmmode_crtc->rotate_bo); + intel->shadow_present = TRUE; + return rotate_pixmap; } static void drmmode_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *data) { + ScrnInfoPtr scrn = crtc->scrn; + intel_screen_private *intel = intel_get_screen_private(scrn); drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; @@ -556,6 +567,7 @@ drmmode_crtc_shadow_destroy(xf86CrtcPtr crtc, PixmapPtr rotate_pixmap, void *dat dri_bo_unreference(drmmode_crtc->rotate_bo); drmmode_crtc->rotate_bo = NULL; } + intel->shadow_present = FALSE; } static void @@ -1377,15 +1389,119 @@ drmmode_xf86crtc_resize (ScrnInfoPtr scrn, int width, int height) return FALSE; } +Bool +drmmode_do_pageflip(ScreenPtr screen, dri_bo *new_front, dri_bo *old_front, + void *data) +{ + ScrnInfoPtr scrn = xf86Screens[screen->myNum]; + intel_screen_private *intel = intel_get_screen_private(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 * intel->cpp; + int i, old_fb_id; + unsigned int crtc_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++) { + xf86CrtcPtr crtc = config->crtc[i]; + + if (!crtc->enabled) + continue; + + drmmode_crtc = crtc->driver_private; + crtc_id = drmmode_crtc->mode_crtc->crtc_id; + drmmode->event_data = data; + drmmode->flip_count++; + if (drmModePageFlip(drmmode->fd, 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; + } + } + + dri_bo_pin(new_front, 0); + dri_bo_unpin(new_front); + + scrn->fbOffset = new_front->offset; + intel->front_buffer->bo = new_front; + intel->front_buffer->offset = new_front->offset; + 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; +} + static const xf86CrtcConfigFuncsRec drmmode_xf86crtc_config_funcs = { drmmode_xf86crtc_resize }; +static void +drmmode_vblank_handler(int fd, unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + I830DRI2FrameEventHandler(frame, tv_sec, tv_usec, event_data); +} + +static void +drmmode_page_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); + + I830DRI2FlipEventHandler(frame, tv_sec, tv_usec, drmmode->event_data); +} + +static void +drm_wakeup_handler(pointer data, int err, pointer p) +{ + drmmode_ptr drmmode = data; + fd_set *read_mask = p; + + if (err >= 0 && FD_ISSET(drmmode->fd, read_mask)) + drmHandleEvent(drmmode->fd, &drmmode->event_context); +} + Bool drmmode_pre_init(ScrnInfoPtr scrn, int fd, int cpp) { + intel_screen_private *intel = intel_get_screen_private(scrn); xf86CrtcConfigPtr xf86_config; + struct drm_i915_getparam gp; drmmode_ptr drmmode; - int i; + unsigned int i; + int has_flipping = 0; drmmode = xnfalloc(sizeof *drmmode); drmmode->fd = fd; @@ -1412,6 +1528,23 @@ Bool drmmode_pre_init(ScrnInfoPtr scrn, int fd, int cpp) xf86InitialConfiguration(scrn, TRUE); + gp.param = I915_PARAM_HAS_PAGEFLIPPING; + gp.value = &has_flipping; + (void)drmCommandWriteRead(intel->drmSubFD, DRM_I915_GETPARAM, &gp, + sizeof(gp)); + if (has_flipping) { + xf86DrvMsg(scrn->scrnIndex, X_INFO, + "Kernel page flipping support detected, enabling\n"); + intel->use_pageflipping = TRUE; + drmmode->event_context.version = DRM_EVENT_CONTEXT_VERSION; + drmmode->event_context.vblank_handler = drmmode_vblank_handler; + drmmode->event_context.page_flip_handler = + drmmode_page_flip_handler; + AddGeneralSocket(fd); + RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA, + drm_wakeup_handler, drmmode); + } + return TRUE; } diff --git a/src/i830.h b/src/i830.h index a66038a..cdce6e5 100644 --- a/src/i830.h +++ b/src/i830.h @@ -65,6 +65,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include "sarea.h" #define _XF86DRI_SERVER_ #include "dri.h" +#include "dri2.h" #include "GL/glxint.h" #include "i830_dri.h" #include "intel_bufmgr.h" @@ -287,6 +288,8 @@ typedef struct intel_screen_private { CreateScreenResourcesProcPtr CreateScreenResources; + Bool shadow_present; + Bool need_mi_flush; Bool tiling; @@ -372,6 +375,8 @@ typedef struct intel_screen_private { int drmSubFD; char *deviceName; + Bool use_pageflipping; + /* Broken-out options. */ OptionInfoPtr Options; @@ -393,6 +398,11 @@ enum { DEBUG_FLUSH_WAIT = 0x4, }; +extern Bool drmmode_pre_init(ScrnInfoPtr pScrn, int fd, int cpp); +extern int drmmode_get_pipe_from_crtc_id(drm_intel_bufmgr *bufmgr, xf86CrtcPtr crtc); +extern int drmmode_output_dpms_status(xf86OutputPtr output); +extern Bool drmmode_do_pageflip(ScreenPtr screen, dri_bo *new_front, + dri_bo *old_front, void *data); static inline intel_screen_private * intel_get_screen_private(ScrnInfoPtr scrn) @@ -424,6 +434,10 @@ extern xf86CrtcPtr i830_pipe_to_crtc(ScrnInfoPtr scrn, int pipe); Bool I830DRI2ScreenInit(ScreenPtr pScreen); void I830DRI2CloseScreen(ScreenPtr pScreen); +void I830DRI2FrameEventHandler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *user_data); +void I830DRI2FlipEventHandler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *user_data); extern Bool drmmode_pre_init(ScrnInfoPtr scrn, int fd, int cpp); extern void drmmode_closefb(ScrnInfoPtr scrn); diff --git a/src/i830_dri.c b/src/i830_dri.c index 0246e61..e1c1470 100644 --- a/src/i830_dri.c +++ b/src/i830_dri.c @@ -44,6 +44,9 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include #include +#include +#include +#include #include "xf86.h" #include "xf86_OSproc.h" @@ -72,11 +75,6 @@ USE OR OTHER DEALINGS IN THE SOFTWARE. extern XF86ModuleData dri2ModuleData; #endif -typedef struct { - PixmapPtr pixmap; - unsigned int attachment; -} I830DRI2BufferPrivateRec, *I830DRI2BufferPrivatePtr; - #ifndef USE_DRI2_1_1_0 static DRI2BufferPtr I830DRI2CreateBuffers(DrawablePtr drawable, unsigned int *attachments, @@ -271,6 +269,29 @@ static void I830DRI2DestroyBuffer(DrawablePtr drawable, DRI2Buffer2Ptr buffer) #endif +static int +I830DRI2DrawablePipe(DrawablePtr pDraw) +{ + ScreenPtr pScreen = pDraw->pScreen; + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + BoxRec box, crtcbox; + xf86CrtcPtr crtc; + int pipe = -1; + + box.x1 = pDraw->x; + box.y1 = pDraw->y; + box.x2 = box.x1 + pDraw->width; + box.y2 = box.y1 + pDraw->height; + + crtc = i830_covering_crtc(pScrn, &box, NULL, &crtcbox); + + /* Make sure the CRTC is valid and this is the real front buffer */ + if (crtc != NULL && !crtc->rotatedData) + pipe = i830_crtc_to_pipe(crtc); + + return pipe; +} + static void I830DRI2CopyRegion(DrawablePtr drawable, RegionPtr pRegion, DRI2BufferPtr destBuffer, DRI2BufferPtr sourceBuffer) @@ -359,6 +380,466 @@ I830DRI2CopyRegion(DrawablePtr drawable, RegionPtr pRegion, } +#if DRI2INFOREC_VERSION >= 4 + +enum DRI2FrameEventType { + DRI2_SWAP, + DRI2_FLIP, + DRI2_WAITMSC, +}; + +typedef struct _DRI2FrameEvent { + DrawablePtr pDraw; + ClientPtr client; + enum DRI2FrameEventType type; + int frame; + + /* for swaps & flips only */ + DRI2SwapEventPtr event_complete; + void *event_data; + DRI2BufferPtr front; + DRI2BufferPtr back; +} DRI2FrameEventRec, *DRI2FrameEventPtr; + +static void +I830DRI2ExchangeBuffers(DrawablePtr draw, DRI2BufferPtr front, + DRI2BufferPtr back) +{ + I830DRI2BufferPrivatePtr front_priv, back_priv; + dri_bo *tmp_bo; + int tmp; + + front_priv = front->driverPrivate; + back_priv = back->driverPrivate; + + /* Swap BO names so DRI works */ + tmp = front->name; + front->name = back->name; + back->name = tmp; + + /* Swap pixmap bos */ + dri_bo_reference(i830_get_pixmap_bo(front_priv->pixmap)); + + tmp_bo = i830_get_pixmap_bo(front_priv->pixmap); + i830_set_pixmap_bo(front_priv->pixmap, + i830_get_pixmap_bo(back_priv->pixmap)); + i830_set_pixmap_bo(back_priv->pixmap, tmp_bo); /* should be screen */ +} + +/* + * Our internal swap routine takes care of actually exchanging, blitting, or + * flipping buffers as necessary. + */ +static Bool +I830DRI2ScheduleFlip(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, + DRI2BufferPtr back, DRI2SwapEventPtr func, void *data) +{ + ScreenPtr screen = draw->pScreen; + I830DRI2BufferPrivatePtr front_priv, back_priv; + dri_bo *tmp_bo; + DRI2FrameEventPtr flip_info; + Bool ret; + + flip_info = xcalloc(1, sizeof(DRI2FrameEventRec)); + if (!flip_info) + return FALSE; + + flip_info->pDraw = draw; + flip_info->client = client; + flip_info->type = DRI2_SWAP; + flip_info->event_complete = func; + flip_info->event_data = data; + + front_priv = front->driverPrivate; + back_priv = back->driverPrivate; + tmp_bo = i830_get_pixmap_bo(front_priv->pixmap); + + I830DRI2ExchangeBuffers(draw, front, back); + + /* Page flip the full screen buffer */ + ret = drmmode_do_pageflip(screen, + i830_get_pixmap_bo(front_priv->pixmap), + i830_get_pixmap_bo(back_priv->pixmap), + flip_info); + + /* Unwind in case of failure */ + if (!ret) { + i830_set_pixmap_bo(back_priv->pixmap, + i830_get_pixmap_bo(front_priv->pixmap)); + i830_set_pixmap_bo(front_priv->pixmap, tmp_bo); + return FALSE; + } + + return ret; +} + +void I830DRI2FrameEventHandler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + DRI2FrameEventPtr event = event_data; + DrawablePtr pDraw = event->pDraw; + ScreenPtr screen = pDraw->pScreen; + ScrnInfoPtr scrn = xf86Screens[screen->myNum]; + intel_screen_private *intel = intel_get_screen_private(scrn); + + switch (event->type) { + case DRI2_FLIP: + /* If we can still flip... */ + if (DRI2CanFlip(pDraw) && !intel->shadow_present && + intel->use_pageflipping && + I830DRI2ScheduleFlip(event->client, pDraw, event->front, + event->back, event->event_complete, + event->event_data)) { + break; + } + /* else fall through to exchange/blit */ + case DRI2_SWAP: { + int swap_type; + + if (DRI2CanExchange(pDraw)) { + I830DRI2ExchangeBuffers(pDraw, event->front, event->back); + swap_type = DRI2_EXCHANGE_COMPLETE; + } else { + BoxRec box; + RegionRec region; + + box.x1 = 0; + box.y1 = 0; + box.x2 = pDraw->width; + box.y2 = pDraw->height; + REGION_INIT(pScreen, ®ion, &box, 0); + + I830DRI2CopyRegion(pDraw, ®ion, event->front, event->back); + swap_type = DRI2_BLIT_COMPLETE; + } + DRI2SwapComplete(event->client, pDraw, frame, tv_sec, tv_usec, + swap_type, event->event_complete, event->event_data); + break; + } + case DRI2_WAITMSC: + DRI2WaitMSCComplete(event->client, pDraw, frame, tv_sec, tv_usec); + break; + default: + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "%s: unknown vblank event received\n", __func__); + /* Unknown type */ + break; + } + + xfree(event); +} + +void I830DRI2FlipEventHandler(unsigned int frame, unsigned int tv_sec, + unsigned int tv_usec, void *event_data) +{ + DRI2FrameEventPtr flip = event_data; + DrawablePtr pDraw = flip->pDraw; + ScreenPtr screen = pDraw->pScreen; + ScrnInfoPtr scrn = xf86Screens[screen->myNum]; + + /* We assume our flips arrive in order, so we don't check the frame */ + switch (flip->type) { + case DRI2_SWAP: + DRI2SwapComplete(flip->client, pDraw, 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; + } + + xfree(flip); +} + +/* + * ScheduleSwap is responsible for requesting a DRM vblank event for the + * appropriate frame. + * + * In the case of a blit (e.g. for a windowed swap) or buffer exchange, + * the vblank requested can simply be the last queued swap frame + the swap + * interval for the drawable. + * + * In the case of a page flip, we request an event for the last queued swap + * frame + swap interval - 1, since we'll need to queue the flip for the frame + * immediately following the received event. + * + * The client will be blocked if it tries to perform further GL commands + * after queueing a swap, though in the Intel case after queueing a flip, the + * client is free to queue more commands; they'll block in the kernel if + * they access buffers busy with the flip. + * + * When the swap is complete, the driver should call into the server so it + * can send any swap complete events that have been requested. + */ +static int +I830DRI2ScheduleSwap(ClientPtr client, DrawablePtr draw, DRI2BufferPtr front, + DRI2BufferPtr back, CARD64 *target_msc, CARD64 divisor, + CARD64 remainder, DRI2SwapEventPtr func, void *data) +{ + ScreenPtr screen = draw->pScreen; + ScrnInfoPtr scrn = xf86Screens[screen->myNum]; + intel_screen_private *intel = intel_get_screen_private(scrn); + drmVBlank vbl; + int ret, pipe = I830DRI2DrawablePipe(draw), flip = 0; + DRI2FrameEventPtr swap_info; + enum DRI2FrameEventType swap_type = DRI2_SWAP; + + swap_info = xcalloc(1, sizeof(DRI2FrameEventRec)); + + /* Drawable not displayed... just complete the swap */ + if (pipe == -1 || !swap_info) { + BoxRec box; + RegionRec region; + + box.x1 = 0; + box.y1 = 0; + box.x2 = draw->width; + box.y2 = draw->height; + REGION_INIT(pScreen, ®ion, &box, 0); + + I830DRI2CopyRegion(draw, ®ion, front, back); + + DRI2SwapComplete(client, draw, 0, 0, 0, DRI2_BLIT_COMPLETE, func, + data); + if (swap_info) + xfree(swap_info); + return TRUE; + } + + swap_info->pDraw = draw; + swap_info->client = client; + swap_info->event_complete = func; + swap_info->event_data = data; + swap_info->front = front; + swap_info->back = back; + + /* Get current count */ + vbl.request.type = DRM_VBLANK_RELATIVE; + if (pipe > 0) + vbl.request.type |= DRM_VBLANK_SECONDARY; + vbl.request.sequence = 0; + ret = drmWaitVBlank(intel->drmSubFD, &vbl); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "first get vblank counter failed: %s\n", + strerror(errno)); + return FALSE; + } + + /* Flips need to be submitted one frame before */ + if (DRI2CanFlip(draw) && !intel->shadow_present && + intel->use_pageflipping) { + swap_type = DRI2_FLIP; + flip = 1; + } + + swap_info->type = swap_type; + + /* + * If divisor is zero, we just need to make sure target_msc passes + * before waking up the client. + */ + if (divisor == 0) { + vbl.request.type = DRM_VBLANK_NEXTONMISS | + DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; + if (pipe > 0) + vbl.request.type |= DRM_VBLANK_SECONDARY; + + vbl.request.sequence = *target_msc; + vbl.request.sequence -= flip; + vbl.request.signal = (unsigned long)swap_info; + ret = drmWaitVBlank(intel->drmSubFD, &vbl); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "divisor 0 get vblank counter failed: %s\n", + strerror(errno)); + return FALSE; + } + + *target_msc = vbl.reply.sequence; + swap_info->frame = *target_msc; + + return TRUE; + } + + /* + * If we get here, target_msc has already passed or we don't have one, + * so we queue an event that will satisfy the divisor/remainderequation. + */ + if ((vbl.reply.sequence % divisor) == remainder) + return FALSE; + + vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; + if (pipe > 0) + vbl.request.type |= DRM_VBLANK_SECONDARY; + + /* + * If we have no remainder, and the test above failed, it means we've + * passed the last point where seq % divisor == remainder, so we need + * to wait for the next time that will happen. + */ + if (!remainder) + vbl.request.sequence += divisor; + + vbl.request.sequence = vbl.reply.sequence - + (vbl.reply.sequence % divisor) + remainder; + vbl.request.sequence -= flip; + vbl.request.signal = (unsigned long)swap_info; + ret = drmWaitVBlank(intel->drmSubFD, &vbl); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "final get vblank counter failed: %s\n", + strerror(errno)); + return FALSE; + } + + *target_msc = vbl.reply.sequence; + swap_info->frame = *target_msc; + + return TRUE; +} + +/* + * Get current frame count and frame count timestamp, based on drawable's + * crtc. + */ +static int +I830DRI2GetMSC(DrawablePtr draw, CARD64 *ust, CARD64 *msc) +{ + ScreenPtr screen = draw->pScreen; + ScrnInfoPtr scrn = xf86Screens[screen->myNum]; + intel_screen_private *intel = intel_get_screen_private(scrn); + drmVBlank vbl; + int ret, pipe = I830DRI2DrawablePipe(draw); + + /* Drawable not displayed, make up a value */ + if (pipe == -1) { + *ust = 0; + *msc = 0; + return TRUE; + } + + vbl.request.type = DRM_VBLANK_RELATIVE; + if (pipe > 0) + vbl.request.type |= DRM_VBLANK_SECONDARY; + vbl.request.sequence = 0; + + ret = drmWaitVBlank(intel->drmSubFD, &vbl); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "get vblank counter failed: %s\n", strerror(errno)); + return FALSE; + } + + *ust = ((CARD64)vbl.reply.tval_sec * 1000000) + vbl.reply.tval_usec; + *msc = vbl.reply.sequence; + + return TRUE; +} + +/* + * Request a DRM event when the requested conditions will be satisfied. + * + * We need to handle the event and ask the server to wake up the client when + * we receive it. + */ +static int +I830DRI2ScheduleWaitMSC(ClientPtr client, DrawablePtr draw, CARD64 target_msc, + CARD64 divisor, CARD64 remainder) +{ + ScreenPtr screen = draw->pScreen; + ScrnInfoPtr scrn = xf86Screens[screen->myNum]; + intel_screen_private *intel = intel_get_screen_private(scrn); + DRI2FrameEventPtr wait_info; + drmVBlank vbl; + int ret, pipe = I830DRI2DrawablePipe(draw); + + /* Drawable not visible, return immediately */ + if (pipe == -1) { + DRI2WaitMSCComplete(client, draw, target_msc, 0, 0); + return TRUE; + } + + wait_info = xcalloc(1, sizeof(DRI2FrameEventRec)); + if (!wait_info) { + DRI2WaitMSCComplete(client, draw, 0, 0, 0); + return TRUE; + } + + wait_info->pDraw = draw; + wait_info->client = client; + wait_info->type = DRI2_WAITMSC; + + /* Get current count */ + vbl.request.type = DRM_VBLANK_RELATIVE; + if (pipe > 0) + vbl.request.type |= DRM_VBLANK_SECONDARY; + vbl.request.sequence = 0; + ret = drmWaitVBlank(intel->drmSubFD, &vbl); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "get vblank counter failed: %s\n", strerror(errno)); + return FALSE; + } + + /* + * If divisor is zero, we just need to make sure target_msc passes + * before waking up the client. + */ + if (divisor == 0) { + vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; + if (pipe > 0) + vbl.request.type |= DRM_VBLANK_SECONDARY; + vbl.request.sequence = target_msc; + vbl.request.signal = (unsigned long)wait_info; + ret = drmWaitVBlank(intel->drmSubFD, &vbl); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "get vblank counter failed: %s\n", strerror(errno)); + return FALSE; + } + + wait_info->frame = vbl.reply.sequence; + DRI2BlockClient(client, draw); + return TRUE; + } + + /* + * If we get here, target_msc has already passed or we don't have one, + * so we queue an event that will satisfy the divisor/remainder equation. + */ + vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; + if (pipe > 0) + vbl.request.type |= DRM_VBLANK_SECONDARY; + + /* + * If we have no remainder and the condition isn't satisified, it means + * we've passed the last point where seq % divisor == remainder, so we need + * to wait for the next time that will happen. + */ + if (((vbl.reply.sequence % divisor) != remainder) && !remainder) + vbl.request.sequence += divisor; + + vbl.request.sequence = vbl.reply.sequence - (vbl.reply.sequence % divisor) + + remainder; + vbl.request.signal = (unsigned long)wait_info; + ret = drmWaitVBlank(intel->drmSubFD, &vbl); + if (ret) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "get vblank counter failed: %s\n", strerror(errno)); + return FALSE; + } + + wait_info->frame = vbl.reply.sequence; + DRI2BlockClient(client, draw); + + return TRUE; +} +#endif + Bool I830DRI2ScreenInit(ScreenPtr screen) { ScrnInfoPtr scrn = xf86Screens[screen->myNum]; @@ -405,6 +886,12 @@ Bool I830DRI2ScreenInit(ScreenPtr screen) #endif info.CopyRegion = I830DRI2CopyRegion; +#if DRI2INFOREC_VERSION >= 4 + info.version = 4; + info.ScheduleSwap = I830DRI2ScheduleSwap; + info.GetMSC = I830DRI2GetMSC; + info.ScheduleWaitMSC = I830DRI2ScheduleWaitMSC; +#endif return DRI2ScreenInit(screen, &info); } diff --git a/src/i830_dri.h b/src/i830_dri.h index 9802356..babcac3 100644 --- a/src/i830_dri.h +++ b/src/i830_dri.h @@ -2,6 +2,7 @@ #ifndef _I830_DRI_H #define _I830_DRI_H +#include "xorg-server.h" #include "xf86drm.h" #include "i830_common.h" @@ -58,4 +59,9 @@ typedef struct { int dummy; } I830DRIContextRec, *I830DRIContextPtr; +typedef struct { + PixmapPtr pixmap; + unsigned int attachment; +} I830DRI2BufferPrivateRec, *I830DRI2BufferPrivatePtr; + #endif