From 82c5e0d932e4cd068396e793e71a3b405dff5c83 Mon Sep 17 00:00:00 2001 From: Fedora X Ninjas Date: Thu, 14 Aug 2008 19:05:38 +1000 Subject: [PATCH] exa: update to latest EXA code from master --- exa/Makefile.am | 1 + exa/exa.c | 58 ++- exa/exa_accel.c | 185 +++++++--- exa/exa_glyphs.c | 897 ++++++++++++++++++++++++++++++++++++++++++++ exa/exa_migration.c | 8 +- exa/exa_priv.h | 67 +++- exa/exa_render.c | 287 +++++++++++++-- exa/exa_unaccel.c | 30 +- hw/xfree86/exa/exa.man.pre | 6 - hw/xfree86/exa/examodule.c | 9 +- 10 files changed, 1406 insertions(+), 142 deletions(-) create mode 100644 exa/exa_glyphs.c diff --git a/exa/Makefile.am b/exa/Makefile.am index e2f7ed3..2b3f1e4 100644 --- a/exa/Makefile.am +++ b/exa/Makefile.am @@ -18,6 +18,7 @@ libexa_la_SOURCES = \ exa.c \ exa.h \ exa_accel.c \ + exa_glyphs.c \ exa_migration.c \ exa_offscreen.c \ exa_render.c \ diff --git a/exa/exa.c b/exa/exa.c index 4bd3d81..c276d9a 100644 --- a/exa/exa.c +++ b/exa/exa.c @@ -35,8 +35,6 @@ #include #include "exa_priv.h" -#include -#include "dixfontstr.h" #include "exa.h" #include "cw.h" @@ -161,7 +159,7 @@ exaPixmapDirty (PixmapPtr pPix, int x1, int y1, int x2, int y2) RegionPtr pDamageReg; RegionRec region; - if (!pExaPixmap) + if (!pExaPixmap || !pExaPixmap->pDamage) return; box.x1 = max(x1, 0); @@ -261,6 +259,21 @@ exaSetFbPitch(ExaScreenPrivPtr pExaScr, ExaPixmapPrivPtr pExaPixmap, pExaScr->info->pixmapPitchAlign); } + +static void +ExaDamageReport(DamagePtr pDamage, RegionPtr pReg, void *pClosure) +{ + PixmapPtr pPixmap = pClosure; + ExaPixmapPriv(pPixmap); + RegionPtr pDamageReg = DamageRegion(pDamage); + + if (pExaPixmap->pendingDamage) { + REGION_UNION(pScreen, pDamageReg, pDamageReg, pReg); + pExaPixmap->pendingDamage = FALSE; + } +} + + /** * exaCreatePixmap() creates a new pixmap. * @@ -321,6 +334,7 @@ exaCreatePixmap(ScreenPtr pScreen, int w, int h, int depth, paddedWidth, NULL); pExaPixmap->score = EXA_PIXMAP_SCORE_PINNED; pExaPixmap->fb_ptr = NULL; + pExaPixmap->pDamage = NULL; } else { pExaPixmap->driverPriv = NULL; /* Scratch pixmaps may have w/h equal to zero, and may not be @@ -345,21 +359,22 @@ exaCreatePixmap(ScreenPtr pScreen, int w, int h, int depth, fbDestroyPixmap(pPixmap); return NULL; } - } - - pExaPixmap->area = NULL; - /* Set up damage tracking */ - pExaPixmap->pDamage = DamageCreate (NULL, NULL, DamageReportNone, TRUE, - pScreen, pPixmap); + /* Set up damage tracking */ + pExaPixmap->pDamage = DamageCreate (ExaDamageReport, NULL, + DamageReportRawRegion, TRUE, + pScreen, pPixmap); - if (pExaPixmap->pDamage == NULL) { - fbDestroyPixmap (pPixmap); - return NULL; - } + if (pExaPixmap->pDamage == NULL) { + fbDestroyPixmap (pPixmap); + return NULL; + } - DamageRegister (&pPixmap->drawable, pExaPixmap->pDamage); - DamageSetReportAfterOp (pExaPixmap->pDamage, TRUE); + DamageRegister (&pPixmap->drawable, pExaPixmap->pDamage); + DamageSetReportAfterOp (pExaPixmap->pDamage, TRUE); + } + + pExaPixmap->area = NULL; /* None of the pixmap bits are valid initially */ REGION_NULL(pScreen, &pExaPixmap->validSys); @@ -737,6 +752,8 @@ exaCloseScreen(int i, ScreenPtr pScreen) PictureScreenPtr ps = GetPictureScreenIfSet(pScreen); #endif + exaGlyphsFini(pScreen); + pScreen->CreateGC = pExaScr->SavedCreateGC; pScreen->CloseScreen = pExaScr->SavedCloseScreen; pScreen->GetImage = pExaScr->SavedGetImage; @@ -750,8 +767,9 @@ exaCloseScreen(int i, ScreenPtr pScreen) #ifdef RENDER if (ps) { ps->Composite = pExaScr->SavedComposite; + ps->Glyphs = pExaScr->SavedGlyphs; ps->Trapezoids = pExaScr->SavedTrapezoids; - ps->AddTraps = pExaScr->SavedAddTraps; + ps->Triangles = pExaScr->SavedTriangles; } #endif @@ -913,14 +931,14 @@ exaDriverInit (ScreenPtr pScreen, pExaScr->SavedComposite = ps->Composite; ps->Composite = exaComposite; + pExaScr->SavedGlyphs = ps->Glyphs; + ps->Glyphs = exaGlyphs; + pExaScr->SavedTriangles = ps->Triangles; ps->Triangles = exaTriangles; pExaScr->SavedTrapezoids = ps->Trapezoids; ps->Trapezoids = exaTrapezoids; - - pExaScr->SavedAddTraps = ps->AddTraps; - ps->AddTraps = ExaCheckAddTraps; } #endif @@ -975,6 +993,8 @@ exaDriverInit (ScreenPtr pScreen, } } + exaGlyphsInit(pScreen); + LogMessage(X_INFO, "EXA(%d): Driver registered support for the following" " operations:\n", pScreen->myNum); assert(pScreenInfo->PrepareSolid != NULL); diff --git a/exa/exa_accel.c b/exa/exa_accel.c index d66dd47..8ac21b8 100644 --- a/exa/exa_accel.c +++ b/exa/exa_accel.c @@ -144,7 +144,6 @@ exaDoPutImage (DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, ExaScreenPriv (pDrawable->pScreen); PixmapPtr pPix = exaGetDrawablePixmap (pDrawable); ExaPixmapPriv(pPix); - ExaMigrationRec pixmaps[1]; RegionPtr pClip; BoxPtr pbox; int nbox; @@ -166,11 +165,16 @@ exaDoPutImage (DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, if (pExaScr->swappedOut) return FALSE; - pixmaps[0].as_dst = TRUE; - pixmaps[0].as_src = FALSE; - pixmaps[0].pPix = pPix; - pixmaps[0].pReg = DamagePendingRegion(pExaPixmap->pDamage); - exaDoMigration (pixmaps, 1, TRUE); + if (pExaPixmap->pDamage) { + ExaMigrationRec pixmaps[1]; + + pixmaps[0].as_dst = TRUE; + pixmaps[0].as_src = FALSE; + pixmaps[0].pPix = pPix; + pixmaps[0].pReg = DamagePendingRegion(pExaPixmap->pDamage); + + exaDoMigration (pixmaps, 1, TRUE); + } pPix = exaGetOffscreenPixmap (pDrawable, &xoff, &yoff); @@ -261,20 +265,16 @@ exaDoShmPutImage(DrawablePtr pDrawable, GCPtr pGC, int depth, if (format == ZPixmap) { - PixmapPtr pPixmap; + PixmapPtr pPixmap = + GetScratchPixmapHeader(pDrawable->pScreen, w, h, depth, + BitsPerPixel(depth), PixmapBytePad(w, depth), + (pointer)data); - pPixmap = GetScratchPixmapHeader(pDrawable->pScreen, w, h, depth, - BitsPerPixel(depth), PixmapBytePad(w, depth), (pointer)data); if (!pPixmap) return FALSE; - if (exaGCReadsDestination(pDrawable, pGC->planemask, pGC->fillStyle, - pGC->alu)) - exaPrepareAccess (pDrawable, EXA_PREPARE_DEST); - else - ExaDoPrepareAccess (pDrawable, EXA_PREPARE_DEST); - fbCopyArea((DrawablePtr)pPixmap, pDrawable, pGC, sx, sy, sw, sh, dx, dy); - exaFinishAccess(pDrawable, EXA_PREPARE_DEST); + pGC->ops->CopyArea(&pPixmap->drawable, pDrawable, pGC, sx, sy, sw, sh, + dx, dy); FreeScratchPixmapHeader(pPixmap); @@ -301,14 +301,19 @@ exaShmPutImage(DrawablePtr pDrawable, GCPtr pGC, int depth, unsigned int format, .x2 = pDrawable->x + dx + sw, .y2 = pDrawable->y + dy + sh }; RegionRec region; int xoff, yoff; - RegionPtr pending_damage = DamagePendingRegion(pExaPixmap->pDamage); + RegionPtr pending_damage = NULL; - REGION_INIT(pScreen, ®ion, &box, 1); + if (pExaPixmap->pDamage) + pending_damage = DamagePendingRegion(pExaPixmap->pDamage); - exaGetDrawableDeltas(pDrawable, pPixmap, &xoff, &yoff); + if (pending_damage) { + REGION_INIT(pScreen, ®ion, &box, 1); - REGION_TRANSLATE(pScreen, ®ion, xoff, yoff); - REGION_UNION(pScreen, pending_damage, pending_damage, ®ion); + exaGetDrawableDeltas(pDrawable, pPixmap, &xoff, &yoff); + + REGION_TRANSLATE(pScreen, ®ion, xoff, yoff); + REGION_UNION(pScreen, pending_damage, pending_damage, ®ion); + } if (!exaDoShmPutImage(pDrawable, pGC, depth, format, w, h, sx, sy, sw, sh, dx, dy, data)) { @@ -316,16 +321,18 @@ exaShmPutImage(DrawablePtr pDrawable, GCPtr pGC, int depth, unsigned int format, pGC->alu)) exaPrepareAccess (pDrawable, EXA_PREPARE_DEST); else - ExaDoPrepareAccess (pDrawable, EXA_PREPARE_DEST); + exaPrepareAccessReg (pDrawable, EXA_PREPARE_DEST, ®ion); fbShmPutImage(pDrawable, pGC, depth, format, w, h, sx, sy, sw, sh, dx, dy, data); exaFinishAccess(pDrawable, EXA_PREPARE_DEST); } - REGION_TRANSLATE(pScreen, ®ion, -xoff, -yoff); - DamageDamageRegion(pDrawable, ®ion); + if (pending_damage) { + REGION_TRANSLATE(pScreen, ®ion, -xoff, -yoff); + DamageDamageRegion(pDrawable, ®ion); - REGION_UNINIT(pScreen, ®ion); + REGION_UNINIT(pScreen, ®ion); + } } ShmFuncs exaShmFuncs = { NULL, exaShmPutImage }; @@ -533,16 +540,36 @@ exaCopyNtoN (DrawablePtr pSrcDrawable, pDstExaPixmap = ExaGetPixmapPriv (pDstPixmap); /* Check whether the accelerator can use this pixmap. - * FIXME: If it cannot, use temporary pixmaps so that the drawing - * happens within limits. + * If the pitch of the pixmaps is out of range, there's nothing + * we can do but fall back to software rendering. */ - if (pSrcExaPixmap->accel_blocked || pDstExaPixmap->accel_blocked) - { + if (pSrcExaPixmap->accel_blocked & EXA_RANGE_PITCH || + pDstExaPixmap->accel_blocked & EXA_RANGE_PITCH) goto fallback; - } else { - exaDoMigration (pixmaps, 2, TRUE); + + /* If the width or the height of either of the pixmaps + * is out of range, check whether the boxes are actually out of the + * addressable range as well. If they aren't, we can still do + * the copying in hardware. + */ + if (pSrcExaPixmap->accel_blocked || pDstExaPixmap->accel_blocked) { + int i; + + for (i = 0; i < nbox; i++) { + /* src */ + if ((pbox[i].x2 + dx + src_off_x) >= pExaScr->info->maxX || + (pbox[i].y2 + dy + src_off_y) >= pExaScr->info->maxY) + goto fallback; + + /* dst */ + if ((pbox[i].x2 + dst_off_x) >= pExaScr->info->maxX || + (pbox[i].y2 + dst_off_y) >= pExaScr->info->maxY) + goto fallback; + } } + exaDoMigration (pixmaps, 2, TRUE); + /* Mixed directions must be handled specially if the card is lame */ if ((pExaScr->info->flags & EXA_TWO_BITBLT_DIRECTIONS) && reverse != upsidedown) { @@ -952,16 +979,23 @@ exaImageGlyphBlt (DrawablePtr pDrawable, FbBits depthMask; PixmapPtr pPixmap = exaGetDrawablePixmap(pDrawable); ExaPixmapPriv(pPixmap); - RegionPtr pending_damage = DamagePendingRegion(pExaPixmap->pDamage); - BoxRec extents = *REGION_EXTENTS(pScreen, pending_damage); + RegionPtr pending_damage = NULL; + BoxRec extents; int xoff, yoff; - if (extents.x1 >= extents.x2 || extents.y1 >= extents.y2) - return; + if (pExaPixmap->pDamage) + pending_damage = DamagePendingRegion(pExaPixmap->pDamage); - depthMask = FbFullMask(pDrawable->depth); + if (pending_damage) { + extents = *REGION_EXTENTS(pScreen, pending_damage); + + if (extents.x1 >= extents.x2 || extents.y1 >= extents.y2) + return; - if ((pGC->planemask & depthMask) != depthMask) + depthMask = FbFullMask(pDrawable->depth); + } + + if (!pending_damage || (pGC->planemask & depthMask) != depthMask) { ExaCheckImageGlyphBlt(pDrawable, pGC, x, y, nglyph, ppciInit, pglyphBase); return; @@ -1195,6 +1229,7 @@ exaFillRegionTiled (DrawablePtr pDrawable, int nbox = REGION_NUM_RECTS (pRegion); BoxPtr pBox = REGION_RECTS (pRegion); Bool ret = FALSE; + int i; tileWidth = pTile->drawable.width; tileHeight = pTile->drawable.height; @@ -1217,14 +1252,11 @@ exaFillRegionTiled (DrawablePtr pDrawable, pixmaps[1].pPix = pTile; pixmaps[1].pReg = NULL; - exaGetDrawableDeltas(pDrawable, pPixmap, &xoff, &yoff); - REGION_TRANSLATE(pScreen, pRegion, xoff, yoff); - pExaPixmap = ExaGetPixmapPriv (pPixmap); if (pExaPixmap->accel_blocked || pTileExaPixmap->accel_blocked) { - goto out; + return FALSE; } else { exaDoMigration (pixmaps, 2, TRUE); } @@ -1232,24 +1264,33 @@ exaFillRegionTiled (DrawablePtr pDrawable, pPixmap = exaGetOffscreenPixmap (pDrawable, &xoff, &yoff); if (!pPixmap || !exaPixmapIsOffscreen(pTile)) - goto out; + return FALSE; if ((*pExaScr->info->PrepareCopy) (pTile, pPixmap, 1, 1, alu, planemask)) { - while (nbox--) + if (xoff || yoff) + REGION_TRANSLATE(pScreen, pRegion, xoff, yoff); + + for (i = 0; i < nbox; i++) { - int height = pBox->y2 - pBox->y1; - int dstY = pBox->y1; + int height = pBox[i].y2 - pBox[i].y1; + int dstY = pBox[i].y1; int tileY; + if (alu == GXcopy) + height = min(height, tileHeight); + modulus(dstY - yoff - pDrawable->y - pPatOrg->y, tileHeight, tileY); while (height > 0) { - int width = pBox->x2 - pBox->x1; - int dstX = pBox->x1; + int width = pBox[i].x2 - pBox[i].x1; + int dstX = pBox[i].x1; int tileX; int h = tileHeight - tileY; + if (alu == GXcopy) + width = min(width, tileWidth); + if (h > height) h = height; height -= h; @@ -1271,17 +1312,57 @@ exaFillRegionTiled (DrawablePtr pDrawable, dstY += h; tileY = 0; } - pBox++; } (*pExaScr->info->DoneCopy) (pPixmap); + + /* With GXcopy, we only need to do the basic algorithm up to the tile + * size; then, we can just keep doubling the destination in each + * direction until it fills the box. This way, the number of copy + * operations is O(log(rx)) + O(log(ry)) instead of O(rx * ry), where + * rx/ry is the ratio between box and tile width/height. This can make + * a big difference if each driver copy incurs a significant constant + * overhead. + */ + if (alu != GXcopy) + ret = TRUE; + else if ((*pExaScr->info->PrepareCopy) (pPixmap, pPixmap, 1, 1, alu, + planemask)) { + for (i = 0; i < nbox; i++) + { + int dstX = pBox[i].x1 + tileWidth; + int dstY = pBox[i].y1 + tileHeight; + int width = min(pBox[i].x2 - dstX, tileWidth); + int height = min(pBox[i].y2 - pBox[i].y1, tileHeight); + + while (dstX < pBox[i].x2) { + (*pExaScr->info->Copy) (pPixmap, pBox[i].x1, pBox[i].y1, + dstX, pBox[i].y1, width, height); + dstX += width; + width = min(pBox[i].x2 - dstX, width * 2); + } + + width = pBox[i].x2 - pBox[i].x1; + height = min(pBox[i].y2 - dstY, tileHeight); + + while (dstY < pBox[i].y2) { + (*pExaScr->info->Copy) (pPixmap, pBox[i].x1, pBox[i].y1, + pBox[i].x1, dstY, width, height); + dstY += height; + height = min(pBox[i].y2 - dstY, height * 2); + } + } + + (*pExaScr->info->DoneCopy) (pPixmap); + + ret = TRUE; + } + exaMarkSync(pDrawable->pScreen); - ret = TRUE; + if (xoff || yoff) + REGION_TRANSLATE(pScreen, pRegion, -xoff, -yoff); } -out: - REGION_TRANSLATE(pScreen, pRegion, -xoff, -yoff); - return ret; } diff --git a/exa/exa_glyphs.c b/exa/exa_glyphs.c new file mode 100644 index 0000000..ff665d5 --- /dev/null +++ b/exa/exa_glyphs.c @@ -0,0 +1,897 @@ +/* + * Copyright © 2008 Red Hat, Inc. + * Partly based on code Copyright © 2000 SuSE, Inc. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of Red Hat not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. Red Hat makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * Red Hat DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL Red Hat + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Permission to use, copy, modify, distribute, and sell this software and its + * documentation for any purpose is hereby granted without fee, provided that + * the above copyright notice appear in all copies and that both that + * copyright notice and this permission notice appear in supporting + * documentation, and that the name of SuSE not be used in advertising or + * publicity pertaining to distribution of the software without specific, + * written prior permission. SuSE makes no representations about the + * suitability of this software for any purpose. It is provided "as is" + * without express or implied warranty. + * + * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE + * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION + * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Author: Owen Taylor + * Based on code by: Keith Packard + */ + +#ifdef HAVE_DIX_CONFIG_H +#include +#endif + +#include + +#include "exa_priv.h" + +#include "mipict.h" + +#if DEBUG_GLYPH_CACHE +#define DBG_GLYPH_CACHE(a) ErrorF a +#else +#define DBG_GLYPH_CACHE(a) +#endif + +/* Width of the pixmaps we use for the caches; this should be less than + * max texture size of the driver; this may need to actually come from + * the driver. + */ +#define CACHE_PICTURE_WIDTH 1024 + +/* Maximum number of glyphs we buffer on the stack before flushing + * rendering to the mask or destination surface. + */ +#define GLYPH_BUFFER_SIZE 256 + +typedef struct { + PicturePtr source; + ExaCompositeRectRec rects[GLYPH_BUFFER_SIZE]; + int count; +} ExaGlyphBuffer, *ExaGlyphBufferPtr; + +typedef enum { + ExaGlyphSuccess, /* Glyph added to render buffer */ + ExaGlyphFail, /* out of memory, etc */ + ExaGlyphNeedFlush, /* would evict a glyph already in the buffer */ +} ExaGlyphCacheResult; + +void +exaGlyphsInit(ScreenPtr pScreen) +{ + ExaScreenPriv(pScreen); + int i = 0; + + memset(pExaScr->glyphCaches, 0, sizeof(pExaScr->glyphCaches)); + + pExaScr->glyphCaches[i].format = PICT_a8; + pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 16; + i++; + pExaScr->glyphCaches[i].format = PICT_a8; + pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 32; + i++; + pExaScr->glyphCaches[i].format = PICT_a8r8g8b8; + pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 16; + i++; + pExaScr->glyphCaches[i].format = PICT_a8r8g8b8; + pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 32; + i++; + + assert(i == EXA_NUM_GLYPH_CACHES); + + for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { + pExaScr->glyphCaches[i].columns = CACHE_PICTURE_WIDTH / pExaScr->glyphCaches[i].glyphWidth; + pExaScr->glyphCaches[i].size = 256; + pExaScr->glyphCaches[i].hashSize = 557; + } +} + +static void +exaUnrealizeGlyphCaches(ScreenPtr pScreen, + unsigned int format) +{ + ExaScreenPriv(pScreen); + int i; + + for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { + ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; + + if (cache->format != format) + continue; + + if (cache->picture) { + FreePicture ((pointer) cache->picture, (XID) 0); + cache->picture = NULL; + } + + if (cache->hashEntries) { + xfree(cache->hashEntries); + cache->hashEntries = NULL; + } + + if (cache->glyphs) { + xfree(cache->glyphs); + cache->glyphs = NULL; + } + cache->glyphCount = 0; + } +} + +/* All caches for a single format share a single pixmap for glyph storage, + * allowing mixing glyphs of different sizes without paying a penalty + * for switching between source pixmaps. (Note that for a size of font + * right at the border between two sizes, we might be switching for almost + * every glyph.) + * + * This function allocates the storage pixmap, and then fills in the + * rest of the allocated structures for all caches with the given format. + */ +static Bool +exaRealizeGlyphCaches(ScreenPtr pScreen, + unsigned int format) +{ + ExaScreenPriv(pScreen); + + int depth = PIXMAN_FORMAT_DEPTH(format); + PictFormatPtr pPictFormat; + PixmapPtr pPixmap; + PicturePtr pPicture; + int height; + int i; + int error; + + pPictFormat = PictureMatchFormat(pScreen, depth, format); + if (!pPictFormat) + return FALSE; + + /* Compute the total vertical size needed for the format */ + + height = 0; + for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { + ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; + int rows; + + if (cache->format != format) + continue; + + cache->yOffset = height; + + rows = (cache->size + cache->columns - 1) / cache->columns; + height += rows * cache->glyphHeight; + } + + /* Now allocate the pixmap and picture */ + + pPixmap = (*pScreen->CreatePixmap) (pScreen, + CACHE_PICTURE_WIDTH, + height, depth, 0); + if (!pPixmap) + return FALSE; + + pPicture = CreatePicture(0, &pPixmap->drawable, pPictFormat, + 0, 0, serverClient, &error); + + (*pScreen->DestroyPixmap) (pPixmap); /* picture holds a refcount */ + + if (!pPicture) + return FALSE; + + /* And store the picture in all the caches for the format */ + + for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { + ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; + int j; + + if (cache->format != format) + continue; + + cache->picture = pPicture; + cache->picture->refcnt++; + cache->hashEntries = xalloc(sizeof(int) * cache->hashSize); + cache->glyphs = xalloc(sizeof(ExaCachedGlyphRec) * cache->size); + cache->glyphCount = 0; + + if (!cache->hashEntries || !cache->glyphs) + goto bail; + + for (j = 0; j < cache->hashSize; j++) + cache->hashEntries[j] = -1; + + cache->evictionPosition = rand() % cache->size; + } + + /* Each cache references the picture individually */ + FreePicture ((pointer) pPicture, (XID) 0); + return TRUE; + +bail: + exaUnrealizeGlyphCaches(pScreen, format); + return FALSE; +} + +void +exaGlyphsFini (ScreenPtr pScreen) +{ + ExaScreenPriv(pScreen); + int i; + + for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { + ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; + + if (cache->picture) + exaUnrealizeGlyphCaches(pScreen, cache->format); + } +} + +static int +exaGlyphCacheHashLookup(ExaGlyphCachePtr cache, + GlyphPtr pGlyph) +{ + int slot; + + slot = (*(CARD32 *) pGlyph->sha1) % cache->hashSize; + + while (TRUE) { /* hash table can never be full */ + int entryPos = cache->hashEntries[slot]; + if (entryPos == -1) + return -1; + + if (memcmp(pGlyph->sha1, cache->glyphs[entryPos].sha1, sizeof(pGlyph->sha1)) == 0){ + return entryPos; + } + + slot--; + if (slot < 0) + slot = cache->hashSize - 1; + } +} + +static void +exaGlyphCacheHashInsert(ExaGlyphCachePtr cache, + GlyphPtr pGlyph, + int pos) +{ + int slot; + + memcpy(cache->glyphs[pos].sha1, pGlyph->sha1, sizeof(pGlyph->sha1)); + + slot = (*(CARD32 *) pGlyph->sha1) % cache->hashSize; + + while (TRUE) { /* hash table can never be full */ + if (cache->hashEntries[slot] == -1) { + cache->hashEntries[slot] = pos; + return; + } + + slot--; + if (slot < 0) + slot = cache->hashSize - 1; + } +} + +static void +exaGlyphCacheHashRemove(ExaGlyphCachePtr cache, + int pos) +{ + int slot; + int emptiedSlot = -1; + + slot = (*(CARD32 *) cache->glyphs[pos].sha1) % cache->hashSize; + + while (TRUE) { /* hash table can never be full */ + int entryPos = cache->hashEntries[slot]; + + if (entryPos == -1) + return; + + if (entryPos == pos) { + cache->hashEntries[slot] = -1; + emptiedSlot = slot; + } else if (emptiedSlot != -1) { + /* See if we can move this entry into the emptied slot, we can't + * do that if if entry would have hashed between the current position + * and the emptied slot. (taking wrapping into account). Bad positions + * are: + * + * | XXXXXXXXXX | + * i j + * + * |XXX XXXX| + * j i + * + * i - slot, j - emptiedSlot + * + * (Knuth 6.4R) + */ + + int entrySlot = (*(CARD32 *) cache->glyphs[entryPos].sha1) % cache->hashSize; + + if (!((entrySlot >= slot && entrySlot < emptiedSlot) || + (emptiedSlot < slot && (entrySlot < emptiedSlot || entrySlot >= slot)))) + { + cache->hashEntries[emptiedSlot] = entryPos; + cache->hashEntries[slot] = -1; + emptiedSlot = slot; + } + } + + slot--; + if (slot < 0) + slot = cache->hashSize - 1; + } +} + +#define CACHE_X(pos) (((pos) % cache->columns) * cache->glyphWidth) +#define CACHE_Y(pos) (cache->yOffset + ((pos) / cache->columns) * cache->glyphHeight) + +/* The most efficient thing to way to upload the glyph to the screen + * is to use the UploadToScreen() driver hook; this allows us to + * pipeline glyph uploads and to avoid creating offscreen pixmaps for + * glyphs that we'll never use again. + */ +static Bool +exaGlyphCacheUploadGlyph(ScreenPtr pScreen, + ExaGlyphCachePtr cache, + int pos, + GlyphPtr pGlyph) +{ + ExaScreenPriv(pScreen); + PicturePtr pGlyphPicture = GlyphPicture(pGlyph)[pScreen->myNum]; + PixmapPtr pGlyphPixmap = (PixmapPtr)pGlyphPicture->pDrawable; + ExaPixmapPriv(pGlyphPixmap); + PixmapPtr pCachePixmap = (PixmapPtr)cache->picture->pDrawable; + ExaMigrationRec pixmaps[1]; + int cacheXoff, cacheYoff; + + if (!pExaScr->info->UploadToScreen || pExaScr->swappedOut || pExaPixmap->accel_blocked) + return FALSE; + + /* If the glyph pixmap is already uploaded, no point in doing + * things this way */ + if (exaPixmapIsOffscreen(pGlyphPixmap)) + return FALSE; + + /* UploadToScreen only works if bpp match */ + if (pGlyphPixmap->drawable.bitsPerPixel != pCachePixmap->drawable.bitsPerPixel) + return FALSE; + + /* cache pixmap must be offscreen. */ + pixmaps[0].as_dst = TRUE; + pixmaps[0].as_src = FALSE; + pixmaps[0].pPix = pCachePixmap; + pixmaps[0].pReg = NULL; + exaDoMigration (pixmaps, 1, TRUE); + + pCachePixmap = exaGetOffscreenPixmap ((DrawablePtr)pCachePixmap, &cacheXoff, &cacheYoff); + if (!pCachePixmap) + return FALSE; + + if (!pExaScr->info->UploadToScreen(pCachePixmap, + CACHE_X(pos) + cacheXoff, + CACHE_Y(pos) + cacheYoff, + pGlyph->info.width, + pGlyph->info.height, + (char *)pExaPixmap->sys_ptr, + pExaPixmap->sys_pitch)) + return FALSE; + + exaPixmapDirty (pCachePixmap, + CACHE_X(pos) + cacheXoff, + CACHE_Y(pos) + cacheYoff, + CACHE_X(pos) + cacheXoff + pGlyph->info.width, + CACHE_Y(pos) + cacheYoff + pGlyph->info.height); + + return TRUE; +} + +static ExaGlyphCacheResult +exaGlyphCacheBufferGlyph(ScreenPtr pScreen, + ExaGlyphCachePtr cache, + ExaGlyphBufferPtr buffer, + GlyphPtr pGlyph, + int xGlyph, + int yGlyph) +{ + ExaCompositeRectPtr rect; + int pos; + + if (buffer->source && buffer->source != cache->picture) + return ExaGlyphNeedFlush; + + if (!cache->picture) { + if (!exaRealizeGlyphCaches(pScreen, cache->format)) + return ExaGlyphFail; + } + + DBG_GLYPH_CACHE(("(%d,%d,%s): buffering glyph %lx\n", + cache->glyphWidth, cache->glyphHeight, cache->format == PICT_a8 ? "A" : "ARGB", + (long)*(CARD32 *) pGlyph->sha1)); + + pos = exaGlyphCacheHashLookup(cache, pGlyph); + if (pos != -1) { + DBG_GLYPH_CACHE((" found existing glyph at %d\n", pos)); + } else { + if (cache->glyphCount < cache->size) { + /* Space remaining; we fill from the start */ + pos = cache->glyphCount; + cache->glyphCount++; + DBG_GLYPH_CACHE((" storing glyph in free space at %d\n", pos)); + + exaGlyphCacheHashInsert(cache, pGlyph, pos); + + } else { + /* Need to evict an entry. We have to see if any glyphs + * already in the output buffer were at this position in + * the cache + */ + + pos = cache->evictionPosition; + DBG_GLYPH_CACHE((" evicting glyph at %d\n", pos)); + if (buffer->count) { + int x, y; + int i; + + x = CACHE_X(pos); + y = CACHE_Y(pos); + + for (i = 0; i < buffer->count; i++) { + if (buffer->rects[i].xSrc == x && buffer->rects[i].ySrc == y) { + DBG_GLYPH_CACHE((" must flush buffer\n")); + return ExaGlyphNeedFlush; + } + } + } + + /* OK, we're all set, swap in the new glyph */ + exaGlyphCacheHashRemove(cache, pos); + exaGlyphCacheHashInsert(cache, pGlyph, pos); + + /* And pick a new eviction position */ + cache->evictionPosition = rand() % cache->size; + } + + /* Now actually upload the glyph into the cache picture; if + * we can't do it with UploadToScreen (because the glyph is + * offscreen, etc), we fall back to CompositePicture. + */ + if (!exaGlyphCacheUploadGlyph(pScreen, cache, pos, pGlyph)) { + CompositePicture (PictOpSrc, + GlyphPicture(pGlyph)[pScreen->myNum], + None, + cache->picture, + 0, 0, + 0, 0, + CACHE_X(pos), + CACHE_Y(pos), + pGlyph->info.width, + pGlyph->info.height); + } + + } + + + buffer->source = cache->picture; + + rect = &buffer->rects[buffer->count]; + rect->xSrc = CACHE_X(pos); + rect->ySrc = CACHE_Y(pos); + rect->xDst = xGlyph - pGlyph->info.x; + rect->yDst = yGlyph - pGlyph->info.y; + rect->width = pGlyph->info.width; + rect->height = pGlyph->info.height; + + buffer->count++; + + return ExaGlyphSuccess; +} + +#undef CACHE_X +#undef CACHE_Y + +static ExaGlyphCacheResult +exaBufferGlyph(ScreenPtr pScreen, + ExaGlyphBufferPtr buffer, + GlyphPtr pGlyph, + int xGlyph, + int yGlyph) +{ + ExaScreenPriv(pScreen); + unsigned int format = (GlyphPicture(pGlyph)[pScreen->myNum])->format; + int width = pGlyph->info.width; + int height = pGlyph->info.height; + ExaCompositeRectPtr rect; + PicturePtr source; + int i; + + if (buffer->count == GLYPH_BUFFER_SIZE) + return ExaGlyphNeedFlush; + + if (PICT_FORMAT_BPP(format) == 1) + format = PICT_a8; + + for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) { + ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i]; + + if (format == cache->format && + width <= cache->glyphWidth && + height <= cache->glyphHeight) { + ExaGlyphCacheResult result = exaGlyphCacheBufferGlyph(pScreen, &pExaScr->glyphCaches[i], + buffer, + pGlyph, xGlyph, yGlyph); + switch (result) { + case ExaGlyphFail: + break; + case ExaGlyphSuccess: + case ExaGlyphNeedFlush: + return result; + } + } + } + + /* Couldn't find the glyph in the cache, use the glyph picture directly */ + + source = GlyphPicture(pGlyph)[pScreen->myNum]; + if (buffer->source && buffer->source != source) + return ExaGlyphNeedFlush; + + buffer->source = source; + + rect = &buffer->rects[buffer->count]; + rect->xSrc = 0; + rect->ySrc = 0; + rect->xDst = xGlyph - pGlyph->info.x; + rect->yDst = yGlyph - pGlyph->info.y; + rect->width = pGlyph->info.width; + rect->height = pGlyph->info.height; + + buffer->count++; + + return ExaGlyphSuccess; +} + +static void +exaGlyphsToMask(PicturePtr pMask, + ExaGlyphBufferPtr buffer) +{ + exaCompositeRects(PictOpAdd, buffer->source, pMask, + buffer->count, buffer->rects); + + buffer->count = 0; + buffer->source = NULL; +} + +static void +exaGlyphsToDst(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + ExaGlyphBufferPtr buffer, + INT16 xSrc, + INT16 ySrc, + INT16 xDst, + INT16 yDst) +{ + int i; + + for (i = 0; i < buffer->count; i++) { + ExaCompositeRectPtr rect = &buffer->rects[i]; + + CompositePicture (op, + pSrc, + buffer->source, + pDst, + xSrc + rect->xDst - xDst, + ySrc + rect->yDst - yDst, + rect->xSrc, + rect->ySrc, + rect->xDst, + rect->yDst, + rect->width, + rect->height); + } + + buffer->count = 0; + buffer->source = NULL; +} + +/* Cut and paste from render/glyph.c - probably should export it instead */ +static void +GlyphExtents (int nlist, + GlyphListPtr list, + GlyphPtr *glyphs, + BoxPtr extents) +{ + int x1, x2, y1, y2; + int n; + GlyphPtr glyph; + int x, y; + + x = 0; + y = 0; + extents->x1 = MAXSHORT; + extents->x2 = MINSHORT; + extents->y1 = MAXSHORT; + extents->y2 = MINSHORT; + while (nlist--) + { + x += list->xOff; + y += list->yOff; + n = list->len; + list++; + while (n--) + { + glyph = *glyphs++; + x1 = x - glyph->info.x; + if (x1 < MINSHORT) + x1 = MINSHORT; + y1 = y - glyph->info.y; + if (y1 < MINSHORT) + y1 = MINSHORT; + x2 = x1 + glyph->info.width; + if (x2 > MAXSHORT) + x2 = MAXSHORT; + y2 = y1 + glyph->info.height; + if (y2 > MAXSHORT) + y2 = MAXSHORT; + if (x1 < extents->x1) + extents->x1 = x1; + if (x2 > extents->x2) + extents->x2 = x2; + if (y1 < extents->y1) + extents->y1 = y1; + if (y2 > extents->y2) + extents->y2 = y2; + x += glyph->info.xOff; + y += glyph->info.yOff; + } + } +} + +/** + * Returns TRUE if the glyphs in the lists intersect. Only checks based on + * bounding box, which appears to be good enough to catch most cases at least. + */ +static Bool +exaGlyphsIntersect(int nlist, GlyphListPtr list, GlyphPtr *glyphs) +{ + int x1, x2, y1, y2; + int n; + GlyphPtr glyph; + int x, y; + BoxRec extents; + Bool first = TRUE; + + x = 0; + y = 0; + while (nlist--) { + x += list->xOff; + y += list->yOff; + n = list->len; + list++; + while (n--) { + glyph = *glyphs++; + + if (glyph->info.width == 0 || glyph->info.height == 0) { + x += glyph->info.xOff; + y += glyph->info.yOff; + continue; + } + + x1 = x - glyph->info.x; + if (x1 < MINSHORT) + x1 = MINSHORT; + y1 = y - glyph->info.y; + if (y1 < MINSHORT) + y1 = MINSHORT; + x2 = x1 + glyph->info.width; + if (x2 > MAXSHORT) + x2 = MAXSHORT; + y2 = y1 + glyph->info.height; + if (y2 > MAXSHORT) + y2 = MAXSHORT; + + if (first) { + extents.x1 = x1; + extents.y1 = y1; + extents.x2 = x2; + extents.y2 = y2; + first = FALSE; + } else { + if (x1 < extents.x2 && x2 > extents.x1 && + y1 < extents.y2 && y2 > extents.y1) + { + return TRUE; + } + + if (x1 < extents.x1) + extents.x1 = x1; + if (x2 > extents.x2) + extents.x2 = x2; + if (y1 < extents.y1) + extents.y1 = y1; + if (y2 > extents.y2) + extents.y2 = y2; + } + x += glyph->info.xOff; + y += glyph->info.yOff; + } + } + + return FALSE; +} + +#define NeedsComponent(f) (PICT_FORMAT_A(f) != 0 && PICT_FORMAT_RGB(f) != 0) + +void +exaGlyphs (CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + PictFormatPtr maskFormat, + INT16 xSrc, + INT16 ySrc, + int nlist, + GlyphListPtr list, + GlyphPtr *glyphs) +{ + PicturePtr pPicture; + PixmapPtr pMaskPixmap = 0; + PicturePtr pMask; + ScreenPtr pScreen = pDst->pDrawable->pScreen; + int width = 0, height = 0; + int x, y; + int xDst = list->xOff, yDst = list->yOff; + int n; + GlyphPtr glyph; + int error; + BoxRec extents = {0, 0, 0, 0}; + CARD32 component_alpha; + ExaGlyphBuffer buffer; + + /* If we don't have a mask format but all the glyphs have the same format + * and don't intersect, use the glyph format as mask format for the full + * benefits of the glyph cache. + */ + if (!maskFormat) { + Bool sameFormat = TRUE; + int i; + + maskFormat = list[0].format; + + for (i = 0; i < nlist; i++) { + if (maskFormat->format != list[i].format->format) { + sameFormat = FALSE; + break; + } + } + + if (!sameFormat || (maskFormat->depth != 1 && + exaGlyphsIntersect(nlist, list, glyphs))) { + maskFormat = NULL; + } + } + + if (maskFormat) + { + GCPtr pGC; + xRectangle rect; + + GlyphExtents (nlist, list, glyphs, &extents); + + if (extents.x2 <= extents.x1 || extents.y2 <= extents.y1) + return; + width = extents.x2 - extents.x1; + height = extents.y2 - extents.y1; + + if (maskFormat->depth == 1) { + PictFormatPtr a8Format = PictureMatchFormat (pScreen, 8, PICT_a8); + + if (a8Format) + maskFormat = a8Format; + } + + pMaskPixmap = (*pScreen->CreatePixmap) (pScreen, width, height, + maskFormat->depth, + CREATE_PIXMAP_USAGE_SCRATCH); + if (!pMaskPixmap) + return; + component_alpha = NeedsComponent(maskFormat->format); + pMask = CreatePicture (0, &pMaskPixmap->drawable, + maskFormat, CPComponentAlpha, &component_alpha, + serverClient, &error); + if (!pMask) + { + (*pScreen->DestroyPixmap) (pMaskPixmap); + return; + } + pGC = GetScratchGC (pMaskPixmap->drawable.depth, pScreen); + ValidateGC (&pMaskPixmap->drawable, pGC); + rect.x = 0; + rect.y = 0; + rect.width = width; + rect.height = height; + (*pGC->ops->PolyFillRect) (&pMaskPixmap->drawable, pGC, 1, &rect); + FreeScratchGC (pGC); + x = -extents.x1; + y = -extents.y1; + } + else + { + pMask = pDst; + x = 0; + y = 0; + } + buffer.count = 0; + buffer.source = NULL; + while (nlist--) + { + x += list->xOff; + y += list->yOff; + n = list->len; + while (n--) + { + glyph = *glyphs++; + pPicture = GlyphPicture (glyph)[pScreen->myNum]; + + if (glyph->info.width > 0 && glyph->info.height > 0 && + exaBufferGlyph(pScreen, &buffer, glyph, x, y) == ExaGlyphNeedFlush) + { + if (maskFormat) + exaGlyphsToMask(pMask, &buffer); + else + exaGlyphsToDst(op, pSrc, pDst, &buffer, + xSrc, ySrc, xDst, yDst); + + exaBufferGlyph(pScreen, &buffer, glyph, x, y); + } + + x += glyph->info.xOff; + y += glyph->info.yOff; + } + list++; + } + + if (maskFormat) + exaGlyphsToMask(pMask, &buffer); + else + exaGlyphsToDst(op, pSrc, pDst, &buffer, + xSrc, ySrc, xDst, yDst); + + if (maskFormat) + { + x = extents.x1; + y = extents.y1; + CompositePicture (op, + pSrc, + pMask, + pDst, + xSrc + x - xDst, + ySrc + y - yDst, + 0, 0, + x, y, + width, height); + FreePicture ((pointer) pMask, (XID) 0); + (*pScreen->DestroyPixmap) (pMaskPixmap); + } +} diff --git a/exa/exa_migration.c b/exa/exa_migration.c index 5f22474..25ea73d 100644 --- a/exa/exa_migration.c +++ b/exa/exa_migration.c @@ -33,8 +33,6 @@ #include #include "exa_priv.h" -#include -#include "dixfontstr.h" #include "exa.h" #include "cw.h" @@ -301,6 +299,9 @@ exaDoMoveInPixmap (ExaMigrationPtr migrate) ExaScreenPriv (pScreen); ExaPixmapPriv (pPixmap); + if (migrate->as_dst) + pExaPixmap->pendingDamage = TRUE; + /* If we're VT-switched away, no touching card memory allowed. */ if (pExaScr->swappedOut) return; @@ -369,6 +370,9 @@ exaDoMoveOutPixmap (ExaMigrationPtr migrate) PixmapPtr pPixmap = migrate->pPix; ExaPixmapPriv (pPixmap); + if (migrate->as_dst) + pExaPixmap->pendingDamage = TRUE; + if (!pExaPixmap->area || exaPixmapIsPinned(pPixmap)) return; diff --git a/exa/exa_priv.h b/exa/exa_priv.h index 21e6f0b..9ec2a56 100644 --- a/exa/exa_priv.h +++ b/exa/exa_priv.h @@ -61,6 +61,7 @@ #define DEBUG_MIGRATE 0 #define DEBUG_PIXMAP 0 #define DEBUG_OFFSCREEN 0 +#define DEBUG_GLYPH_CACHE 0 #if DEBUG_TRACE_FALL #define EXA_FALLBACK(x) \ @@ -95,6 +96,38 @@ enum ExaMigrationHeuristic { ExaMigrationSmart }; +typedef struct { + unsigned char sha1[20]; +} ExaCachedGlyphRec, *ExaCachedGlyphPtr; + +typedef struct { + /* The identity of the cache, statically configured at initialization */ + unsigned int format; + int glyphWidth; + int glyphHeight; + + int size; /* Size of cache; eventually this should be dynamically determined */ + + /* Hash table mapping from glyph sha1 to position in the glyph; we use + * open addressing with a hash table size determined based on size and large + * enough so that we always have a good amount of free space, so we can + * use linear probing. (Linear probing is preferrable to double hashing + * here because it allows us to easily remove entries.) + */ + int *hashEntries; + int hashSize; + + ExaCachedGlyphPtr glyphs; + int glyphCount; /* Current number of glyphs */ + + PicturePtr picture; /* Where the glyphs of the cache are stored */ + int yOffset; /* y location within the picture where the cache starts */ + int columns; /* Number of columns the glyphs are layed out in */ + int evictionPosition; /* Next random position to evict a glyph */ +} ExaGlyphCacheRec, *ExaGlyphCachePtr; + +#define EXA_NUM_GLYPH_CACHES 4 + typedef void (*EnableDisableFBAccessProcPtr)(int, Bool); typedef struct { ExaDriverPtr info; @@ -114,7 +147,6 @@ typedef struct { TrianglesProcPtr SavedTriangles; GlyphsProcPtr SavedGlyphs; TrapezoidsProcPtr SavedTrapezoids; - AddTrapsProcPtr SavedAddTraps; #endif Bool swappedOut; @@ -123,6 +155,8 @@ typedef struct { unsigned disableFbCount; Bool optimize_migration; unsigned offScreenCounter; + + ExaGlyphCacheRec glyphCaches[EXA_NUM_GLYPH_CACHES]; } ExaScreenPrivRec, *ExaScreenPrivPtr; /* @@ -192,6 +226,7 @@ typedef struct { * location. */ DamagePtr pDamage; + Bool pendingDamage; /** * The valid regions mark the valid bits (at least, as they're derived from * damage, which may be overreported) of a pixmap's system and FB copies. @@ -210,6 +245,15 @@ typedef struct _ExaMigrationRec { RegionPtr pReg; } ExaMigrationRec, *ExaMigrationPtr; +typedef struct { + INT16 xSrc; + INT16 ySrc; + INT16 xDst; + INT16 yDst; + INT16 width; + INT16 height; +} ExaCompositeRectRec, *ExaCompositeRectPtr; + /** * exaDDXDriverInit must be implemented by the DDX using EXA, and is the place * to set EXA options or hook in screen functions to handle using EXA as the AA. @@ -294,13 +338,6 @@ ExaCheckGetSpans (DrawablePtr pDrawable, int nspans, char *pdstStart); -void -ExaCheckAddTraps (PicturePtr pPicture, - INT16 x_off, - INT16 y_off, - int ntrap, - xTrap *traps); - /* exa_accel.c */ static _X_INLINE Bool @@ -431,6 +468,13 @@ exaComposite(CARD8 op, CARD16 height); void +exaCompositeRects(CARD8 op, + PicturePtr Src, + PicturePtr pDst, + int nrect, + ExaCompositeRectPtr rects); + +void exaTrapezoids (CARD8 op, PicturePtr pSrc, PicturePtr pDst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int ntrap, xTrapezoid *traps); @@ -440,6 +484,13 @@ exaTriangles (CARD8 op, PicturePtr pSrc, PicturePtr pDst, PictFormatPtr maskFormat, INT16 xSrc, INT16 ySrc, int ntri, xTriangle *tris); +/* exa_glyph.c */ +void +exaGlyphsInit(ScreenPtr pScreen); + +void +exaGlyphsFini (ScreenPtr pScreen); + void exaGlyphs (CARD8 op, PicturePtr pSrc, diff --git a/exa/exa_render.c b/exa/exa_render.c index 1d7b897..7042285 100644 --- a/exa/exa_render.c +++ b/exa/exa_render.c @@ -332,6 +332,235 @@ exaTryDriverSolidFill(PicturePtr pSrc, } static int +exaTryDriverCompositeRects(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + int nrect, + ExaCompositeRectPtr rects) +{ + ExaScreenPriv (pDst->pDrawable->pScreen); + int src_off_x, src_off_y, dst_off_x, dst_off_y; + PixmapPtr pSrcPix, pDstPix; + ExaPixmapPrivPtr pSrcExaPix, pDstExaPix; + struct _Pixmap scratch; + ExaMigrationRec pixmaps[2]; + + if (!pExaScr->info->PrepareComposite) + return -1; + + pSrcPix = exaGetDrawablePixmap(pSrc->pDrawable); + pSrcExaPix = ExaGetPixmapPriv(pSrcPix); + + pDstPix = exaGetDrawablePixmap(pDst->pDrawable); + pDstExaPix = ExaGetPixmapPriv(pDstPix); + + /* Check whether the accelerator can use these pixmaps. + * FIXME: If it cannot, use temporary pixmaps so that the drawing + * happens within limits. + */ + if (pSrcExaPix->accel_blocked || + pDstExaPix->accel_blocked) + { + return -1; + } + + if (pExaScr->info->CheckComposite && + !(*pExaScr->info->CheckComposite) (op, pSrc, NULL, pDst)) + { + return -1; + } + + exaGetDrawableDeltas (pDst->pDrawable, pDstPix, &dst_off_x, &dst_off_y); + + pixmaps[0].as_dst = TRUE; + pixmaps[0].as_src = exaOpReadsDestination(op); + pixmaps[0].pPix = pDstPix; + pixmaps[0].pReg = NULL; + pixmaps[1].as_dst = FALSE; + pixmaps[1].as_src = TRUE; + pixmaps[1].pPix = pSrcPix; + pixmaps[1].pReg = NULL; + exaDoMigration(pixmaps, 2, TRUE); + + pSrcPix = exaGetOffscreenPixmap (pSrc->pDrawable, &src_off_x, &src_off_y); + if (!exaPixmapIsOffscreen(pDstPix)) + return 0; + + if (!pSrcPix && pExaScr->info->UploadToScratch) + { + pSrcPix = exaGetDrawablePixmap (pSrc->pDrawable); + if ((*pExaScr->info->UploadToScratch) (pSrcPix, &scratch)) + pSrcPix = &scratch; + } + + if (!pSrcPix) + return 0; + + if (!(*pExaScr->info->PrepareComposite) (op, pSrc, NULL, pDst, pSrcPix, + NULL, pDstPix)) + return -1; + + while (nrect--) + { + INT16 xDst = rects->xDst + pDst->pDrawable->x; + INT16 yDst = rects->yDst + pDst->pDrawable->y; + INT16 xSrc = rects->xSrc + pSrc->pDrawable->x; + INT16 ySrc = rects->ySrc + pSrc->pDrawable->y; + + RegionRec region; + BoxPtr pbox; + int nbox; + + if (!miComputeCompositeRegion (®ion, pSrc, NULL, pDst, + xSrc, ySrc, 0, 0, xDst, yDst, + rects->width, rects->height)) + goto next_rect; + + REGION_TRANSLATE(pScreen, ®ion, dst_off_x, dst_off_y); + + nbox = REGION_NUM_RECTS(®ion); + pbox = REGION_RECTS(®ion); + + xSrc = xSrc + src_off_x - xDst - dst_off_x; + ySrc = ySrc + src_off_y - yDst - dst_off_y; + + while (nbox--) + { + (*pExaScr->info->Composite) (pDstPix, + pbox->x1 + xSrc, + pbox->y1 + ySrc, + 0, 0, + pbox->x1, + pbox->y1, + pbox->x2 - pbox->x1, + pbox->y2 - pbox->y1); + pbox++; + } + + next_rect: + REGION_UNINIT(pDst->pDrawable->pScreen, ®ion); + + rects++; + } + + (*pExaScr->info->DoneComposite) (pDstPix); + exaMarkSync(pDst->pDrawable->pScreen); + + return 1; +} + +/** + * Copy a number of rectangles from source to destination in a single + * operation. This is specialized for building a glyph mask: we don'y + * have a mask argument because we don't need it for that, and we + * don't have he special-case fallbacks found in exaComposite() - if the + * driver can support it, we use the driver functionality, otherwise we + * fallback straight to software. + */ +void +exaCompositeRects(CARD8 op, + PicturePtr pSrc, + PicturePtr pDst, + int nrect, + ExaCompositeRectPtr rects) +{ + PixmapPtr pPixmap = exaGetDrawablePixmap(pDst->pDrawable); + ExaPixmapPriv(pPixmap); + RegionRec region; + int n; + ExaCompositeRectPtr r; + + if (pExaPixmap->pDamage) { + int xoff, yoff; + int x1 = MAXSHORT; + int y1 = MAXSHORT; + int x2 = MINSHORT; + int y2 = MINSHORT; + RegionPtr pending_damage; + BoxRec box; + + /* We have to manage the damage ourselves, since CompositeRects isn't + * something in the screen that can be managed by the damage extension, + * and EXA depends on damage to track what needs to be migrated between + * offscreen and onscreen. + */ + + /* Compute the overall extents of the composited region - we're making + * the assumption here that we are compositing a bunch of glyphs that + * cluster closely together and damaging each glyph individually would + * be a loss compared to damaging the bounding box. + */ + n = nrect; + r = rects; + while (n--) { + int rect_x2 = r->xDst + r->width; + int rect_y2 = r->yDst + r->width; + + if (r->xDst < x1) x1 = r->xDst; + if (r->xDst < y1) y1 = r->xDst; + if (rect_x2 > x2) x2 = rect_x2; + if (rect_y2 > y2) y2 = rect_y2; + + r++; + } + + if (x2 <= x1 && y2 <= y1) + return; + + box.x1 = x1; + box.x2 = x2 < MAXSHORT ? x2 : MAXSHORT; + box.y1 = y1; + box.y2 = y2 < MAXSHORT ? y2 : MAXSHORT; + + /* The pixmap migration code relies on pendingDamage indicating + * the bounds of the current rendering, so we need to force + * the actual damage into that region before we do anything, and + * (see use of DamagePendingRegion in exaCopyDirty) + */ + + REGION_INIT(pScreen, ®ion, &box, 1); + + exaGetDrawableDeltas(pDst->pDrawable, pPixmap, &xoff, &yoff); + + REGION_TRANSLATE(pScreen, ®ion, xoff, yoff); + pending_damage = DamagePendingRegion(pExaPixmap->pDamage); + REGION_UNION(pScreen, pending_damage, pending_damage, ®ion); + REGION_TRANSLATE(pScreen, ®ion, -xoff, -yoff); + } + + /************************************************************/ + + ValidatePicture (pSrc); + ValidatePicture (pDst); + + if (exaTryDriverCompositeRects(op, pSrc, pDst, nrect, rects) != 1) { + n = nrect; + r = rects; + while (n--) { + ExaCheckComposite (op, pSrc, NULL, pDst, + r->xSrc, r->ySrc, + 0, 0, + r->xDst, r->yDst, + r->width, r->height); + r++; + } + } + + /************************************************************/ + + if (pExaPixmap->pDamage) { + /* Now we have to flush the damage out from pendingDamage => damage + * Calling DamageDamageRegion has that effect. (We could pass + * in an empty region here, but we pass in the same region we + * use above; the effect is the same.) + */ + + DamageDamageRegion(pDst->pDrawable, ®ion); + REGION_UNINIT(pScreen, ®ion); + } +} + +static int exaTryDriverComposite(CARD8 op, PicturePtr pSrc, PicturePtr pMask, @@ -842,23 +1071,26 @@ exaTrapezoids (CARD8 op, PicturePtr pSrc, PicturePtr pDst, DrawablePtr pDraw = pDst->pDrawable; PixmapPtr pixmap = exaGetDrawablePixmap (pDraw); ExaPixmapPriv (pixmap); - RegionRec migration; - RegionPtr pending_damage = DamagePendingRegion(pExaPixmap->pDamage); - int xoff, yoff; - exaGetDrawableDeltas(pDraw, pixmap, &xoff, &yoff); + if (pExaPixmap->pDamage) { + RegionRec migration; + RegionPtr pending_damage = DamagePendingRegion(pExaPixmap->pDamage); + int xoff, yoff; + + exaGetDrawableDeltas(pDraw, pixmap, &xoff, &yoff); - xoff += pDraw->x; - yoff += pDraw->y; + xoff += pDraw->x; + yoff += pDraw->y; - bounds.x1 += xoff; - bounds.y1 += yoff; - bounds.x2 += xoff; - bounds.y2 += yoff; + bounds.x1 += xoff; + bounds.y1 += yoff; + bounds.x2 += xoff; + bounds.y2 += yoff; - REGION_INIT(pScreen, &migration, &bounds, 1); - REGION_UNION(pScreen, pending_damage, pending_damage, &migration); - REGION_UNINIT(pScreen, &migration); + REGION_INIT(pScreen, &migration, &bounds, 1); + REGION_UNION(pScreen, pending_damage, pending_damage, &migration); + REGION_UNINIT(pScreen, &migration); + } exaPrepareAccess(pDraw, EXA_PREPARE_DEST); @@ -945,23 +1177,26 @@ exaTriangles (CARD8 op, PicturePtr pSrc, PicturePtr pDst, DrawablePtr pDraw = pDst->pDrawable; PixmapPtr pixmap = exaGetDrawablePixmap (pDraw); ExaPixmapPriv (pixmap); - RegionRec migration; - RegionPtr pending_damage = DamagePendingRegion(pExaPixmap->pDamage); - int xoff, yoff; - exaGetDrawableDeltas(pDraw, pixmap, &xoff, &yoff); + if (pExaPixmap->pDamage) { + RegionRec migration; + RegionPtr pending_damage = DamagePendingRegion(pExaPixmap->pDamage); + int xoff, yoff; + + exaGetDrawableDeltas(pDraw, pixmap, &xoff, &yoff); - xoff += pDraw->x; - yoff += pDraw->y; + xoff += pDraw->x; + yoff += pDraw->y; - bounds.x1 += xoff; - bounds.y1 += yoff; - bounds.x2 += xoff; - bounds.y2 += yoff; + bounds.x1 += xoff; + bounds.y1 += yoff; + bounds.x2 += xoff; + bounds.y2 += yoff; - REGION_INIT(pScreen, &migration, &bounds, 1); - REGION_UNION(pScreen, pending_damage, pending_damage, &migration); - REGION_UNINIT(pScreen, &migration); + REGION_INIT(pScreen, &migration, &bounds, 1); + REGION_UNION(pScreen, pending_damage, pending_damage, &migration); + REGION_UNINIT(pScreen, &migration); + } exaPrepareAccess(pDraw, EXA_PREPARE_DEST); (*ps->AddTriangles) (pDst, 0, 0, ntri, tris); diff --git a/exa/exa_unaccel.c b/exa/exa_unaccel.c index d7bd06c..d5d6a30 100644 --- a/exa/exa_unaccel.c +++ b/exa/exa_unaccel.c @@ -97,12 +97,15 @@ ExaCheckPutImage (DrawablePtr pDrawable, GCPtr pGC, int depth, int x, int y, int w, int h, int leftPad, int format, char *bits) { + ExaPixmapPriv(exaGetDrawablePixmap(pDrawable)); + EXA_FALLBACK(("to %p (%c)\n", pDrawable, exaDrawableLocation(pDrawable))); if (exaGCReadsDestination(pDrawable, pGC->planemask, pGC->fillStyle, pGC->alu)) exaPrepareAccess (pDrawable, EXA_PREPARE_DEST); else - ExaDoPrepareAccess (pDrawable, EXA_PREPARE_DEST); + exaPrepareAccessReg (pDrawable, EXA_PREPARE_DEST, pExaPixmap->pDamage ? + DamagePendingRegion(pExaPixmap->pDamage) : NULL); fbPutImage (pDrawable, pGC, depth, x, y, w, h, leftPad, format, bits); exaFinishAccess (pDrawable, EXA_PREPARE_DEST); } @@ -350,20 +353,6 @@ ExaCheckComposite (CARD8 op, REGION_UNINIT(pScreen, ®ion); } -void -ExaCheckAddTraps (PicturePtr pPicture, - INT16 x_off, - INT16 y_off, - int ntrap, - xTrap *traps) -{ - EXA_FALLBACK(("to pict %p (%c)\n", - exaDrawableLocation(pPicture->pDrawable))); - exaPrepareAccess(pPicture->pDrawable, EXA_PREPARE_DEST); - fbAddTraps (pPicture, x_off, y_off, ntrap, traps); - exaFinishAccess(pPicture->pDrawable, EXA_PREPARE_DEST); -} - /** * Gets the 0,0 pixel of a pixmap. Used for doing solid fills of tiled pixmaps * that happen to be 1x1. Pixmap must be at least 8bpp. @@ -373,23 +362,22 @@ ExaCheckAddTraps (PicturePtr pPicture, CARD32 exaGetPixmapFirstPixel (PixmapPtr pPixmap) { - ExaScreenPriv(pPixmap->drawable.pScreen); CARD32 pixel; void *fb; Bool need_finish = FALSE; BoxRec box; RegionRec migration; ExaPixmapPriv (pPixmap); - Bool sys_valid = !miPointInRegion(&pExaPixmap->validSys, 0, 0, &box); - Bool damaged = miPointInRegion(DamageRegion(pExaPixmap->pDamage), 0, 0, - &box); + Bool sys_valid = pExaPixmap->pDamage && + !miPointInRegion(&pExaPixmap->validSys, 0, 0, &box); + Bool damaged = pExaPixmap->pDamage && + miPointInRegion(DamageRegion(pExaPixmap->pDamage), 0, 0, &box); Bool offscreen = exaPixmapIsOffscreen(pPixmap); fb = pExaPixmap->sys_ptr; /* Try to avoid framebuffer readbacks */ - if (pExaScr->info->CreatePixmap || - (!offscreen && !sys_valid && !damaged) || + if ((!offscreen && !sys_valid && !damaged) || (offscreen && (!sys_valid || damaged))) { box.x1 = 0; diff --git a/hw/xfree86/exa/exa.man.pre b/hw/xfree86/exa/exa.man.pre index 14859bc..31e1cfe 100644 --- a/hw/xfree86/exa/exa.man.pre +++ b/hw/xfree86/exa/exa.man.pre @@ -31,12 +31,6 @@ Disables acceleration of downloading of pixmap data from the framebuffer. Not usable with drivers which rely on DownloadFromScreen succeeding. Default: No. .TP -.BI "Option \*qEXAOptimizeMigration\*q \*q" boolean \*q -Enables an additional optimization for migration of destination pixmaps. This -may improve performance in some cases (e.g. when switching virtual desktops with -no compositing manager) but causes corruption in others (e.g. when starting -compiz). Default: No. -.TP .BI "Option \*qMigrationHeuristic\*q \*q" anystr \*q Chooses an alternate pixmap migration heuristic, for debugging purposes. The default is intended to be the best performing one for general use, though others diff --git a/hw/xfree86/exa/examodule.c b/hw/xfree86/exa/examodule.c index e18da0a..4a8d8f2 100644 --- a/hw/xfree86/exa/examodule.c +++ b/hw/xfree86/exa/examodule.c @@ -145,7 +145,7 @@ exaDDXDriverInit(ScreenPtr pScreen) pExaScr->optimize_migration = xf86ReturnOptValBool(pScreenPriv->options, EXAOPT_OPTIMIZE_MIGRATION, - FALSE); + TRUE); } if (xf86ReturnOptValBool(pScreenPriv->options, @@ -179,13 +179,6 @@ exaDDXDriverInit(ScreenPtr pScreen) } -/*ARGSUSED*/ -static const OptionInfoRec * -EXAAvailableOptions(void *unused) -{ - return (EXAOptions); -} - static XF86ModuleVersionInfo exaVersRec = { "exa", -- 1.5.5.1