Home | History | Annotate | Line # | Download | only in wscons
wsdisplay_glyphcache.c revision 1.5
      1 /*	$NetBSD: wsdisplay_glyphcache.c,v 1.5 2012/11/13 20:29:03 macallan Exp $	*/
      2 
      3 /*
      4  * Copyright (c) 2012 Michael Lorenz
      5  * All rights reserved.
      6  *
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
     17  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
     18  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
     19  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
     20  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
     21  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
     22  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
     23  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
     25  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 /*
     29  * a simple glyph cache in offscreen memory
     30  * For now it only caches glyphs with the default attribute ( assuming they're
     31  * the most commonly used glyphs ) but the API should at least not prevent
     32  * more sophisticated caching algorithms
     33  */
     34 
     35 #include <sys/systm.h>
     36 #include <sys/atomic.h>
     37 #include <sys/errno.h>
     38 #include <sys/kmem.h>
     39 #include <dev/wscons/wsdisplay_glyphcachevar.h>
     40 #include "opt_glyphcache.h"
     41 
     42 #ifdef GLYPHCACHE_DEBUG
     43 #define DPRINTF aprint_normal
     44 #else
     45 #define DPRINTF while (0) printf
     46 #endif
     47 
     48 static inline int
     49 attr2idx(long attr)
     50 {
     51 	if ((attr & 0xf0f0fff8) != 0)
     52 		return -1;
     53 
     54 	return (((attr >> 16) & 0x0f) | ((attr >> 20) & 0xf0));
     55 }
     56 
     57 /* first line, lines, width, attr */
     58 int
     59 glyphcache_init(glyphcache *gc, int first, int lines, int width,
     60     int cellwidth, int cellheight, long attr)
     61 {
     62 	int cache_lines, buckets, i, usedcells = 0, idx;
     63 	gc_bucket *b;
     64 
     65 	/* first the geometry stuff */
     66 	gc->gc_cellwidth = cellwidth;
     67 	gc->gc_cellheight = cellheight;
     68 	gc->gc_firstline = first;
     69 	gc->gc_cellsperline = width / cellwidth;
     70 	if (lines < 0) lines = 0;
     71 	cache_lines = lines / cellheight;
     72 	gc->gc_numcells = cache_lines * gc->gc_cellsperline;
     73 
     74 	/* now allocate buckets */
     75 	buckets = (gc->gc_numcells / 223);
     76 	if ((buckets * 223) < gc->gc_numcells)
     77 		buckets++;
     78 
     79 	/*
     80 	 * if we don't have enough video memory to cache at least a few glyphs
     81 	 * we stop right here
     82 	 */
     83 	if (buckets < 1)
     84 		return ENOMEM;
     85 
     86 	gc->gc_buckets = kmem_alloc(sizeof(gc_bucket) * buckets, KM_SLEEP);
     87 	if (gc->gc_buckets == NULL) {
     88 		aprint_error("%s: can't allocate memory\n", __func__);
     89 		return ENOMEM;
     90 	}
     91 	gc->gc_numbuckets = buckets;
     92 
     93 	DPRINTF("%s: using %d buckets\n", __func__, buckets);
     94 	for (i = 0; i < buckets; i++) {
     95 		b = &gc->gc_buckets[i];
     96 		b->gb_firstcell = usedcells;
     97 		b->gb_numcells = min(223, gc->gc_numcells - usedcells);
     98 		usedcells += 223;
     99 		b->gb_usedcells = 0;
    100 		b->gb_index = -1;
    101 	}
    102 
    103 	/* initialize the attribute map... */
    104 	for (i = 0; i < 256; i++) {
    105 		gc->gc_attrmap[i] = -1;
    106 	}
    107 
    108 	/* first bucket goes to default attr */
    109 	idx = attr2idx(attr);
    110 	if (idx >= 0) {
    111 		gc->gc_attrmap[idx] = 0;
    112 		gc->gc_buckets[0].gb_index = idx;
    113 	}
    114 
    115 	glyphcache_wipe(gc);
    116 	DPRINTF("%s: using %d cells total, from %d width %d\n", __func__,
    117 	    gc->gc_numcells, gc->gc_firstline, gc->gc_cellsperline);
    118 	return 0;
    119 }
    120 
    121 void
    122 glyphcache_wipe(glyphcache *gc)
    123 {
    124 	gc_bucket *b;
    125 	int i, j, idx;
    126 
    127 	idx = gc->gc_buckets[0].gb_index;
    128 
    129 	/* empty all the buckets */
    130 	for (i = 0; i < gc->gc_numbuckets; i++) {
    131 		b = &gc->gc_buckets[i];
    132 		b->gb_usedcells = 0;
    133 		b->gb_index = -1;
    134 		for (j = 0; j < b->gb_numcells; j++)
    135 			b->gb_map[j] = -1;
    136 	}
    137 
    138 	for (i = 0; i < 256; i++) {
    139 		gc->gc_attrmap[i] = -1;
    140 	}
    141 
    142 	/* now put the first bucket back where it was */
    143 	gc->gc_attrmap[idx] = 0;
    144 	gc->gc_buckets[0].gb_index = idx;
    145 }
    146 
    147 /*
    148  * add a glyph drawn at (x,y) to the cache as (c)
    149  * call this only if glyphcache_try() returned GC_ADD
    150  * caller or gc_bitblt must make sure the glyph is actually completely drawn
    151  */
    152 int
    153 glyphcache_add(glyphcache *gc, int c, int x, int y)
    154 {
    155 	gc_bucket *b = gc->gc_next;
    156 	int cell;
    157 	int cx, cy;
    158 
    159 	if (b->gb_usedcells >= b->gb_numcells)
    160 		return ENOMEM;
    161 	cell = atomic_add_int_nv(&b->gb_usedcells, 1) - 1;
    162 	cell += b->gb_firstcell;
    163 	cy = gc->gc_firstline +
    164 	    (cell / gc->gc_cellsperline) * gc->gc_cellheight;
    165 	cx = (cell % gc->gc_cellsperline) * gc->gc_cellwidth;
    166 	b->gb_map[c - 33] = (cx << 16) | cy;
    167 	gc->gc_bitblt(gc->gc_blitcookie, x, y, cx, cy,
    168 	    gc->gc_cellwidth, gc->gc_cellheight, gc->gc_rop);
    169 	if (gc->gc_underline & 1) {
    170 		glyphcache_underline(gc, x, y, gc->gc_underline);
    171 	}
    172 	return 0;
    173 }
    174 
    175 void
    176 glyphcache_underline(glyphcache *gc, int x, int y, long attr)
    177 {
    178 	if (gc->gc_rectfill == NULL)
    179 		return;
    180 
    181 	gc->gc_rectfill(gc->gc_blitcookie, x, y + gc->gc_cellheight - 2,
    182 	    gc->gc_cellwidth, 1, attr);
    183 }
    184 /*
    185  * check if (c) is in the cache, if so draw it at (x,y)
    186  * return:
    187  * - GC_OK when the glyph was found
    188  * - GC_ADD when the glyph wasn't found but can be added
    189  * - GC_NOPE when the glyph can't be cached
    190  */
    191 int
    192 glyphcache_try(glyphcache *gc, int c, int x, int y, long attr)
    193 {
    194 	int cell, cx, cy, idx, bi;
    195 	gc_bucket *b;
    196 
    197 	idx = attr2idx(attr);
    198 	/* see if we're in range */
    199 	if ((c < 33) || (c > 255) || (idx < 0))
    200 		return GC_NOPE;
    201 	/* see if there's already a bucket for this attribute */
    202 	bi = gc->gc_attrmap[idx];
    203 	if (bi == -1) {
    204 		/* nope, see if there's an empty one left */
    205 		bi = 1;
    206 		while ((bi < gc->gc_numbuckets) &&
    207 		       (gc->gc_buckets[bi].gb_index != -1)) {
    208 			bi++;
    209 		}
    210 		if (bi < gc->gc_numbuckets) {
    211 			/* found one -> grab it */
    212 			gc->gc_attrmap[idx] = bi;
    213 			b = &gc->gc_buckets[bi];
    214 			b->gb_index = idx;
    215 			b->gb_usedcells = 0;
    216 			/* make sure this doesn't get evicted right away */
    217 			b->gb_lastread = time_uptime;
    218 		} else {
    219 			/*
    220 			 * still nothing
    221 			 * steal the least recently read bucket
    222 			 */
    223 			time_t moo = time_uptime;
    224 			int i, oldest = 1;
    225 
    226 			for (i = 1; i < gc->gc_numbuckets; i++) {
    227 				if (gc->gc_buckets[i].gb_lastread < moo) {
    228 					oldest = i;
    229 					moo = gc->gc_buckets[i].gb_lastread;
    230 				}
    231 			}
    232 
    233 			/* if we end up here all buckets must be in use */
    234 			b = &gc->gc_buckets[oldest];
    235 			gc->gc_attrmap[b->gb_index] = -1;
    236 			b->gb_index = idx;
    237 			b->gb_usedcells = 0;
    238 			gc->gc_attrmap[idx] = oldest;
    239 			/* now scrub it */
    240 			for (i = 0; i < b->gb_numcells; i++)
    241 				b->gb_map[i] = -1;
    242 			/* and set the time stamp */
    243 			b->gb_lastread = time_uptime;
    244 		}
    245 	} else {
    246 		/* found one */
    247 		b = &gc->gc_buckets[bi];
    248 	}
    249 
    250 	/* see if there's room in the bucket */
    251 	if (b->gb_usedcells >= b->gb_numcells)
    252 		return GC_NOPE;
    253 
    254 	cell = b->gb_map[c - 33];
    255 	if (cell == -1) {
    256 		gc->gc_next = b;
    257 		gc->gc_underline = attr;
    258 		return GC_ADD;
    259 	}
    260 
    261 	/* it's in the cache - draw it */
    262 	cy = cell & 0xffff;
    263 	cx = (cell >> 16) & 0xffff;
    264 	gc->gc_bitblt(gc->gc_blitcookie, cx, cy, x, y,
    265 	    gc->gc_cellwidth, gc->gc_cellheight, gc->gc_rop);
    266 	/* and underline it if needed */
    267 	if (attr & 1)
    268 		glyphcache_underline(gc, x, y, attr);
    269 	/* update bucket's time stamp */
    270 	b->gb_lastread = time_uptime;
    271 	return GC_OK;
    272 }
    273