diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c index fe3315a9c4..7a7d5c3887 100644 --- a/hw/xfree86/drivers/modesetting/driver.c +++ b/hw/xfree86/drivers/modesetting/driver.c @@ -145,6 +145,7 @@ static const OptionInfoRec Options[] = { {OPTION_VARIABLE_REFRESH, "VariableRefresh", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_USE_GAMMA_LUT, "UseGammaLUT", OPTV_BOOLEAN, {0}, FALSE}, {OPTION_ASYNC_FLIP_SECONDARIES, "AsyncFlipSecondaries", OPTV_BOOLEAN, {0}, FALSE}, + {OPTION_TEARFREE, "TearFree", OPTV_BOOLEAN, {0}, FALSE}, {-1, NULL, OPTV_NONE, {0}, FALSE} }; @@ -548,14 +549,16 @@ rotate_clip(PixmapPtr pixmap, BoxPtr rect, drmModeClip *clip, Rotation rotation) } static int -dispatch_dirty_region(ScrnInfoPtr scrn, xf86CrtcPtr crtc, - PixmapPtr pixmap, DamagePtr damage, int fb_id) +dispatch_damages(ScrnInfoPtr scrn, xf86CrtcPtr crtc, RegionPtr dirty, + PixmapPtr pixmap, DamagePtr damage, int fb_id) { modesettingPtr ms = modesettingPTR(scrn); - RegionPtr dirty = DamageRegion(damage); unsigned num_cliprects = REGION_NUM_RECTS(dirty); int ret = 0; + if (!ms->dirty_enabled) + return 0; + if (num_cliprects) { drmModeClip *clip = xallocarray(num_cliprects, sizeof(drmModeClip)); BoxPtr rect = REGION_RECTS(dirty); @@ -579,12 +582,102 @@ dispatch_dirty_region(ScrnInfoPtr scrn, xf86CrtcPtr crtc, } } + if (ret == -EINVAL || ret == -ENOSYS) { + xf86DrvMsg(scrn->scrnIndex, X_INFO, + "Disabling kernel dirty updates, not required.\n"); + ms->dirty_enabled = FALSE; + } + free(clip); - DamageEmpty(damage); + if (damage) + DamageEmpty(damage); } return ret; } +static int +dispatch_dirty_region(ScrnInfoPtr scrn, xf86CrtcPtr crtc, + PixmapPtr pixmap, DamagePtr damage, int fb_id) +{ + return dispatch_damages(scrn, crtc, DamageRegion(damage), + pixmap, damage, fb_id); +} + +static void +ms_tearfree_update_damages(ScreenPtr pScreen) +{ + ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen); + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); + modesettingPtr ms = modesettingPTR(scrn); + RegionPtr dirty = DamageRegion(ms->damage); + int c, i; + + if (RegionNil(dirty)) + return; + + for (c = 0; c < xf86_config->num_crtc; c++) { + xf86CrtcPtr crtc = xf86_config->crtc[c]; + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree; + RegionRec region; + + /* Compute how much of the damage intersects with this CRTC */ + RegionInit(®ion, &crtc->bounds, 0); + RegionIntersect(®ion, ®ion, dirty); + + if (trf->buf[0].px) { + for (i = 0; i < ARRAY_SIZE(trf->buf); i++) + RegionUnion(&trf->buf[i].dmg, &trf->buf[i].dmg, ®ion); + } else { + /* Just notify the kernel of the damages if TearFree isn't used */ + dispatch_damages(scrn, crtc, ®ion, + pScreen->GetScreenPixmap(pScreen), + NULL, ms->drmmode.fb_id); + } + } + DamageEmpty(ms->damage); +} + +static void +ms_tearfree_do_flips(ScreenPtr pScreen) +{ +#ifdef GLAMOR_HAS_GBM + ScrnInfoPtr scrn = xf86ScreenToScrn(pScreen); + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(scrn); + modesettingPtr ms = modesettingPTR(scrn); + int c; + + if (!ms->drmmode.tearfree_enable) + return; + + for (c = 0; c < xf86_config->num_crtc; c++) { + xf86CrtcPtr crtc = xf86_config->crtc[c]; + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree; + + /* Skip disabled CRTCs and those which aren't using TearFree */ + if (!trf->buf[0].px || !crtc->scrn->vtSema || !xf86_crtc_on(crtc)) + continue; + + /* Skip if the last flip is still pending, a DRI client is flipping, or + * there isn't any damage on the front buffer. + */ + if (trf->flip_seq || ms->drmmode.dri2_flipping || + ms->drmmode.present_flipping || + RegionNil(&trf->buf[trf->back_idx ^ 1].dmg)) + continue; + + /* Flip. If it fails, notify the kernel of the front buffer damages */ + if (ms_do_tearfree_flip(pScreen, crtc)) { + dispatch_damages(scrn, crtc, &trf->buf[trf->back_idx ^ 1].dmg, + trf->buf[trf->back_idx ^ 1].px, NULL, + trf->buf[trf->back_idx ^ 1].fb_id); + RegionEmpty(&trf->buf[trf->back_idx ^ 1].dmg); + } + } +#endif +} + static void dispatch_dirty(ScreenPtr pScreen) { @@ -606,12 +699,9 @@ dispatch_dirty(ScreenPtr pScreen) ret = dispatch_dirty_region(scrn, crtc, pixmap, ms->damage, fb_id); if (ret == -EINVAL || ret == -ENOSYS) { - ms->dirty_enabled = FALSE; DamageUnregister(ms->damage); DamageDestroy(ms->damage); ms->damage = NULL; - xf86DrvMsg(scrn->scrnIndex, X_INFO, - "Disabling kernel dirty updates, not required.\n"); return; } } @@ -742,10 +832,13 @@ msBlockHandler(ScreenPtr pScreen, void *timeout) pScreen->BlockHandler = msBlockHandler; if (pScreen->isGPU && !ms->drmmode.reverse_prime_offload_mode) dispatch_secondary_dirty(pScreen); + else if (ms->drmmode.tearfree_enable) + ms_tearfree_update_damages(pScreen); else if (ms->dirty_enabled) dispatch_dirty(pScreen); ms_dirty_update(pScreen, timeout); + ms_tearfree_do_flips(pScreen); } static void @@ -1281,6 +1374,27 @@ PreInit(ScrnInfoPtr pScrn, int flags) ms->atomic_modeset = FALSE; } + /* TearFree requires glamor and, if PageFlip is enabled, universal planes */ + if (xf86ReturnOptValBool(ms->drmmode.Options, OPTION_TEARFREE, FALSE)) { + if (pScrn->is_gpu) { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "TearFree cannot synchronize PRIME; use 'PRIME Synchronization' instead\n"); + } else if (ms->drmmode.glamor) { + /* Atomic modesetting implicitly enables universal planes */ + if (!ms->drmmode.pageflip || ms->atomic_modeset || + !drmSetClientCap(ms->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1)) { + ms->drmmode.tearfree_enable = TRUE; + xf86DrvMsg(pScrn->scrnIndex, X_INFO, "TearFree: enabled\n"); + } else { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "TearFree requires either universal planes, or setting 'Option \"PageFlip\" \"off\"'\n"); + } + } else { + xf86DrvMsg(pScrn->scrnIndex, X_WARNING, + "TearFree requires Glamor acceleration\n"); + } + } + ms->kms_has_modifiers = FALSE; ret = drmGetCap(ms->fd, DRM_CAP_ADDFB2_MODIFIERS, &value); if (ret == 0 && value != 0) @@ -1628,13 +1742,13 @@ CreateScreenResources(ScreenPtr pScreen) err = drmModeDirtyFB(ms->fd, ms->drmmode.fb_id, NULL, 0); - if (err != -EINVAL && err != -ENOSYS) { + if ((err != -EINVAL && err != -ENOSYS) || ms->drmmode.tearfree_enable) { ms->damage = DamageCreate(NULL, NULL, DamageReportNone, TRUE, pScreen, rootPixmap); if (ms->damage) { DamageRegister(&rootPixmap->drawable, ms->damage); - ms->dirty_enabled = TRUE; + ms->dirty_enabled = err != -EINVAL && err != -ENOSYS; xf86DrvMsg(pScrn->scrnIndex, X_INFO, "Damage tracking initialized\n"); } else { diff --git a/hw/xfree86/drivers/modesetting/driver.h b/hw/xfree86/drivers/modesetting/driver.h index 4f62646a09..3f2b1d1aef 100644 --- a/hw/xfree86/drivers/modesetting/driver.h +++ b/hw/xfree86/drivers/modesetting/driver.h @@ -61,6 +61,7 @@ typedef enum { OPTION_VARIABLE_REFRESH, OPTION_USE_GAMMA_LUT, OPTION_ASYNC_FLIP_SECONDARIES, + OPTION_TEARFREE, } modesettingOpts; typedef struct @@ -241,6 +242,8 @@ Bool ms_do_pageflip(ScreenPtr screen, ms_pageflip_abort_proc pageflip_abort, const char *log_prefix); +Bool ms_do_tearfree_flip(ScreenPtr screen, xf86CrtcPtr crtc); + #endif int ms_flush_drm_events(ScreenPtr screen); diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c index 65e9593790..8f8e4060a9 100644 --- a/hw/xfree86/drivers/modesetting/drmmode_display.c +++ b/hw/xfree86/drivers/modesetting/drmmode_display.c @@ -632,6 +632,7 @@ drmmode_crtc_get_fb_id(xf86CrtcPtr crtc, uint32_t *fb_id, int *x, int *y) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_ptr drmmode = drmmode_crtc->drmmode; + drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree; int ret; *fb_id = 0; @@ -646,6 +647,10 @@ drmmode_crtc_get_fb_id(xf86CrtcPtr crtc, uint32_t *fb_id, int *x, int *y) *x = drmmode_crtc->prime_pixmap_x; *y = 0; } + else if (trf->buf[trf->back_idx ^ 1].px) { + *fb_id = trf->buf[trf->back_idx ^ 1].fb_id; + *x = *y = 0; + } else if (drmmode_crtc->rotate_fb_id) { *fb_id = drmmode_crtc->rotate_fb_id; *x = *y = 0; @@ -922,6 +927,10 @@ drmmode_crtc_set_mode(xf86CrtcPtr crtc, Bool test_only) drmmode_ConvertToKMode(crtc->scrn, &kmode, &crtc->mode); ret = drmModeSetCrtc(drmmode->fd, drmmode_crtc->mode_crtc->crtc_id, fb_id, x, y, output_ids, output_count, &kmode); + if (!ret && !ms->atomic_modeset) { + drmmode_crtc->src_x = x; + drmmode_crtc->src_y = y; + } drmmode_set_ctm(crtc, ctm); @@ -951,6 +960,26 @@ drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, int x, int y, return ret; } + /* The frame buffer source coordinates may change when switching between the + * primary frame buffer and a per-CRTC frame buffer. Set the correct source + * coordinates if they differ for this flip. + */ + if (drmmode_crtc->src_x != x || drmmode_crtc->src_y != y) { + ret = drmModeSetPlane(ms->fd, drmmode_crtc->plane_id, + drmmode_crtc->mode_crtc->crtc_id, fb_id, 0, + 0, 0, crtc->mode.HDisplay, crtc->mode.VDisplay, + x << 16, y << 16, crtc->mode.HDisplay << 16, + crtc->mode.VDisplay << 16); + if (ret) { + xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING, + "error changing fb src coordinates for flip: %d\n", ret); + return ret; + } + + drmmode_crtc->src_x = x; + drmmode_crtc->src_y = y; + } + return drmModePageFlip(ms->fd, drmmode_crtc->mode_crtc->crtc_id, fb_id, flags, data); } @@ -1549,6 +1578,90 @@ drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode) #endif } +void +drmmode_copy_damage(xf86CrtcPtr crtc, PixmapPtr dst, RegionPtr dmg, Bool empty) +{ +#ifdef GLAMOR_HAS_GBM + ScreenPtr pScreen = xf86ScrnToScreen(crtc->scrn); + DrawableRec *src; + + /* Copy the screen's pixmap into the destination pixmap */ + if (crtc->rotatedPixmap) { + src = &crtc->rotatedPixmap->drawable; + xf86RotateCrtcRedisplay(crtc, dst, src, dmg, FALSE); + } else { + src = &pScreen->GetScreenPixmap(pScreen)->drawable; + PixmapDirtyCopyArea(dst, src, 0, 0, -crtc->x, -crtc->y, dmg); + } + + /* Reset the damages if requested */ + if (empty) + RegionEmpty(dmg); + + /* Wait until the GC operations finish */ + modesettingPTR(crtc->scrn)->glamor.finish(pScreen); +#endif +} + +static void +drmmode_shadow_fb_destroy(xf86CrtcPtr crtc, PixmapPtr pixmap, + void *data, drmmode_bo *bo, uint32_t *fb_id); +static void +drmmode_destroy_tearfree_shadow(xf86CrtcPtr crtc) +{ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree; + int i; + + if (trf->flip_seq) + ms_drm_abort_seq(crtc->scrn, trf->flip_seq); + + for (i = 0; i < ARRAY_SIZE(trf->buf); i++) { + if (trf->buf[i].px) { + drmmode_shadow_fb_destroy(crtc, trf->buf[i].px, (void *)(long)1, + &trf->buf[i].bo, &trf->buf[i].fb_id); + trf->buf[i].px = NULL; + RegionUninit(&trf->buf[i].dmg); + } + } +} + +static PixmapPtr +drmmode_shadow_fb_create(xf86CrtcPtr crtc, void *data, int width, int height, + drmmode_bo *bo, uint32_t *fb_id); +static Bool +drmmode_create_tearfree_shadow(xf86CrtcPtr crtc) +{ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + drmmode_ptr drmmode = drmmode_crtc->drmmode; + drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree; + uint32_t w = crtc->mode.HDisplay, h = crtc->mode.VDisplay; + int i; + + if (!drmmode->tearfree_enable) + return TRUE; + + /* Destroy the old mode's buffers and make new ones */ + drmmode_destroy_tearfree_shadow(crtc); + for (i = 0; i < ARRAY_SIZE(trf->buf); i++) { + trf->buf[i].px = drmmode_shadow_fb_create(crtc, NULL, w, h, + &trf->buf[i].bo, + &trf->buf[i].fb_id); + if (!trf->buf[i].px) { + drmmode_destroy_tearfree_shadow(crtc); + xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, + "shadow creation failed for TearFree buf%d\n", i); + return FALSE; + } + RegionInit(&trf->buf[i].dmg, &crtc->bounds, 0); + } + + /* Initialize the front buffer with the current scanout */ + drmmode_copy_damage(crtc, trf->buf[trf->back_idx ^ 1].px, + &trf->buf[trf->back_idx ^ 1].dmg, TRUE); + return TRUE; +} + static Bool drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, Rotation rotation, int x, int y) @@ -1582,6 +1695,10 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green, crtc->gamma_blue, crtc->gamma_size); + ret = drmmode_create_tearfree_shadow(crtc); + if (!ret) + goto done; + can_test = drmmode_crtc_can_test_mode(crtc); if (drmmode_crtc_set_mode(crtc, can_test)) { xf86DrvMsg(crtc->scrn->scrnIndex, X_ERROR, @@ -1627,6 +1744,7 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, crtc->y = saved_y; crtc->rotation = saved_rotation; crtc->mode = saved_mode; + drmmode_create_tearfree_shadow(crtc); } else crtc->active = TRUE; @@ -4274,6 +4392,7 @@ drmmode_free_bos(ScrnInfoPtr pScrn, drmmode_ptr drmmode) drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; dumb_bo_destroy(drmmode->fd, drmmode_crtc->cursor_bo); + drmmode_destroy_tearfree_shadow(crtc); } } diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.h b/hw/xfree86/drivers/modesetting/drmmode_display.h index d6ad15501b..145cb8cc7b 100644 --- a/hw/xfree86/drivers/modesetting/drmmode_display.h +++ b/hw/xfree86/drivers/modesetting/drmmode_display.h @@ -135,6 +135,7 @@ typedef struct { Bool async_flip_secondaries; Bool dri2_enable; Bool present_enable; + Bool tearfree_enable; uint32_t vrr_prop_id; Bool use_ctm; @@ -166,6 +167,19 @@ typedef struct { uint64_t *modifiers; } drmmode_format_rec, *drmmode_format_ptr; +typedef struct { + drmmode_bo bo; + uint32_t fb_id; + PixmapPtr px; + RegionRec dmg; +} drmmode_shadow_fb_rec, *drmmode_shadow_fb_ptr; + +typedef struct { + drmmode_shadow_fb_rec buf[2]; + uint32_t back_idx; + uint32_t flip_seq; +} drmmode_tearfree_rec, *drmmode_tearfree_ptr; + typedef struct { drmmode_ptr drmmode; drmModeCrtcPtr mode_crtc; @@ -184,11 +198,14 @@ typedef struct { drmmode_bo rotate_bo; unsigned rotate_fb_id; + drmmode_tearfree_rec tearfree; PixmapPtr prime_pixmap; PixmapPtr prime_pixmap_back; unsigned prime_pixmap_x; + int src_x, src_y; + /** * @{ MSC (vblank count) handling for the PRESENT extension. * @@ -310,6 +327,8 @@ void drmmode_get_default_bpp(ScrnInfoPtr pScrn, drmmode_ptr drmmmode, int *depth, int *bpp); void drmmode_copy_fb(ScrnInfoPtr pScrn, drmmode_ptr drmmode); +void drmmode_copy_damage(xf86CrtcPtr crtc, PixmapPtr dst, RegionPtr damage, + Bool empty); int drmmode_crtc_flip(xf86CrtcPtr crtc, uint32_t fb_id, int x, int y, uint32_t flags, void *data); diff --git a/hw/xfree86/drivers/modesetting/modesetting.man b/hw/xfree86/drivers/modesetting/modesetting.man index 71790011ec..9e40b04f32 100644 --- a/hw/xfree86/drivers/modesetting/modesetting.man +++ b/hw/xfree86/drivers/modesetting/modesetting.man @@ -109,6 +109,17 @@ When enabled, this option allows the driver to use gamma ramps with more entries, if supported by the kernel. By default, GAMMA_LUT will be used for kms drivers which are known to be safe for use of GAMMA_LUT. .TP +.BI "Option \*qTearFree\*q \*q" boolean \*q +Enable tearing prevention using the hardware page flipping mechanism. +It allocates two extra scanout buffers for each CRTC and utilizes damage +tracking to minimize buffer copying and skip unnecessary flips when the +screen's contents have not changed. It works on transformed screens too, such +as rotated and scaled CRTCs. When PageFlip is enabled, fullscreen DRI +applications will still have the discretion to not use tearing prevention. +.br +The default is +.B off. +.TP .SH "SEE ALSO" @xservername@(@appmansuffix@), @xconfigfile@(@filemansuffix@), Xserver(@appmansuffix@), X(@miscmansuffix@) diff --git a/hw/xfree86/drivers/modesetting/pageflip.c b/hw/xfree86/drivers/modesetting/pageflip.c index a51a10a2c1..8d57047efa 100644 --- a/hw/xfree86/drivers/modesetting/pageflip.c +++ b/hw/xfree86/drivers/modesetting/pageflip.c @@ -35,8 +35,8 @@ * Returns a negative value on error, 0 if there was nothing to process, * or 1 if we handled any events. */ -int -ms_flush_drm_events(ScreenPtr screen) +static int +ms_flush_drm_events_timeout(ScreenPtr screen, int timeout) { ScrnInfoPtr scrn = xf86ScreenToScrn(screen); modesettingPtr ms = modesettingPTR(scrn); @@ -45,7 +45,7 @@ ms_flush_drm_events(ScreenPtr screen) int r; do { - r = xserver_poll(&p, 1, 0); + r = xserver_poll(&p, 1, timeout); } while (r == -1 && (errno == EINTR || errno == EAGAIN)); /* If there was an error, r will be < 0. Return that. If there was @@ -63,6 +63,12 @@ ms_flush_drm_events(ScreenPtr screen) return 1; } +int +ms_flush_drm_events(ScreenPtr screen) +{ + return ms_flush_drm_events_timeout(screen, 0); +} + #ifdef GLAMOR_HAS_GBM /* @@ -163,14 +169,22 @@ static Bool do_queue_flip_on_crtc(ScreenPtr screen, xf86CrtcPtr crtc, uint32_t flags, uint32_t seq, uint32_t fb_id, int x, int y) { + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree; + while (drmmode_crtc_flip(crtc, fb_id, x, y, flags, (void *)(long)seq)) { /* We may have failed because the event queue was full. Flush it * and retry. If there was nothing to flush, then we failed for * some other reason and should just return an error. */ if (ms_flush_drm_events(screen) <= 0) { - ms_drm_abort_seq(crtc->scrn, seq); - return TRUE; + /* The failure could be caused by a pending TearFree flip, in which + * case we should wait until there's a new event and try again. + */ + if (!trf->flip_seq || ms_flush_drm_events_timeout(screen, -1) < 0) { + ms_drm_abort_seq(crtc->scrn, seq); + return TRUE; + } } /* We flushed some events, so try again. */ @@ -467,4 +481,50 @@ error_out: #endif /* GLAMOR_HAS_GBM */ } +static void +ms_tearfree_flip_abort(void *data) +{ + drmmode_tearfree_ptr trf = data; + + trf->flip_seq = 0; +} + +static void +ms_tearfree_flip_handler(uint64_t msc, uint64_t usec, void *data) +{ + drmmode_tearfree_ptr trf = data; + + /* Swap the buffers and complete the flip */ + trf->back_idx ^= 1; + trf->flip_seq = 0; +} + +Bool +ms_do_tearfree_flip(ScreenPtr screen, xf86CrtcPtr crtc) +{ + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree; + uint32_t idx = trf->back_idx, seq; + + seq = ms_drm_queue_alloc(crtc, trf, ms_tearfree_flip_handler, + ms_tearfree_flip_abort); + if (!seq) + goto no_flip; + + /* Copy the damage to the back buffer and then flip it at the vblank */ + drmmode_copy_damage(crtc, trf->buf[idx].px, &trf->buf[idx].dmg, TRUE); + if (do_queue_flip_on_crtc(screen, crtc, DRM_MODE_PAGE_FLIP_EVENT, + seq, trf->buf[idx].fb_id, 0, 0)) + goto no_flip; + + trf->flip_seq = seq; + return FALSE; + +no_flip: + xf86DrvMsg(crtc->scrn->scrnIndex, X_WARNING, + "TearFree flip failed, rendering frame without TearFree\n"); + drmmode_copy_damage(crtc, trf->buf[idx ^ 1].px, + &trf->buf[idx ^ 1].dmg, FALSE); + return TRUE; +} #endif diff --git a/hw/xfree86/drivers/modesetting/present.c b/hw/xfree86/drivers/modesetting/present.c index c3266d8711..642f7baaff 100644 --- a/hw/xfree86/drivers/modesetting/present.c +++ b/hw/xfree86/drivers/modesetting/present.c @@ -318,14 +318,32 @@ ms_present_check_flip(RRCrtcPtr crtc, modesettingPtr ms = modesettingPTR(scrn); if (ms->drmmode.sprites_visible > 0) - return FALSE; + goto no_flip; if(!ms_present_check_unflip(crtc, window, pixmap, sync_flip, reason)) - return FALSE; + goto no_flip; ms->flip_window = window; return TRUE; + +no_flip: + /* Export some info about TearFree if Present can't flip anyway */ + if (reason && ms->drmmode.tearfree_enable) { + xf86CrtcPtr xf86_crtc = crtc->devPrivate; + drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private; + drmmode_tearfree_ptr trf = &drmmode_crtc->tearfree; + + if (trf->buf[0].px) { + if (trf->flip_seq) + /* The driver has a TearFree flip pending */ + *reason = PRESENT_FLIP_REASON_DRIVER_TEARFREE_FLIPPING; + else + /* The driver uses TearFree flips and there's no flip pending */ + *reason = PRESENT_FLIP_REASON_DRIVER_TEARFREE; + } + } + return FALSE; } /*