diff --git a/cache.c b/cache.c index f13916c..94811ff 100644 --- a/cache.c +++ b/cache.c @@ -2,6 +2,7 @@ rdesktop: A Remote Desktop Protocol client. Cache routines Copyright (C) Matthew Chapman 1999-2005 + Copyright (C) Jeroen Meijer 2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -20,104 +21,230 @@ #include "rdesktop.h" -#define NUM_ELEMENTS(array) (sizeof(array) / sizeof(array[0])) -#define TOUCH(id, idx) (g_bmpcache[id][idx].usage = ++g_stamp) -#define IS_PERSISTENT(id) (g_pstcache_fd[id] > 0) - -extern int g_pstcache_fd[]; -extern BOOL g_use_rdp5; - -uint32 g_stamp; - -static int g_num_bitmaps_in_memory[3]; - /* BITMAP CACHE */ -static BMPCACHEENTRY g_bmpcache[3][0xa00]; +extern int g_pstcache_fd[]; + +#define NUM_ELEMENTS(array) (sizeof(array) / sizeof(array[0])) +#define IS_PERSISTENT(id) (g_pstcache_fd[id] > 0) +#define TO_TOP -1 +#define NOT_SET -1 +#define IS_SET(idx) (idx >= 0) + +/* + * TODO: Test for optimal value of BUMP_COUNT. TO_TOP gives lowest cpu utilisation but using + * a positive value will hopefully result in less frequently used bitmaps having a greater chance + * of being evicted from the cache, and therby reducing the need to load bitmaps from disk. + * (Jeroen) + */ +#define BUMP_COUNT 40 + +struct bmpcache_entry +{ + HBITMAP bitmap; + sint16 previous; + sint16 next; +}; + +static struct bmpcache_entry g_bmpcache[3][0xa00]; static HBITMAP g_volatile_bc[3]; -/* Remove the least-recently used bitmap from the cache */ -void -cache_remove_lru_bitmap(uint8 cache_id) -{ - uint32 i; - uint16 cache_idx = 0; - uint32 m = 0xffffffff; - BMPCACHEENTRY *pbce; +static int g_bmpcache_lru[3] = { NOT_SET, NOT_SET, NOT_SET }; +static int g_bmpcache_mru[3] = { NOT_SET, NOT_SET, NOT_SET }; +static int g_bmpcache_count[3]; - for (i = 0; i < NUM_ELEMENTS(g_bmpcache[cache_id]); i++) +/* Setup the bitmap cache lru/mru linked list */ +void +cache_rebuild_bmpcache_linked_list(uint8 id, sint16 * idx, int count) +{ + int n = count, c = 0; + sint16 n_idx; + + /* find top, skip evicted bitmaps */ + while (--n >= 0 && g_bmpcache[id][idx[n]].bitmap == NULL); + if (n < 0) { - if (g_bmpcache[cache_id][i].bitmap && g_bmpcache[cache_id][i].usage < m) - { - cache_idx = i; - m = g_bmpcache[cache_id][i].usage; - } + g_bmpcache_mru[id] = g_bmpcache_lru[id] = NOT_SET; + return; } - pbce = &g_bmpcache[cache_id][cache_idx]; - ui_destroy_bitmap(pbce->bitmap); - --g_num_bitmaps_in_memory[cache_id]; - pbce->bitmap = 0; - pbce->usage = 0; + g_bmpcache_mru[id] = idx[n]; + g_bmpcache[id][idx[n]].next = NOT_SET; + n_idx = idx[n]; + c++; + + /* link list */ + while (n >= 0) + { + /* skip evicted bitmaps */ + while (--n >= 0 && g_bmpcache[id][idx[n]].bitmap == NULL); + + if (n < 0) + break; + + g_bmpcache[id][n_idx].previous = idx[n]; + g_bmpcache[id][idx[n]].next = n_idx; + n_idx = idx[n]; + c++; + } + + g_bmpcache[id][n_idx].previous = NOT_SET; + g_bmpcache_lru[id] = n_idx; + + if (c != g_bmpcache_count[id]) + { + error("Oops. %d in bitmap cache linked list, %d in ui cache...\n", c, + g_bmpcache_count[id]); + exit(1); + } +} + +/* Move a bitmap to a new position in the linked list. */ +void +cache_bump_bitmap(uint8 id, uint16 idx, int bump) +{ + int p_idx, n_idx, n; + + if (!IS_PERSISTENT(id)) + return; + + if (g_bmpcache_mru[id] == idx) + return; + + DEBUG_RDP5(("bump bitmap: id=%d, idx=%d, bump=%d\n", id, idx, bump)); + + n_idx = g_bmpcache[id][idx].next; + p_idx = g_bmpcache[id][idx].previous; + + if (IS_SET(n_idx)) + { + /* remove */ + --g_bmpcache_count[id]; + if (IS_SET(p_idx)) + g_bmpcache[id][p_idx].next = n_idx; + else + g_bmpcache_lru[id] = n_idx; + if (IS_SET(n_idx)) + g_bmpcache[id][n_idx].previous = p_idx; + else + g_bmpcache_mru[id] = p_idx; + } + else + { + p_idx = NOT_SET; + n_idx = g_bmpcache_lru[id]; + } + + if (bump >= 0) + { + for (n = 0; n < bump && IS_SET(n_idx); n++) + { + p_idx = n_idx; + n_idx = g_bmpcache[id][p_idx].next; + } + } + else + { + p_idx = g_bmpcache_mru[id]; + n_idx = NOT_SET; + } + + /* insert */ + ++g_bmpcache_count[id]; + g_bmpcache[id][idx].previous = p_idx; + g_bmpcache[id][idx].next = n_idx; + + if (p_idx >= 0) + g_bmpcache[id][p_idx].next = idx; + else + g_bmpcache_lru[id] = idx; + + if (n_idx >= 0) + g_bmpcache[id][n_idx].previous = idx; + else + g_bmpcache_mru[id] = idx; +} + +/* Evict the least-recently used bitmap from the cache */ +void +cache_evict_bitmap(uint8 id) +{ + uint16 idx; + int n_idx; + + if (!IS_PERSISTENT(id)) + return; + + idx = g_bmpcache_lru[id]; + n_idx = g_bmpcache[id][idx].next; + DEBUG_RDP5(("evict bitmap: id=%d idx=%d n_idx=%d bmp=0x%x\n", id, idx, n_idx, + g_bmpcache[id][idx].bitmap)); + + ui_destroy_bitmap(g_bmpcache[id][idx].bitmap); + --g_bmpcache_count[id]; + g_bmpcache[id][idx].bitmap = 0; + + g_bmpcache_lru[id] = n_idx; + g_bmpcache[id][n_idx].previous = NOT_SET; + + pstcache_touch_bitmap(id, idx, 0); } /* Retrieve a bitmap from the cache */ HBITMAP -cache_get_bitmap(uint8 cache_id, uint16 cache_idx) +cache_get_bitmap(uint8 id, uint16 idx) { - HBITMAP *pbitmap; - - if ((cache_id < NUM_ELEMENTS(g_bmpcache)) && (cache_idx < NUM_ELEMENTS(g_bmpcache[0]))) + if ((id < NUM_ELEMENTS(g_bmpcache)) && (idx < NUM_ELEMENTS(g_bmpcache[0]))) { - pbitmap = &g_bmpcache[cache_id][cache_idx].bitmap; - if ((*pbitmap != 0) || pstcache_load_bitmap(cache_id, cache_idx)) + if (g_bmpcache[id][idx].bitmap || pstcache_load_bitmap(id, idx)) { - if (IS_PERSISTENT(cache_id)) - TOUCH(cache_id, cache_idx); + if (IS_PERSISTENT(id)) + cache_bump_bitmap(id, idx, BUMP_COUNT); - return *pbitmap; + return g_bmpcache[id][idx].bitmap; } } - else if ((cache_id < NUM_ELEMENTS(g_volatile_bc)) && (cache_idx == 0x7fff)) + else if ((id < NUM_ELEMENTS(g_volatile_bc)) && (idx == 0x7fff)) { - return g_volatile_bc[cache_id]; + return g_volatile_bc[id]; } - error("get bitmap %d:%d\n", cache_id, cache_idx); + error("get bitmap %d:%d\n", id, idx); return NULL; } /* Store a bitmap in the cache */ void -cache_put_bitmap(uint8 cache_id, uint16 cache_idx, HBITMAP bitmap, uint32 stamp) +cache_put_bitmap(uint8 id, uint16 idx, HBITMAP bitmap) { HBITMAP old; - if ((cache_id < NUM_ELEMENTS(g_bmpcache)) && (cache_idx < NUM_ELEMENTS(g_bmpcache[0]))) + if ((id < NUM_ELEMENTS(g_bmpcache)) && (idx < NUM_ELEMENTS(g_bmpcache[0]))) { - old = g_bmpcache[cache_id][cache_idx].bitmap; + old = g_bmpcache[id][idx].bitmap; if (old != NULL) - { ui_destroy_bitmap(old); - } - else if (g_use_rdp5) - { - if (++g_num_bitmaps_in_memory[cache_id] > BMPCACHE2_C2_CELLS) - cache_remove_lru_bitmap(cache_id); - } + g_bmpcache[id][idx].bitmap = bitmap; - g_bmpcache[cache_id][cache_idx].bitmap = bitmap; - g_bmpcache[cache_id][cache_idx].usage = stamp; + if (IS_PERSISTENT(id)) + { + if (old == NULL) + g_bmpcache[id][idx].previous = g_bmpcache[id][idx].next = NOT_SET; + + cache_bump_bitmap(id, idx, TO_TOP); + if (g_bmpcache_count[id] > BMPCACHE2_C2_CELLS) + cache_evict_bitmap(id); + } } - else if ((cache_id < NUM_ELEMENTS(g_volatile_bc)) && (cache_idx == 0x7fff)) + else if ((id < NUM_ELEMENTS(g_volatile_bc)) && (idx == 0x7fff)) { - old = g_volatile_bc[cache_id]; + old = g_volatile_bc[id]; if (old != NULL) ui_destroy_bitmap(old); - g_volatile_bc[cache_id] = bitmap; + g_volatile_bc[id] = bitmap; } else { - error("put bitmap %d:%d\n", cache_id, cache_idx); + error("put bitmap %d:%d\n", id, idx); } } @@ -125,12 +252,21 @@ cache_put_bitmap(uint8 cache_id, uint16 cache_idx, HBITMAP bitmap, uint32 stamp) void cache_save_state(void) { - uint32 id, idx; + uint32 id = 0, t = 0; + int idx; for (id = 0; id < NUM_ELEMENTS(g_bmpcache); id++) if (IS_PERSISTENT(id)) - for (idx = 0; idx < NUM_ELEMENTS(g_bmpcache[id]); idx++) - pstcache_touch_bitmap(id, idx, g_bmpcache[id][idx].usage); + { + DEBUG_RDP5(("Saving cache state for bitmap cache %d...", id)); + idx = g_bmpcache_lru[id]; + while (idx >= 0) + { + pstcache_touch_bitmap(id, idx, ++t); + idx = g_bmpcache[id][idx].next; + } + DEBUG_RDP5((" %d stamps written.\n", t)); + } } diff --git a/orders.c b/orders.c index 24f4025..e092ef5 100644 --- a/orders.c +++ b/orders.c @@ -688,7 +688,7 @@ process_raw_bmpcache(STREAM s) bitmap = ui_create_bitmap(width, height, inverted); xfree(inverted); - cache_put_bitmap(cache_id, cache_idx, bitmap, 0); + cache_put_bitmap(cache_id, cache_idx, bitmap); } /* Process a bitmap cache order */ @@ -737,7 +737,7 @@ process_bmpcache(STREAM s) if (bitmap_decompress(bmpdata, width, height, data, size, Bpp)) { bitmap = ui_create_bitmap(width, height, bmpdata); - cache_put_bitmap(cache_id, cache_idx, bitmap, 0); + cache_put_bitmap(cache_id, cache_idx, bitmap); } else { @@ -814,10 +814,10 @@ process_bmpcache2(STREAM s, uint16 flags, BOOL compressed) if (bitmap) { - cache_put_bitmap(cache_id, cache_idx, bitmap, 0); + cache_put_bitmap(cache_id, cache_idx, bitmap); if (flags & PERSIST) - pstcache_put_bitmap(cache_id, cache_idx, bitmap_id, width, height, - width * height * Bpp, bmpdata); + pstcache_save_bitmap(cache_id, cache_idx, bitmap_id, width, height, + width * height * Bpp, bmpdata); } else { diff --git a/proto.h b/proto.h index 233af76..0d23cb0 100644 --- a/proto.h +++ b/proto.h @@ -1,8 +1,9 @@ /* bitmap.c */ BOOL bitmap_decompress(uint8 * output, int width, int height, uint8 * input, int size, int Bpp); /* cache.c */ +void cache_rebuild_bmpcache_linked_list(uint8 cache_id, sint16 * cache_idx, int count); HBITMAP cache_get_bitmap(uint8 cache_id, uint16 cache_idx); -void cache_put_bitmap(uint8 cache_id, uint16 cache_idx, HBITMAP bitmap, uint32 stamp); +void cache_put_bitmap(uint8 cache_id, uint16 cache_idx, HBITMAP bitmap); void cache_save_state(void); FONTGLYPH *cache_get_font(uint8 font, uint16 character); void cache_put_font(uint8 font, uint16 character, uint16 offset, uint16 baseline, uint16 width, @@ -66,9 +67,9 @@ void printercache_process(STREAM s); /* pstcache.c */ void pstcache_touch_bitmap(uint8 id, uint16 idx, uint32 stamp); BOOL pstcache_load_bitmap(uint8 id, uint16 idx); -BOOL pstcache_put_bitmap(uint8 id, uint16 idx, uint8 * bmp_id, uint16 wd, - uint16 ht, uint16 len, uint8 * data); -int pstcache_enumerate(uint8 id, uint8 * list); +BOOL pstcache_save_bitmap(uint8 id, uint16 idx, uint8 * hash_key, uint16 wd, + uint16 ht, uint16 len, uint8 * data); +int pstcache_enumerate(uint8 id, HASH_KEY * keylist); BOOL pstcache_init(uint8 id); /* rdesktop.c */ int main(int argc, char *argv[]); diff --git a/pstcache.c b/pstcache.c index f20a64f..6626737 100644 --- a/pstcache.c +++ b/pstcache.c @@ -1,7 +1,7 @@ /* rdesktop: A Remote Desktop Protocol client. Persistent Bitmap Cache routines - Copyright (C) Jeroen Meijer 2004 + Copyright (C) Jeroen Meijer 2004-2005 This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -25,7 +25,6 @@ #define IS_PERSISTENT(id) (id < 8 && g_pstcache_fd[id] > 0) extern int g_server_bpp; -extern uint32 g_stamp; extern BOOL g_bitmap_cache; extern BOOL g_bitmap_cache_persist_enable; extern BOOL g_bitmap_cache_precache; @@ -33,10 +32,10 @@ extern BOOL g_bitmap_cache_precache; int g_pstcache_fd[8]; int g_pstcache_Bpp; BOOL g_pstcache_enumerated = False; -uint8 zero_id[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; +uint8 zero_key[] = { 0, 0, 0, 0, 0, 0, 0, 0 }; -/* Update usage info for a bitmap */ +/* Update mru stamp/index for a bitmap */ void pstcache_touch_bitmap(uint8 cache_id, uint16 cache_idx, uint32 stamp) { @@ -71,10 +70,9 @@ pstcache_load_bitmap(uint8 cache_id, uint16 cache_idx) celldata = (uint8 *) xmalloc(cellhdr.length); rd_read_file(fd, celldata, cellhdr.length); - DEBUG(("Loading bitmap from disk (%d:%d)\n", cache_id, cache_idx)); - bitmap = ui_create_bitmap(cellhdr.width, cellhdr.height, celldata); - cache_put_bitmap(cache_id, cache_idx, bitmap, cellhdr.stamp); + DEBUG(("Load bitmap from disk: id=%d, idx=%d, bmp=0x%x)\n", cache_id, cache_idx, bitmap)); + cache_put_bitmap(cache_id, cache_idx, bitmap); xfree(celldata); return True; @@ -82,8 +80,8 @@ pstcache_load_bitmap(uint8 cache_id, uint16 cache_idx) /* Store a bitmap in the persistent cache */ BOOL -pstcache_put_bitmap(uint8 cache_id, uint16 cache_idx, uint8 * bitmap_id, - uint16 width, uint16 height, uint16 length, uint8 * data) +pstcache_save_bitmap(uint8 cache_id, uint16 cache_idx, uint8 * key, + uint16 width, uint16 height, uint16 length, uint8 * data) { int fd; CELLHEADER cellhdr; @@ -91,7 +89,7 @@ pstcache_put_bitmap(uint8 cache_id, uint16 cache_idx, uint8 * bitmap_id, if (!IS_PERSISTENT(cache_id) || cache_idx >= BMPCACHE2_NUM_PSTCELLS) return False; - memcpy(cellhdr.bitmap_id, bitmap_id, sizeof(BITMAP_ID)); + memcpy(cellhdr.key, key, sizeof(HASH_KEY)); cellhdr.width = width; cellhdr.height = height; cellhdr.length = length; @@ -105,45 +103,47 @@ pstcache_put_bitmap(uint8 cache_id, uint16 cache_idx, uint8 * bitmap_id, return True; } -/* list the bitmaps from the persistent cache file */ +/* List the bitmap keys from the persistent cache file */ int -pstcache_enumerate(uint8 cache_id, uint8 * idlist) +pstcache_enumerate(uint8 id, HASH_KEY * keylist) { - int fd, n, c = 0; + int fd, idx, n; + sint16 mru_idx[0xa00]; + uint32 mru_stamp[0xa00]; CELLHEADER cellhdr; - if (!(g_bitmap_cache && g_bitmap_cache_persist_enable && IS_PERSISTENT(cache_id))) + if (!(g_bitmap_cache && g_bitmap_cache_persist_enable && IS_PERSISTENT(id))) return 0; /* The server disconnects if the bitmap cache content is sent more than once */ if (g_pstcache_enumerated) return 0; - DEBUG(("pstcache enumeration... ")); - for (n = 0; n < BMPCACHE2_NUM_PSTCELLS; n++) + DEBUG_RDP5(("Persistent bitmap cache enumeration... ")); + for (idx = 0; idx < BMPCACHE2_NUM_PSTCELLS; idx++) { - fd = g_pstcache_fd[cache_id]; - rd_lseek_file(fd, n * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER))); + fd = g_pstcache_fd[id]; + rd_lseek_file(fd, idx * (g_pstcache_Bpp * MAX_CELL_SIZE + sizeof(CELLHEADER))); if (rd_read_file(fd, &cellhdr, sizeof(CELLHEADER)) <= 0) break; - if (memcmp(cellhdr.bitmap_id, zero_id, sizeof(BITMAP_ID)) != 0) + if (memcmp(cellhdr.key, zero_key, sizeof(HASH_KEY)) != 0) { - memcpy(idlist + n * sizeof(BITMAP_ID), cellhdr.bitmap_id, - sizeof(BITMAP_ID)); + memcpy(keylist[idx], cellhdr.key, sizeof(HASH_KEY)); - if (cellhdr.stamp) + /* Pre-cache (not possible for 8bpp because 8bpp needs a colourmap) */ + if (g_bitmap_cache_precache && cellhdr.stamp && g_server_bpp > 8) + pstcache_load_bitmap(id, idx); + + /* Sort by stamp */ + for (n = idx; n > 0 && cellhdr.stamp < mru_stamp[n - 1]; n--) { - /* Pre-caching is not possible with 8bpp because a colourmap - * is needed to load them */ - if (g_bitmap_cache_precache && (g_server_bpp > 8)) - { - if (pstcache_load_bitmap(cache_id, n)) - c++; - } - - g_stamp = MAX(g_stamp, cellhdr.stamp); + mru_idx[n] = mru_idx[n - 1]; + mru_stamp[n] = mru_stamp[n - 1]; } + + mru_idx[n] = idx; + mru_stamp[n] = cellhdr.stamp; } else { @@ -151,9 +151,11 @@ pstcache_enumerate(uint8 cache_id, uint8 * idlist) } } - DEBUG(("%d bitmaps in persistent cache, %d bitmaps loaded in memory\n", n, c)); + DEBUG_RDP5(("%d cached bitmaps.\n", idx)); + + cache_rebuild_bmpcache_linked_list(id, mru_idx, idx); g_pstcache_enumerated = True; - return n; + return idx; } /* initialise the persistent bitmap cache */ diff --git a/rdp.c b/rdp.c index bf9fb4b..6349550 100644 --- a/rdp.c +++ b/rdp.c @@ -386,22 +386,22 @@ static void rdp_enum_bmpcache2(void) { STREAM s; - uint8 idlist[BMPCACHE2_NUM_PSTCELLS * sizeof(BITMAP_ID)]; - uint32 nids, offset, count, flags; + HASH_KEY keylist[BMPCACHE2_NUM_PSTCELLS]; + uint32 num_keys, offset, count, flags; offset = 0; - nids = pstcache_enumerate(2, idlist); + num_keys = pstcache_enumerate(2, keylist); - while (offset < nids) + while (offset < num_keys) { - count = MIN(nids - offset, 169); + count = MIN(num_keys - offset, 169); - s = rdp_init_data(24 + count * sizeof(BITMAP_ID)); + s = rdp_init_data(24 + count * sizeof(HASH_KEY)); flags = 0; if (offset == 0) flags |= PDU_FLAG_FIRST; - if (nids - offset <= 169) + if (num_keys - offset <= 169) flags |= PDU_FLAG_LAST; /* header */ @@ -411,12 +411,12 @@ rdp_enum_bmpcache2(void) out_uint16_le(s, 0); out_uint16_le(s, 0); out_uint16_le(s, 0); - out_uint16_le(s, nids); + out_uint16_le(s, num_keys); out_uint32_le(s, 0); out_uint32_le(s, flags); /* list */ - out_uint8a(s, idlist + offset * sizeof(BITMAP_ID), count * sizeof(BITMAP_ID)); + out_uint8a(s, keylist[offset], count * sizeof(HASH_KEY)); s_mark_end(s); rdp_send_data(s, 0x2b); diff --git a/types.h b/types.h index 279c747..43d0d03 100644 --- a/types.h +++ b/types.h @@ -119,22 +119,13 @@ typedef struct _VCHANNEL } VCHANNEL; -typedef struct _BMPCACHEENTRY -{ - HBITMAP bitmap; - uint32 usage; - -} -BMPCACHEENTRY; - /* PSTCACHE */ - -typedef uint8 BITMAP_ID[8]; +typedef uint8 HASH_KEY[8]; /* Header for an entry in the persistent bitmap cache file */ typedef struct _PSTCACHE_CELLHEADER { - BITMAP_ID bitmap_id; + HASH_KEY key; uint8 width, height; uint16 length; uint32 stamp;