1/*
2 * Copyright (c) 2003, 2012, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.  Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26#include <stdlib.h>
27#include "jni.h"
28#include "AccelGlyphCache.h"
29#include "Trace.h"
30
31/**
32 * When the cache is full, we will try to reuse the cache cells that have
33 * been used relatively less than the others (and we will save the cells that
34 * have been rendered more than the threshold defined here).
35 */
36#define TIMES_RENDERED_THRESHOLD 5
37
38/**
39 * Creates a new GlyphCacheInfo structure, fills in the initial values, and
40 * then returns a pointer to the GlyphCacheInfo record.
41 *
42 * Note that this method only sets up a data structure describing a
43 * rectangular region of accelerated memory, containing "virtual" cells of
44 * the requested size.  The cell information is added lazily to the linked
45 * list describing the cache as new glyphs are added.  Platform specific
46 * glyph caching code is responsible for actually creating the accelerated
47 * memory surface that will contain the individual glyph images.
48 *
49 * Each glyph contains a reference to a list of cell infos - one per glyph
50 * cache. There may be multiple glyph caches (for example, one per graphics
51 * adapter), so if the glyph is cached on two devices its cell list will
52 * consists of two elements corresponding to different glyph caches.
53 *
54 * The platform-specific glyph caching code is supposed to use
55 * GetCellInfoForCache method for retrieving cache infos from the glyph's list.
56 *
57 * Note that if it is guaranteed that there will be only one global glyph
58 * cache then it one does not have to use AccelGlyphCache_GetCellInfoForCache
59 * for retrieving cell info for the glyph, but instead just use the struct's
60 * field directly.
61 */
62GlyphCacheInfo *
63AccelGlyphCache_Init(jint width, jint height,
64                     jint cellWidth, jint cellHeight,
65                     FlushFunc *func)
66{
67    GlyphCacheInfo *gcinfo;
68
69    J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_Init");
70
71    gcinfo = (GlyphCacheInfo *)malloc(sizeof(GlyphCacheInfo));
72    if (gcinfo == NULL) {
73        J2dRlsTraceLn(J2D_TRACE_ERROR,
74            "AccelGlyphCache_Init: could not allocate GlyphCacheInfo");
75        return NULL;
76    }
77
78    gcinfo->head = NULL;
79    gcinfo->tail = NULL;
80    gcinfo->width = width;
81    gcinfo->height = height;
82    gcinfo->cellWidth = cellWidth;
83    gcinfo->cellHeight = cellHeight;
84    gcinfo->isFull = JNI_FALSE;
85    gcinfo->Flush = func;
86
87    return gcinfo;
88}
89
90/**
91 * Attempts to add the provided glyph to the specified cache.  If the
92 * operation is successful, a pointer to the newly occupied cache cell is
93 * stored in the glyph's cellInfo field; otherwise, its cellInfo field is
94 * set to NULL, indicating that the glyph's original bits should be rendered
95 * instead.  If the cache is full, the least-recently-used glyph is
96 * invalidated and its cache cell is reassigned to the new glyph being added.
97 *
98 * Note that this method only ensures that a rectangular region in the
99 * "virtual" glyph cache is available for the glyph image.  Platform specific
100 * glyph caching code is responsible for actually caching the glyph image
101 * in the associated accelerated memory surface.
102 *
103 * Returns created cell info if it was successfully created and added to the
104 * cache and glyph's cell lists, NULL otherwise.
105 */
106CacheCellInfo *
107AccelGlyphCache_AddGlyph(GlyphCacheInfo *cache, GlyphInfo *glyph)
108{
109    CacheCellInfo *cellinfo = NULL;
110    jint w = glyph->width;
111    jint h = glyph->height;
112
113    J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_AddGlyph");
114
115    if ((glyph->width > cache->cellWidth) ||
116        (glyph->height > cache->cellHeight))
117    {
118        return NULL;
119    }
120
121    if (!cache->isFull) {
122        jint x, y;
123
124        if (cache->head == NULL) {
125            x = 0;
126            y = 0;
127        } else {
128            x = cache->tail->x + cache->cellWidth;
129            y = cache->tail->y;
130            if ((x + cache->cellWidth) > cache->width) {
131                x = 0;
132                y += cache->cellHeight;
133                if ((y + cache->cellHeight) > cache->height) {
134                    // no room left for a new cell; we'll go through the
135                    // isFull path below
136                    cache->isFull = JNI_TRUE;
137                }
138            }
139        }
140
141        if (!cache->isFull) {
142            // create new CacheCellInfo
143            cellinfo = (CacheCellInfo *)malloc(sizeof(CacheCellInfo));
144            if (cellinfo == NULL) {
145                J2dTraceLn(J2D_TRACE_ERROR, "could not allocate CellInfo");
146                return NULL;
147            }
148
149            cellinfo->cacheInfo = cache;
150            cellinfo->glyphInfo = glyph;
151            cellinfo->timesRendered = 0;
152            cellinfo->x = x;
153            cellinfo->y = y;
154            cellinfo->leftOff = 0;
155            cellinfo->rightOff = 0;
156            cellinfo->tx1 = (jfloat)cellinfo->x / cache->width;
157            cellinfo->ty1 = (jfloat)cellinfo->y / cache->height;
158            cellinfo->tx2 = cellinfo->tx1 + ((jfloat)w / cache->width);
159            cellinfo->ty2 = cellinfo->ty1 + ((jfloat)h / cache->height);
160
161            if (cache->head == NULL) {
162                // initialize the head cell
163                cache->head = cellinfo;
164            } else {
165                // update existing tail cell
166                cache->tail->next = cellinfo;
167            }
168
169            // add the new cell to the end of the list
170            cache->tail = cellinfo;
171            cellinfo->next = NULL;
172            cellinfo->nextGCI = NULL;
173        }
174    }
175
176    if (cache->isFull) {
177        /**
178         * Search through the cells, and for each cell:
179         *   - reset its timesRendered counter to zero
180         *   - toss it to the end of the list
181         * Eventually we will find a cell that either:
182         *   - is empty, or
183         *   - has been used less than the threshold
184         * When we find such a cell, we will:
185         *   - break out of the loop
186         *   - invalidate any glyph that may be residing in that cell
187         *   - update the cell with the new resident glyph's information
188         *
189         * The goal here is to keep the glyphs rendered most often in the
190         * cache, while younger glyphs hang out near the end of the list.
191         * Those young glyphs that have only been used a few times will move
192         * towards the head of the list and will eventually be kicked to
193         * the curb.
194         *
195         * In the worst-case scenario, all cells will be occupied and they
196         * will all have timesRendered counts above the threshold, so we will
197         * end up iterating through all the cells exactly once.  Since we are
198         * resetting their counters along the way, we are guaranteed to
199         * eventually hit the original "head" cell, whose counter is now zero.
200         * This avoids the possibility of an infinite loop.
201         */
202
203        do {
204            // the head cell will be updated on each iteration
205            CacheCellInfo *current = cache->head;
206
207            if ((current->glyphInfo == NULL) ||
208                (current->timesRendered < TIMES_RENDERED_THRESHOLD))
209            {
210                // all bow before the chosen one (we will break out of the
211                // loop now that we've found an appropriate cell)
212                cellinfo = current;
213            }
214
215            // move cell to the end of the list; update existing head and
216            // tail pointers
217            cache->head = current->next;
218            cache->tail->next = current;
219            cache->tail = current;
220            current->next = NULL;
221            current->timesRendered = 0;
222        } while (cellinfo == NULL);
223
224        if (cellinfo->glyphInfo != NULL) {
225            // flush in case any pending vertices are depending on the
226            // glyph that is about to be kicked out
227            if (cache->Flush != NULL) {
228                cache->Flush();
229            }
230
231            // if the cell is occupied, notify the base glyph that the
232            // cached version for this cache is about to be kicked out
233            AccelGlyphCache_RemoveCellInfo(cellinfo->glyphInfo, cellinfo);
234        }
235
236        // update cellinfo with glyph's occupied region information
237        cellinfo->glyphInfo = glyph;
238        cellinfo->tx2 = cellinfo->tx1 + ((jfloat)w / cache->width);
239        cellinfo->ty2 = cellinfo->ty1 + ((jfloat)h / cache->height);
240    }
241
242    // add cache cell to the glyph's cells list
243    AccelGlyphCache_AddCellInfo(glyph, cellinfo);
244    return cellinfo;
245}
246
247/**
248 * Invalidates all cells in the cache.  Note that this method does not
249 * attempt to compact the cache in any way; it just invalidates any cells
250 * that already exist.
251 */
252void
253AccelGlyphCache_Invalidate(GlyphCacheInfo *cache)
254{
255    CacheCellInfo *cellinfo;
256
257    J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_Invalidate");
258
259    if (cache == NULL) {
260        return;
261    }
262
263    // flush any pending vertices that may be depending on the current
264    // glyph cache layout
265    if (cache->Flush != NULL) {
266        cache->Flush();
267    }
268
269    cellinfo = cache->head;
270    while (cellinfo != NULL) {
271        if (cellinfo->glyphInfo != NULL) {
272            // if the cell is occupied, notify the base glyph that its
273            // cached version for this cache is about to be invalidated
274            AccelGlyphCache_RemoveCellInfo(cellinfo->glyphInfo, cellinfo);
275        }
276        cellinfo = cellinfo->next;
277    }
278}
279
280/**
281 * Invalidates and frees all cells and the cache itself. The "cache" pointer
282 * becomes invalid after this function returns.
283 */
284void
285AccelGlyphCache_Free(GlyphCacheInfo *cache)
286{
287    CacheCellInfo *cellinfo;
288
289    J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_Free");
290
291    if (cache == NULL) {
292        return;
293    }
294
295    // flush any pending vertices that may be depending on the current
296    // glyph cache
297    if (cache->Flush != NULL) {
298        cache->Flush();
299    }
300
301    while (cache->head != NULL) {
302        cellinfo = cache->head;
303        if (cellinfo->glyphInfo != NULL) {
304            // if the cell is occupied, notify the base glyph that its
305            // cached version for this cache is about to be invalidated
306            AccelGlyphCache_RemoveCellInfo(cellinfo->glyphInfo, cellinfo);
307        }
308        cache->head = cellinfo->next;
309        free(cellinfo);
310    }
311    free(cache);
312}
313
314/**
315 * Add cell info to the head of the glyph's list of cached cells.
316 */
317void
318AccelGlyphCache_AddCellInfo(GlyphInfo *glyph, CacheCellInfo *cellInfo)
319{
320    // assert (glyph != NULL && cellInfo != NULL)
321    J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_AddCellInfo");
322    J2dTraceLn2(J2D_TRACE_VERBOSE, "  glyph 0x%x: adding cell 0x%x to the list",
323                glyph, cellInfo);
324
325    cellInfo->glyphInfo = glyph;
326    cellInfo->nextGCI = glyph->cellInfo;
327    glyph->cellInfo = cellInfo;
328    glyph->managed = MANAGED_GLYPH;
329}
330
331/**
332 * Removes cell info from the glyph's list of cached cells.
333 */
334void
335AccelGlyphCache_RemoveCellInfo(GlyphInfo *glyph, CacheCellInfo *cellInfo)
336{
337    CacheCellInfo *currCellInfo = glyph->cellInfo;
338    CacheCellInfo *prevInfo = NULL;
339    // assert (glyph!= NULL && glyph->cellInfo != NULL && cellInfo != NULL)
340    J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_RemoveCellInfo");
341    do {
342        if (currCellInfo == cellInfo) {
343            J2dTraceLn2(J2D_TRACE_VERBOSE,
344                        "  glyph 0x%x: removing cell 0x%x from glyph's list",
345                        glyph, currCellInfo);
346            if (prevInfo == NULL) { // it's the head, chop-chop
347                glyph->cellInfo = currCellInfo->nextGCI;
348            } else {
349                prevInfo->nextGCI = currCellInfo->nextGCI;
350            }
351            currCellInfo->glyphInfo = NULL;
352            currCellInfo->nextGCI = NULL;
353            return;
354        }
355        prevInfo = currCellInfo;
356        currCellInfo = currCellInfo->nextGCI;
357    } while (currCellInfo != NULL);
358    J2dTraceLn2(J2D_TRACE_WARNING, "AccelGlyphCache_RemoveCellInfo: "\
359                "no cell 0x%x in glyph 0x%x's cell list",
360                cellInfo, glyph);
361}
362
363/**
364 * Removes cell info from the glyph's list of cached cells.
365 */
366JNIEXPORT void
367AccelGlyphCache_RemoveAllCellInfos(GlyphInfo *glyph)
368{
369    CacheCellInfo *currCell, *prevCell;
370
371    J2dTraceLn(J2D_TRACE_INFO, "AccelGlyphCache_RemoveAllCellInfos");
372
373    if (glyph == NULL || glyph->cellInfo == NULL) {
374        return;
375    }
376
377    // invalidate all of this glyph's accelerated cache cells
378    currCell = glyph->cellInfo;
379    do {
380        currCell->glyphInfo = NULL;
381        prevCell = currCell;
382        currCell = currCell->nextGCI;
383        prevCell->nextGCI = NULL;
384    } while (currCell != NULL);
385
386    glyph->cellInfo = NULL;
387}
388
389/**
390 * Returns cell info associated with particular cache from the glyph's list of
391 * cached cells.
392 */
393CacheCellInfo *
394AccelGlyphCache_GetCellInfoForCache(GlyphInfo *glyph, GlyphCacheInfo *cache)
395{
396    // assert (glyph != NULL && cache != NULL)
397    J2dTraceLn(J2D_TRACE_VERBOSE2, "AccelGlyphCache_GetCellInfoForCache");
398
399    if (glyph->cellInfo != NULL) {
400        CacheCellInfo *cellInfo = glyph->cellInfo;
401        do {
402            if (cellInfo->cacheInfo == cache) {
403                J2dTraceLn3(J2D_TRACE_VERBOSE2,
404                            "  glyph 0x%x: found cell 0x%x for cache 0x%x",
405                            glyph, cellInfo, cache);
406                return cellInfo;
407            }
408            cellInfo = cellInfo->nextGCI;
409        } while (cellInfo != NULL);
410    }
411    J2dTraceLn2(J2D_TRACE_VERBOSE2, "  glyph 0x%x: no cell for cache 0x%x",
412                glyph, cache);
413    return NULL;
414}
415
416