Commit c57da333 authored by Michel Dänzer's avatar Michel Dänzer Committed by Michel Dänzer

Add Option "TearFree"

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

(Cherry picked from radeon commit 43159ef400c3b18b9f4d3e6fa1c4aef2d60d38fe)
Reviewed-by: default avatarAlex Deucher <alexander.deucher@amd.com>
parent bd0aca09
......@@ -143,6 +143,7 @@ typedef enum {
OPTION_ACCEL_METHOD,
OPTION_DRI3,
OPTION_SHADOW_PRIMARY,
OPTION_TEAR_FREE,
} AMDGPUOpts;
#define AMDGPU_VSYNC_TIMEOUT 20000 /* Maximum wait for VSYNC (in usecs) */
......@@ -205,6 +206,7 @@ typedef struct {
uint_fast32_t gpu_synced;
Bool use_glamor;
Bool shadow_primary;
Bool tear_free;
/* general */
OptionInfoPtr Options;
......
......@@ -68,6 +68,7 @@ const OptionInfoRec AMDGPUOptions_KMS[] = {
{OPTION_ACCEL_METHOD, "AccelMethod", OPTV_STRING, {0}, FALSE},
{ OPTION_DRI3, "DRI3", OPTV_BOOLEAN, {0}, FALSE },
{OPTION_SHADOW_PRIMARY, "ShadowPrimary", OPTV_BOOLEAN, {0}, FALSE},
{OPTION_TEAR_FREE, "TearFree", OPTV_BOOLEAN, {0}, FALSE},
{-1, NULL, OPTV_NONE, {0}, FALSE}
};
......@@ -222,20 +223,9 @@ amdgpu_scanout_extents_intersect(BoxPtr extents, int x, int y, int w, int h)
return (extents->x1 < extents->x2 && extents->y1 < extents->y2);
}
static void
amdgpu_scanout_update_abort(ScrnInfoPtr scrn, void *event_data)
{
xf86CrtcPtr xf86_crtc = event_data;
drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
drmmode_crtc->scanout_update_pending = FALSE;
}
static void
amdgpu_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
void *event_data)
static Bool
amdgpu_scanout_do_update(xf86CrtcPtr xf86_crtc, int scanout_id)
{
xf86CrtcPtr xf86_crtc = event_data;
drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
DamagePtr pDamage;
RegionPtr pRegion;
......@@ -244,23 +234,24 @@ amdgpu_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
GCPtr gc;
BoxRec extents;
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 (!amdgpu_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);
......@@ -273,15 +264,29 @@ amdgpu_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
extents.x1, extents.y1);
FreeScratchGC(gc);
amdgpu_glamor_flush(scrn);
amdgpu_glamor_flush(xf86_crtc->scrn);
clear_damage:
RegionEmpty(pRegion);
return TRUE;
}
static void
amdgpu_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
amdgpu_scanout_update_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec,
void *event_data)
{
amdgpu_scanout_do_update(event_data, 0);
amdgpu_scanout_update_abort(scrn, event_data);
}
static void
amdgpu_scanout_update(xf86CrtcPtr xf86_crtc)
{
......@@ -295,11 +300,11 @@ amdgpu_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;
......@@ -307,7 +312,7 @@ amdgpu_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 (!amdgpu_scanout_extents_intersect(&extents, xf86_crtc->x, xf86_crtc->y,
pDraw->width, pDraw->height))
......@@ -339,6 +344,61 @@ amdgpu_scanout_update(xf86CrtcPtr xf86_crtc)
drmmode_crtc->scanout_update_pending = TRUE;
}
static void
amdgpu_scanout_flip_abort(ScrnInfoPtr scrn, void *event_data)
{
drmmode_crtc_private_ptr drmmode_crtc = event_data;
drmmode_crtc->scanout_update_pending = FALSE;
}
static void
amdgpu_scanout_flip_handler(ScrnInfoPtr scrn, uint32_t frame, uint64_t usec, void *event_data)
{
amdgpu_scanout_flip_abort(scrn, event_data);
}
static void
amdgpu_scanout_flip(ScreenPtr pScreen, AMDGPUInfoPtr info,
xf86CrtcPtr xf86_crtc)
{
drmmode_crtc_private_ptr drmmode_crtc = xf86_crtc->driver_private;
ScrnInfoPtr scrn;
struct amdgpu_drm_queue_entry *drm_queue_entry;
unsigned scanout_id;
if (drmmode_crtc->scanout_update_pending)
return;
scanout_id = drmmode_crtc->scanout_id ^ 1;
if (!amdgpu_scanout_do_update(xf86_crtc, scanout_id))
return;
scrn = xf86_crtc->scrn;
drm_queue_entry = amdgpu_drm_queue_alloc(scrn, AMDGPU_DRM_QUEUE_CLIENT_DEFAULT,
AMDGPU_DRM_QUEUE_ID_DEFAULT,
drmmode_crtc,
amdgpu_scanout_flip_handler,
amdgpu_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 AMDGPUBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL)
{
SCREEN_PTR(arg);
......@@ -349,12 +409,16 @@ static void AMDGPUBlockHandler_KMS(BLOCKHANDLER_ARGS_DECL)
(*pScreen->BlockHandler) (BLOCKHANDLER_ARGS);
pScreen->BlockHandler = AMDGPUBlockHandler_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++)
amdgpu_scanout_update(xf86_config->crtc[c]);
for (c = 0; c < xf86_config->num_crtc; c++) {
if (info->tear_free)
amdgpu_scanout_flip(pScreen, info, xf86_config->crtc[c]);
else
amdgpu_scanout_update(xf86_config->crtc[c]);
}
}
if (info->use_glamor)
......@@ -681,6 +745,13 @@ Bool AMDGPUPreInit_KMS(ScrnInfoPtr pScrn, int flags)
}
if (info->use_glamor) {
info->tear_free = xf86ReturnOptValBool(info->Options,
OPTION_TEAR_FREE, FALSE);
if (info->tear_free)
xf86DrvMsg(pScrn->scrnIndex, X_CONFIG,
"TearFree enabled\n");
info->shadow_primary =
xf86ReturnOptValBool(info->Options, OPTION_SHADOW_PRIMARY, FALSE);
......@@ -691,11 +762,12 @@ Bool AMDGPUPreInit_KMS(ScrnInfoPtr pScrn, int flags)
info->allowPageFlip = xf86ReturnOptValBool(info->Options,
OPTION_PAGE_FLIP,
TRUE);
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 {
xf86DrvMsg(pScrn->scrnIndex, X_INFO,
......
......@@ -325,6 +325,10 @@ drmmode_crtc_scanout_destroy(drmmode_ptr drmmode,
scanout->bo = NULL;
}
if (scanout->damage) {
DamageDestroy(scanout->damage);
scanout->damage = NULL;
}
}
void
......@@ -338,12 +342,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]);
}
}
......@@ -527,43 +528,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);
if (drmmode_crtc->scanout.pixmap) {
RegionPtr pRegion;
BoxPtr pBox;
if (!drmmode_crtc->scanout_damage) {
drmmode_crtc->scanout_damage =
DamageCreate(amdgpu_screen_damage_report,
NULL, DamageReportRawRegion,
TRUE, pScreen, NULL);
DamageRegister(&pScreen->GetScreenPixmap(pScreen)->drawable,
drmmode_crtc->scanout_damage);
}
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[i].pixmap) {
RegionPtr pRegion;
BoxPtr pBox;
if (!drmmode_crtc->scanout[i].damage) {
drmmode_crtc->scanout[i].damage =
DamageCreate(amdgpu_screen_damage_report,
NULL, DamageReportRawRegion,
TRUE, pScreen, NULL);
DamageRegister(&pScreen->GetScreenPixmap(pScreen)->drawable,
drmmode_crtc->scanout[i].damage);
}
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);
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);
}
}
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;
}
}
......
......@@ -73,6 +73,7 @@ typedef struct {
struct drmmode_scanout {
struct amdgpu_buffer *bo;
PixmapPtr pixmap;
DamagePtr damage;
unsigned fb_id;
int width, height;
};
......@@ -83,8 +84,8 @@ typedef struct {
int hw_id;
struct amdgpu_buffer *cursor_buffer;
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;
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment