xref: /reactos/base/applications/mstsc/cache.c (revision c2c66aff)
1 /* -*- c-basic-offset: 8 -*-
2    rdesktop: A Remote Desktop Protocol client.
3    Cache routines
4    Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
5    Copyright (C) Jeroen Meijer <jeroen@oldambt7.com> 2005
6    Copyright 2003-2011 Peter Astrand <astrand@cendio.se> for Cendio AB
7 
8    This program is free software: you can redistribute it and/or modify
9    it under the terms of the GNU General Public License as published by
10    the Free Software Foundation, either version 3 of the License, or
11    (at your option) any later version.
12 
13    This program is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16    GNU General Public License for more details.
17 
18    You should have received a copy of the GNU General Public License
19    along with this program.  If not, see <http://www.gnu.org/licenses/>.
20 */
21 
22 #include "precomp.h"
23 
24 /* BITMAP CACHE */
25 extern int g_pstcache_fd[];
26 
27 #define NUM_ELEMENTS(array) (sizeof(array) / sizeof(array[0]))
28 //#define IS_PERSISTENT(id) (g_pstcache_fd[id] > 0)
29 #define TO_TOP -1
30 #define NOT_SET -1
31 #define IS_SET(idx) (idx >= 0)
32 
33 /*
34  * TODO: Test for optimal value of BUMP_COUNT. TO_TOP gives lowest cpu utilisation but using
35  * a positive value will hopefully result in less frequently used bitmaps having a greater chance
36  * of being evicted from the cache, and thereby reducing the need to load bitmaps from disk.
37  * (Jeroen)
38  */
39 #define BUMP_COUNT 40
40 
41 struct bmpcache_entry
42 {
43 	RD_HBITMAP bitmap;
44 	sint16 previous;
45 	sint16 next;
46 };
47 
48 static struct bmpcache_entry g_bmpcache[3][0xa00];
49 static RD_HBITMAP g_volatile_bc[3];
50 
51 static int g_bmpcache_lru[3] = { NOT_SET, NOT_SET, NOT_SET };
52 static int g_bmpcache_mru[3] = { NOT_SET, NOT_SET, NOT_SET };
53 
54 static int g_bmpcache_count[3];
55 
56 /* Setup the bitmap cache lru/mru linked list */
57 void
cache_rebuild_bmpcache_linked_list(uint8 id,sint16 * idx,int count)58 cache_rebuild_bmpcache_linked_list(uint8 id, sint16 * idx, int count)
59 {
60 	int n = count, c = 0;
61 	sint16 n_idx;
62 
63 	/* find top, skip evicted bitmaps */
64 	while (--n >= 0 && g_bmpcache[id][idx[n]].bitmap == NULL);
65 	if (n < 0)
66 	{
67 		g_bmpcache_mru[id] = g_bmpcache_lru[id] = NOT_SET;
68 		return;
69 	}
70 
71 	g_bmpcache_mru[id] = idx[n];
72 	g_bmpcache[id][idx[n]].next = NOT_SET;
73 	n_idx = idx[n];
74 	c++;
75 
76 	/* link list */
77 	while (n >= 0)
78 	{
79 		/* skip evicted bitmaps */
80 		while (--n >= 0 && g_bmpcache[id][idx[n]].bitmap == NULL);
81 
82 		if (n < 0)
83 			break;
84 
85 		g_bmpcache[id][n_idx].previous = idx[n];
86 		g_bmpcache[id][idx[n]].next = n_idx;
87 		n_idx = idx[n];
88 		c++;
89 	}
90 
91 	g_bmpcache[id][n_idx].previous = NOT_SET;
92 	g_bmpcache_lru[id] = n_idx;
93 
94 	if (c != g_bmpcache_count[id])
95 	{
96 		error("Oops. %d in bitmap cache linked list, %d in ui cache...\n", c,
97 		      g_bmpcache_count[id]);
98 		exit(EX_SOFTWARE);
99 	}
100 }
101 
102 /* Move a bitmap to a new position in the linked list. */
103 void
cache_bump_bitmap(uint8 id,uint16 idx,int bump)104 cache_bump_bitmap(uint8 id, uint16 idx, int bump)
105 {
106 	int p_idx, n_idx, n;
107 
108 	if (!IS_PERSISTENT(id))
109 		return;
110 
111 	if (g_bmpcache_mru[id] == idx)
112 		return;
113 
114 	DEBUG_RDP5(("bump bitmap: id=%d, idx=%d, bump=%d\n", id, idx, bump));
115 
116 	n_idx = g_bmpcache[id][idx].next;
117 	p_idx = g_bmpcache[id][idx].previous;
118 
119 	if (IS_SET(n_idx))
120 	{
121 		/* remove */
122 		--g_bmpcache_count[id];
123 		if (IS_SET(p_idx))
124 			g_bmpcache[id][p_idx].next = n_idx;
125 		else
126 			g_bmpcache_lru[id] = n_idx;
127 		if (IS_SET(n_idx))
128 			g_bmpcache[id][n_idx].previous = p_idx;
129 		else
130 			g_bmpcache_mru[id] = p_idx;
131 	}
132 	else
133 	{
134 		p_idx = NOT_SET;
135 		n_idx = g_bmpcache_lru[id];
136 	}
137 
138 	if (bump >= 0)
139 	{
140 		for (n = 0; n < bump && IS_SET(n_idx); n++)
141 		{
142 			p_idx = n_idx;
143 			n_idx = g_bmpcache[id][p_idx].next;
144 		}
145 	}
146 	else
147 	{
148 		p_idx = g_bmpcache_mru[id];
149 		n_idx = NOT_SET;
150 	}
151 
152 	/* insert */
153 	++g_bmpcache_count[id];
154 	g_bmpcache[id][idx].previous = p_idx;
155 	g_bmpcache[id][idx].next = n_idx;
156 
157 	if (p_idx >= 0)
158 		g_bmpcache[id][p_idx].next = idx;
159 	else
160 		g_bmpcache_lru[id] = idx;
161 
162 	if (n_idx >= 0)
163 		g_bmpcache[id][n_idx].previous = idx;
164 	else
165 		g_bmpcache_mru[id] = idx;
166 }
167 
168 /* Evict the least-recently used bitmap from the cache */
169 void
cache_evict_bitmap(uint8 id)170 cache_evict_bitmap(uint8 id)
171 {
172 	uint16 idx;
173 	int n_idx;
174 
175 	if (!IS_PERSISTENT(id))
176 		return;
177 
178 	idx = g_bmpcache_lru[id];
179 	n_idx = g_bmpcache[id][idx].next;
180 	DEBUG_RDP5(("evict bitmap: id=%d idx=%d n_idx=%d bmp=%p\n", id, idx, n_idx,
181 		    g_bmpcache[id][idx].bitmap));
182 
183 	ui_destroy_bitmap(g_bmpcache[id][idx].bitmap);
184 	--g_bmpcache_count[id];
185 	g_bmpcache[id][idx].bitmap = 0;
186 
187 	g_bmpcache_lru[id] = n_idx;
188 	g_bmpcache[id][n_idx].previous = NOT_SET;
189 
190 	pstcache_touch_bitmap(id, idx, 0);
191 }
192 
193 /* Retrieve a bitmap from the cache */
194 RD_HBITMAP
cache_get_bitmap(uint8 id,uint16 idx)195 cache_get_bitmap(uint8 id, uint16 idx)
196 {
197 	if ((id < NUM_ELEMENTS(g_bmpcache)) && (idx < NUM_ELEMENTS(g_bmpcache[0])))
198 	{
199 		if (g_bmpcache[id][idx].bitmap || pstcache_load_bitmap(id, idx))
200 		{
201 			if (IS_PERSISTENT(id))
202 				cache_bump_bitmap(id, idx, BUMP_COUNT);
203 
204 			return g_bmpcache[id][idx].bitmap;
205 		}
206 	}
207 	else if ((id < NUM_ELEMENTS(g_volatile_bc)) && (idx == 0x7fff))
208 	{
209 		return g_volatile_bc[id];
210 	}
211 
212 	error("get bitmap %d:%d\n", id, idx);
213 	return NULL;
214 }
215 
216 /* Store a bitmap in the cache */
217 void
cache_put_bitmap(uint8 id,uint16 idx,RD_HBITMAP bitmap)218 cache_put_bitmap(uint8 id, uint16 idx, RD_HBITMAP bitmap)
219 {
220 	RD_HBITMAP old;
221 
222 	if ((id < NUM_ELEMENTS(g_bmpcache)) && (idx < NUM_ELEMENTS(g_bmpcache[0])))
223 	{
224 		old = g_bmpcache[id][idx].bitmap;
225 		if (old != NULL)
226 			ui_destroy_bitmap(old);
227 		g_bmpcache[id][idx].bitmap = bitmap;
228 
229 		if (IS_PERSISTENT(id))
230 		{
231 			if (old == NULL)
232 				g_bmpcache[id][idx].previous = g_bmpcache[id][idx].next = NOT_SET;
233 
234 			cache_bump_bitmap(id, idx, TO_TOP);
235 			if (g_bmpcache_count[id] > BMPCACHE2_C2_CELLS)
236 				cache_evict_bitmap(id);
237 		}
238 	}
239 	else if ((id < NUM_ELEMENTS(g_volatile_bc)) && (idx == 0x7fff))
240 	{
241 		old = g_volatile_bc[id];
242 		if (old != NULL)
243 			ui_destroy_bitmap(old);
244 		g_volatile_bc[id] = bitmap;
245 	}
246 	else
247 	{
248 		error("put bitmap %d:%d\n", id, idx);
249 	}
250 }
251 
252 /* Updates the persistent bitmap cache MRU information on exit */
253 void
cache_save_state(void)254 cache_save_state(void)
255 {
256 	uint32 id = 0, t = 0;
257 	int idx;
258 
259 	for (id = 0; id < NUM_ELEMENTS(g_bmpcache); id++)
260 		if (IS_PERSISTENT(id))
261 		{
262 			DEBUG_RDP5(("Saving cache state for bitmap cache %d...", id));
263 			idx = g_bmpcache_lru[id];
264 			while (idx >= 0)
265 			{
266 				pstcache_touch_bitmap(id, idx, ++t);
267 				idx = g_bmpcache[id][idx].next;
268 			}
269 			DEBUG_RDP5((" %d stamps written.\n", t));
270 		}
271 }
272 
273 
274 /* FONT CACHE */
275 static FONTGLYPH g_fontcache[12][256];
276 
277 /* Retrieve a glyph from the font cache */
278 FONTGLYPH *
cache_get_font(uint8 font,uint16 character)279 cache_get_font(uint8 font, uint16 character)
280 {
281 	FONTGLYPH *glyph;
282 
283 	if ((font < NUM_ELEMENTS(g_fontcache)) && (character < NUM_ELEMENTS(g_fontcache[0])))
284 	{
285 		glyph = &g_fontcache[font][character];
286 		if (glyph->pixmap != NULL)
287 			return glyph;
288 	}
289 
290 	error("get font %d:%d\n", font, character);
291 	return NULL;
292 }
293 
294 /* Store a glyph in the font cache */
295 void
cache_put_font(uint8 font,uint16 character,uint16 offset,uint16 baseline,uint16 width,uint16 height,RD_HGLYPH pixmap)296 cache_put_font(uint8 font, uint16 character, uint16 offset,
297 	       uint16 baseline, uint16 width, uint16 height, RD_HGLYPH pixmap)
298 {
299 	FONTGLYPH *glyph;
300 
301 	if ((font < NUM_ELEMENTS(g_fontcache)) && (character < NUM_ELEMENTS(g_fontcache[0])))
302 	{
303 		glyph = &g_fontcache[font][character];
304 		if (glyph->pixmap != NULL)
305 			ui_destroy_glyph(glyph->pixmap);
306 
307 		glyph->offset = offset;
308 		glyph->baseline = baseline;
309 		glyph->width = width;
310 		glyph->height = height;
311 		glyph->pixmap = pixmap;
312 	}
313 	else
314 	{
315 		error("put font %d:%d\n", font, character);
316 	}
317 }
318 
319 
320 /* TEXT CACHE */
321 static DATABLOB g_textcache[256];
322 
323 /* Retrieve a text item from the cache */
324 DATABLOB *
cache_get_text(uint8 cache_id)325 cache_get_text(uint8 cache_id)
326 {
327 	DATABLOB *text;
328 
329 	text = &g_textcache[cache_id];
330 	return text;
331 }
332 
333 /* Store a text item in the cache */
334 void
cache_put_text(uint8 cache_id,void * data,int length)335 cache_put_text(uint8 cache_id, void *data, int length)
336 {
337 	DATABLOB *text;
338 
339 	text = &g_textcache[cache_id];
340 	if (text->data != NULL)
341 		xfree(text->data);
342 	text->data = xmalloc(length);
343 	text->size = length;
344 	memcpy(text->data, data, length);
345 }
346 
347 
348 /* DESKTOP CACHE */
349 static uint8 g_deskcache[0x38400 * 4];
350 
351 /* Retrieve desktop data from the cache */
352 uint8 *
cache_get_desktop(uint32 offset,int cx,int cy,int bytes_per_pixel)353 cache_get_desktop(uint32 offset, int cx, int cy, int bytes_per_pixel)
354 {
355 	int length = cx * cy * bytes_per_pixel;
356 
357 	if (offset > sizeof(g_deskcache))
358 		offset = 0;
359 
360 	if ((offset + length) <= sizeof(g_deskcache))
361 	{
362 		return &g_deskcache[offset];
363 	}
364 
365 	error("get desktop %d:%d\n", offset, length);
366 	return NULL;
367 }
368 
369 /* Store desktop data in the cache */
370 void
cache_put_desktop(uint32 offset,int cx,int cy,int scanline,int bytes_per_pixel,uint8 * data)371 cache_put_desktop(uint32 offset, int cx, int cy, int scanline, int bytes_per_pixel, uint8 * data)
372 {
373 	int length = cx * cy * bytes_per_pixel;
374 
375 	if (offset > sizeof(g_deskcache))
376 		offset = 0;
377 
378 	if ((offset + length) <= sizeof(g_deskcache))
379 	{
380 		cx *= bytes_per_pixel;
381 		while (cy--)
382 		{
383 			memcpy(&g_deskcache[offset], data, cx);
384 			data += scanline;
385 			offset += cx;
386 		}
387 	}
388 	else
389 	{
390 		error("put desktop %d:%d\n", offset, length);
391 	}
392 }
393 
394 
395 /* CURSOR CACHE */
396 static RD_HCURSOR g_cursorcache[0x20];
397 
398 /* Retrieve cursor from cache */
399 RD_HCURSOR
cache_get_cursor(uint16 cache_idx)400 cache_get_cursor(uint16 cache_idx)
401 {
402 	RD_HCURSOR cursor;
403 
404 	if (cache_idx < NUM_ELEMENTS(g_cursorcache))
405 	{
406 		cursor = g_cursorcache[cache_idx];
407 		if (cursor != NULL)
408 			return cursor;
409 	}
410 
411 	error("get cursor %d\n", cache_idx);
412 	return NULL;
413 }
414 
415 /* Store cursor in cache */
416 void
cache_put_cursor(uint16 cache_idx,RD_HCURSOR cursor)417 cache_put_cursor(uint16 cache_idx, RD_HCURSOR cursor)
418 {
419 	RD_HCURSOR old;
420 
421 	if (cache_idx < NUM_ELEMENTS(g_cursorcache))
422 	{
423 		old = g_cursorcache[cache_idx];
424 		if (old != NULL)
425 			ui_destroy_cursor(old);
426 
427 		g_cursorcache[cache_idx] = cursor;
428 	}
429 	else
430 	{
431 		error("put cursor %d\n", cache_idx);
432 	}
433 }
434 
435 /* BRUSH CACHE */
436 /* index 0 is 2 colour brush, index 1 is muti colour brush */
437 static BRUSHDATA g_brushcache[2][64];
438 
439 /* Retrieve brush from cache */
440 BRUSHDATA *
cache_get_brush_data(uint8 colour_code,uint8 idx)441 cache_get_brush_data(uint8 colour_code, uint8 idx)
442 {
443 	colour_code = colour_code == 1 ? 0 : 1;
444 	if (idx < NUM_ELEMENTS(g_brushcache[0]))
445 	{
446 		return &g_brushcache[colour_code][idx];
447 	}
448 	error("get brush %d %d\n", colour_code, idx);
449 	return NULL;
450 }
451 
452 /* Store brush in cache */
453 /* this function takes over the data pointer in struct, eg, caller gives it up */
454 void
cache_put_brush_data(uint8 colour_code,uint8 idx,BRUSHDATA * brush_data)455 cache_put_brush_data(uint8 colour_code, uint8 idx, BRUSHDATA * brush_data)
456 {
457 	BRUSHDATA *bd;
458 
459 	colour_code = colour_code == 1 ? 0 : 1;
460 	if (idx < NUM_ELEMENTS(g_brushcache[0]))
461 	{
462 		bd = &g_brushcache[colour_code][idx];
463 		if (bd->data != 0)
464 		{
465 			xfree(bd->data);
466 		}
467 		memcpy(bd, brush_data, sizeof(BRUSHDATA));
468 	}
469 	else
470 	{
471 		error("put brush %d %d\n", colour_code, idx);
472 	}
473 }
474