#include #include "gimprc.h" #include "tile_cache.h" #include "tile_swap.h" /* This is the percentage of the maximum cache size that should be cleared * from the cache when an eviction is necessary */ #define FREE_QUANTUM 0.1 static void tile_cache_init (void); static void tile_cache_zorch_next (void); static guint tile_cache_hash (Tile *tile); static int initialize = TRUE; static GHashTable *tile_hash_table = NULL; static GList *tile_list_head = NULL; static GList *tile_list_tail = NULL; static unsigned long max_tile_size = TILE_WIDTH * TILE_HEIGHT * 4; static unsigned long cur_cache_size = 0; static unsigned long max_cache_size = 0; void tile_cache_insert (Tile *tile) { GList *tmp; if (initialize) tile_cache_init (); /* First check and see if the tile is already * in the cache. In that case we will simply place * it at the end of the tile list to indicate that * it was the most recently accessed tile. */ tmp = g_hash_table_lookup (tile_hash_table, tile); if (tmp) { /* The tile was already in the cache. Place it at * the end of the tile list. */ if (tmp == tile_list_tail) tile_list_tail = tile_list_tail->prev; tile_list_head = g_list_remove_link (tile_list_head, tmp); if (!tile_list_head) tile_list_tail = NULL; g_list_free (tmp); /* Remove the old reference to the tiles list node * in the tile hash table. */ g_hash_table_remove (tile_hash_table, tile); tile_list_tail = g_list_append (tile_list_tail, tile); if (!tile_list_head) tile_list_head = tile_list_tail; tile_list_tail = g_list_last (tile_list_tail); /* Add the tiles list node to the tile hash table. The * list node is indexed by the tile itself. This makes * for a quick lookup of which list node the tile is in. */ g_hash_table_insert (tile_hash_table, tile, tile_list_tail); } else { /* The tile was not in the cache. First check and see * if there is room in the cache. If not then we'll have * to make room first. Note: it might be the case that the * cache is smaller than the size of a tile in which case * it won't be possible to put it in the cache. */ if ((cur_cache_size + max_tile_size) > max_cache_size) { while (tile_list_head && (cur_cache_size + max_cache_size * FREE_QUANTUM) > max_cache_size) tile_cache_zorch_next (); if ((cur_cache_size + max_tile_size) > max_cache_size) return; } /* Place the tile at the end of the tile list. */ tile_list_tail = g_list_append (tile_list_tail, tile); if (!tile_list_head) tile_list_head = tile_list_tail; tile_list_tail = g_list_last (tile_list_tail); /* Add the tiles list node to the tile hash table. */ g_hash_table_insert (tile_hash_table, tile, tile_list_tail); /* Note the increase in the number of bytes the cache * is referencing. */ cur_cache_size += tile_size (tile); /* Reference the tile so that it won't be swapped out * to disk. Swap the tile in if necessary. * "tile_ref" cannot be used here since it calls this * function. */ tile->ref_count += 1; { extern int tile_ref_count; tile_ref_count += 1; } if (tile->ref_count == 1) { tile_swap_in (tile); /* the tile must be clean */ tile->dirty = FALSE; } } } void tile_cache_flush (Tile *tile) { GList *tmp; if (initialize) tile_cache_init (); /* Find where the tile is in the cache. */ tmp = g_hash_table_lookup (tile_hash_table, tile); if (tmp) { /* If the tile is in the cache, then remove it from the * tile list. */ if (tmp == tile_list_tail) tile_list_tail = tile_list_tail->prev; tile_list_head = g_list_remove_link (tile_list_head, tmp); if (!tile_list_head) tile_list_tail = NULL; g_list_free (tmp); /* Remove the tile from the tile hash table. */ g_hash_table_remove (tile_hash_table, tile); /* Note the decrease in the number of bytes the cache * is referencing. */ cur_cache_size -= tile_size (tile); /* Unreference the tile. * "tile_unref" may be used here since it does not call * this function (or any of the cache functions). */ tile_unref (tile, FALSE); } } void tile_cache_set_size (unsigned long cache_size) { if (initialize) tile_cache_init (); max_cache_size = cache_size; if (max_cache_size >= max_tile_size) { while (cur_cache_size > max_cache_size) tile_cache_zorch_next (); } } static void tile_cache_init () { if (initialize) { initialize = FALSE; tile_hash_table = g_hash_table_new ((GHashFunc) tile_cache_hash, NULL); max_cache_size = tile_cache_size; } } static void tile_cache_zorch_next () { if (tile_list_head) tile_cache_flush ((Tile*) tile_list_head->data); } static guint tile_cache_hash (Tile *tile) { return (gulong) tile; }