1706f2543Smrg/*
2706f2543Smrg * Copyright © 2008 Red Hat, Inc.
3706f2543Smrg * Partly based on code Copyright © 2000 SuSE, Inc.
4706f2543Smrg *
5706f2543Smrg * Permission to use, copy, modify, distribute, and sell this software and its
6706f2543Smrg * documentation for any purpose is hereby granted without fee, provided that
7706f2543Smrg * the above copyright notice appear in all copies and that both that
8706f2543Smrg * copyright notice and this permission notice appear in supporting
9706f2543Smrg * documentation, and that the name of Red Hat not be used in advertising or
10706f2543Smrg * publicity pertaining to distribution of the software without specific,
11706f2543Smrg * written prior permission.  Red Hat makes no representations about the
12706f2543Smrg * suitability of this software for any purpose.  It is provided "as is"
13706f2543Smrg * without express or implied warranty.
14706f2543Smrg *
15706f2543Smrg * Red Hat DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
16706f2543Smrg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL Red Hat
17706f2543Smrg * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
18706f2543Smrg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
19706f2543Smrg * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
20706f2543Smrg * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
21706f2543Smrg *
22706f2543Smrg * Permission to use, copy, modify, distribute, and sell this software and its
23706f2543Smrg * documentation for any purpose is hereby granted without fee, provided that
24706f2543Smrg * the above copyright notice appear in all copies and that both that
25706f2543Smrg * copyright notice and this permission notice appear in supporting
26706f2543Smrg * documentation, and that the name of SuSE not be used in advertising or
27706f2543Smrg * publicity pertaining to distribution of the software without specific,
28706f2543Smrg * written prior permission.  SuSE makes no representations about the
29706f2543Smrg * suitability of this software for any purpose.  It is provided "as is"
30706f2543Smrg * without express or implied warranty.
31706f2543Smrg *
32706f2543Smrg * SuSE DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
33706f2543Smrg * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL SuSE
34706f2543Smrg * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
35706f2543Smrg * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
36706f2543Smrg * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
37706f2543Smrg * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
38706f2543Smrg *
39706f2543Smrg * Author: Owen Taylor <otaylor@fishsoup.net>
40706f2543Smrg * Based on code by: Keith Packard
41706f2543Smrg */
42706f2543Smrg
43706f2543Smrg#ifdef HAVE_DIX_CONFIG_H
44706f2543Smrg#include <dix-config.h>
45706f2543Smrg#endif
46706f2543Smrg
47706f2543Smrg#include <stdlib.h>
48706f2543Smrg
49706f2543Smrg#include "exa_priv.h"
50706f2543Smrg
51706f2543Smrg#include "mipict.h"
52706f2543Smrg
53706f2543Smrg#if DEBUG_GLYPH_CACHE
54706f2543Smrg#define DBG_GLYPH_CACHE(a) ErrorF a
55706f2543Smrg#else
56706f2543Smrg#define DBG_GLYPH_CACHE(a)
57706f2543Smrg#endif
58706f2543Smrg
59706f2543Smrg/* Width of the pixmaps we use for the caches; this should be less than
60706f2543Smrg * max texture size of the driver; this may need to actually come from
61706f2543Smrg * the driver.
62706f2543Smrg */
63706f2543Smrg#define CACHE_PICTURE_WIDTH 1024
64706f2543Smrg
65706f2543Smrg/* Maximum number of glyphs we buffer on the stack before flushing
66706f2543Smrg * rendering to the mask or destination surface.
67706f2543Smrg */
68706f2543Smrg#define GLYPH_BUFFER_SIZE 256
69706f2543Smrg
70706f2543Smrgtypedef struct {
71706f2543Smrg    PicturePtr mask;
72706f2543Smrg    ExaCompositeRectRec rects[GLYPH_BUFFER_SIZE];
73706f2543Smrg    int count;
74706f2543Smrg} ExaGlyphBuffer, *ExaGlyphBufferPtr;
75706f2543Smrg
76706f2543Smrgtypedef enum {
77706f2543Smrg    ExaGlyphSuccess,    /* Glyph added to render buffer */
78706f2543Smrg    ExaGlyphFail,       /* out of memory, etc */
79706f2543Smrg    ExaGlyphNeedFlush,  /* would evict a glyph already in the buffer */
80706f2543Smrg} ExaGlyphCacheResult;
81706f2543Smrg
82706f2543Smrgvoid
83706f2543SmrgexaGlyphsInit(ScreenPtr pScreen)
84706f2543Smrg{
85706f2543Smrg    ExaScreenPriv(pScreen);
86706f2543Smrg    int i = 0;
87706f2543Smrg
88706f2543Smrg    memset(pExaScr->glyphCaches, 0, sizeof(pExaScr->glyphCaches));
89706f2543Smrg
90706f2543Smrg    pExaScr->glyphCaches[i].format = PICT_a8;
91706f2543Smrg    pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 16;
92706f2543Smrg    i++;
93706f2543Smrg    pExaScr->glyphCaches[i].format = PICT_a8;
94706f2543Smrg    pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 32;
95706f2543Smrg    i++;
96706f2543Smrg    pExaScr->glyphCaches[i].format = PICT_a8r8g8b8;
97706f2543Smrg    pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 16;
98706f2543Smrg    i++;
99706f2543Smrg    pExaScr->glyphCaches[i].format = PICT_a8r8g8b8;
100706f2543Smrg    pExaScr->glyphCaches[i].glyphWidth = pExaScr->glyphCaches[i].glyphHeight = 32;
101706f2543Smrg    i++;
102706f2543Smrg
103706f2543Smrg    assert(i == EXA_NUM_GLYPH_CACHES);
104706f2543Smrg
105706f2543Smrg    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
106706f2543Smrg	pExaScr->glyphCaches[i].columns = CACHE_PICTURE_WIDTH / pExaScr->glyphCaches[i].glyphWidth;
107706f2543Smrg	pExaScr->glyphCaches[i].size = 256;
108706f2543Smrg	pExaScr->glyphCaches[i].hashSize = 557;
109706f2543Smrg    }
110706f2543Smrg}
111706f2543Smrg
112706f2543Smrgstatic void
113706f2543SmrgexaUnrealizeGlyphCaches(ScreenPtr    pScreen,
114706f2543Smrg			unsigned int format)
115706f2543Smrg{
116706f2543Smrg    ExaScreenPriv(pScreen);
117706f2543Smrg    int i;
118706f2543Smrg
119706f2543Smrg    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
120706f2543Smrg	ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i];
121706f2543Smrg
122706f2543Smrg	if (cache->format != format)
123706f2543Smrg	    continue;
124706f2543Smrg
125706f2543Smrg	if (cache->picture) {
126706f2543Smrg	    FreePicture ((pointer) cache->picture, (XID) 0);
127706f2543Smrg	    cache->picture = NULL;
128706f2543Smrg	}
129706f2543Smrg
130706f2543Smrg	free(cache->hashEntries);
131706f2543Smrg	cache->hashEntries = NULL;
132706f2543Smrg
133706f2543Smrg	free(cache->glyphs);
134706f2543Smrg	cache->glyphs = NULL;
135706f2543Smrg	cache->glyphCount = 0;
136706f2543Smrg    }
137706f2543Smrg}
138706f2543Smrg
139706f2543Smrg#define NeedsComponent(f) (PICT_FORMAT_A(f) != 0 && PICT_FORMAT_RGB(f) != 0)
140706f2543Smrg
141706f2543Smrg/* All caches for a single format share a single pixmap for glyph storage,
142706f2543Smrg * allowing mixing glyphs of different sizes without paying a penalty
143706f2543Smrg * for switching between mask pixmaps. (Note that for a size of font
144706f2543Smrg * right at the border between two sizes, we might be switching for almost
145706f2543Smrg * every glyph.)
146706f2543Smrg *
147706f2543Smrg * This function allocates the storage pixmap, and then fills in the
148706f2543Smrg * rest of the allocated structures for all caches with the given format.
149706f2543Smrg */
150706f2543Smrgstatic Bool
151706f2543SmrgexaRealizeGlyphCaches(ScreenPtr    pScreen,
152706f2543Smrg		      unsigned int format)
153706f2543Smrg{
154706f2543Smrg    ExaScreenPriv(pScreen);
155706f2543Smrg
156706f2543Smrg    int depth = PIXMAN_FORMAT_DEPTH(format);
157706f2543Smrg    PictFormatPtr pPictFormat;
158706f2543Smrg    PixmapPtr pPixmap;
159706f2543Smrg    PicturePtr pPicture;
160706f2543Smrg    CARD32 component_alpha;
161706f2543Smrg    int height;
162706f2543Smrg    int i;
163706f2543Smrg    int	error;
164706f2543Smrg
165706f2543Smrg    pPictFormat = PictureMatchFormat(pScreen, depth, format);
166706f2543Smrg    if (!pPictFormat)
167706f2543Smrg	return FALSE;
168706f2543Smrg
169706f2543Smrg    /* Compute the total vertical size needed for the format */
170706f2543Smrg
171706f2543Smrg    height = 0;
172706f2543Smrg    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
173706f2543Smrg	ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i];
174706f2543Smrg	int rows;
175706f2543Smrg
176706f2543Smrg	if (cache->format != format)
177706f2543Smrg	    continue;
178706f2543Smrg
179706f2543Smrg	cache->yOffset = height;
180706f2543Smrg
181706f2543Smrg	rows = (cache->size + cache->columns - 1) / cache->columns;
182706f2543Smrg	height += rows * cache->glyphHeight;
183706f2543Smrg    }
184706f2543Smrg
185706f2543Smrg    /* Now allocate the pixmap and picture */
186706f2543Smrg    pPixmap = (*pScreen->CreatePixmap) (pScreen,
187706f2543Smrg					CACHE_PICTURE_WIDTH,
188706f2543Smrg					height, depth, 0);
189706f2543Smrg    if (!pPixmap)
190706f2543Smrg	return FALSE;
191706f2543Smrg
192706f2543Smrg    component_alpha = NeedsComponent(pPictFormat->format);
193706f2543Smrg    pPicture = CreatePicture(0, &pPixmap->drawable, pPictFormat,
194706f2543Smrg			     CPComponentAlpha, &component_alpha, serverClient,
195706f2543Smrg			     &error);
196706f2543Smrg
197706f2543Smrg    (*pScreen->DestroyPixmap) (pPixmap); /* picture holds a refcount */
198706f2543Smrg
199706f2543Smrg    if (!pPicture)
200706f2543Smrg	return FALSE;
201706f2543Smrg
202706f2543Smrg    /* And store the picture in all the caches for the format */
203706f2543Smrg    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
204706f2543Smrg	ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i];
205706f2543Smrg	int j;
206706f2543Smrg
207706f2543Smrg	if (cache->format != format)
208706f2543Smrg	    continue;
209706f2543Smrg
210706f2543Smrg	cache->picture = pPicture;
211706f2543Smrg	cache->picture->refcnt++;
212706f2543Smrg	cache->hashEntries = malloc(sizeof(int) * cache->hashSize);
213706f2543Smrg	cache->glyphs = malloc(sizeof(ExaCachedGlyphRec) * cache->size);
214706f2543Smrg	cache->glyphCount = 0;
215706f2543Smrg
216706f2543Smrg	if (!cache->hashEntries || !cache->glyphs)
217706f2543Smrg	    goto bail;
218706f2543Smrg
219706f2543Smrg	for (j = 0; j < cache->hashSize; j++)
220706f2543Smrg	    cache->hashEntries[j] = -1;
221706f2543Smrg
222706f2543Smrg	cache->evictionPosition = rand() % cache->size;
223706f2543Smrg    }
224706f2543Smrg
225706f2543Smrg    /* Each cache references the picture individually */
226706f2543Smrg    FreePicture ((pointer) pPicture, (XID) 0);
227706f2543Smrg    return TRUE;
228706f2543Smrg
229706f2543Smrgbail:
230706f2543Smrg    exaUnrealizeGlyphCaches(pScreen, format);
231706f2543Smrg    return FALSE;
232706f2543Smrg}
233706f2543Smrg
234706f2543Smrgvoid
235706f2543SmrgexaGlyphsFini (ScreenPtr pScreen)
236706f2543Smrg{
237706f2543Smrg    ExaScreenPriv(pScreen);
238706f2543Smrg    int i;
239706f2543Smrg
240706f2543Smrg    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
241706f2543Smrg	ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i];
242706f2543Smrg
243706f2543Smrg	if (cache->picture)
244706f2543Smrg	    exaUnrealizeGlyphCaches(pScreen, cache->format);
245706f2543Smrg    }
246706f2543Smrg}
247706f2543Smrg
248706f2543Smrgstatic int
249706f2543SmrgexaGlyphCacheHashLookup(ExaGlyphCachePtr cache,
250706f2543Smrg			GlyphPtr         pGlyph)
251706f2543Smrg{
252706f2543Smrg    int slot;
253706f2543Smrg
254706f2543Smrg    slot = (*(CARD32 *) pGlyph->sha1) % cache->hashSize;
255706f2543Smrg
256706f2543Smrg    while (TRUE) { /* hash table can never be full */
257706f2543Smrg	int entryPos = cache->hashEntries[slot];
258706f2543Smrg	if (entryPos == -1)
259706f2543Smrg	    return -1;
260706f2543Smrg
261706f2543Smrg	if (memcmp(pGlyph->sha1, cache->glyphs[entryPos].sha1, sizeof(pGlyph->sha1)) == 0){
262706f2543Smrg	    return entryPos;
263706f2543Smrg	}
264706f2543Smrg
265706f2543Smrg	slot--;
266706f2543Smrg	if (slot < 0)
267706f2543Smrg	    slot = cache->hashSize - 1;
268706f2543Smrg    }
269706f2543Smrg}
270706f2543Smrg
271706f2543Smrgstatic void
272706f2543SmrgexaGlyphCacheHashInsert(ExaGlyphCachePtr cache,
273706f2543Smrg			GlyphPtr         pGlyph,
274706f2543Smrg			int              pos)
275706f2543Smrg{
276706f2543Smrg    int slot;
277706f2543Smrg
278706f2543Smrg    memcpy(cache->glyphs[pos].sha1, pGlyph->sha1, sizeof(pGlyph->sha1));
279706f2543Smrg
280706f2543Smrg    slot = (*(CARD32 *) pGlyph->sha1) % cache->hashSize;
281706f2543Smrg
282706f2543Smrg    while (TRUE) { /* hash table can never be full */
283706f2543Smrg	if (cache->hashEntries[slot] == -1) {
284706f2543Smrg	    cache->hashEntries[slot] = pos;
285706f2543Smrg	    return;
286706f2543Smrg	}
287706f2543Smrg
288706f2543Smrg	slot--;
289706f2543Smrg	if (slot < 0)
290706f2543Smrg	    slot = cache->hashSize - 1;
291706f2543Smrg    }
292706f2543Smrg}
293706f2543Smrg
294706f2543Smrgstatic void
295706f2543SmrgexaGlyphCacheHashRemove(ExaGlyphCachePtr cache,
296706f2543Smrg			int              pos)
297706f2543Smrg{
298706f2543Smrg    int slot;
299706f2543Smrg    int emptiedSlot = -1;
300706f2543Smrg
301706f2543Smrg    slot = (*(CARD32 *) cache->glyphs[pos].sha1) % cache->hashSize;
302706f2543Smrg
303706f2543Smrg    while (TRUE) { /* hash table can never be full */
304706f2543Smrg	int entryPos = cache->hashEntries[slot];
305706f2543Smrg
306706f2543Smrg	if (entryPos == -1)
307706f2543Smrg	    return;
308706f2543Smrg
309706f2543Smrg	if (entryPos == pos) {
310706f2543Smrg	    cache->hashEntries[slot] = -1;
311706f2543Smrg	    emptiedSlot = slot;
312706f2543Smrg	} else if (emptiedSlot != -1) {
313706f2543Smrg	    /* See if we can move this entry into the emptied slot, we can't
314706f2543Smrg	     * do that if if entry would have hashed between the current position
315706f2543Smrg	     * and the emptied slot. (taking wrapping into account). Bad positions
316706f2543Smrg	     * are:
317706f2543Smrg	     *
318706f2543Smrg	     * |   XXXXXXXXXX             |
319706f2543Smrg	     *     i         j
320706f2543Smrg	     *
321706f2543Smrg	     * |XXX                   XXXX|
322706f2543Smrg	     *     j                  i
323706f2543Smrg	     *
324706f2543Smrg	     * i - slot, j - emptiedSlot
325706f2543Smrg	     *
326706f2543Smrg	     * (Knuth 6.4R)
327706f2543Smrg	     */
328706f2543Smrg
329706f2543Smrg	    int entrySlot = (*(CARD32 *) cache->glyphs[entryPos].sha1) % cache->hashSize;
330706f2543Smrg
331706f2543Smrg	    if (!((entrySlot >= slot && entrySlot < emptiedSlot) ||
332706f2543Smrg		  (emptiedSlot < slot && (entrySlot < emptiedSlot || entrySlot >= slot))))
333706f2543Smrg	    {
334706f2543Smrg		cache->hashEntries[emptiedSlot] = entryPos;
335706f2543Smrg		cache->hashEntries[slot] = -1;
336706f2543Smrg		emptiedSlot = slot;
337706f2543Smrg	    }
338706f2543Smrg	}
339706f2543Smrg
340706f2543Smrg	slot--;
341706f2543Smrg	if (slot < 0)
342706f2543Smrg	    slot = cache->hashSize - 1;
343706f2543Smrg    }
344706f2543Smrg}
345706f2543Smrg
346706f2543Smrg#define CACHE_X(pos) (((pos) % cache->columns) * cache->glyphWidth)
347706f2543Smrg#define CACHE_Y(pos) (cache->yOffset + ((pos) / cache->columns) * cache->glyphHeight)
348706f2543Smrg
349706f2543Smrg/* The most efficient thing to way to upload the glyph to the screen
350706f2543Smrg * is to use the UploadToScreen() driver hook; this allows us to
351706f2543Smrg * pipeline glyph uploads and to avoid creating gpu backed pixmaps for
352706f2543Smrg * glyphs that we'll never use again.
353706f2543Smrg *
354706f2543Smrg * If we can't do it with UploadToScreen (because the glyph has a gpu copy,
355706f2543Smrg * etc), we fall back to CompositePicture.
356706f2543Smrg *
357706f2543Smrg * We need to damage the cache pixmap manually in either case because the damage
358706f2543Smrg * layer unwrapped the picture screen before calling exaGlyphs.
359706f2543Smrg */
360706f2543Smrgstatic void
361706f2543SmrgexaGlyphCacheUploadGlyph(ScreenPtr         pScreen,
362706f2543Smrg			 ExaGlyphCachePtr  cache,
363706f2543Smrg			 int               x,
364706f2543Smrg			 int               y,
365706f2543Smrg			 GlyphPtr          pGlyph)
366706f2543Smrg{
367706f2543Smrg    ExaScreenPriv(pScreen);
368706f2543Smrg    PicturePtr pGlyphPicture = GlyphPicture(pGlyph)[pScreen->myNum];
369706f2543Smrg    PixmapPtr pGlyphPixmap = (PixmapPtr)pGlyphPicture->pDrawable;
370706f2543Smrg    ExaPixmapPriv(pGlyphPixmap);
371706f2543Smrg    PixmapPtr pCachePixmap = (PixmapPtr)cache->picture->pDrawable;
372706f2543Smrg
373706f2543Smrg    if (!pExaScr->info->UploadToScreen || pExaScr->swappedOut || pExaPixmap->accel_blocked)
374706f2543Smrg	goto composite;
375706f2543Smrg
376706f2543Smrg    /* If the glyph pixmap is already uploaded, no point in doing
377706f2543Smrg     * things this way */
378706f2543Smrg    if (exaPixmapHasGpuCopy(pGlyphPixmap))
379706f2543Smrg	goto composite;
380706f2543Smrg
381706f2543Smrg    /* UploadToScreen only works if bpp match */
382706f2543Smrg    if (pGlyphPixmap->drawable.bitsPerPixel != pCachePixmap->drawable.bitsPerPixel)
383706f2543Smrg	goto composite;
384706f2543Smrg
385706f2543Smrg    if (pExaScr->do_migration) {
386706f2543Smrg	ExaMigrationRec pixmaps[1];
387706f2543Smrg
388706f2543Smrg	/* cache pixmap must have a gpu copy. */
389706f2543Smrg	pixmaps[0].as_dst = TRUE;
390706f2543Smrg	pixmaps[0].as_src = FALSE;
391706f2543Smrg	pixmaps[0].pPix = pCachePixmap;
392706f2543Smrg	pixmaps[0].pReg = NULL;
393706f2543Smrg	exaDoMigration (pixmaps, 1, TRUE);
394706f2543Smrg    }
395706f2543Smrg
396706f2543Smrg    if (!exaPixmapHasGpuCopy(pCachePixmap))
397706f2543Smrg	goto composite;
398706f2543Smrg
399706f2543Smrg    /* x,y are in pixmap coordinates, no need for cache{X,Y}off */
400706f2543Smrg    if (pExaScr->info->UploadToScreen(pCachePixmap,
401706f2543Smrg				      x,
402706f2543Smrg				      y,
403706f2543Smrg				      pGlyph->info.width,
404706f2543Smrg				      pGlyph->info.height,
405706f2543Smrg				      (char *)pExaPixmap->sys_ptr,
406706f2543Smrg				      pExaPixmap->sys_pitch))
407706f2543Smrg	goto damage;
408706f2543Smrg
409706f2543Smrgcomposite:
410706f2543Smrg    CompositePicture (PictOpSrc,
411706f2543Smrg		      pGlyphPicture,
412706f2543Smrg		      None,
413706f2543Smrg		      cache->picture,
414706f2543Smrg		      0, 0,
415706f2543Smrg		      0, 0,
416706f2543Smrg		      x,
417706f2543Smrg		      y,
418706f2543Smrg		      pGlyph->info.width,
419706f2543Smrg		      pGlyph->info.height);
420706f2543Smrg
421706f2543Smrgdamage:
422706f2543Smrg    /* The cache pixmap isn't a window, so no need to offset coordinates. */
423706f2543Smrg    exaPixmapDirty (pCachePixmap,
424706f2543Smrg		    x,
425706f2543Smrg		    y,
426706f2543Smrg		    x + cache->glyphWidth,
427706f2543Smrg		    y + cache->glyphHeight);
428706f2543Smrg}
429706f2543Smrg
430706f2543Smrgstatic ExaGlyphCacheResult
431706f2543SmrgexaGlyphCacheBufferGlyph(ScreenPtr         pScreen,
432706f2543Smrg			 ExaGlyphCachePtr  cache,
433706f2543Smrg			 ExaGlyphBufferPtr buffer,
434706f2543Smrg			 GlyphPtr          pGlyph,
435706f2543Smrg			 PicturePtr        pSrc,
436706f2543Smrg			 PicturePtr        pDst,
437706f2543Smrg			 INT16             xSrc,
438706f2543Smrg			 INT16             ySrc,
439706f2543Smrg			 INT16             xMask,
440706f2543Smrg			 INT16             yMask,
441706f2543Smrg			 INT16             xDst,
442706f2543Smrg			 INT16             yDst)
443706f2543Smrg{
444706f2543Smrg    ExaCompositeRectPtr rect;
445706f2543Smrg    int pos;
446706f2543Smrg    int x, y;
447706f2543Smrg
448706f2543Smrg    if (buffer->mask && buffer->mask != cache->picture)
449706f2543Smrg	return ExaGlyphNeedFlush;
450706f2543Smrg
451706f2543Smrg    if (!cache->picture) {
452706f2543Smrg	if (!exaRealizeGlyphCaches(pScreen, cache->format))
453706f2543Smrg	    return ExaGlyphFail;
454706f2543Smrg    }
455706f2543Smrg
456706f2543Smrg    DBG_GLYPH_CACHE(("(%d,%d,%s): buffering glyph %lx\n",
457706f2543Smrg		     cache->glyphWidth, cache->glyphHeight, cache->format == PICT_a8 ? "A" : "ARGB",
458706f2543Smrg		     (long)*(CARD32 *) pGlyph->sha1));
459706f2543Smrg
460706f2543Smrg    pos = exaGlyphCacheHashLookup(cache, pGlyph);
461706f2543Smrg    if (pos != -1) {
462706f2543Smrg	DBG_GLYPH_CACHE(("  found existing glyph at %d\n", pos));
463706f2543Smrg	x = CACHE_X(pos);
464706f2543Smrg	y = CACHE_Y(pos);
465706f2543Smrg    } else {
466706f2543Smrg	if (cache->glyphCount < cache->size) {
467706f2543Smrg	    /* Space remaining; we fill from the start */
468706f2543Smrg	    pos = cache->glyphCount;
469706f2543Smrg	    x = CACHE_X(pos);
470706f2543Smrg	    y = CACHE_Y(pos);
471706f2543Smrg	    cache->glyphCount++;
472706f2543Smrg	    DBG_GLYPH_CACHE(("  storing glyph in free space at %d\n", pos));
473706f2543Smrg
474706f2543Smrg	    exaGlyphCacheHashInsert(cache, pGlyph, pos);
475706f2543Smrg
476706f2543Smrg	} else {
477706f2543Smrg	    /* Need to evict an entry. We have to see if any glyphs
478706f2543Smrg	     * already in the output buffer were at this position in
479706f2543Smrg	     * the cache
480706f2543Smrg	     */
481706f2543Smrg	    pos = cache->evictionPosition;
482706f2543Smrg	    x = CACHE_X(pos);
483706f2543Smrg	    y = CACHE_Y(pos);
484706f2543Smrg	    DBG_GLYPH_CACHE(("  evicting glyph at %d\n", pos));
485706f2543Smrg	    if (buffer->count) {
486706f2543Smrg		int i;
487706f2543Smrg
488706f2543Smrg		for (i = 0; i < buffer->count; i++) {
489706f2543Smrg		    if (pSrc ?
490706f2543Smrg			(buffer->rects[i].xMask == x && buffer->rects[i].yMask == y) :
491706f2543Smrg			(buffer->rects[i].xSrc == x && buffer->rects[i].ySrc == y)) {
492706f2543Smrg			DBG_GLYPH_CACHE(("  must flush buffer\n"));
493706f2543Smrg			return ExaGlyphNeedFlush;
494706f2543Smrg		    }
495706f2543Smrg		}
496706f2543Smrg	    }
497706f2543Smrg
498706f2543Smrg	    /* OK, we're all set, swap in the new glyph */
499706f2543Smrg	    exaGlyphCacheHashRemove(cache, pos);
500706f2543Smrg	    exaGlyphCacheHashInsert(cache, pGlyph, pos);
501706f2543Smrg
502706f2543Smrg	    /* And pick a new eviction position */
503706f2543Smrg	    cache->evictionPosition = rand() % cache->size;
504706f2543Smrg	}
505706f2543Smrg
506706f2543Smrg	exaGlyphCacheUploadGlyph(pScreen, cache, x, y, pGlyph);
507706f2543Smrg    }
508706f2543Smrg
509706f2543Smrg    buffer->mask = cache->picture;
510706f2543Smrg
511706f2543Smrg    rect = &buffer->rects[buffer->count];
512706f2543Smrg
513706f2543Smrg    if (pSrc)
514706f2543Smrg    {
515706f2543Smrg	rect->xSrc = xSrc;
516706f2543Smrg	rect->ySrc = ySrc;
517706f2543Smrg	rect->xMask = x;
518706f2543Smrg	rect->yMask = y;
519706f2543Smrg    }
520706f2543Smrg    else
521706f2543Smrg    {
522706f2543Smrg	rect->xSrc = x;
523706f2543Smrg	rect->ySrc = y;
524706f2543Smrg	rect->xMask = 0;
525706f2543Smrg	rect->yMask = 0;
526706f2543Smrg    }
527706f2543Smrg
528706f2543Smrg    rect->pDst = pDst;
529706f2543Smrg    rect->xDst = xDst;
530706f2543Smrg    rect->yDst = yDst;
531706f2543Smrg    rect->width = pGlyph->info.width;
532706f2543Smrg    rect->height = pGlyph->info.height;
533706f2543Smrg
534706f2543Smrg    buffer->count++;
535706f2543Smrg
536706f2543Smrg    return ExaGlyphSuccess;
537706f2543Smrg}
538706f2543Smrg
539706f2543Smrg#undef CACHE_X
540706f2543Smrg#undef CACHE_Y
541706f2543Smrg
542706f2543Smrgstatic ExaGlyphCacheResult
543706f2543SmrgexaBufferGlyph(ScreenPtr         pScreen,
544706f2543Smrg	       ExaGlyphBufferPtr buffer,
545706f2543Smrg	       GlyphPtr          pGlyph,
546706f2543Smrg	       PicturePtr        pSrc,
547706f2543Smrg	       PicturePtr        pDst,
548706f2543Smrg	       INT16             xSrc,
549706f2543Smrg	       INT16             ySrc,
550706f2543Smrg	       INT16             xMask,
551706f2543Smrg	       INT16             yMask,
552706f2543Smrg	       INT16             xDst,
553706f2543Smrg	       INT16             yDst)
554706f2543Smrg{
555706f2543Smrg    ExaScreenPriv(pScreen);
556706f2543Smrg    unsigned int format = (GlyphPicture(pGlyph)[pScreen->myNum])->format;
557706f2543Smrg    int width = pGlyph->info.width;
558706f2543Smrg    int height = pGlyph->info.height;
559706f2543Smrg    ExaCompositeRectPtr rect;
560706f2543Smrg    PicturePtr mask;
561706f2543Smrg    int i;
562706f2543Smrg
563706f2543Smrg    if (buffer->count == GLYPH_BUFFER_SIZE)
564706f2543Smrg	return ExaGlyphNeedFlush;
565706f2543Smrg
566706f2543Smrg    if (PICT_FORMAT_BPP(format) == 1)
567706f2543Smrg	format = PICT_a8;
568706f2543Smrg
569706f2543Smrg    for (i = 0; i < EXA_NUM_GLYPH_CACHES; i++) {
570706f2543Smrg	ExaGlyphCachePtr cache = &pExaScr->glyphCaches[i];
571706f2543Smrg
572706f2543Smrg	if (format == cache->format &&
573706f2543Smrg	    width <= cache->glyphWidth &&
574706f2543Smrg	    height <= cache->glyphHeight) {
575706f2543Smrg	    ExaGlyphCacheResult result = exaGlyphCacheBufferGlyph(pScreen,
576706f2543Smrg								  &pExaScr->glyphCaches[i],
577706f2543Smrg								  buffer,
578706f2543Smrg								  pGlyph,
579706f2543Smrg								  pSrc,
580706f2543Smrg								  pDst,
581706f2543Smrg								  xSrc, ySrc,
582706f2543Smrg								  xMask, yMask,
583706f2543Smrg								  xDst, yDst);
584706f2543Smrg	    switch (result) {
585706f2543Smrg	    case ExaGlyphFail:
586706f2543Smrg		break;
587706f2543Smrg	    case ExaGlyphSuccess:
588706f2543Smrg	    case ExaGlyphNeedFlush:
589706f2543Smrg		return result;
590706f2543Smrg	    }
591706f2543Smrg	}
592706f2543Smrg    }
593706f2543Smrg
594706f2543Smrg    /* Couldn't find the glyph in the cache, use the glyph picture directly */
595706f2543Smrg
596706f2543Smrg    mask = GlyphPicture(pGlyph)[pScreen->myNum];
597706f2543Smrg    if (buffer->mask && buffer->mask != mask)
598706f2543Smrg	return ExaGlyphNeedFlush;
599706f2543Smrg
600706f2543Smrg    buffer->mask = mask;
601706f2543Smrg
602706f2543Smrg    rect = &buffer->rects[buffer->count];
603706f2543Smrg    rect->xSrc = xSrc;
604706f2543Smrg    rect->ySrc = ySrc;
605706f2543Smrg    rect->xMask = xMask;
606706f2543Smrg    rect->yMask = yMask;
607706f2543Smrg    rect->xDst = xDst;
608706f2543Smrg    rect->yDst = yDst;
609706f2543Smrg    rect->width = width;
610706f2543Smrg    rect->height = height;
611706f2543Smrg
612706f2543Smrg    buffer->count++;
613706f2543Smrg
614706f2543Smrg    return ExaGlyphSuccess;
615706f2543Smrg}
616706f2543Smrg
617706f2543Smrgstatic void
618706f2543SmrgexaGlyphsToMask(PicturePtr        pMask,
619706f2543Smrg		ExaGlyphBufferPtr buffer)
620706f2543Smrg{
621706f2543Smrg    exaCompositeRects(PictOpAdd, buffer->mask, NULL, pMask,
622706f2543Smrg		      buffer->count, buffer->rects);
623706f2543Smrg
624706f2543Smrg    buffer->count = 0;
625706f2543Smrg    buffer->mask = NULL;
626706f2543Smrg}
627706f2543Smrg
628706f2543Smrgstatic void
629706f2543SmrgexaGlyphsToDst(PicturePtr	 pSrc,
630706f2543Smrg	       PicturePtr	 pDst,
631706f2543Smrg	       ExaGlyphBufferPtr buffer)
632706f2543Smrg{
633706f2543Smrg    exaCompositeRects(PictOpOver, pSrc, buffer->mask, pDst, buffer->count,
634706f2543Smrg		      buffer->rects);
635706f2543Smrg
636706f2543Smrg    buffer->count = 0;
637706f2543Smrg    buffer->mask = NULL;
638706f2543Smrg}
639706f2543Smrg
640706f2543Smrg/* Cut and paste from render/glyph.c - probably should export it instead */
641706f2543Smrgstatic void
642706f2543SmrgGlyphExtents (int		nlist,
643706f2543Smrg	      GlyphListPtr	list,
644706f2543Smrg	      GlyphPtr	       *glyphs,
645706f2543Smrg	      BoxPtr		extents)
646706f2543Smrg{
647706f2543Smrg    int		x1, x2, y1, y2;
648706f2543Smrg    int		n;
649706f2543Smrg    GlyphPtr	glyph;
650706f2543Smrg    int		x, y;
651706f2543Smrg
652706f2543Smrg    x = 0;
653706f2543Smrg    y = 0;
654706f2543Smrg    extents->x1 = MAXSHORT;
655706f2543Smrg    extents->x2 = MINSHORT;
656706f2543Smrg    extents->y1 = MAXSHORT;
657706f2543Smrg    extents->y2 = MINSHORT;
658706f2543Smrg    while (nlist--)
659706f2543Smrg    {
660706f2543Smrg	x += list->xOff;
661706f2543Smrg	y += list->yOff;
662706f2543Smrg	n = list->len;
663706f2543Smrg	list++;
664706f2543Smrg	while (n--)
665706f2543Smrg	{
666706f2543Smrg	    glyph = *glyphs++;
667706f2543Smrg	    x1 = x - glyph->info.x;
668706f2543Smrg	    if (x1 < MINSHORT)
669706f2543Smrg		x1 = MINSHORT;
670706f2543Smrg	    y1 = y - glyph->info.y;
671706f2543Smrg	    if (y1 < MINSHORT)
672706f2543Smrg		y1 = MINSHORT;
673706f2543Smrg	    x2 = x1 + glyph->info.width;
674706f2543Smrg	    if (x2 > MAXSHORT)
675706f2543Smrg		x2 = MAXSHORT;
676706f2543Smrg	    y2 = y1 + glyph->info.height;
677706f2543Smrg	    if (y2 > MAXSHORT)
678706f2543Smrg		y2 = MAXSHORT;
679706f2543Smrg	    if (x1 < extents->x1)
680706f2543Smrg		extents->x1 = x1;
681706f2543Smrg	    if (x2 > extents->x2)
682706f2543Smrg		extents->x2 = x2;
683706f2543Smrg	    if (y1 < extents->y1)
684706f2543Smrg		extents->y1 = y1;
685706f2543Smrg	    if (y2 > extents->y2)
686706f2543Smrg		extents->y2 = y2;
687706f2543Smrg	    x += glyph->info.xOff;
688706f2543Smrg	    y += glyph->info.yOff;
689706f2543Smrg	}
690706f2543Smrg    }
691706f2543Smrg}
692706f2543Smrg
693706f2543Smrgvoid
694706f2543SmrgexaGlyphs (CARD8 	 op,
695706f2543Smrg	   PicturePtr	 pSrc,
696706f2543Smrg	   PicturePtr	 pDst,
697706f2543Smrg	   PictFormatPtr maskFormat,
698706f2543Smrg	   INT16	 xSrc,
699706f2543Smrg	   INT16	 ySrc,
700706f2543Smrg	   int		 nlist,
701706f2543Smrg	   GlyphListPtr	 list,
702706f2543Smrg	   GlyphPtr	*glyphs)
703706f2543Smrg{
704706f2543Smrg    PixmapPtr   pMaskPixmap = 0;
705706f2543Smrg    PicturePtr  pMask = NULL;
706706f2543Smrg    ScreenPtr   pScreen = pDst->pDrawable->pScreen;
707706f2543Smrg    int		width = 0, height = 0;
708706f2543Smrg    int		x, y;
709706f2543Smrg    int		first_xOff = list->xOff, first_yOff = list->yOff;
710706f2543Smrg    int		n;
711706f2543Smrg    GlyphPtr	glyph;
712706f2543Smrg    int		error;
713706f2543Smrg    BoxRec	extents = {0, 0, 0, 0};
714706f2543Smrg    CARD32	component_alpha;
715706f2543Smrg    ExaGlyphBuffer buffer;
716706f2543Smrg
717706f2543Smrg    if (maskFormat)
718706f2543Smrg    {
719706f2543Smrg	ExaScreenPriv(pScreen);
720706f2543Smrg	GCPtr	    pGC;
721706f2543Smrg	xRectangle  rect;
722706f2543Smrg
723706f2543Smrg	GlyphExtents (nlist, list, glyphs, &extents);
724706f2543Smrg
725706f2543Smrg	if (extents.x2 <= extents.x1 || extents.y2 <= extents.y1)
726706f2543Smrg	    return;
727706f2543Smrg	width = extents.x2 - extents.x1;
728706f2543Smrg	height = extents.y2 - extents.y1;
729706f2543Smrg
730706f2543Smrg	if (maskFormat->depth == 1) {
731706f2543Smrg	    PictFormatPtr a8Format = PictureMatchFormat (pScreen, 8, PICT_a8);
732706f2543Smrg
733706f2543Smrg	    if (a8Format)
734706f2543Smrg		maskFormat = a8Format;
735706f2543Smrg	}
736706f2543Smrg
737706f2543Smrg	pMaskPixmap = (*pScreen->CreatePixmap) (pScreen, width, height,
738706f2543Smrg						maskFormat->depth,
739706f2543Smrg						CREATE_PIXMAP_USAGE_SCRATCH);
740706f2543Smrg	if (!pMaskPixmap)
741706f2543Smrg	    return;
742706f2543Smrg	component_alpha = NeedsComponent(maskFormat->format);
743706f2543Smrg	pMask = CreatePicture (0, &pMaskPixmap->drawable,
744706f2543Smrg			       maskFormat, CPComponentAlpha, &component_alpha,
745706f2543Smrg			       serverClient, &error);
746706f2543Smrg	if (!pMask ||
747706f2543Smrg	    (!component_alpha && pExaScr->info->CheckComposite &&
748706f2543Smrg	     !(*pExaScr->info->CheckComposite) (PictOpAdd, pSrc, NULL, pMask)))
749706f2543Smrg	{
750706f2543Smrg	    PictFormatPtr argbFormat;
751706f2543Smrg
752706f2543Smrg	    (*pScreen->DestroyPixmap) (pMaskPixmap);
753706f2543Smrg
754706f2543Smrg	    if (!pMask)
755706f2543Smrg		return;
756706f2543Smrg
757706f2543Smrg	    /* The driver can't seem to composite to a8, let's try argb (but
758706f2543Smrg	     * without component-alpha) */
759706f2543Smrg	    FreePicture ((pointer) pMask, (XID) 0);
760706f2543Smrg
761706f2543Smrg	    argbFormat = PictureMatchFormat (pScreen, 32, PICT_a8r8g8b8);
762706f2543Smrg
763706f2543Smrg	    if (argbFormat)
764706f2543Smrg		maskFormat = argbFormat;
765706f2543Smrg
766706f2543Smrg	    pMaskPixmap = (*pScreen->CreatePixmap) (pScreen, width, height,
767706f2543Smrg						    maskFormat->depth,
768706f2543Smrg						    CREATE_PIXMAP_USAGE_SCRATCH);
769706f2543Smrg	    if (!pMaskPixmap)
770706f2543Smrg		return;
771706f2543Smrg
772706f2543Smrg	    pMask = CreatePicture (0, &pMaskPixmap->drawable, maskFormat, 0, 0,
773706f2543Smrg				   serverClient, &error);
774706f2543Smrg	    if (!pMask) {
775706f2543Smrg		(*pScreen->DestroyPixmap) (pMaskPixmap);
776706f2543Smrg		return;
777706f2543Smrg	    }
778706f2543Smrg	}
779706f2543Smrg	pGC = GetScratchGC (pMaskPixmap->drawable.depth, pScreen);
780706f2543Smrg	ValidateGC (&pMaskPixmap->drawable, pGC);
781706f2543Smrg	rect.x = 0;
782706f2543Smrg	rect.y = 0;
783706f2543Smrg	rect.width = width;
784706f2543Smrg	rect.height = height;
785706f2543Smrg	(*pGC->ops->PolyFillRect) (&pMaskPixmap->drawable, pGC, 1, &rect);
786706f2543Smrg	FreeScratchGC (pGC);
787706f2543Smrg	x = -extents.x1;
788706f2543Smrg	y = -extents.y1;
789706f2543Smrg    }
790706f2543Smrg    else
791706f2543Smrg    {
792706f2543Smrg	x = 0;
793706f2543Smrg	y = 0;
794706f2543Smrg    }
795706f2543Smrg    buffer.count = 0;
796706f2543Smrg    buffer.mask = NULL;
797706f2543Smrg    while (nlist--)
798706f2543Smrg    {
799706f2543Smrg	x += list->xOff;
800706f2543Smrg	y += list->yOff;
801706f2543Smrg	n = list->len;
802706f2543Smrg	while (n--)
803706f2543Smrg	{
804706f2543Smrg	    glyph = *glyphs++;
805706f2543Smrg
806706f2543Smrg	    if (glyph->info.width > 0 && glyph->info.height > 0)
807706f2543Smrg	    {
808706f2543Smrg		/* pGlyph->info.{x,y} compensate for empty space in the glyph. */
809706f2543Smrg		if (maskFormat)
810706f2543Smrg		{
811706f2543Smrg		    if (exaBufferGlyph(pScreen, &buffer, glyph, NULL, pMask,
812706f2543Smrg				       0, 0, 0, 0, x - glyph->info.x, y - glyph->info.y) == ExaGlyphNeedFlush)
813706f2543Smrg		    {
814706f2543Smrg			exaGlyphsToMask(pMask, &buffer);
815706f2543Smrg			exaBufferGlyph(pScreen, &buffer, glyph, NULL, pMask,
816706f2543Smrg				       0, 0, 0, 0, x - glyph->info.x, y - glyph->info.y);
817706f2543Smrg		    }
818706f2543Smrg		}
819706f2543Smrg		else
820706f2543Smrg		{
821706f2543Smrg		    if (exaBufferGlyph(pScreen, &buffer, glyph, pSrc, pDst,
822706f2543Smrg				       xSrc + (x - glyph->info.x) - first_xOff, ySrc + (y - glyph->info.y) - first_yOff,
823706f2543Smrg				       0, 0, x - glyph->info.x, y - glyph->info.y)
824706f2543Smrg			== ExaGlyphNeedFlush)
825706f2543Smrg		    {
826706f2543Smrg			exaGlyphsToDst(pSrc, pDst, &buffer);
827706f2543Smrg			exaBufferGlyph(pScreen, &buffer, glyph, pSrc, pDst,
828706f2543Smrg				       xSrc + (x - glyph->info.x) - first_xOff, ySrc + (y - glyph->info.y) - first_yOff,
829706f2543Smrg				       0, 0, x - glyph->info.x, y - glyph->info.y);
830706f2543Smrg		    }
831706f2543Smrg		}
832706f2543Smrg	    }
833706f2543Smrg
834706f2543Smrg	    x += glyph->info.xOff;
835706f2543Smrg	    y += glyph->info.yOff;
836706f2543Smrg	}
837706f2543Smrg	list++;
838706f2543Smrg    }
839706f2543Smrg
840706f2543Smrg    if (buffer.count) {
841706f2543Smrg        if (maskFormat)
842706f2543Smrg	    exaGlyphsToMask(pMask, &buffer);
843706f2543Smrg        else
844706f2543Smrg	    exaGlyphsToDst(pSrc, pDst, &buffer);
845706f2543Smrg    }
846706f2543Smrg
847706f2543Smrg    if (maskFormat)
848706f2543Smrg    {
849706f2543Smrg	x = extents.x1;
850706f2543Smrg	y = extents.y1;
851706f2543Smrg	CompositePicture (op,
852706f2543Smrg			  pSrc,
853706f2543Smrg			  pMask,
854706f2543Smrg			  pDst,
855706f2543Smrg			  xSrc + x - first_xOff,
856706f2543Smrg			  ySrc + y - first_yOff,
857706f2543Smrg			  0, 0,
858706f2543Smrg			  x, y,
859706f2543Smrg			  width, height);
860706f2543Smrg	FreePicture ((pointer) pMask, (XID) 0);
861706f2543Smrg	(*pScreen->DestroyPixmap) (pMaskPixmap);
862706f2543Smrg    }
863706f2543Smrg}
864