Blob Blame History Raw
From 46ac5c2c39a476c44fae207ca9745a8a60c7ea72 Mon Sep 17 00:00:00 2001
From: Fedora X Ninjas <x@fedoraproject.org>
Date: Wed, 19 Aug 2015 13:45:43 -0400
Subject: [PATCH] kms: implement a double-buffered shadow mode

Signed-off-by: Fedora X Ninjas <x@fedoraproject.org>
---
 hw/xfree86/drivers/modesetting/driver.c          | 127 ++++++++++++++++++++++-
 hw/xfree86/drivers/modesetting/drmmode_display.c |   7 ++
 hw/xfree86/drivers/modesetting/drmmode_display.h |   2 +
 3 files changed, 131 insertions(+), 5 deletions(-)

diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c
index 1215cb3..b0c2f17 100644
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -124,6 +124,7 @@ typedef enum {
     OPTION_DEVICE_PATH,
     OPTION_SHADOW_FB,
     OPTION_ACCEL_METHOD,
+    OPTION_DOUBLE_SHADOW,
 } modesettingOpts;
 
 static const OptionInfoRec Options[] = {
@@ -131,6 +132,7 @@ static const OptionInfoRec Options[] = {
     {OPTION_DEVICE_PATH, "kmsdev", OPTV_STRING, {0}, FALSE},
     {OPTION_SHADOW_FB, "ShadowFB", OPTV_BOOLEAN, {0}, FALSE},
     {OPTION_ACCEL_METHOD, "AccelMethod", OPTV_STRING, {0}, FALSE},
+    {OPTION_DOUBLE_SHADOW, "DoubleShadow", OPTV_BOOLEAN, {0}, FALSE},
     {-1, NULL, OPTV_NONE, {0}, FALSE}
 };
 
@@ -615,6 +617,32 @@ try_enable_glamor(ScrnInfoPtr pScrn)
 #endif
 }
 
+static Bool
+msShouldDoubleShadow(ScrnInfoPtr pScrn, modesettingPtr ms)
+{
+    Bool ret = FALSE, asked;
+    int from;
+    drmVersionPtr v = drmGetVersion(ms->fd);
+
+    if (!strcmp(v->name, "mgag200") ||
+	!strcmp(v->name, "ast")) /* XXX || rn50 */
+	ret = TRUE;
+
+    drmFreeVersion(v);
+
+    asked = xf86GetOptValBool(ms->Options, OPTION_DOUBLE_SHADOW, &ret);
+
+    if (asked)
+	from = X_CONFIG;
+    else
+	from = X_INFO;
+
+    xf86DrvMsg(pScrn->scrnIndex, from,
+	       "Double-buffered shadow updates: %s", ret ? "on" : "off");
+
+    return ret;
+}
+
 #ifndef DRM_CAP_CURSOR_WIDTH
 #define DRM_CAP_CURSOR_WIDTH 0x8
 #endif
@@ -814,6 +842,8 @@ PreInit(ScrnInfoPtr pScrn, int flags)
                    prefer_shadow ? "YES" : "NO",
 		   ms->drmmode.force_24_32 ? "FORCE" :
                    ms->drmmode.shadow_enable ? "YES" : "NO");
+
+	ms->drmmode.shadow_enable2 = msShouldDoubleShadow(pScrn, ms);
     }
 
     if (drmmode_pre_init(pScrn, &ms->drmmode, pScrn->bitsPerPixel / 8) == FALSE) {
@@ -872,10 +902,91 @@ msShadowWindow(ScreenPtr screen, CARD32 row, CARD32 offset, int mode,
     return ((uint8_t *) ms->drmmode.front_bo.dumb->ptr + row * stride + offset);
 }
 
+/* somewhat arbitrary tile size, in pixels */
+#define TILE 16
+
+static int
+msUpdateIntersect(modesettingPtr ms, shadowBufPtr pBuf, BoxPtr box,
+		  xRectangle *prect)
+{
+    int i, dirty = 0, stride = pBuf->pPixmap->devKind, cpp = ms->drmmode.cpp;
+    int width = (box->x2 - box->x1) * cpp;
+    unsigned char *old, *new;
+
+    old = ms->drmmode.shadow_fb2;
+    old += (box->y1 * stride) + (box->x1 * cpp);
+    new = ms->drmmode.shadow_fb;
+    new += (box->y1 * stride) + (box->x1 * cpp);
+
+    for (i = box->y2 - box->y1 - 1; i >= 0; i--) {
+	unsigned char *o = old + i * stride,
+		      *n = new + i * stride;
+	if (memcmp(o, n, width) != 0) {
+	    dirty = 1;
+	    memcpy(o, n, width);
+	}
+    }
+
+    if (dirty) {
+	prect->x = box->x1;
+	prect->y = box->y1;
+	prect->width = box->x2 - box->x1;
+	prect->height = box->y2 - box->y1;
+    }
+
+    return dirty;
+}
+
 static void
 msUpdatePacked(ScreenPtr pScreen, shadowBufPtr pBuf)
 {
-    shadowUpdatePacked(pScreen, pBuf);
+    ScrnInfoPtr pScrn = xf86ScreenToScrn(pScreen);
+    modesettingPtr ms = modesettingPTR(pScrn);
+    Bool use_ms_shadow = ms->drmmode.force_24_32 && pScrn->bitsPerPixel == 32;
+
+    if (ms->drmmode.shadow_enable2 && ms->drmmode.shadow_fb2) do {
+	RegionPtr damage = DamageRegion(pBuf->pDamage), tiles;
+	BoxPtr extents = RegionExtents(damage);
+	xRectangle *prect;
+	int nrects;
+	int i, j, tx1, tx2, ty1, ty2;
+
+	tx1 = extents->x1 / TILE;
+	tx2 = (extents->x2 + TILE - 1) / TILE;
+	ty1 = extents->y1 / TILE;
+	ty2 = (extents->y2 + TILE - 1) / TILE;
+
+	nrects = (tx2 - tx1) * (ty2 - ty1);
+	if (!(prect = calloc(nrects, sizeof(xRectangle))))
+	    break;
+
+	nrects = 0;
+	for (j = ty2 - 1; j >= ty1; j--) {
+	    for (i = tx2 - 1; i >= tx1; i--) {
+		BoxRec box;
+
+		box.x1 = max(i * TILE, extents->x1);
+		box.y1 = max(j * TILE, extents->y1);
+		box.x2 = min((i+1) * TILE, extents->x2);
+		box.y2 = min((j+1) * TILE, extents->y2);
+
+		if (RegionContainsRect(damage, &box) != rgnOUT) {
+		    if (msUpdateIntersect(ms, pBuf, &box, prect + nrects)) {
+			nrects++;
+		    }
+		}
+	    }
+	}
+
+	tiles = RegionFromRects(nrects, prect, CT_NONE);
+	RegionIntersect(damage, damage, tiles);
+	RegionDestroy(tiles);
+    } while (0);
+
+    if (use_ms_shadow)
+	ms_shadowUpdate32to24(pScreen, pBuf);
+    else
+	shadowUpdatePacked(pScreen, pBuf);
 }
 
 static Bool
@@ -887,7 +998,6 @@ CreateScreenResources(ScreenPtr pScreen)
     Bool ret;
     void *pixels = NULL;
     int err;
-    Bool use_ms_shadow = ms->drmmode.force_24_32 && pScrn->bitsPerPixel == 32;
 
     pScreen->CreateScreenResources = ms->createScreenResources;
     ret = pScreen->CreateScreenResources(pScreen);
@@ -915,13 +1025,18 @@ CreateScreenResources(ScreenPtr pScreen)
     if (ms->drmmode.shadow_enable)
         pixels = ms->drmmode.shadow_fb;
 
+    if (ms->drmmode.shadow_enable2) {
+	ms->drmmode.shadow_fb2 = calloc(1, pScrn->displayWidth * pScrn->virtualY * ((pScrn->bitsPerPixel + 7) >> 3));
+	if (!ms->drmmode.shadow_fb2)
+	    ms->drmmode.shadow_enable2 = FALSE;
+    }
+
     if (!pScreen->ModifyPixmapHeader(rootPixmap, -1, -1, -1, -1, -1, pixels))
         FatalError("Couldn't adjust screen pixmap\n");
 
     if (ms->drmmode.shadow_enable) {
-        if (!shadowAdd(pScreen, rootPixmap,
-		       use_ms_shadow ? ms_shadowUpdate32to24 : msUpdatePacked,
-                       msShadowWindow, 0, 0))
+        if (!shadowAdd(pScreen, rootPixmap, msUpdatePacked, msShadowWindow,
+		       0, 0))
             return FALSE;
     }
 
@@ -1238,6 +1353,8 @@ CloseScreen(ScreenPtr pScreen)
         shadowRemove(pScreen, pScreen->GetScreenPixmap(pScreen));
         free(ms->drmmode.shadow_fb);
         ms->drmmode.shadow_fb = NULL;
+	free(ms->drmmode.shadow_fb2);
+	ms->drmmode.shadow_fb2 = NULL;
     }
     drmmode_uevent_fini(pScrn, &ms->drmmode);
 
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c
index 079fceb..66f0bbe 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
@@ -1382,6 +1382,13 @@ drmmode_xf86crtc_resize(ScrnInfoPtr scrn, int width, int height)
         drmmode->shadow_fb = new_pixels;
     }
 
+    if (drmmode->shadow_enable2) {
+        uint32_t size = scrn->displayWidth * scrn->virtualY * cpp;
+        void *fb2 = calloc(1, size);
+	free(drmmode->shadow_fb2);
+	drmmode->shadow_fb2 = fb2;
+    }
+
     screen->ModifyPixmapHeader(ppix, width, height, -1, -1,
 			       scrn->displayWidth * cpp, new_pixels);
 
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.h b/hw/xfree86/drivers/modesetting/drmmode_display.h
index 927efec..3a7d222 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.h
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.h
@@ -65,8 +65,10 @@ typedef struct {
 
     Bool glamor;
     Bool shadow_enable;
+    Bool shadow_enable2;
     Bool force_24_32;
     void *shadow_fb;
+    void *shadow_fb2;
 
     /**
      * A screen-sized pixmap when we're doing triple-buffered DRI2
-- 
2.5.0