diff options
-rw-r--r-- | man/radeon.man | 17 | ||||
-rw-r--r-- | src/drmmode_display.c | 134 | ||||
-rw-r--r-- | src/drmmode_display.h | 12 | ||||
-rw-r--r-- | src/evergreen_exa.c | 21 | ||||
-rw-r--r-- | src/r600_exa.c | 21 | ||||
-rw-r--r-- | src/radeon.h | 8 | ||||
-rw-r--r-- | src/radeon_dri2.c | 278 | ||||
-rw-r--r-- | src/radeon_dri2.h | 2 | ||||
-rw-r--r-- | src/radeon_exa.c | 11 | ||||
-rw-r--r-- | src/radeon_kms.c | 23 |
10 files changed, 461 insertions, 66 deletions
diff --git a/man/radeon.man b/man/radeon.man index 609f7672..285cb397 100644 --- a/man/radeon.man +++ b/man/radeon.man @@ -266,7 +266,22 @@ to use VRAM for non-essential pixmaps. This option allows us to override the heuristic. The default is .B on with > 32MB VRAM, off with < 32MB. - +.TP +.BI "Option \*qSwapbuffersWait\*q \*q" boolean \*q +This option controls the behavior of glXSwapBuffers and glXCopySubBufferMESA +calls by GL applications. If enabled, the calls will avoid tearing by making +sure the display scanline is outside of the area to be copied before the copy +occurs. If disabled, no scanline synchronization is performed, meaning tearing +will likely occur. Note that when enabled, this option can adversely affect +the framerate of applications that render frames at less than refresh rate. +.IP +The default value is +.B on. +.TP +.BI "Option \*qEnablePageFlip\*q \*q" boolean \*q +Enable DRI2 page flipping. The default is +.B on. +Pageflipping is supported on all radeon hardware. .PP The following driver .B Options diff --git a/src/drmmode_display.c b/src/drmmode_display.c index 0a6e338e..9248cb0e 100644 --- a/src/drmmode_display.c +++ b/src/drmmode_display.c @@ -29,6 +29,7 @@ #include "config.h" #endif +#include <errno.h> #ifdef XF86DRM_MODE #include <sys/ioctl.h> #include "micmap.h" @@ -266,9 +267,10 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, uint32_t tiling_flags = 0; int height; - /* no tiled scanout on r6xx+ yet */ if (info->allowColorTiling) { - if (info->ChipFamily < CHIP_FAMILY_R600) + if (info->ChipFamily >= CHIP_FAMILY_R600) + tiling_flags |= RADEON_TILING_MICRO; + else tiling_flags |= RADEON_TILING_MACRO; } @@ -1167,9 +1169,10 @@ drmmode_xf86crtc_resize (ScrnInfoPtr scrn, int width, int height) if (front_bo) radeon_bo_wait(front_bo); - /* no tiled scanout on r6xx+ yet */ if (info->allowColorTiling) { - if (info->ChipFamily < CHIP_FAMILY_R600) + if (info->ChipFamily >= CHIP_FAMILY_R600) + tiling_flags |= RADEON_TILING_MICRO; + else tiling_flags |= RADEON_TILING_MACRO; } @@ -1278,6 +1281,39 @@ 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_flipevtcarrier_ptr flipcarrier = event_data; + drmmode_ptr drmmode = flipcarrier->drmmode; + + /* Is this the event whose info shall be delivered to higher level? */ + if (flipcarrier->dispatch_me) { + /* Yes: Cache msc, ust for later delivery. */ + drmmode->fe_frame = frame; + drmmode->fe_tv_sec = tv_sec; + drmmode->fe_tv_usec = tv_usec; + } + free(flipcarrier); + + /* Last crtc completed flip? */ + drmmode->flip_count--; + if (drmmode->flip_count > 0) + return; + + /* Release framebuffer */ + drmModeRmFB(drmmode->fd, drmmode->old_fb_id); + + if (drmmode->event_data == NULL) + return; + + /* Deliver cached msc, ust from reference crtc to flip event handler */ + radeon_dri2_flip_event_handler(drmmode->fe_frame, drmmode->fe_tv_sec, + drmmode->fe_tv_usec, drmmode->event_data); +} + + +static void drm_wakeup_handler(pointer data, int err, pointer p) { drmmode_ptr drmmode = data; @@ -1320,8 +1356,9 @@ 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 (!pRADEONEnt->fd_wakeup_registered && info->dri->pKernelDRMVersion->version_minor >= 4) { + drmmode->flip_count = 0; AddGeneralSocket(drmmode->fd); RegisterBlockAndWakeupHandlers((BlockHandlerProcPtr)NoopDDA, drm_wakeup_handler, drmmode); @@ -1561,4 +1598,91 @@ void drmmode_uevent_fini(ScrnInfoPtr scrn, drmmode_ptr drmmode) #endif } +Bool radeon_do_pageflip(ScrnInfoPtr scrn, struct radeon_bo *new_front, void *data, int ref_crtc_hw_id) +{ + 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; + uint32_t tiling_flags = 0; + int height; + drmmode_flipevtcarrier_ptr flipcarrier; + + if (info->allowColorTiling) { + if (info->ChipFamily >= CHIP_FAMILY_R600) + tiling_flags |= RADEON_TILING_MICRO; + else + tiling_flags |= RADEON_TILING_MACRO; + } + + pitch = RADEON_ALIGN(pitch, drmmode_get_pitch_align(scrn, info->CurrentLayout.pixel_bytes, tiling_flags)); + height = RADEON_ALIGN(scrn->virtualY, drmmode_get_height_align(scrn, tiling_flags)); + + /* + * Create a new handle for the back buffer + */ + old_fb_id = drmmode->fb_id; + if (drmModeAddFB(drmmode->fd, scrn->virtualX, height, + 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. + */ + drmmode->fe_frame = 0; + drmmode->fe_tv_sec = 0; + drmmode->fe_tv_usec = 0; + + 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; + + flipcarrier = calloc(1, sizeof(drmmode_flipevtcarrier_rec)); + if (!flipcarrier) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "flip queue: carrier alloc failed.\n"); + goto error_undo; + } + + /* Only the reference crtc will finally deliver its page flip + * completion event. All other crtc's events will be discarded. + */ + flipcarrier->dispatch_me = (drmmode_crtc->hw_id == ref_crtc_hw_id); + flipcarrier->drmmode = drmmode; + + if (drmModePageFlip(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, + drmmode->fb_id, DRM_MODE_PAGE_FLIP_EVENT, flipcarrier)) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "flip queue failed: %s\n", strerror(errno)); + free(flipcarrier); + 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 e6bfd50a..548907bb 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,9 +51,18 @@ typedef struct { #endif drmEventContext event_context; int flip_count; + void *event_data; + unsigned int fe_frame; + unsigned int fe_tv_sec; + unsigned int fe_tv_usec; } drmmode_rec, *drmmode_ptr; typedef struct { + drmmode_ptr drmmode; + Bool dispatch_me; +} drmmode_flipevtcarrier_rec, *drmmode_flipevtcarrier_ptr; + +typedef struct { drmmode_ptr drmmode; drmModeCrtcPtr mode_crtc; int hw_id; @@ -99,6 +109,8 @@ extern int drmmode_get_height_align(ScrnInfoPtr scrn, uint32_t tiling); extern int drmmode_get_pitch_align(ScrnInfoPtr scrn, int bpe, uint32_t tiling); extern int drmmode_get_base_align(ScrnInfoPtr scrn, int bpe, uint32_t tiling); +Bool radeon_do_pageflip(ScrnInfoPtr scrn, struct radeon_bo *new_front, void *data, int ref_crtc_hw_id); + #endif #endif diff --git a/src/evergreen_exa.c b/src/evergreen_exa.c index c6ef33d4..0fe00d80 100644 --- a/src/evergreen_exa.c +++ b/src/evergreen_exa.c @@ -1647,22 +1647,29 @@ EVERGREENDownloadFromScreen(PixmapPtr pSrc, int x, int y, int w, Bool flush = FALSE; Bool r; struct r600_accel_object src_obj, dst_obj; + uint32_t tiling_flags = 0, pitch = 0; if (bpp < 8) return FALSE; driver_priv = exaGetPixmapDriverPrivate(pSrc); + ret = radeon_bo_get_tiling(driver_priv->bo, &tiling_flags, &pitch); + if (ret) + ErrorF("radeon_bo_get_tiling failed\n"); + /* If we know the BO won't end up in VRAM anyway, don't bother with a scratch */ copy_src = driver_priv->bo; copy_pitch = pSrc->devKind; - if (radeon_bo_is_referenced_by_cs(driver_priv->bo, info->cs)) { - src_domain = radeon_bo_get_src_domain(driver_priv->bo); - if ((src_domain & (RADEON_GEM_DOMAIN_GTT | RADEON_GEM_DOMAIN_VRAM)) == - (RADEON_GEM_DOMAIN_GTT | RADEON_GEM_DOMAIN_VRAM)) - src_domain = 0; - else /* A write may be scheduled */ - flush = TRUE; + if (!(tiling_flags & (RADEON_TILING_MACRO | RADEON_TILING_MICRO))) { + if (radeon_bo_is_referenced_by_cs(driver_priv->bo, info->cs)) { + src_domain = radeon_bo_get_src_domain(driver_priv->bo); + if ((src_domain & (RADEON_GEM_DOMAIN_GTT | RADEON_GEM_DOMAIN_VRAM)) == + (RADEON_GEM_DOMAIN_GTT | RADEON_GEM_DOMAIN_VRAM)) + src_domain = 0; + else /* A write may be scheduled */ + flush = TRUE; + } } if (!src_domain) diff --git a/src/r600_exa.c b/src/r600_exa.c index 15db42ac..5dfc770e 100644 --- a/src/r600_exa.c +++ b/src/r600_exa.c @@ -1895,22 +1895,29 @@ R600DownloadFromScreenCS(PixmapPtr pSrc, int x, int y, int w, Bool flush = FALSE; Bool r; struct r600_accel_object src_obj, dst_obj; + uint32_t tiling_flags = 0, pitch = 0; if (bpp < 8) return FALSE; driver_priv = exaGetPixmapDriverPrivate(pSrc); + ret = radeon_bo_get_tiling(driver_priv->bo, &tiling_flags, &pitch); + if (ret) + ErrorF("radeon_bo_get_tiling failed\n"); + /* If we know the BO won't end up in VRAM anyway, don't bother with a scratch */ copy_src = driver_priv->bo; copy_pitch = pSrc->devKind; - if (radeon_bo_is_referenced_by_cs(driver_priv->bo, info->cs)) { - src_domain = radeon_bo_get_src_domain(driver_priv->bo); - if ((src_domain & (RADEON_GEM_DOMAIN_GTT | RADEON_GEM_DOMAIN_VRAM)) == - (RADEON_GEM_DOMAIN_GTT | RADEON_GEM_DOMAIN_VRAM)) - src_domain = 0; - else /* A write may be scheduled */ - flush = TRUE; + if (!(tiling_flags & (RADEON_TILING_MACRO | RADEON_TILING_MICRO))) { + if (radeon_bo_is_referenced_by_cs(driver_priv->bo, info->cs)) { + src_domain = radeon_bo_get_src_domain(driver_priv->bo); + if ((src_domain & (RADEON_GEM_DOMAIN_GTT | RADEON_GEM_DOMAIN_VRAM)) == + (RADEON_GEM_DOMAIN_GTT | RADEON_GEM_DOMAIN_VRAM)) + src_domain = 0; + else /* A write may be scheduled */ + flush = TRUE; + } } if (!src_domain) diff --git a/src/radeon.h b/src/radeon.h index 7f55feec..4da9bf76 100644 --- a/src/radeon.h +++ b/src/radeon.h @@ -224,7 +224,8 @@ typedef enum { OPTION_FORCE_LOW_POWER, OPTION_DYNAMIC_PM, OPTION_NEW_PLL, - OPTION_ZAPHOD_HEADS + OPTION_ZAPHOD_HEADS, + OPTION_SWAPBUFFERS_WAIT } RADEONOpts; @@ -1079,6 +1080,11 @@ typedef struct { struct radeon_bo *bicubic_bo; void *bicubic_memory; int bicubic_offset; + /* kms pageflipping */ + Bool allowPageFlip; + + /* Perform vsync'ed SwapBuffers? */ + Bool swapBuffersWait; } RADEONInfoRec, *RADEONInfoPtr; #define RADEONWaitForFifo(pScrn, entries) \ diff --git a/src/radeon_dri2.c b/src/radeon_dri2.c index f2ea0bb4..e8e16ff9 100644 --- a/src/radeon_dri2.c +++ b/src/radeon_dri2.c @@ -82,6 +82,10 @@ radeon_dri2_create_buffers(DrawablePtr drawable, struct radeon_exa_pixmap_priv *driver_priv; int i, r, need_enlarge = 0; int flags = 0; + unsigned front_width; + + pixmap = screen->GetScreenPixmap(screen); + front_width = pixmap->drawable.width; buffers = calloc(count, sizeof *buffers); if (buffers == NULL) { @@ -162,12 +166,18 @@ radeon_dri2_create_buffers(DrawablePtr drawable, drawable->depth, flags); - } else + } else { + unsigned aligned_width = drawable->width; + + if (aligned_width == front_width) + aligned_width = pScrn->virtualX; + pixmap = (*pScreen->CreatePixmap)(pScreen, - drawable->width, + aligned_width, drawable->height, drawable->depth, flags); + } } if (attachments[i] == DRI2BufferDepth) { @@ -206,6 +216,10 @@ radeon_dri2_create_buffer(DrawablePtr drawable, struct radeon_exa_pixmap_priv *driver_priv; int r, need_enlarge = 0; int flags; + unsigned front_width; + + pixmap = pScreen->GetScreenPixmap(pScreen); + front_width = pixmap->drawable.width; buffers = calloc(1, sizeof *buffers); if (buffers == NULL) { @@ -287,12 +301,18 @@ radeon_dri2_create_buffer(DrawablePtr drawable, (format != 0)?format:drawable->depth, flags); - } else + } else { + unsigned aligned_width = drawable->width; + + if (aligned_width == front_width) + aligned_width = pScrn->virtualX; + pixmap = (*pScreen->CreatePixmap)(pScreen, - drawable->width, + aligned_width, drawable->height, (format != 0)?format:drawable->depth, flags); + } } if (attachment == DRI2BufferDepth) { @@ -423,7 +443,9 @@ radeon_dri2_copy_region(DrawablePtr drawable, } vsync = info->accel_state->vsync; - info->accel_state->vsync = TRUE; + + /* Driver option "SwapbuffersWait" defines if we vsync DRI2 copy-swaps. */ + info->accel_state->vsync = info->swapBuffersWait; (*gc->ops->CopyArea)(src_drawable, dst_drawable, gc, 0, 0, drawable->width, drawable->height, 0, 0); @@ -549,12 +571,124 @@ radeon_dri2_unref_buffer(BufferPtr buffer) } } +static int radeon_dri2_drawable_crtc(DrawablePtr pDraw) +{ + ScreenPtr pScreen = pDraw->pScreen; + ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; + xf86CrtcPtr crtc; + int crtc_id = -1; + + crtc = radeon_pick_best_crtc(pScrn, + pDraw->x, + pDraw->x + pDraw->width, + pDraw->y, + pDraw->y + pDraw->height); + + /* Make sure the CRTC is valid and this is the real front buffer */ + if (crtc != NULL && !crtc->rotatedData) { + crtc_id = drmmode_get_crtc_id(crtc); + } + return crtc_id; +} + +static Bool +radeon_dri2_schedule_flip(ScrnInfoPtr scrn, ClientPtr client, + DrawablePtr draw, DRI2BufferPtr front, + DRI2BufferPtr back, DRI2SwapEventPtr func, + void *data, unsigned int target_msc) +{ + struct dri2_buffer_priv *back_priv; + struct radeon_exa_pixmap_priv *exa_priv; + DRI2FrameEventPtr flip_info; + + /* Main crtc for this drawable shall finally deliver pageflip event. */ + int ref_crtc_hw_id = radeon_dri2_drawable_crtc(draw); + + 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; + flip_info->frame = target_msc; + + xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, RADEON_LOGLEVEL_DEBUG, + "%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, ref_crtc_hw_id); +} + +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) + 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 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); + 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); + 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) { DRI2FrameEventPtr event = event_data; + RADEONInfoPtr info; DrawablePtr drawable; - ClientPtr client; ScreenPtr screen; ScrnInfoPtr scrn; int status; @@ -563,7 +697,7 @@ void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec, RegionRec region; if (!event->valid) - goto cleanup; + goto cleanup; status = dixLookupDrawable(&drawable, event->drawable_id, serverClient, M_ANY, DixWriteAccess); @@ -572,25 +706,46 @@ void radeon_dri2_frame_event_handler(unsigned int frame, unsigned int tv_sec, screen = drawable->pScreen; scrn = xf86Screens[screen->myNum]; - client = event->client; + info = RADEONPTR(scrn); switch (event->type) { case DRI2_FLIP: + if (info->allowPageFlip && + 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, + event->frame)) { + 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; - box.x2 = drawable->width; - box.y2 = drawable->height; - REGION_INIT(pScreen, ®ion, &box, 0); - radeon_dri2_copy_region(drawable, ®ion, event->front, event->back); - swap_type = DRI2_BLIT_COMPLETE; - - DRI2SwapComplete(client, drawable, frame, tv_sec, tv_usec, + if (DRI2CanExchange(drawable) && + can_exchange(event->front, event->back)) { + radeon_dri2_exchange_buffers(drawable, event->front, event->back); + swap_type = DRI2_EXCHANGE_COMPLETE; + } else { + box.x1 = 0; + box.y1 = 0; + box.x2 = drawable->width; + box.y2 = drawable->height; + REGION_INIT(pScreen, ®ion, &box, 0); + radeon_dri2_copy_region(drawable, ®ion, event->front, event->back); + swap_type = DRI2_BLIT_COMPLETE; + } + + DRI2SwapComplete(event->client, drawable, frame, tv_sec, tv_usec, swap_type, event->event_complete, event->event_data); break; case DRI2_WAITMSC: - DRI2WaitMSCComplete(client, drawable, frame, tv_sec, tv_usec); + DRI2WaitMSCComplete(event->client, drawable, frame, tv_sec, tv_usec); break; default: /* Unknown type */ @@ -607,26 +762,6 @@ cleanup: free(event); } -static int radeon_dri2_drawable_crtc(DrawablePtr pDraw) -{ - ScreenPtr pScreen = pDraw->pScreen; - ScrnInfoPtr pScrn = xf86Screens[pScreen->myNum]; - xf86CrtcPtr crtc; - int crtc_id = -1; - - crtc = radeon_pick_best_crtc(pScrn, - pDraw->x, - pDraw->x + pDraw->width, - pDraw->y, - pDraw->y + pDraw->height); - - /* Make sure the CRTC is valid and this is the real front buffer */ - if (crtc != NULL && !crtc->rotatedData) { - crtc_id = drmmode_get_crtc_id(crtc); - } - return crtc_id; -} - /* * Get current frame count and frame count timestamp, based on drawable's * crtc. @@ -638,7 +773,7 @@ static int radeon_dri2_get_msc(DrawablePtr draw, CARD64 *ust, CARD64 *msc) RADEONInfoPtr info = RADEONPTR(scrn); drmVBlank vbl; int ret; - int crtc= radeon_dri2_drawable_crtc(draw); + int crtc = radeon_dri2_drawable_crtc(draw); /* Drawable not displayed, make up a value */ if (crtc == -1) { @@ -796,6 +931,59 @@ 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) { + free(flip); + return; + } + + screen = drawable->pScreen; + scrn = xf86Screens[screen->myNum]; + + pixmap = screen->GetScreenPixmap(screen); + xf86DrvMsgVerb(scrn->scrnIndex, X_INFO, RADEON_LOGLEVEL_DEBUG, + "%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: + /* Check for too small vblank count of pageflip completion, taking wraparound + * into account. This usually means some defective kms pageflip completion, + * causing wrong (msc, ust) return values and possible visual corruption. + */ + if ((frame < flip->frame) && (flip->frame - frame < 5)) { + xf86DrvMsg(scrn->scrnIndex, X_WARNING, + "%s: Pageflip completion event has impossible msc %d < target_msc %d\n", + __func__, frame, flip->frame); + /* All-Zero values signal failure of (msc, ust) timestamping to client. */ + frame = tv_sec = tv_usec = 0; + } + + 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. @@ -883,6 +1071,15 @@ static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, } current_msc = vbl.reply.sequence; + + /* Flips need to be submitted one frame before */ + if (info->allowPageFlip && + 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. @@ -899,9 +1096,6 @@ static int radeon_dri2_schedule_swap(ClientPtr client, DrawablePtr draw, */ if (divisor == 0 || current_msc < *target_msc) { vbl.request.type = DRM_VBLANK_ABSOLUTE | DRM_VBLANK_EVENT; - if (crtc > 0) - vbl.request.type |= DRM_VBLANK_SECONDARY; - /* If non-pageflipping, but blitting/exchanging, we need to use * DRM_VBLANK_NEXTONMISS to avoid unreliable timestamping later * on. diff --git a/src/radeon_dri2.h b/src/radeon_dri2.h index 688530f2..79952862 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 diff --git a/src/radeon_exa.c b/src/radeon_exa.c index 9c40da79..9df7251e 100644 --- a/src/radeon_exa.c +++ b/src/radeon_exa.c @@ -318,6 +318,17 @@ Bool RADEONPrepareAccess_CS(PixmapPtr pPix, int index) if (!driver_priv) return FALSE; + if (info->ChipFamily >= CHIP_FAMILY_R600) { + uint32_t tiling_flags = 0, pitch = 0; + + ret = radeon_bo_get_tiling(driver_priv->bo, &tiling_flags, &pitch); + if (ret) + return FALSE; + /* untile in DFS/UTS */ + if (tiling_flags & (RADEON_TILING_MACRO | RADEON_TILING_MICRO)) + return FALSE; + } + /* if we have more refs than just the BO then flush */ if (radeon_bo_is_referenced_by_cs(driver_priv->bo, info->cs)) { flush = TRUE; diff --git a/src/radeon_kms.c b/src/radeon_kms.c index f4c54b30..59f82818 100644 --- a/src/radeon_kms.c +++ b/src/radeon_kms.c @@ -70,6 +70,8 @@ const OptionInfoRec RADEONOptions_KMS[] = { { OPTION_EXA_VSYNC, "EXAVSync", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_EXA_PIXMAPS, "EXAPixmaps", OPTV_BOOLEAN, {0}, FALSE }, { OPTION_ZAPHOD_HEADS, "ZaphodHeads", OPTV_STRING, {0}, FALSE }, + { OPTION_PAGE_FLIP, "EnablePageFlip", OPTV_BOOLEAN, {0}, FALSE }, + { OPTION_SWAPBUFFERS_WAIT,"SwapbuffersWait", OPTV_BOOLEAN, {0}, FALSE }, { -1, NULL, OPTV_NONE, {0}, FALSE } }; @@ -620,6 +622,18 @@ Bool RADEONPreInit_KMS(ScrnInfoPtr pScrn, int flags) xf86DrvMsg(pScrn->scrnIndex, X_INFO, "KMS Color Tiling: %sabled\n", info->allowColorTiling ? "en" : "dis"); + if (info->dri->pKernelDRMVersion->version_minor >= 8) { + info->allowPageFlip = xf86ReturnOptValBool(info->Options, + OPTION_PAGE_FLIP, TRUE); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "KMS Pageflipping: %sabled\n", info->allowPageFlip ? "en" : "dis"); + } + + info->swapBuffersWait = xf86ReturnOptValBool(info->Options, + OPTION_SWAPBUFFERS_WAIT, TRUE); + xf86DrvMsg(pScrn->scrnIndex, X_INFO, + "SwapBuffers wait for vsync: %sabled\n", info->swapBuffersWait ? "en" : "dis"); + if (drmmode_pre_init(pScrn, &info->drmmode, pScrn->bitsPerPixel / 8) == FALSE) { xf86DrvMsg(pScrn->scrnIndex, X_ERROR, "Kernel modesetting setup failed\n"); goto fail; @@ -678,7 +692,9 @@ Bool RADEONPreInit_KMS(ScrnInfoPtr pScrn, int flags) /* no tiled scanout on r6xx+ yet */ if (info->allowColorTiling) { - if (info->ChipFamily < CHIP_FAMILY_R600) + if (info->ChipFamily >= CHIP_FAMILY_R600) + tiling |= RADEON_TILING_MICRO; + else tiling |= RADEON_TILING_MACRO; } cpp = pScrn->bitsPerPixel / 8; @@ -1115,9 +1131,10 @@ static Bool radeon_setup_kernel_mem(ScreenPtr pScreen) return FALSE; } - /* no tiled scanout on r6xx+ yet */ if (info->allowColorTiling) { - if (info->ChipFamily < CHIP_FAMILY_R600) + if (info->ChipFamily >= CHIP_FAMILY_R600) + tiling_flags |= RADEON_TILING_MICRO; + else tiling_flags |= RADEON_TILING_MACRO; } pitch = RADEON_ALIGN(pScrn->displayWidth, drmmode_get_pitch_align(pScrn, cpp, tiling_flags)) * cpp; |