diff --git a/man/radeon.man b/man/radeon.man index 8990ae21..5301dd7f 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 fcac1562..5b0236da 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 6bbf71c1..bd3f5f98 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 039a620b..bfff232c 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 d0dcf890..c108ceab 100644 --- a/src/radeon_dri2.c +++ b/src/radeon_dri2.c @@ -47,6 +47,7 @@ #include "radeon_bo_gem.h" #include +#include #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; - return draw->type == DRAWABLE_WINDOW && - info->allowPageFlip && - !info->hwcursor_disabled && - !info->drmmode.present_flipping && - pScrn->vtSema && - DRI2CanFlip(draw) && - can_exchange(pScrn, draw, front, back); + 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 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 0ed7680e..331f3f1c 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,