summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAdam Jackson <ajax@redhat.com>2015-07-22 12:14:08 -0400
committerHans de Goede <hdegoede@redhat.com>2016-07-02 09:54:07 +0200
commitfcbafdfd79fd03205bc324e23b6d4e4d4f7180e1 (patch)
tree8ffe0417fed499ff9d691751cfdec82aceff2deb
parent75e660e379d921a53eb7c3bc9c2e412fa58aec02 (diff)
modesetting: Implement a double-buffered shadow mode
Server GPUs often have a VNC feature attached to allow remote console. The controller implementing this feature is usually not very powerful, and we can easily swamp it with work. This is made somewhat worse by damage over-reporting the size of the dirty region, and a whole lot worse by applications (or shells) that update the screen with identical pixel content as was already there. Fix this by double-buffering the shadow fb, using memcmp to identify dirty tiles on each update pass. Since both shadows are in host memory the memcmp is cheap, and worth it given the win in network bandwidth. The tile size is somewhat arbitrarily chosen to be one cacheline wide at 32bpp on Intel Core. By default we enable this behaviour for (a subset of) known server GPUs; the heuristic could use work. Signed-off-by: Adam Jackson <ajax@redhat.com> Reviewed-by: Alex Deucher <alexander.deucher@amd.com> Signed-off-by: Hans de Goede <hdegoede@redhat.com>
-rw-r--r--hw/xfree86/drivers/modesetting/driver.c130
-rw-r--r--hw/xfree86/drivers/modesetting/driver.h1
-rw-r--r--hw/xfree86/drivers/modesetting/drmmode_display.c7
-rw-r--r--hw/xfree86/drivers/modesetting/drmmode_display.h2
4 files changed, 135 insertions, 5 deletions
diff --git a/hw/xfree86/drivers/modesetting/driver.c b/hw/xfree86/drivers/modesetting/driver.c
index 7167800a1..848b75549 100644
--- a/hw/xfree86/drivers/modesetting/driver.c
+++ b/hw/xfree86/drivers/modesetting/driver.c
@@ -130,6 +130,7 @@ static const OptionInfoRec Options[] = {
{OPTION_ACCEL_METHOD, "AccelMethod", OPTV_STRING, {0}, FALSE},
{OPTION_PAGEFLIP, "PageFlip", OPTV_BOOLEAN, {0}, FALSE},
{OPTION_ZAPHOD_HEADS, "ZaphodHeads", OPTV_STRING, {0}, FALSE},
+ {OPTION_DOUBLE_SHADOW, "DoubleShadow", OPTV_BOOLEAN, {0}, FALSE},
{-1, NULL, OPTV_NONE, {0}, FALSE}
};
@@ -732,6 +733,35 @@ 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 (!ms->drmmode.shadow_enable)
+ return FALSE;
+
+ if (!strcmp(v->name, "mgag200") ||
+ !strcmp(v->name, "ast")) /* XXX || rn50 */
+ ret = TRUE;
+
+ drmFreeVersion(v);
+
+ asked = xf86GetOptValBool(ms->drmmode.Options, OPTION_DOUBLE_SHADOW, &ret);
+
+ if (asked)
+ from = X_CONFIG;
+ else
+ from = X_INFO;
+
+ xf86DrvMsg(pScrn->scrnIndex, from,
+ "Double-buffered shadow updates: %s\n", ret ? "on" : "off");
+
+ return ret;
+}
+
#ifndef DRM_CAP_CURSOR_WIDTH
#define DRM_CAP_CURSOR_WIDTH 0x8
#endif
@@ -934,6 +964,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);
}
ms->drmmode.pageflip =
@@ -1011,10 +1043,92 @@ 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);
+ free(prect);
+ } while (0);
+
+ if (use_ms_shadow)
+ ms_shadowUpdate32to24(pScreen, pBuf);
+ else
+ shadowUpdatePacked(pScreen, pBuf);
}
static Bool
@@ -1160,7 +1274,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);
@@ -1188,13 +1301,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;
}
@@ -1650,6 +1768,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/driver.h b/hw/xfree86/drivers/modesetting/driver.h
index 3a604497a..498b9bff9 100644
--- a/hw/xfree86/drivers/modesetting/driver.h
+++ b/hw/xfree86/drivers/modesetting/driver.h
@@ -52,6 +52,7 @@ typedef enum {
OPTION_ACCEL_METHOD,
OPTION_PAGEFLIP,
OPTION_ZAPHOD_HEADS,
+ OPTION_DOUBLE_SHADOW,
} modesettingOpts;
typedef struct
diff --git a/hw/xfree86/drivers/modesetting/drmmode_display.c b/hw/xfree86/drivers/modesetting/drmmode_display.c
index 4f407bb42..83a5a0e03 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.c
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.c
@@ -2027,6 +2027,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 f9a456700..d9b04d7c4 100644
--- a/hw/xfree86/drivers/modesetting/drmmode_display.h
+++ b/hw/xfree86/drivers/modesetting/drmmode_display.h
@@ -66,10 +66,12 @@ typedef struct {
Bool glamor;
Bool shadow_enable;
+ Bool shadow_enable2;
/** Is Option "PageFlip" enabled? */
Bool pageflip;
Bool force_24_32;
void *shadow_fb;
+ void *shadow_fb2;
DevPrivateKeyRec pixmapPrivateKeyRec;