xorg-x11-server/xserver-1.4.99-exa-master-upgrade.patch

1983 lines
57 KiB
Diff
Raw Normal View History

2008-08-14 09:13:47 +00:00
From 82c5e0d932e4cd068396e793e71a3b405dff5c83 Mon Sep 17 00:00:00 2001
2008-08-14 09:11:12 +00:00
From: Fedora X Ninjas <x@fedoraproject.org>
2008-08-14 09:13:47 +00:00
Date: Thu, 14 Aug 2008 19:05:38 +1000
Subject: [PATCH] exa: update to latest EXA code from master
---
2008-08-14 09:13:47 +00:00
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(-)
2008-08-14 09:11:12 +00:00
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 <stdlib.h>
#include "exa_priv.h"
-#include <X11/fonts/fontstruct.h>
-#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, &region, &box, 1);
+ if (pExaPixmap->pDamage)
+ pending_damage = DamagePendingRegion(pExaPixmap->pDamage);
- exaGetDrawableDeltas(pDrawable, pPixmap, &xoff, &yoff);
+ if (pending_damage) {
+ REGION_INIT(pScreen, &region, &box, 1);
- REGION_TRANSLATE(pScreen, &region, xoff, yoff);
- REGION_UNION(pScreen, pending_damage, pending_damage, &region);
+ exaGetDrawableDeltas(pDrawable, pPixmap, &xoff, &yoff);
+
+ REGION_TRANSLATE(pScreen, &region, xoff, yoff);
+ REGION_UNION(pScreen, pending_damage, pending_damage, &region);
+ }
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, &region);
fbShmPutImage(pDrawable, pGC, depth, format, w, h, sx, sy, sw, sh, dx, dy,
data);
exaFinishAccess(pDrawable, EXA_PREPARE_DEST);
}
- REGION_TRANSLATE(pScreen, &region, -xoff, -yoff);
- DamageDamageRegion(pDrawable, &region);
+ if (pending_damage) {
+ REGION_TRANSLATE(pScreen, &region, -xoff, -yoff);
+ DamageDamageRegion(pDrawable, &region);
- REGION_UNINIT(pScreen, &region);
+ REGION_UNINIT(pScreen, &region);
+ }
}
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;
}
2008-08-14 09:11:12 +00:00
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 <otaylor@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
+
+/* 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 <string.h>
#include "exa_priv.h"
-#include <X11/fonts/fontstruct.h>
-#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 (&region, pSrc, NULL, pDst,
+ xSrc, ySrc, 0, 0, xDst, yDst,
+ rects->width, rects->height))
+ goto next_rect;
+
+ REGION_TRANSLATE(pScreen, &region, dst_off_x, dst_off_y);
+
+ nbox = REGION_NUM_RECTS(&region);
+ pbox = REGION_RECTS(&region);
+
+ 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, &region);
+
+ 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, &region, &box, 1);
+
+ exaGetDrawableDeltas(pDst->pDrawable, pPixmap, &xoff, &yoff);
+
+ REGION_TRANSLATE(pScreen, &region, xoff, yoff);
+ pending_damage = DamagePendingRegion(pExaPixmap->pDamage);
+ REGION_UNION(pScreen, pending_damage, pending_damage, &region);
+ REGION_TRANSLATE(pScreen, &region, -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, &region);
+ REGION_UNINIT(pScreen, &region);
+ }
+}
+
+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, &region);
}
-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;
2008-08-14 09:13:47 +00:00
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",
--
2008-08-14 09:11:12 +00:00
1.5.5.1