Add Option "TearFree" v4

Avoids tearing by flipping between two scanout BOs per (non-rotated) CRTC

v2:
* Fix condition for TearFree log message (Richard Wilbur)
* Log warning message about DRI page flipping being enabled because of
  TearFree (or ShadowPrimary) also when building without glamor support

v3:
* Only override fb_id/x/y if all scanout pixmaps have been successfully
  allocated

v4:
* Make log warning clearer if drmModePageFlip returns an error

Reviewed-by: Alex Deucher <alexander.deucher@amd.com> (v1)
This commit is contained in:
Michel Dänzer
2015-04-20 18:44:36 +09:00
committed by Michel Dänzer
parent ed401f5b4f
commit 43159ef400
5 changed files with 160 additions and 71 deletions

View File

@@ -276,6 +276,13 @@ Enable DRI2 page flipping. The default is
.B on.
Pageflipping is supported on all radeon hardware.
.TP
.BI "Option \*qTearFree\*q \*q" boolean \*q
Enable tearing prevention using the hardware page flipping mechanism. This
option currently doesn't have any effect for rotated CRTCs. It requires
allocating two separate scanout buffers for each non-rotated CRTC. Enabling
this option currently disables Option \*qEnablePageFlip\*q. The default is
.B off.
.TP
.BI "Option \*qAccelMethod\*q \*q" "string" \*q
Chooses between available acceleration architectures. Valid values are
.B EXA

View File

@@ -488,6 +488,10 @@ drmmode_crtc_scanout_destroy(drmmode_ptr drmmode,
scanout->bo = NULL;
}
if (scanout->damage) {
DamageDestroy(scanout->damage);
scanout->damage = NULL;
}
}
void
@@ -501,12 +505,9 @@ drmmode_scanout_free(ScrnInfoPtr scrn)
xf86_config->crtc[c]->driver_private;
drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode,
&drmmode_crtc->scanout);
if (drmmode_crtc->scanout_damage) {
DamageDestroy(drmmode_crtc->scanout_damage);
drmmode_crtc->scanout_damage = NULL;
}
&drmmode_crtc->scanout[0]);
drmmode_crtc_scanout_destroy(drmmode_crtc->drmmode,
&drmmode_crtc->scanout[1]);
}
}
@@ -704,43 +705,51 @@ drmmode_set_mode_major(xf86CrtcPtr crtc, DisplayModePtr mode,
x = drmmode_crtc->prime_pixmap_x;
y = 0;
drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout);
drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout[0]);
drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout[1]);
} else
#endif
if (drmmode_crtc->rotate.fb_id) {
fb_id = drmmode_crtc->rotate.fb_id;
x = y = 0;
drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout);
} else if (info->shadow_primary) {
drmmode_crtc_scanout_create(crtc,
&drmmode_crtc->scanout,
NULL, mode->HDisplay,
mode->VDisplay);
drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout[0]);
drmmode_crtc_scanout_destroy(drmmode, &drmmode_crtc->scanout[1]);
} else if (info->tear_free || info->shadow_primary) {
for (i = 0; i < (info->tear_free ? 2 : 1); i++) {
drmmode_crtc_scanout_create(crtc,
&drmmode_crtc->scanout[i],
NULL, mode->HDisplay,
mode->VDisplay);
if (drmmode_crtc->scanout.pixmap) {
RegionPtr pRegion;
BoxPtr pBox;
if (drmmode_crtc->scanout[i].pixmap) {
RegionPtr pRegion;
BoxPtr pBox;
if (!drmmode_crtc->scanout_damage) {
drmmode_crtc->scanout_damage =
DamageCreate(radeon_screen_damage_report,
NULL, DamageReportRawRegion,
TRUE, pScreen, NULL);
DamageRegister(&pScreen->GetScreenPixmap(pScreen)->drawable,
drmmode_crtc->scanout_damage);
if (!drmmode_crtc->scanout[i].damage) {
drmmode_crtc->scanout[i].damage =
DamageCreate(radeon_screen_damage_report,
NULL, DamageReportRawRegion,
TRUE, pScreen, NULL);
DamageRegister(&pScreen->GetScreenPixmap(pScreen)->drawable,
drmmode_crtc->scanout[i].damage);
}
pRegion = DamageRegion(drmmode_crtc->scanout[i].damage);
RegionUninit(pRegion);
pRegion->data = NULL;
pBox = RegionExtents(pRegion);
pBox->x1 = min(pBox->x1, x);
pBox->y1 = min(pBox->y1, y);
pBox->x2 = max(pBox->x2, x + mode->HDisplay);
pBox->y2 = max(pBox->y2, y + mode->VDisplay);
}
}
pRegion = DamageRegion(drmmode_crtc->scanout_damage);
RegionUninit(pRegion);
pRegion->data = NULL;
pBox = RegionExtents(pRegion);
pBox->x1 = min(pBox->x1, x);
pBox->y1 = min(pBox->y1, y);
pBox->x2 = max(pBox->x2, x + mode->HDisplay);
pBox->y2 = max(pBox->y2, y + mode->VDisplay);
fb_id = drmmode_crtc->scanout.fb_id;
if (drmmode_crtc->scanout[0].pixmap &&
(!info->tear_free || drmmode_crtc->scanout[1].pixmap)) {
drmmode_crtc->scanout_id = 0;
fb_id = drmmode_crtc->scanout[0].fb_id;
x = y = 0;
}
}

View File

@@ -75,6 +75,7 @@ typedef struct {
struct drmmode_scanout {
struct radeon_bo *bo;
PixmapPtr pixmap;
DamagePtr damage;
unsigned fb_id;
int width, height;
};
@@ -85,8 +86,8 @@ typedef struct {
int hw_id;
struct radeon_bo *cursor_bo;
struct drmmode_scanout rotate;
struct drmmode_scanout scanout;
DamagePtr scanout_damage;
struct drmmode_scanout scanout[2];
unsigned scanout_id;
Bool scanout_update_pending;
int dpms_mode;
CARD64 dpms_last_ust;

View File

@@ -152,6 +152,7 @@ typedef enum {
OPTION_DELETE_DP12,
OPTION_DRI3,
OPTION_SHADOW_PRIMARY,
OPTION_TEAR_FREE,
} RADEONOpts;
@@ -477,6 +478,7 @@ typedef struct {
Bool accelOn;
Bool use_glamor;
Bool shadow_primary;
Bool tear_free;
Bool exa_pixmaps;
Bool exa_force_create;
XF86ModReqInfo exaReq;

View File

@@ -78,6 +78,7 @@ const OptionInfoRec RADEONOptions_KMS[] = {
{ OPTION_SWAPBUFFERS_WAIT,"SwapbuffersWait", OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_DELETE_DP12, "DeleteUnusedDP12Displays", OPTV_BOOLEAN, {0}, FALSE},
{ OPTION_DRI3, "DRI3", OPTV_BOOLEAN, {0}, FALSE },
{ OPTION_TEAR_FREE, "TearFree", OPTV_BOOLEAN, {0}, FALSE },
{ -1, NULL, OPTV_NONE, {0}, FALSE }
};
@@ -320,21 +321,11 @@ radeon_scanout_extents_intersect(BoxPtr extents, int x, int y, int w, int h)
return (extents->x1 < extents->x2 && extents->y1 < extents->y2);
}
static void
radeon_scanout_update_abort(ScrnInfoPtr scrn, void *event_data)
static Bool
radeon_scanout_do_update(xf86CrtcPtr xf86_crtc, int scanout_id)
{
xf86CrtcPtr xf86_crtc = event_data;
drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
drmmode_crtc->scanout_update_pending = FALSE;
}
static void
radeon_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
void *event_data)
{
xf86CrtcPtr xf86_crtc = event_data;
drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
ScrnInfoPtr scrn;
DamagePtr pDamage;
RegionPtr pRegion;
DrawablePtr pDraw;
@@ -344,26 +335,28 @@ radeon_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
RADEONInfoPtr info;
Bool force;
if (!drmmode_crtc->scanout.pixmap ||
drmmode_crtc->dpms_mode != DPMSModeOn)
goto out;
if (drmmode_crtc->dpms_mode != DPMSModeOn ||
!drmmode_crtc->scanout[scanout_id].pixmap)
return FALSE;
pDamage = drmmode_crtc->scanout_damage;
pDamage = drmmode_crtc->scanout[scanout_id].damage;
if (!pDamage)
goto out;
return FALSE;
pRegion = DamageRegion(pDamage);
if (!RegionNotEmpty(pRegion))
goto out;
return FALSE;
pDraw = &drmmode_crtc->scanout.pixmap->drawable;
pDraw = &drmmode_crtc->scanout[scanout_id].pixmap->drawable;
extents = *RegionExtents(pRegion);
RegionEmpty(pRegion);
if (!radeon_scanout_extents_intersect(&extents, xf86_crtc->x, xf86_crtc->y,
pDraw->width, pDraw->height))
goto clear_damage;
return FALSE;
pScreen = pDraw->pScreen;
gc = GetScratchGC(pDraw->depth, pScreen);
scrn = xf86_crtc->scrn;
info = RADEONPTR(scrn);
force = info->accel_state->force;
info->accel_state->force = TRUE;
@@ -380,13 +373,27 @@ radeon_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
radeon_cs_flush_indirect(scrn);
clear_damage:
RegionEmpty(pRegion);
return TRUE;
}
static void
radeon_scanout_update_abort(ScrnInfoPtr scrn, void *event_data)
{
xf86CrtcPtr xf86_crtc = event_data;
drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
out:
drmmode_crtc->scanout_update_pending = FALSE;
}
static void
radeon_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
void *event_data)
{
radeon_scanout_do_update(event_data, 0);
radeon_scanout_update_abort(scrn, event_data);
}
static void
radeon_scanout_update(xf86CrtcPtr xf86_crtc)
{
@@ -400,11 +407,11 @@ radeon_scanout_update(xf86CrtcPtr xf86_crtc)
BoxRec extents;
if (drmmode_crtc->scanout_update_pending ||
!drmmode_crtc->scanout.pixmap ||
!drmmode_crtc->scanout[0].pixmap ||
drmmode_crtc->dpms_mode != DPMSModeOn)
return;
pDamage = drmmode_crtc->scanout_damage;
pDamage = drmmode_crtc->scanout[0].damage;
if (!pDamage)
return;
@@ -412,7 +419,7 @@ radeon_scanout_update(xf86CrtcPtr xf86_crtc)
if (!RegionNotEmpty(pRegion))
return;
pDraw = &drmmode_crtc->scanout.pixmap->drawable;
pDraw = &drmmode_crtc->scanout[0].pixmap->drawable;
extents = *RegionExtents(pRegion);
if (!radeon_scanout_extents_intersect(&extents, xf86_crtc->x, xf86_crtc->y,
pDraw->width, pDraw->height))
@@ -445,6 +452,60 @@ radeon_scanout_update(xf86CrtcPtr xf86_crtc)
drmmode_crtc->scanout_update_pending = TRUE;
}
static void
radeon_scanout_flip_abort(ScrnInfoPtr scrn, void *event_data)
{
drmmode_crtc_private_ptr drmmode_crtc = event_data;
drmmode_crtc->scanout_update_pending = FALSE;
}
static void
radeon_scanout_flip_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec, void *event_data)
{
radeon_scanout_flip_abort(scrn, event_data);
}
static void
radeon_scanout_flip(ScreenPtr pScreen, RADEONInfoPtr info,
xf86CrtcPtr xf86_crtc)
{
drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
ScrnInfoPtr scrn;
struct radeon_drm_queue_entry *drm_queue_entry;
unsigned scanout_id;
if (drmmode_crtc->scanout_update_pending)
return;
scanout_id = drmmode_crtc->scanout_id ^ 1;
if (!radeon_scanout_do_update(xf86_crtc, scanout_id))
return;
scrn = xf86_crtc->scrn;
drm_queue_entry = radeon_drm_queue_alloc(scrn, RADEON_DRM_QUEUE_CLIENT_DEFAULT,
RADEON_DRM_QUEUE_ID_DEFAULT,
drmmode_crtc,
radeon_scanout_flip_handler,
radeon_scanout_flip_abort);
if (!drm_queue_entry) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING,
"Allocating DRM event queue entry failed.\n");
return;
}
if (drmModePageFlip(drmmode_crtc->drmmode->fd, drmmode_crtc->mode_crtc->crtc_id,
drmmode_crtc->scanout[scanout_id].fb_id,
DRM_MODE_PAGE_FLIP_EVENT, drm_queue_entry)) {
xf86DrvMsg(scrn->scrnIndex, X_WARNING, "flip queue failed in %s: %s\n",
__func__, strerror(errno));
return;
}
drmmode_crtc->scanout_id = scanout_id;
drmmode_crtc->scanout_update_pending = TRUE;
}
static void RADEONBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL)
{
SCREEN_PTR(arg);
@@ -455,12 +516,16 @@ static void RADEONBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL)
(*pScreen->BlockHandler) (BLOCKHANDLER_ARGS);
pScreen->BlockHandler = RADEONBlockHandler_KMS;
if (info->shadow_primary) {
if (info->tear_free || info->shadow_primary) {
xf86CrtcConfigPtr xf86_config = XF86_CRTC_CONFIG_PTR(pScrn);
int c;
for (c = 0; c < xf86_config->num_crtc; c++)
radeon_scanout_update(xf86_config->crtc[c]);
for (c = 0; c < xf86_config->num_crtc; c++) {
if (info->tear_free)
radeon_scanout_flip(pScreen, info, xf86_config->crtc[c]);
else
radeon_scanout_update(xf86_config->crtc[c]);
}
}
radeon_cs_flush_indirect(pScrn);
@@ -1092,19 +1157,24 @@ Bool RADEONPreInit_KMS(ScrnInfoPtr pScrn, int flags)
}
#endif
info->tear_free = xf86ReturnOptValBool(info->Options, OPTION_TEAR_FREE,
FALSE);
if (info->tear_free)
xf86DrvMsg(pScrn->scrnIndex, X_CONFIG, "TearFree enabled\n");
if (info->dri2.pKernelDRMVersion->version_minor >= 8) {
info->allowPageFlip = xf86ReturnOptValBool(info->Options,
OPTION_PAGE_FLIP, TRUE);
#if USE_GLAMOR
if (info->shadow_primary) {
if (info->tear_free || info->shadow_primary) {
xf86DrvMsg(pScrn->scrnIndex,
info->allowPageFlip ? X_WARNING : X_DEFAULT,
"KMS Pageflipping: disabled%s\n",
info->allowPageFlip ? " because of ShadowPrimary" : "");
info->allowPageFlip ?
" because of ShadowPrimary/TearFree" : "");
info->allowPageFlip = FALSE;
} else
#endif
{
} else {
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
"KMS Pageflipping: %sabled\n", info->allowPageFlip ? "en" : "dis");
}