From: Michel Dänzer <michel.daenzer@xxxxxxx> Option "TearFree" now sets the default value of the output property. See the manpage update for details. TearFree is now enabled by default for outputs using rotation or other RandR transforms, and for RandR 1.4 slave outputs. Signed-off-by: Michel Dänzer <michel.daenzer at amd.com> --- v2: Fix build against xserver 1.10 man/radeon.man | 15 +++-- src/drmmode_display.c | 161 +++++++++++++++++++++++++++++++++++++++++++++----- src/drmmode_display.h | 2 + src/radeon.h | 2 +- src/radeon_dri2.c | 35 ++++++++--- src/radeon_kms.c | 43 +++++++++----- 6 files changed, 216 insertions(+), 42 deletions(-) diff --git a/man/radeon.man b/man/radeon.man index 8990ae21d..5301dd7f0 100644 --- a/man/radeon.man +++ b/man/radeon.man @@ -281,10 +281,17 @@ Enable DRI2 page flipping. The default is Pageflipping is supported on all radeon hardware. .TP .BI "Option \*qTearFree\*q \*q" boolean \*q -Enable tearing prevention using the hardware page flipping mechanism. Requires allocating two -separate scanout buffers for each CRTC. Enabling this option currently disables Option -\*qEnablePageFlip\*q. The default is -.B off. +Set the default value of the per-output 'TearFree' property, which controls +tearing prevention using the hardware page flipping mechanism. TearFree is +on for any CRTC associated with one or more outputs with TearFree on. Two +separate scanout buffers need to be allocated for each CRTC with TearFree +on. While TearFree is on for any CRTC, it currently prevents clients from using +DRI page flipping. If this option is set, the default value of the property is +'on' or 'off' accordingly. If this option isn't set, the default value of the +property is +.B auto, +which means that TearFree is on for outputs with rotation or other RandR +transforms, and for RandR 1.4 slave outputs, otherwise off. .TP .BI "Option \*qAccelMethod\*q \*q" "string" \*q Chooses between available acceleration architectures. Valid values are diff --git a/src/drmmode_display.c b/src/drmmode_display.c index fcac1562b..5b0236da4 100644 --- a/src/drmmode_display.c +++ b/src/drmmode_display.c @@ -670,6 +670,34 @@ drmmode_can_use_hw_cursor(xf86CrtcPtr crtc) return TRUE; } +static void +drmmode_crtc_update_tear_free(xf86CrtcPtr crtc) +{ + RADEONInfoPtr info = RADEONPTR(crtc->scrn); + xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + int i; + + drmmode_crtc->tear_free = FALSE; + + for (i = 0; i < xf86_config->num_output; i++) { + xf86OutputPtr output = xf86_config->output[i]; + drmmode_output_private_ptr drmmode_output = output->driver_private; + + if (output->crtc != crtc) + continue; + + if (drmmode_output->tear_free == 1 || + (drmmode_output->tear_free == 2 && + (radeon_is_gpu_screen(crtc->scrn->pScreen) || + info->shadow_primary || + crtc->transformPresent || crtc->rotation != RR_Rotate_0))) { + drmmode_crtc->tear_free = TRUE; + return; + } + } +} + #if XF86_CRTC_VERSION >= 4 static Bool @@ -683,10 +711,11 @@ drmmode_handle_transform(xf86CrtcPtr crtc) else crtc->driverIsPerformingTransform = XF86DriverTransformNone; #else + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; RADEONInfoPtr info = RADEONPTR(crtc->scrn); crtc->driverIsPerformingTransform = crtc->transformPresent || - (info->tear_free && crtc->rotation != RR_Rotate_0); + (drmmode_crtc->tear_free && crtc->rotation != RR_Rotate_0); #endif ret = xf86CrtcRotate(crtc); @@ -706,24 +735,87 @@ drmmode_handle_transform(xf86CrtcPtr crtc) #endif +#ifdef RADEON_PIXMAP_SHARING + static void +drmmode_crtc_prime_scanout_update(xf86CrtcPtr crtc, DisplayModePtr mode, + unsigned scanout_id, int *fb_id, int *x, + int *y) +{ + ScrnInfoPtr scrn = crtc->scrn; + ScreenPtr screen = scrn->pScreen; + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + + if (drmmode_crtc->tear_free && + !drmmode_crtc->scanout[1].pixmap) { + RegionPtr region; + BoxPtr box; + + drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[1], + mode->HDisplay, + mode->VDisplay); + region = &drmmode_crtc->scanout_last_region; + RegionUninit(region); + region->data = NULL; + box = RegionExtents(region); + box->x1 = crtc->x; + box->y1 = crtc->y; + box->x2 = crtc->x + mode->HDisplay; + box->y2 = crtc->y + mode->VDisplay; + } + + if (scanout_id != drmmode_crtc->scanout_id) { + PixmapDirtyUpdatePtr dirty = NULL; + + xorg_list_for_each_entry(dirty, &screen->pixmap_dirty_list, + ent) { + if (dirty->src == crtc->randr_crtc->scanout_pixmap && + dirty->slave_dst == + drmmode_crtc->scanout[drmmode_crtc->scanout_id].pixmap) { + dirty->slave_dst = + drmmode_crtc->scanout[scanout_id].pixmap; + break; + } + } + + if (!drmmode_crtc->tear_free) { + GCPtr gc = GetScratchGC(scrn->depth, screen); + + ValidateGC(&drmmode_crtc->scanout[0].pixmap->drawable, gc); + gc->ops->CopyArea(&drmmode_crtc->scanout[1].pixmap->drawable, + &drmmode_crtc->scanout[0].pixmap->drawable, + gc, 0, 0, mode->HDisplay, mode->VDisplay, + 0, 0); + FreeScratchGC(gc); + radeon_cs_flush_indirect(scrn); + radeon_bo_wait(drmmode_crtc->scanout[0].bo); + } + } + + *fb_id = drmmode_crtc->scanout[scanout_id].fb_id; + *x = *y = 0; + drmmode_crtc->scanout_id = scanout_id; +} + +#endif /* RADEON_PIXMAP_SHARING */ + + static void drmmode_crtc_scanout_update(xf86CrtcPtr crtc, DisplayModePtr mode, unsigned scanout_id, int *fb_id, int *x, int *y) { ScrnInfoPtr scrn = crtc->scrn; ScreenPtr screen = scrn->pScreen; - RADEONInfoPtr info = RADEONPTR(scrn); drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[0], mode->HDisplay, mode->VDisplay); - if (info->tear_free) { + if (drmmode_crtc->tear_free) { drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[1], mode->HDisplay, mode->VDisplay); } if (drmmode_crtc->scanout[0].pixmap && - (!info->tear_free || drmmode_crtc->scanout[1].pixmap)) { + (!drmmode_crtc->tear_free || drmmode_crtc->scanout[1].pixmap)) { RegionPtr region; BoxPtr box; @@ -762,7 +854,7 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, RADEONInfoPtr info = RADEONPTR(pScrn); xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(crtc->scrn); drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; - unsigned scanout_id = drmmode_crtc->scanout_id ^ info->tear_free; + unsigned scanout_id = 0; drmmode_ptr drmmode = drmmode_crtc->drmmode; int saved_x, saved_y; Rotation saved_rotation; @@ -804,6 +896,10 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, if (!drmmode_handle_transform(crtc)) goto done; + drmmode_crtc_update_tear_free(crtc); + if (drmmode_crtc->tear_free) + scanout_id = drmmode_crtc->scanout_id; + crtc->funcs->gamma_set(crtc, crtc->gamma_red, crtc->gamma_green, crtc->gamma_blue, crtc->gamma_size); @@ -812,8 +908,8 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, fb_id = drmmode->fb_id; #ifdef RADEON_PIXMAP_SHARING if (crtc->randr_crtc && crtc->randr_crtc->scanout_pixmap) { - fb_id = drmmode_crtc->scanout[scanout_id].fb_id; - x = y = 0; + drmmode_crtc_prime_scanout_update(crtc, mode, scanout_id, + &fb_id, &x, &y); } else #endif if (drmmode_crtc->rotate.fb_id) { @@ -821,7 +917,7 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode, x = y = 0; } else if (!radeon_is_gpu_screen(pScreen) && - (info->tear_free || + (drmmode_crtc->tear_free || #if XF86_CRTC_VERSION >= 4 crtc->driverIsPerformingTransform || #endif @@ -905,6 +1001,10 @@ done: if (fb_id != drmmode_crtc->scanout[scanout_id].fb_id) drmmode_crtc_scanout_free(drmmode_crtc); + else if (!drmmode_crtc->tear_free) { + drmmode_crtc_scanout_destroy(drmmode, + &drmmode_crtc->scanout[1]); + } } free(output_ids); @@ -1142,7 +1242,6 @@ drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix) { drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; unsigned scanout_id = drmmode_crtc->scanout_id; - RADEONInfoPtr info = RADEONPTR(crtc->scrn); ScreenPtr screen = crtc->scrn->pScreen; PixmapDirtyUpdatePtr dirty; @@ -1163,7 +1262,7 @@ drmmode_set_scanout_pixmap(xf86CrtcPtr crtc, PixmapPtr ppix) ppix->drawable.height)) return FALSE; - if (info->tear_free && + if (drmmode_crtc->tear_free && !drmmode_crtc_scanout_create(crtc, &drmmode_crtc->scanout[1], ppix->drawable.width, ppix->drawable.height)) { @@ -1418,13 +1517,14 @@ drmmode_property_ignore(drmModePropertyPtr prop) static void drmmode_output_create_resources(xf86OutputPtr output) { + RADEONInfoPtr info = RADEONPTR(output->scrn); drmmode_output_private_ptr drmmode_output = output->driver_private; drmModeConnectorPtr mode_output = drmmode_output->mode_output; drmmode_ptr drmmode = drmmode_output->drmmode; - drmModePropertyPtr drmmode_prop; + drmModePropertyPtr drmmode_prop, tearfree_prop; int i, j, err; - drmmode_output->props = calloc(mode_output->count_props, sizeof(drmmode_prop_rec)); + drmmode_output->props = calloc(mode_output->count_props + 1, sizeof(drmmode_prop_rec)); if (!drmmode_output->props) return; @@ -1441,6 +1541,23 @@ drmmode_output_create_resources(xf86OutputPtr output) j++; } + /* Userspace-only property for TearFree */ + tearfree_prop = calloc(1, sizeof(*tearfree_prop)); + tearfree_prop->flags = DRM_MODE_PROP_ENUM; + strncpy(tearfree_prop->name, "TearFree", 8); + tearfree_prop->count_enums = 3; + tearfree_prop->enums = calloc(tearfree_prop->count_enums, + sizeof(*tearfree_prop->enums)); + strncpy(tearfree_prop->enums[0].name, "off", 3); + strncpy(tearfree_prop->enums[1].name, "on", 2); + tearfree_prop->enums[1].value = 1; + strncpy(tearfree_prop->enums[2].name, "auto", 4); + tearfree_prop->enums[2].value = 2; + drmmode_output->props[j].mode_prop = tearfree_prop; + drmmode_output->props[j].value = info->tear_free; + drmmode_output->tear_free = info->tear_free; + drmmode_output->num_props++; + for (i = 0; i < drmmode_output->num_props; i++) { drmmode_prop_ptr p = &drmmode_output->props[i]; drmmode_prop = p->mode_prop; @@ -1540,8 +1657,24 @@ drmmode_output_set_property(xf86OutputPtr output, Atom property, /* search for matching name string, then set its value down */ for (j = 0; j < p->mode_prop->count_enums; j++) { if (!strcmp(p->mode_prop->enums[j].name, name)) { - drmModeConnectorSetProperty(drmmode->fd, drmmode_output->output_id, - p->mode_prop->prop_id, p->mode_prop->enums[j].value); + if (i == (drmmode_output->num_props - 1)) { + if (drmmode_output->tear_free != j) { + xf86CrtcPtr crtc = output->crtc; + + drmmode_output->tear_free = j; + if (crtc) { + drmmode_set_mode_major(crtc, &crtc->mode, + crtc->rotation, + crtc->x, crtc->y); + } + } + } else { + drmModeConnectorSetProperty(drmmode->fd, + drmmode_output->output_id, + p->mode_prop->prop_id, + p->mode_prop->enums[j].value); + } + return TRUE; } } diff --git a/src/drmmode_display.h b/src/drmmode_display.h index 6bbf71c18..bd3f5f987 100644 --- a/src/drmmode_display.h +++ b/src/drmmode_display.h @@ -89,6 +89,7 @@ typedef struct { RegionRec scanout_last_region; unsigned scanout_id; Bool scanout_update_pending; + Bool tear_free; int dpms_mode; /* For when a flip is pending when DPMS off requested */ int pending_dpms_mode; @@ -124,6 +125,7 @@ typedef struct { drmmode_prop_ptr props; int enc_mask; int enc_clone_mask; + int tear_free; } drmmode_output_private_rec, *drmmode_output_private_ptr; diff --git a/src/radeon.h b/src/radeon.h index 039a620be..bfff232c4 100644 --- a/src/radeon.h +++ b/src/radeon.h @@ -507,7 +507,7 @@ typedef struct { Bool accelOn; Bool use_glamor; Bool shadow_primary; - Bool tear_free; + int tear_free; Bool exa_pixmaps; Bool exa_force_create; XF86ModReqInfo exaReq; diff --git a/src/radeon_dri2.c b/src/radeon_dri2.c index d0dcf8906..c108ceab2 100644 --- a/src/radeon_dri2.c +++ b/src/radeon_dri2.c @@ -47,6 +47,7 @@ #include "radeon_bo_gem.h" #include <xf86Priv.h> +#include <X11/extensions/dpmsconst.h> #if DRI2INFOREC_VERSION >= 9 #define USE_DRI2_PRIME @@ -756,14 +757,34 @@ can_flip(ScrnInfoPtr pScrn, DrawablePtr draw, DRI2BufferPtr front, DRI2BufferPtr back) { RADEONInfoPtr info = RADEONPTR(pScrn); + xf86CrtcConfigPtr config = XF86_CRTC_CONFIG_PTR(pScrn); + int num_crtcs_on; + int i; + + if (draw->type != DRAWABLE_WINDOW || + !info->allowPageFlip || + info->hwcursor_disabled || + info->drmmode.present_flipping || + !pScrn->vtSema || + !DRI2CanFlip(draw)) + return FALSE; + + for (i = 0, num_crtcs_on = 0; i < config->num_crtc; i++) { + xf86CrtcPtr crtc = config->crtc[i]; + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + + if (!crtc->enabled) + continue; + + if (!drmmode_crtc || drmmode_crtc->rotate.bo || + drmmode_crtc->scanout[0].bo) + return FALSE; + + if (drmmode_crtc->pending_dpms_mode == DPMSModeOn) + num_crtcs_on++; + } - return draw->type == DRAWABLE_WINDOW && - info->allowPageFlip && - !info->hwcursor_disabled && - !info->drmmode.present_flipping && - pScrn->vtSema && - DRI2CanFlip(draw) && - can_exchange(pScrn, draw, front, back); + return num_crtcs_on > 0 && can_exchange(pScrn, draw, front, back); } static void diff --git a/src/radeon_kms.c b/src/radeon_kms.c index 0ed7680e7..331f3f1c9 100644 --- a/src/radeon_kms.c +++ b/src/radeon_kms.c @@ -669,7 +669,6 @@ radeon_prime_scanout_do_update(xf86CrtcPtr crtc, unsigned scanout_id) { ScrnInfoPtr scrn = crtc->scrn; ScreenPtr screen = scrn->pScreen; - RADEONInfoPtr info = RADEONPTR(scrn); drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; PixmapPtr scanoutpix = crtc->randr_crtc->scanout_pixmap; PixmapDirtyUpdatePtr dirty; @@ -677,7 +676,7 @@ radeon_prime_scanout_do_update(xf86CrtcPtr crtc, unsigned scanout_id) xorg_list_for_each_entry(dirty, &screen->pixmap_dirty_list, ent) { if (dirty->src == scanoutpix && dirty->slave_dst == - drmmode_crtc->scanout[scanout_id ^ info->tear_free].pixmap) { + drmmode_crtc->scanout[scanout_id ^ drmmode_crtc->tear_free].pixmap) { RegionPtr region; if (master_has_sync_shared_pixmap(scrn, dirty)) @@ -687,7 +686,7 @@ radeon_prime_scanout_do_update(xf86CrtcPtr crtc, unsigned scanout_id) if (RegionNil(region)) goto destroy; - if (info->tear_free) { + if (drmmode_crtc->tear_free) { RegionTranslate(region, crtc->x, crtc->y); radeon_sync_scanout_pixmaps(crtc, region, scanout_id); radeon_cs_flush_indirect(scrn); @@ -823,7 +822,6 @@ radeon_prime_scanout_flip(PixmapDirtyUpdatePtr ent) static void radeon_dirty_update(ScrnInfoPtr scrn) { - RADEONInfoPtr info = RADEONPTR(scrn); ScreenPtr screen = scrn->pScreen; PixmapDirtyUpdatePtr ent; RegionPtr region; @@ -844,7 +842,13 @@ radeon_dirty_update(ScrnInfoPtr scrn) region = dirty_region(region_ent); if (RegionNotEmpty(region)) { - if (info->tear_free) + xf86CrtcPtr crtc = radeon_prime_dirty_to_crtc(ent); + drmmode_crtc_private_ptr drmmode_crtc = NULL; + + if (crtc) + drmmode_crtc = crtc->driver_private; + + if (drmmode_crtc && drmmode_crtc->tear_free) radeon_prime_scanout_flip(ent); else radeon_prime_scanout_update(ent); @@ -890,7 +894,7 @@ radeon_scanout_do_update(xf86CrtcPtr xf86_crtc, int scanout_id) if (!radeon_scanout_extents_intersect(xf86_crtc, &extents)) return FALSE; - if (info->tear_free) { + if (drmmode_crtc->tear_free) { radeon_sync_scanout_pixmaps(xf86_crtc, pRegion, scanout_id); RegionCopy(&drmmode_crtc->scanout_last_region, pRegion); } @@ -1112,14 +1116,17 @@ static void RADEONBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL) if (!radeon_is_gpu_screen(pScreen)) { for (c = 0; c < xf86_config->num_crtc; c++) { - if (info->tear_free) - radeon_scanout_flip(pScreen, info, xf86_config->crtc[c]); + xf86CrtcPtr crtc = xf86_config->crtc[c]; + drmmode_crtc_private_ptr drmmode_crtc = crtc->driver_private; + + if (drmmode_crtc->tear_free) + radeon_scanout_flip(pScreen, info, crtc); else if (info->shadow_primary #if XF86_CRTC_VERSION >= 4 - || xf86_config->crtc[c]->driverIsPerformingTransform + || crtc->driverIsPerformingTransform #endif ) - radeon_scanout_update(xf86_config->crtc[c]); + radeon_scanout_update(crtc); } } @@ -1633,6 +1640,7 @@ Bool RADEONPreInit_KMS(ScrnInfoPtr pScrn, int flags) { RADEONInfoPtr info; RADEONEntPtr pRADEONEnt; + MessageType from; DevUnion* pPriv; Gamma zeros = { 0.0, 0.0, 0.0 }; uint32_t tiling = 0; @@ -1778,11 +1786,14 @@ Bool RADEONPreInit_KMS(ScrnInfoPtr pScrn, int flags) #endif if (!info->r600_shadow_fb) { - info->tear_free = xf86ReturnOptValBool(info->Options, OPTION_TEAR_FREE, - FALSE); + from = X_DEFAULT; - if (info->tear_free) - xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "TearFree enabled\n"); + info->tear_free = 2; + if (xf86GetOptValBool(info->Options, OPTION_TEAR_FREE, + &info->tear_free)) + from = X_CONFIG; + xf86DrvMsg(pScrn->scrnIndex, from, "TearFree property default: %s\n", + info->tear_free == 2 ? "auto" : (info->tear_free ? "on" : "off")); } if (info->dri2.pKernelDRMVersion->version_minor >= 8) { @@ -1791,13 +1802,13 @@ Bool RADEONPreInit_KMS(ScrnInfoPtr pScrn, int flags) info->allowPageFlip = xf86ReturnOptValBool(info->Options, OPTION_PAGE_FLIP, TRUE); - if (sw_cursor || info->tear_free || info->shadow_primary) { + if (sw_cursor || info->shadow_primary) { xf86DrvMsg(pScrn->scrnIndex, info->allowPageFlip ? X_WARNING : X_DEFAULT, "KMS Pageflipping: disabled%s\n", info->allowPageFlip ? (sw_cursor ? " because of SWcursor" : - " because of ShadowPrimary/TearFree") : ""); + " because of ShadowPrimary") : ""); info->allowPageFlip = FALSE; } else { xf86DrvMsg(pScrn->scrnIndex, X_INFO, -- 2.11.0