xserver: Branch 'master' - 8 commits
Michel Daenzer
daenzer at kemper.freedesktop.org
Mon Apr 28 12:18:14 PDT 2008
exa/Makefile.am | 1
exa/exa.c | 9
exa/exa_glyphs.c | 897 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
exa/exa_priv.h | 58 +++
exa/exa_render.c | 222 +++++++++++++
5 files changed, 1187 insertions(+)
New commits:
commit 8349732a6720652bfbad7874a952be73a0e8e77b
Author: Michel Dänzer <michel at tungstengraphics.com>
Date: Mon Apr 28 21:09:35 2008 +0200
EXA: Try to accelerate non-antialiased text via the glyph cache as well.
Treat 1 bit glyphs and masks as PICT_a8 in the glyph cache. We're not able to
accelerate them otherwise.
diff --git a/exa/exa_glyphs.c b/exa/exa_glyphs.c
index 08ec097..ff665d5 100644
--- a/exa/exa_glyphs.c
+++ b/exa/exa_glyphs.c
@@ -374,6 +374,10 @@ exaGlyphCacheUploadGlyph(ScreenPtr pScreen,
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;
@@ -524,6 +528,9 @@ exaBufferGlyph(ScreenPtr pScreen,
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];
@@ -796,6 +803,14 @@ exaGlyphs (CARD8 op,
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);
commit a65d530040bb561ba88c5d8c71633a7c0bf11e89
Author: Michel Dänzer <michel at tungstengraphics.com>
Date: Mon Apr 28 21:03:12 2008 +0200
EXA: Accumulate glyphs whenever possible, for full benefits of the glyph cache.
diff --git a/exa/exa_glyphs.c b/exa/exa_glyphs.c
index b618365..08ec097 100644
--- a/exa/exa_glyphs.c
+++ b/exa/exa_glyphs.c
@@ -662,6 +662,79 @@ GlyphExtents (int nlist,
}
}
+/**
+ * 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
@@ -689,6 +762,29 @@ exaGlyphs (CARD8 op,
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;
commit e7eaac59c424a205dd106fc7d70734ff4b390f28
Author: Michel Dänzer <michel at tungstengraphics.com>
Date: Mon Apr 28 21:00:55 2008 +0200
EXA: Glyph cache upload tweaks.
Track damage after using UploadToScreen directly.
Don't waste any effort on empty glyphs.
diff --git a/exa/exa_glyphs.c b/exa/exa_glyphs.c
index 95ff4d8..b618365 100644
--- a/exa/exa_glyphs.c
+++ b/exa/exa_glyphs.c
@@ -394,6 +394,12 @@ exaGlyphCacheUploadGlyph(ScreenPtr pScreen,
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;
}
@@ -737,7 +743,8 @@ exaGlyphs (CARD8 op,
glyph = *glyphs++;
pPicture = GlyphPicture (glyph)[pScreen->myNum];
- if (exaBufferGlyph(pScreen, &buffer, glyph, x, y) == ExaGlyphNeedFlush)
+ if (glyph->info.width > 0 && glyph->info.height > 0 &&
+ exaBufferGlyph(pScreen, &buffer, glyph, x, y) == ExaGlyphNeedFlush)
{
if (maskFormat)
exaGlyphsToMask(pMask, &buffer);
commit cc08c06665ffe29ad44d023d75d0f86e5338875d
Author: Owen Taylor <otaylor at huygens.home.fishsoup.net>
Date: Mon Apr 28 21:00:55 2008 +0200
EXA: Use UploadToScreen() for uploads to glyph cache
When possible, use UploadToScreen() rather than CompositePicture()
to upload glyphs onto the glyph cache pixmap. This avoids allocating
offscreen memory for each glyph making management of offscreen
areas much more efficient.
diff --git a/exa/exa_glyphs.c b/exa/exa_glyphs.c
index 27ecd4a..95ff4d8 100644
--- a/exa/exa_glyphs.c
+++ b/exa/exa_glyphs.c
@@ -347,6 +347,56 @@ exaGlyphCacheHashRemove(ExaGlyphCachePtr cache,
#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;
+
+ /* 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;
+
+ return TRUE;
+}
+
static ExaGlyphCacheResult
exaGlyphCacheBufferGlyph(ScreenPtr pScreen,
ExaGlyphCachePtr cache,
@@ -413,18 +463,23 @@ exaGlyphCacheBufferGlyph(ScreenPtr pScreen,
cache->evictionPosition = rand() % cache->size;
}
- /* Now actually upload the glyph into the cache picture */
+ /* 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);
+ }
- 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);
}
commit 13fd2256300b61d88b840952d838f834523f5dd7
Author: Owen Taylor <otaylor at huygens.home.fishsoup.net>
Date: Mon Apr 28 21:00:55 2008 +0200
EXA: Clean up debug messages
diff --git a/exa/exa_glyphs.c b/exa/exa_glyphs.c
index 851e439..27ecd4a 100644
--- a/exa/exa_glyphs.c
+++ b/exa/exa_glyphs.c
@@ -260,11 +260,9 @@ exaGlyphCacheHashLookup(ExaGlyphCachePtr cache,
return -1;
if (memcmp(pGlyph->sha1, cache->glyphs[entryPos].sha1, sizeof(pGlyph->sha1)) == 0){
- DBG_GLYPH_CACHE((" found entry at %d\n", slot));
return entryPos;
}
- DBG_GLYPH_CACHE((" lookup linear probe bumpalong\n"));
slot--;
if (slot < 0)
slot = cache->hashSize - 1;
@@ -284,7 +282,6 @@ exaGlyphCacheHashInsert(ExaGlyphCachePtr cache,
while (TRUE) { /* hash table can never be full */
if (cache->hashEntries[slot] == -1) {
- DBG_GLYPH_CACHE((" inserting entry at %d\n", slot));
cache->hashEntries[slot] = pos;
return;
}
diff --git a/exa/exa_priv.h b/exa/exa_priv.h
index 8a17f65..f3b72ae 100644
--- a/exa/exa_priv.h
+++ b/exa/exa_priv.h
@@ -61,7 +61,7 @@
#define DEBUG_MIGRATE 0
#define DEBUG_PIXMAP 0
#define DEBUG_OFFSCREEN 0
-#define DEBUG_GLYPH_CACHE 1
+#define DEBUG_GLYPH_CACHE 0
#if DEBUG_TRACE_FALL
#define EXA_FALLBACK(x) \
commit fcb5949928f1c27f67f40c094c3c673786574422
Author: Owen Taylor <otaylor at huygens.home.fishsoup.net>
Date: Mon Apr 28 21:00:54 2008 +0200
EXA: Fix overlapping glyphs in glyph cache
Allocate each cache at a different vertical position in the
per-format pixmap. Fix width/height confusion when choosing
the cache for a glyph.
diff --git a/exa/exa_glyphs.c b/exa/exa_glyphs.c
index 55fdb01..851e439 100644
--- a/exa/exa_glyphs.c
+++ b/exa/exa_glyphs.c
@@ -173,12 +173,13 @@ exaRealizeGlyphCaches(ScreenPtr pScreen,
for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i];
int rows;
-
+
if (cache->format != format)
continue;
- rows = (cache->size + cache->columns - 1) / cache->columns;
+ cache->yOffset = height;
+ rows = (cache->size + cache->columns - 1) / cache->columns;
height += rows * cache->glyphHeight;
}
@@ -346,6 +347,9 @@ exaGlyphCacheHashRemove(ExaGlyphCachePtr cache,
}
}
+#define CACHE_X(pos) (((pos) % cache->columns) * cache->glyphWidth)
+#define CACHE_Y(pos) (cache->yOffset + ((pos) / cache->columns) * cache->glyphHeight)
+
static ExaGlyphCacheResult
exaGlyphCacheBufferGlyph(ScreenPtr pScreen,
ExaGlyphCachePtr cache,
@@ -393,8 +397,8 @@ exaGlyphCacheBufferGlyph(ScreenPtr pScreen,
int x, y;
int i;
- x = (pos % cache->columns) * cache->glyphWidth;
- y = (pos / cache->columns) * cache->glyphHeight;
+ 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) {
@@ -420,8 +424,8 @@ exaGlyphCacheBufferGlyph(ScreenPtr pScreen,
cache->picture,
0, 0,
0, 0,
- (pos % cache->columns) * cache->glyphWidth,
- (pos / cache->columns) * cache->glyphHeight,
+ CACHE_X(pos),
+ CACHE_Y(pos),
pGlyph->info.width,
pGlyph->info.height);
}
@@ -430,8 +434,8 @@ exaGlyphCacheBufferGlyph(ScreenPtr pScreen,
buffer->source = cache->picture;
rect = &buffer->rects[buffer->count];
- rect->xSrc = (pos % cache->columns) * cache->glyphWidth;
- rect->ySrc = (pos / cache->columns) * cache->glyphHeight;
+ 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;
@@ -442,6 +446,9 @@ exaGlyphCacheBufferGlyph(ScreenPtr pScreen,
return ExaGlyphSuccess;
}
+#undef CACHE_X
+#undef CACHE_Y
+
static ExaGlyphCacheResult
exaBufferGlyph(ScreenPtr pScreen,
ExaGlyphBufferPtr buffer,
@@ -452,7 +459,7 @@ exaBufferGlyph(ScreenPtr pScreen,
ExaScreenPriv(pScreen);
unsigned int format = (GlyphPicture(pGlyph)[pScreen->myNum])->format;
int width = pGlyph->info.width;
- int height = pGlyph->info.width;
+ int height = pGlyph->info.height;
ExaCompositeRectPtr rect;
PicturePtr source;
int i;
diff --git a/exa/exa_priv.h b/exa/exa_priv.h
index 0d5d0f5..8a17f65 100644
--- a/exa/exa_priv.h
+++ b/exa/exa_priv.h
@@ -61,7 +61,7 @@
#define DEBUG_MIGRATE 0
#define DEBUG_PIXMAP 0
#define DEBUG_OFFSCREEN 0
-#define DEBUG_GLYPH_CACHE 0
+#define DEBUG_GLYPH_CACHE 1
#if DEBUG_TRACE_FALL
#define EXA_FALLBACK(x) \
@@ -121,6 +121,7 @@ typedef struct {
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;
commit 40eb14c9482457969e0bde97c49edad536285e02
Author: Owen Taylor <otaylor at huygens.home.fishsoup.net>
Date: Mon Apr 28 21:00:54 2008 +0200
EXA: Add exaCompositeRects()
Add a function to composite multiple independent rectangles
from the same source to the same destination in a single
operation: this is useful for building a glyph mask.
diff --git a/exa/exa_glyphs.c b/exa/exa_glyphs.c
index 3fe433a..55fdb01 100644
--- a/exa/exa_glyphs.c
+++ b/exa/exa_glyphs.c
@@ -56,16 +56,6 @@
#define DBG_GLYPH_CACHE(a)
#endif
-/* Instructions for rendering a single glyph */
-typedef struct {
- INT16 xSrc;
- INT16 ySrc;
- INT16 xDst;
- INT16 yDst;
- INT16 width;
- INT16 height;
-} ExaGlyphRenderRec, *ExaGlyphRenderPtr;
-
/* 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.
@@ -79,7 +69,7 @@ typedef struct {
typedef struct {
PicturePtr source;
- ExaGlyphRenderRec glyphs[GLYPH_BUFFER_SIZE];
+ ExaCompositeRectRec rects[GLYPH_BUFFER_SIZE];
int count;
} ExaGlyphBuffer, *ExaGlyphBufferPtr;
@@ -364,7 +354,7 @@ exaGlyphCacheBufferGlyph(ScreenPtr pScreen,
int xGlyph,
int yGlyph)
{
- ExaGlyphRenderPtr glyphRec;
+ ExaCompositeRectPtr rect;
int pos;
if (buffer->source && buffer->source != cache->picture)
@@ -407,7 +397,7 @@ exaGlyphCacheBufferGlyph(ScreenPtr pScreen,
y = (pos / cache->columns) * cache->glyphHeight;
for (i = 0; i < buffer->count; i++) {
- if (buffer->glyphs[i].xSrc == x && buffer->glyphs[i].ySrc == y) {
+ if (buffer->rects[i].xSrc == x && buffer->rects[i].ySrc == y) {
DBG_GLYPH_CACHE((" must flush buffer\n"));
return ExaGlyphNeedFlush;
}
@@ -439,13 +429,13 @@ exaGlyphCacheBufferGlyph(ScreenPtr pScreen,
buffer->source = cache->picture;
- glyphRec = &buffer->glyphs[buffer->count];
- glyphRec->xSrc = (pos % cache->columns) * cache->glyphWidth;
- glyphRec->ySrc = (pos / cache->columns) * cache->glyphHeight;
- glyphRec->xDst = xGlyph - pGlyph->info.x;
- glyphRec->yDst = yGlyph - pGlyph->info.y;
- glyphRec->width = pGlyph->info.width;
- glyphRec->height = pGlyph->info.height;
+ rect = &buffer->rects[buffer->count];
+ rect->xSrc = (pos % cache->columns) * cache->glyphWidth;
+ rect->ySrc = (pos / cache->columns) * cache->glyphHeight;
+ rect->xDst = xGlyph - pGlyph->info.x;
+ rect->yDst = yGlyph - pGlyph->info.y;
+ rect->width = pGlyph->info.width;
+ rect->height = pGlyph->info.height;
buffer->count++;
@@ -463,7 +453,7 @@ exaBufferGlyph(ScreenPtr pScreen,
unsigned int format = (GlyphPicture(pGlyph)[pScreen->myNum])->format;
int width = pGlyph->info.width;
int height = pGlyph->info.width;
- ExaGlyphRenderPtr glyphRec;
+ ExaCompositeRectPtr rect;
PicturePtr source;
int i;
@@ -497,13 +487,13 @@ exaBufferGlyph(ScreenPtr pScreen,
buffer->source = source;
- glyphRec = &buffer->glyphs[buffer->count];
- glyphRec->xSrc = 0;
- glyphRec->ySrc = 0;
- glyphRec->xDst = xGlyph - pGlyph->info.x;
- glyphRec->yDst = yGlyph - pGlyph->info.y;
- glyphRec->width = pGlyph->info.width;
- glyphRec->height = pGlyph->info.height;
+ 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++;
@@ -514,23 +504,8 @@ static void
exaGlyphsToMask(PicturePtr pMask,
ExaGlyphBufferPtr buffer)
{
- int i;
-
- for (i = 0; i < buffer->count; i++) {
- ExaGlyphRenderPtr glyphRec = &buffer->glyphs[i];
-
- CompositePicture (PictOpAdd,
- buffer->source,
- None,
- pMask,
- glyphRec->xSrc,
- glyphRec->ySrc,
- 0, 0,
- glyphRec->xDst,
- glyphRec->yDst,
- glyphRec->width,
- glyphRec->height);
- }
+ exaCompositeRects(PictOpAdd, buffer->source, pMask,
+ buffer->count, buffer->rects);
buffer->count = 0;
buffer->source = NULL;
@@ -549,20 +524,20 @@ exaGlyphsToDst(CARD8 op,
int i;
for (i = 0; i < buffer->count; i++) {
- ExaGlyphRenderPtr glyphRec = &buffer->glyphs[i];
+ ExaCompositeRectPtr rect = &buffer->rects[i];
CompositePicture (op,
pSrc,
buffer->source,
pDst,
- xSrc + glyphRec->xDst - xDst,
- ySrc + glyphRec->yDst - yDst,
- glyphRec->xSrc,
- glyphRec->ySrc,
- glyphRec->xDst,
- glyphRec->yDst,
- glyphRec->width,
- glyphRec->height);
+ xSrc + rect->xDst - xDst,
+ ySrc + rect->yDst - yDst,
+ rect->xSrc,
+ rect->ySrc,
+ rect->xDst,
+ rect->yDst,
+ rect->width,
+ rect->height);
}
buffer->count = 0;
diff --git a/exa/exa_priv.h b/exa/exa_priv.h
index aaceeb8..0d5d0f5 100644
--- a/exa/exa_priv.h
+++ b/exa/exa_priv.h
@@ -243,6 +243,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.
@@ -457,6 +466,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);
diff --git a/exa/exa_render.c b/exa/exa_render.c
index 1d7b897..43b0029 100644
--- a/exa/exa_render.c
+++ b/exa/exa_render.c
@@ -332,6 +332,228 @@ 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];
+
+ 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);
+
+ int xoff, yoff;
+ int x1 = MAXSHORT;
+ int y1 = MAXSHORT;
+ int x2 = MINSHORT;
+ int y2 = MINSHORT;
+ RegionRec region;
+ RegionPtr pending_damage;
+ BoxRec box;
+ int n;
+ ExaCompositeRectPtr r;
+
+ /* 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++;
+ }
+ }
+
+ /************************************************************/
+
+ /* 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,
commit 54184110f6f3e5d7276d5431e739a4fcf0c3523e
Author: Owen Taylor <otaylor at huygens.home.fishsoup.net>
Date: Mon Apr 28 21:00:54 2008 +0200
EXA: Use a single large glyph cache pixmap
Add back exaGlyphs(); the new version copies the glyph images
onto a single large glyph pixmap and draws from their to the
destination surface. This reduces the management of small
offscreen areas and will allow us to avoid texture unit setup
between each glyph.
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 3a6ad98..809fb4b 100644
--- a/exa/exa.c
+++ b/exa/exa.c
@@ -739,6 +739,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;
@@ -752,7 +754,9 @@ exaCloseScreen(int i, ScreenPtr pScreen)
#ifdef RENDER
if (ps) {
ps->Composite = pExaScr->SavedComposite;
+ ps->Glyphs = pExaScr->SavedGlyphs;
ps->Trapezoids = pExaScr->SavedTrapezoids;
+ ps->Triangles = pExaScr->SavedTriangles;
}
#endif
@@ -914,6 +918,9 @@ exaDriverInit (ScreenPtr pScreen,
pExaScr->SavedComposite = ps->Composite;
ps->Composite = exaComposite;
+ pExaScr->SavedGlyphs = ps->Glyphs;
+ ps->Glyphs = exaGlyphs;
+
pExaScr->SavedTriangles = ps->Triangles;
ps->Triangles = exaTriangles;
@@ -973,6 +980,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_glyphs.c b/exa/exa_glyphs.c
new file mode 100644
index 0000000..3fe433a
--- /dev/null
+++ b/exa/exa_glyphs.c
@@ -0,0 +1,745 @@
+/*
+ * 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 <otaylor at fishsoup.net>
+ * Based on code by: Keith Packard
+ */
+
+#ifdef HAVE_DIX_CONFIG_H
+#include <dix-config.h>
+#endif
+
+#include <stdlib.h>
+
+#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
+
+/* Instructions for rendering a single glyph */
+typedef struct {
+ INT16 xSrc;
+ INT16 ySrc;
+ INT16 xDst;
+ INT16 yDst;
+ INT16 width;
+ INT16 height;
+} ExaGlyphRenderRec, *ExaGlyphRenderPtr;
+
+/* 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;
+ ExaGlyphRenderRec glyphs[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;
+
+ 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){
+ DBG_GLYPH_CACHE((" found entry at %d\n", slot));
+ return entryPos;
+ }
+
+ DBG_GLYPH_CACHE((" lookup linear probe bumpalong\n"));
+ 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) {
+ DBG_GLYPH_CACHE((" inserting entry at %d\n", slot));
+ 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;
+ }
+}
+
+static ExaGlyphCacheResult
+exaGlyphCacheBufferGlyph(ScreenPtr pScreen,
+ ExaGlyphCachePtr cache,
+ ExaGlyphBufferPtr buffer,
+ GlyphPtr pGlyph,
+ int xGlyph,
+ int yGlyph)
+{
+ ExaGlyphRenderPtr glyphRec;
+ 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 = (pos % cache->columns) * cache->glyphWidth;
+ y = (pos / cache->columns) * cache->glyphHeight;
+
+ for (i = 0; i < buffer->count; i++) {
+ if (buffer->glyphs[i].xSrc == x && buffer->glyphs[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 */
+
+ CompositePicture (PictOpSrc,
+ GlyphPicture(pGlyph)[pScreen->myNum],
+ None,
+ cache->picture,
+ 0, 0,
+ 0, 0,
+ (pos % cache->columns) * cache->glyphWidth,
+ (pos / cache->columns) * cache->glyphHeight,
+ pGlyph->info.width,
+ pGlyph->info.height);
+ }
+
+
+ buffer->source = cache->picture;
+
+ glyphRec = &buffer->glyphs[buffer->count];
+ glyphRec->xSrc = (pos % cache->columns) * cache->glyphWidth;
+ glyphRec->ySrc = (pos / cache->columns) * cache->glyphHeight;
+ glyphRec->xDst = xGlyph - pGlyph->info.x;
+ glyphRec->yDst = yGlyph - pGlyph->info.y;
+ glyphRec->width = pGlyph->info.width;
+ glyphRec->height = pGlyph->info.height;
+
+ buffer->count++;
+
+ return ExaGlyphSuccess;
+}
+
+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.width;
+ ExaGlyphRenderPtr glyphRec;
+ PicturePtr source;
+ int i;
+
+ if (buffer->count == GLYPH_BUFFER_SIZE)
+ return ExaGlyphNeedFlush;
+
+ 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;
+
+ glyphRec = &buffer->glyphs[buffer->count];
+ glyphRec->xSrc = 0;
+ glyphRec->ySrc = 0;
+ glyphRec->xDst = xGlyph - pGlyph->info.x;
+ glyphRec->yDst = yGlyph - pGlyph->info.y;
+ glyphRec->width = pGlyph->info.width;
+ glyphRec->height = pGlyph->info.height;
+
+ buffer->count++;
+
+ return ExaGlyphSuccess;
+}
+
+static void
+exaGlyphsToMask(PicturePtr pMask,
+ ExaGlyphBufferPtr buffer)
+{
+ int i;
+
+ for (i = 0; i < buffer->count; i++) {
+ ExaGlyphRenderPtr glyphRec = &buffer->glyphs[i];
+
+ CompositePicture (PictOpAdd,
+ buffer->source,
+ None,
+ pMask,
+ glyphRec->xSrc,
+ glyphRec->ySrc,
+ 0, 0,
+ glyphRec->xDst,
+ glyphRec->yDst,
+ glyphRec->width,
+ glyphRec->height);
+ }
+
+ 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++) {
+ ExaGlyphRenderPtr glyphRec = &buffer->glyphs[i];
+
+ CompositePicture (op,
+ pSrc,
+ buffer->source,
+ pDst,
+ xSrc + glyphRec->xDst - xDst,
+ ySrc + glyphRec->yDst - yDst,
+ glyphRec->xSrc,
+ glyphRec->ySrc,
+ glyphRec->xDst,
+ glyphRec->yDst,
+ glyphRec->width,
+ glyphRec->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;
+ }
+ }
+}
+
+#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 (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;
+ 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 (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_priv.h b/exa/exa_priv.h
index 0138e4a..aaceeb8 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,37 @@ 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 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;
@@ -122,6 +154,8 @@ typedef struct {
unsigned disableFbCount;
Bool optimize_migration;
unsigned offScreenCounter;
+
+ ExaGlyphCacheRec glyphCaches[EXA_NUM_GLYPH_CACHES];
} ExaScreenPrivRec, *ExaScreenPrivPtr;
/*
@@ -432,6 +466,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,
More information about the xorg-commit
mailing list