| /* |
| Copyright (C) 2009-2010 Samsung Electronics |
| Copyright (C) 2009-2010 ProFUSION embedded systems |
| |
| This library is free software; you can redistribute it and/or |
| modify it under the terms of the GNU Library General Public |
| License as published by the Free Software Foundation; either |
| version 2 of the License, or (at your option) any later version. |
| |
| This library is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
| Library General Public License for more details. |
| |
| You should have received a copy of the GNU Library General Public License |
| along with this library; see the file COPYING.LIB. If not, write to |
| the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, |
| Boston, MA 02110-1301, USA. |
| */ |
| |
| #define __STDC_FORMAT_MACROS |
| #include "config.h" |
| |
| #include "ewk_private.h" |
| #include "ewk_tiled_backing_store_private.h" |
| #include "ewk_tiled_model_private.h" |
| #include <Ecore_Evas.h> |
| #include <Eina.h> |
| #include <eina_safety_checks.h> |
| #include <errno.h> |
| #include <inttypes.h> |
| #include <stdio.h> // XXX REMOVE ME LATER |
| #include <stdlib.h> |
| #include <string.h> |
| |
| #ifdef TILE_STATS_ACCOUNT_RENDER_TIME |
| #include <sys/time.h> |
| #endif |
| |
| #define IDX(col, row, rowspan) (col + (rowidth * rowspan)) |
| |
| #ifdef DEBUG_MEM_LEAKS |
| static uint64_t tiles_allocated = 0; |
| static uint64_t tiles_freed = 0; |
| static uint64_t bytes_allocated = 0; |
| static uint64_t bytes_freed = 0; |
| |
| struct tile_account { |
| Evas_Coord size; |
| struct { |
| uint64_t allocated; |
| uint64_t freed; |
| } tiles, bytes; |
| }; |
| |
| static size_t accounting_len = 0; |
| static struct tile_account* accounting = 0; |
| |
| static inline struct tile_account* _ewk_tile_account_get(const Ewk_Tile* tile) |
| { |
| struct tile_account* acc; |
| |
| for (size_t i = 0; i < accounting_len; i++) { |
| if (accounting[i].size == tile->width) |
| return accounting + i; |
| } |
| |
| accounting = static_cast<struct tile_account*>(realloc(accounting, sizeof(struct tile_account) * (accounting_len + 1))); |
| |
| acc = accounting + accounting_len; |
| acc->size = tile->width; |
| acc->tiles.allocated = 0; |
| acc->tiles.freed = 0; |
| acc->bytes.allocated = 0; |
| acc->bytes.freed = 0; |
| |
| accounting_len++; |
| |
| return acc; |
| } |
| |
| static inline void _ewk_tile_account_allocated(const Ewk_Tile* tile) |
| { |
| struct tile_account* acc = _ewk_tile_account_get(tile); |
| if (!acc) |
| return; |
| acc->bytes.allocated += tile->bytes; |
| acc->tiles.allocated++; |
| |
| bytes_allocated += tile->bytes; |
| tiles_allocated++; |
| } |
| |
| static inline void _ewk_tile_account_freed(const Ewk_Tile* tile) |
| { |
| struct tile_account* acc = _ewk_tile_account_get(tile); |
| if (!acc) |
| return; |
| |
| acc->bytes.freed += tile->bytes; |
| acc->tiles.freed++; |
| |
| bytes_freed += tile->bytes; |
| tiles_freed++; |
| } |
| |
| void ewk_tile_accounting_dbg() |
| { |
| struct tile_account* acc; |
| struct tile_account* acc_end; |
| |
| printf("TILE BALANCE: tiles[+%" PRIu64 ",-%" PRIu64 ":%" PRIu64 "] " |
| "bytes[+%" PRIu64 ",-%" PRIu64 ":%" PRIu64 "]\n", |
| tiles_allocated, tiles_freed, tiles_allocated - tiles_freed, |
| bytes_allocated, bytes_freed, bytes_allocated - bytes_freed); |
| |
| if (!accounting_len) |
| return; |
| |
| acc = accounting; |
| acc_end = acc + accounting_len; |
| printf("BEGIN: TILE BALANCE DETAILS (TO THIS MOMENT!):\n"); |
| for (; acc < acc_end; acc++) { |
| uint64_t tiles, bytes; |
| |
| tiles = acc->tiles.allocated - acc->tiles.freed; |
| bytes = acc->bytes.allocated - acc->bytes.freed; |
| |
| printf(" %4d: tiles[+%4" PRIu64 ",-%4" PRIu64 ":%4" PRIu64 "] " |
| "bytes[+%8" PRIu64 ",-%8" PRIu64 ":%8" PRIu64 "]%s\n", |
| acc->size, |
| acc->tiles.allocated, acc->tiles.freed, tiles, |
| acc->bytes.allocated, acc->bytes.freed, bytes, |
| (bytes || tiles) ? " POSSIBLE LEAK" : ""); |
| } |
| printf("END: TILE BALANCE DETAILS (TO THIS MOMENT!):\n"); |
| } |
| #else |
| |
| static inline void _ewk_tile_account_allocated(const Ewk_Tile*) { } |
| static inline void _ewk_tile_account_freed(const Ewk_Tile*) { } |
| |
| void ewk_tile_accounting_dbg() |
| { |
| printf("compile webkit with DEBUG_MEM_LEAKS defined!\n"); |
| } |
| #endif |
| |
| /** |
| * Create a new tile of given size, zoom level and colorspace. |
| * |
| * After created these properties are immutable as they're the basic |
| * characteristic of the tile and any change will lead to invalid |
| * memory access. |
| * |
| * Other members are of free-access and no getters/setters are |
| * provided in orderr to avoid expensive operations on those, however |
| * some are better manipulated with provided functions, such as |
| * ewk_tile_show() and ewk_tile_hide() to change |
| * @c visible or ewk_tile_update_full(), ewk_tile_update_area(), |
| * ewk_tile_updates_clear() to change @c stats.misses, |
| * @c stats.full_update and @c updates. |
| */ |
| Ewk_Tile* ewk_tile_new(Evas* evas, Evas_Coord width, Evas_Coord height, float zoom, Evas_Colorspace colorSpace) |
| { |
| Evas_Coord* evasCoord; |
| Evas_Colorspace* evasColorSpace; |
| float* tileZoom; |
| size_t* tileSize; |
| Ewk_Tile* tile; |
| unsigned int area; |
| size_t bytes; |
| Ecore_Evas* ecoreEvas; |
| const char* engine; |
| |
| area = width * height; |
| |
| if (colorSpace == EVAS_COLORSPACE_ARGB8888) |
| bytes = area * 4; |
| else if (colorSpace == EVAS_COLORSPACE_RGB565_A5P) |
| bytes = area * 2; |
| else { |
| ERR("unknown color space: %d", colorSpace); |
| return 0; |
| } |
| |
| DBG("size: %dx%d (%d), zoom: %f, cspace=%d", width, height, area, (double)zoom, colorSpace); |
| |
| tile = static_cast<Ewk_Tile*>(malloc(sizeof(Ewk_Tile))); |
| if (!tile) |
| return 0; |
| |
| tile->image = evas_object_image_add(evas); |
| |
| ecoreEvas = ecore_evas_ecore_evas_get(evas); |
| engine = ecore_evas_engine_name_get(ecoreEvas); |
| if (engine && !strcmp(engine, "opengl_x11")) |
| evas_object_image_content_hint_set(tile->image, EVAS_IMAGE_CONTENT_HINT_DYNAMIC); |
| |
| tile->visible = 0; |
| tile->updates = 0; |
| |
| memset(&tile->stats, 0, sizeof(Ewk_Tile_Stats)); |
| tile->stats.area = area; |
| |
| /* ugly, but let's avoid at all costs having users to modify those */ |
| evasCoord = (Evas_Coord*)&tile->width; |
| *evasCoord = width; |
| |
| evasCoord = (Evas_Coord*)&tile->height; |
| *evasCoord = height; |
| |
| evasColorSpace = (Evas_Colorspace*)&tile->cspace; |
| *evasColorSpace = colorSpace; |
| |
| tileZoom = (float*)&tile->zoom; |
| *tileZoom = zoom; |
| |
| tileSize = (size_t*)&tile->bytes; |
| *tileSize = bytes; |
| |
| evas_object_image_size_set(tile->image, tile->width, tile->height); |
| evas_object_image_colorspace_set(tile->image, tile->cspace); |
| _ewk_tile_account_allocated(tile); |
| |
| return tile; |
| } |
| |
| /** |
| * Free tile memory. |
| */ |
| void ewk_tile_free(Ewk_Tile* tile) |
| { |
| _ewk_tile_account_freed(tile); |
| |
| if (tile->updates) |
| eina_tiler_free(tile->updates); |
| |
| evas_object_del(tile->image); |
| free(tile); |
| } |
| |
| /** |
| * @internal |
| * Returns memory size used by given tile |
| * |
| * @param t tile to size check |
| * @return Returns used memory or zero if object is NULL. |
| */ |
| size_t ewk_tile_memory_size_get(const Ewk_Tile* tile) |
| { |
| EINA_SAFETY_ON_NULL_RETURN_VAL(tile, 0); |
| return sizeof(Ewk_Tile) + tile->bytes; |
| } |
| |
| /** |
| * Make the tile visible, incrementing its counter. |
| */ |
| void ewk_tile_show(Ewk_Tile* tile) |
| { |
| tile->visible++; |
| evas_object_show(tile->image); |
| } |
| |
| /** |
| * Decrement the visibility counter, making it invisible if necessary. |
| */ |
| void ewk_tile_hide(Ewk_Tile* tile) |
| { |
| tile->visible--; |
| if (!tile->visible) |
| evas_object_hide(tile->image); |
| } |
| |
| /** |
| * Returns true if the tile is visible, false otherwise. |
| */ |
| Eina_Bool ewk_tile_visible_get(Ewk_Tile* tile) |
| { |
| return !!tile->visible; |
| } |
| |
| /** |
| * Mark whole tile as dirty and requiring update. |
| */ |
| void ewk_tile_update_full(Ewk_Tile* tile) |
| { |
| /* TODO: list of tiles pending updates? */ |
| tile->stats.misses++; |
| |
| if (!tile->stats.full_update) { |
| tile->stats.full_update = true; |
| if (tile->updates) { |
| eina_tiler_free(tile->updates); |
| tile->updates = 0; |
| } |
| } |
| } |
| |
| /** |
| * Mark the specific subarea as dirty and requiring update. |
| */ |
| void ewk_tile_update_area(Ewk_Tile* tile, const Eina_Rectangle* rect) |
| { |
| /* TODO: list of tiles pending updates? */ |
| tile->stats.misses++; |
| |
| if (tile->stats.full_update) |
| return; |
| |
| if (!rect->x && !rect->y && rect->w == tile->width && rect->h == tile->height) { |
| tile->stats.full_update = true; |
| if (tile->updates) { |
| eina_tiler_free(tile->updates); |
| tile->updates = 0; |
| } |
| return; |
| } |
| |
| if (!tile->updates) { |
| tile->updates = eina_tiler_new(tile->width, tile->height); |
| if (!tile->updates) { |
| CRITICAL("could not create eina_tiler %dx%d.", tile->width, tile->height); |
| return; |
| } |
| } |
| |
| eina_tiler_rect_add(tile->updates, rect); |
| } |
| |
| /** |
| * For each updated region, call the given function. |
| * |
| * This will not change the tile statistics or clear the processed |
| * updates, use ewk_tile_updates_clear() for that. |
| */ |
| void ewk_tile_updates_process(Ewk_Tile* tile, void (*callback)(void* data, Ewk_Tile* tile, const Eina_Rectangle* update), const void* data) |
| { |
| if (tile->stats.full_update) { |
| Eina_Rectangle rect; |
| rect.x = 0; |
| rect.y = 0; |
| rect.w = tile->width; |
| rect.h = tile->height; |
| #ifdef TILE_STATS_ACCOUNT_RENDER_TIME |
| struct timeval timev; |
| double renderStartTime; |
| gettimeofday(&timev, 0); |
| renderStartTime = (double)timev.tv_sec + |
| (((double)timev.tv_usec) / 1000000); |
| #endif |
| callback((void*)data, tile, &rect); |
| #ifdef TILE_STATS_ACCOUNT_RENDER_TIME |
| gettimeofday(&timev, 0); |
| tile->stats.render_time = (double)timev.tv_sec + |
| (((double)timev.tv_usec) / 1000000) - renderStartTime; |
| #endif |
| } else if (tile->updates) { |
| Eina_Iterator* itr = eina_tiler_iterator_new(tile->updates); |
| Eina_Rectangle* rect; |
| if (!itr) { |
| CRITICAL("could not create tiler iterator!"); |
| return; |
| } |
| EINA_ITERATOR_FOREACH(itr, rect) |
| callback((void*)data, tile, rect); |
| eina_iterator_free(itr); |
| } |
| } |
| |
| /** |
| * Clear all updates in region, if any. |
| * |
| * This will change the tile statistics, specially zero stat.misses |
| * and unset stats.full_update. If tile->updates existed, then it will be |
| * destroyed. |
| * |
| * This function is usually called after ewk_tile_updates_process() is |
| * called. |
| */ |
| void ewk_tile_updates_clear(Ewk_Tile* tile) |
| { |
| /* TODO: remove from list of pending updates? */ |
| tile->stats.misses = 0; |
| |
| if (tile->stats.full_update) |
| tile->stats.full_update = 0; |
| else if (tile->updates) { |
| eina_tiler_free(tile->updates); |
| tile->updates = 0; |
| } |
| } |
| |
| typedef struct _Ewk_Tile_Unused_Cache_Entry Ewk_Tile_Unused_Cache_Entry; |
| struct _Ewk_Tile_Unused_Cache_Entry { |
| Ewk_Tile* tile; |
| int weight; |
| struct { |
| void (*callback)(void* data, Ewk_Tile* tile); |
| void* data; |
| } tile_free; |
| }; |
| |
| struct _Ewk_Tile_Unused_Cache { |
| struct { |
| Eina_List* list; |
| size_t count; |
| size_t allocated; |
| } entries; |
| struct { |
| size_t max; /**< watermark (in bytes) to start freeing tiles */ |
| size_t used; /**< in bytes, maybe more than max. */ |
| } memory; |
| struct { |
| Evas_Coord x, y, width, height; |
| float zoom; |
| Eina_Bool locked; |
| } locked; |
| int references; |
| unsigned int frozen; |
| }; |
| |
| static const size_t TILE_UNUSED_CACHE_ALLOCATE_INITIAL = 128; |
| static const size_t TILE_UNUSED_CACHE_ALLOCATE_STEP = 16; |
| static const size_t TILE_UNUSED_CACHE_MAX_FREE = 32; |
| |
| /** |
| * Cache of unused tiles (those that are not visible). |
| * |
| * The cache of unused tiles. |
| * |
| * @param max cache size in bytes. |
| * |
| * @return newly allocated cache of unused tiles, use |
| * ewk_tile_unused_cache_free() to release resources. If not |
| * possible to allocate memory, @c 0 is returned. |
| */ |
| Ewk_Tile_Unused_Cache* ewk_tile_unused_cache_new(size_t max) |
| { |
| Ewk_Tile_Unused_Cache* tileUnusedCache; |
| |
| tileUnusedCache = new Ewk_Tile_Unused_Cache; |
| memset(tileUnusedCache, 0, sizeof(Ewk_Tile_Unused_Cache)); |
| |
| DBG("tileUnusedCache=%p", tileUnusedCache); |
| tileUnusedCache->memory.max = max; |
| tileUnusedCache->references = 1; |
| return tileUnusedCache; |
| } |
| |
| void ewk_tile_unused_cache_lock_area(Ewk_Tile_Unused_Cache* tileUnusedCache, Evas_Coord x, Evas_Coord y, Evas_Coord width, Evas_Coord height, float zoom) |
| { |
| EINA_SAFETY_ON_NULL_RETURN(tileUnusedCache); |
| |
| tileUnusedCache->locked.locked = true; |
| tileUnusedCache->locked.x = x; |
| tileUnusedCache->locked.y = y; |
| tileUnusedCache->locked.width = width; |
| tileUnusedCache->locked.height = height; |
| tileUnusedCache->locked.zoom = zoom; |
| } |
| |
| void ewk_tile_unused_cache_unlock_area(Ewk_Tile_Unused_Cache* tileUnusedCache) |
| { |
| EINA_SAFETY_ON_NULL_RETURN(tileUnusedCache); |
| |
| tileUnusedCache->locked.locked = false; |
| } |
| |
| /** |
| * Free cache of unused tiles. |
| * |
| * This function should be only called by ewk_tile_unused_cache_unref |
| * function. Calling this function without considering reference counting |
| * may lead to unknown results. |
| * |
| * Those tiles that are still visible will remain live. The unused |
| * tiles will be freed. |
| * |
| * @see ewk_tile_unused_cache_unref() |
| */ |
| static void _ewk_tile_unused_cache_free(Ewk_Tile_Unused_Cache* tileUnusedCache) |
| { |
| EINA_SAFETY_ON_NULL_RETURN(tileUnusedCache); |
| |
| DBG("tileUnusedCache=%p, " |
| "entries=(count:%zd, allocated:%zd), " |
| "memory=(max:%zd, used:%zd)", |
| tileUnusedCache, tileUnusedCache->entries.count, tileUnusedCache->entries.allocated, |
| tileUnusedCache->memory.max, tileUnusedCache->memory.used); |
| |
| ewk_tile_unused_cache_clear(tileUnusedCache); |
| delete tileUnusedCache; |
| } |
| |
| /** |
| * Clear cache of unused tiles. |
| * |
| * Any tiles that are in the cache are freed. The only tiles that are |
| * kept are those that aren't in the cache (i.e. that are visible). |
| */ |
| void ewk_tile_unused_cache_clear(Ewk_Tile_Unused_Cache* tileUnusedCache) |
| { |
| EINA_SAFETY_ON_NULL_RETURN(tileUnusedCache); |
| |
| if (!tileUnusedCache->entries.count) |
| return; |
| |
| void* item; |
| EINA_LIST_FREE(tileUnusedCache->entries.list, item) { |
| Ewk_Tile_Unused_Cache_Entry* itr = static_cast<Ewk_Tile_Unused_Cache_Entry*>(item); |
| itr->tile_free.callback(itr->tile_free.data, itr->tile); |
| delete itr; |
| } |
| |
| tileUnusedCache->memory.used = 0; |
| tileUnusedCache->entries.count = 0; |
| } |
| |
| /** |
| * Hold reference to cache. |
| * |
| * @return same pointer as taken. |
| * |
| * @see ewk_tile_unused_cache_unref() |
| */ |
| Ewk_Tile_Unused_Cache* ewk_tile_unused_cache_ref(Ewk_Tile_Unused_Cache* tileUnusedCache) |
| { |
| EINA_SAFETY_ON_NULL_RETURN_VAL(tileUnusedCache, 0); |
| tileUnusedCache->references++; |
| return tileUnusedCache; |
| } |
| |
| /** |
| * Release cache reference, freeing it if it drops to zero. |
| * |
| * @see ewk_tile_unused_cache_ref() |
| * @see ewk_tile_unused_cache_free() |
| */ |
| void ewk_tile_unused_cache_unref(Ewk_Tile_Unused_Cache* tileUnusedCache) |
| { |
| EINA_SAFETY_ON_NULL_RETURN(tileUnusedCache); |
| tileUnusedCache->references--; |
| if (!tileUnusedCache->references) |
| _ewk_tile_unused_cache_free(tileUnusedCache); |
| } |
| |
| void ewk_tile_unused_cache_max_set(Ewk_Tile_Unused_Cache* tileUnusedCache, size_t max) |
| { |
| EINA_SAFETY_ON_NULL_RETURN(tileUnusedCache); |
| size_t oldMax = tileUnusedCache->memory.max; |
| tileUnusedCache->memory.max = max; |
| /* Cache flush when new max is lower then old one */ |
| if (oldMax > max) |
| ewk_tile_unused_cache_auto_flush(tileUnusedCache); |
| } |
| |
| size_t ewk_tile_unused_cache_max_get(const Ewk_Tile_Unused_Cache* tileUnusedCache) |
| { |
| EINA_SAFETY_ON_NULL_RETURN_VAL(tileUnusedCache, 0); |
| return tileUnusedCache->memory.max; |
| } |
| |
| size_t ewk_tile_unused_cache_used_get(const Ewk_Tile_Unused_Cache* tileUnusedCache) |
| { |
| EINA_SAFETY_ON_NULL_RETURN_VAL(tileUnusedCache, 0); |
| return tileUnusedCache->memory.used; |
| } |
| |
| size_t ewk_tile_unused_cache_flush(Ewk_Tile_Unused_Cache* tileUnusedCache, size_t bytes) |
| { |
| Eina_List* list, * listNext; |
| EINA_SAFETY_ON_NULL_RETURN_VAL(tileUnusedCache, 0); |
| size_t done; |
| unsigned int count; |
| |
| if (!tileUnusedCache->entries.count) |
| return 0; |
| if (bytes < 1) |
| return 0; |
| |
| /* |
| * NOTE: the cache is a FIFO queue currently. |
| * Don't need to sort any more. |
| */ |
| |
| void* item; |
| done = 0; |
| count = 0; |
| EINA_LIST_FOREACH_SAFE(tileUnusedCache->entries.list, list, listNext, item) { |
| Ewk_Tile_Unused_Cache_Entry* itr = static_cast<Ewk_Tile_Unused_Cache_Entry*>(item); |
| Ewk_Tile* tile = itr->tile; |
| if (done > bytes) |
| break; |
| if (tileUnusedCache->locked.locked |
| && tile->x + tile->width > tileUnusedCache->locked.x |
| && tile->y + tile->height > tileUnusedCache->locked.y |
| && tile->x < tileUnusedCache->locked.x + tileUnusedCache->locked.width |
| && tile->y < tileUnusedCache->locked.y + tileUnusedCache->locked.height |
| && tile->zoom == tileUnusedCache->locked.zoom) { |
| continue; |
| } |
| done += ewk_tile_memory_size_get(itr->tile); |
| itr->tile_free.callback(itr->tile_free.data, itr->tile); |
| tileUnusedCache->entries.list = eina_list_remove_list(tileUnusedCache->entries.list, list); |
| delete itr; |
| count++; |
| } |
| |
| tileUnusedCache->memory.used -= done; |
| tileUnusedCache->entries.count -= count; |
| |
| return done; |
| } |
| |
| void ewk_tile_unused_cache_auto_flush(Ewk_Tile_Unused_Cache* tileUnusedCache) |
| { |
| EINA_SAFETY_ON_NULL_RETURN(tileUnusedCache); |
| if (tileUnusedCache->memory.used <= tileUnusedCache->memory.max) |
| return; |
| ewk_tile_unused_cache_flush(tileUnusedCache, tileUnusedCache->memory.used - tileUnusedCache->memory.max); |
| if (tileUnusedCache->memory.used > tileUnusedCache->memory.max) |
| CRITICAL("Cache still using too much memory: %zd KB; max: %zd KB", |
| tileUnusedCache->memory.used, tileUnusedCache->memory.max); |
| } |
| |
| /** |
| * Freeze cache to not do maintenance tasks. |
| * |
| * Maintenance tasks optimize cache usage, but maybe we know we should |
| * hold on them until we do the last operation, in this case we freeze |
| * while operating and then thaw when we're done. |
| * |
| * @see ewk_tile_unused_cache_thaw() |
| */ |
| void ewk_tile_unused_cache_freeze(Ewk_Tile_Unused_Cache* tileUnusedCache) |
| { |
| tileUnusedCache->frozen++; |
| } |
| |
| /** |
| * Unfreezes maintenance tasks. |
| * |
| * If this is the last counterpart of freeze, then maintenance tasks |
| * will run immediately. |
| */ |
| void ewk_tile_unused_cache_thaw(Ewk_Tile_Unused_Cache* tileUnusedCache) |
| { |
| if (!tileUnusedCache->frozen) { |
| ERR("thawing more than freezing!"); |
| return; |
| } |
| |
| tileUnusedCache->frozen--; |
| } |
| |
| /** |
| * Get tile from cache of unused tiles, removing it from the cache. |
| * |
| * If the tile is used, then it's not in cache of unused tiles, so it |
| * is removed from the cache and may be given back with |
| * ewk_tile_unused_cache_tile_put(). |
| * |
| * @param tileUnusedCache cache of unused tiles |
| * @param tile the tile to be removed from Ewk_Tile_Unused_Cache. |
| * |
| * @return #true on success, #false otherwise. |
| */ |
| Eina_Bool ewk_tile_unused_cache_tile_get(Ewk_Tile_Unused_Cache* tileUnusedCache, Ewk_Tile* tile) |
| { |
| Eina_List* iterateEntry; |
| void* item; |
| |
| EINA_LIST_FOREACH(tileUnusedCache->entries.list, iterateEntry, item) { |
| Ewk_Tile_Unused_Cache_Entry* entry = static_cast<Ewk_Tile_Unused_Cache_Entry*>(item); |
| if (entry->tile == tile) { |
| tileUnusedCache->entries.count--; |
| tileUnusedCache->memory.used -= ewk_tile_memory_size_get(tile); |
| tileUnusedCache->entries.list = eina_list_remove_list(tileUnusedCache->entries.list, iterateEntry); |
| delete entry; |
| |
| return true; |
| } |
| } |
| |
| ERR("tile %p not found in cache %p", tile, tileUnusedCache); |
| return false; |
| } |
| |
| /** |
| * Put tile into cache of unused tiles, adding it to the cache. |
| * |
| * This should be called when @c tile->visible is @c 0 and no objects are |
| * using the tile anymore, making it available to be expired and have |
| * its memory replaced. |
| * |
| * Note that tiles are not automatically deleted if cache is full, |
| * instead the cache will have more bytes used than maximum and one |
| * can call ewk_tile_unused_cache_auto_flush() to free them. This is done |
| * because usually we want a lazy operation for better performance. |
| * |
| * @param tileUnusedCache cache of unused tiles |
| * @param tile tile to be added to cache. |
| * @param tileFreeCallback function used to free tiles. |
| * @param data context to give back to @a tile_free_cb as first argument. |
| * |
| * @return #true on success, #false otherwise. If @c tile->visible |
| * is not #false, then it will return #false. |
| * |
| * @see ewk_tile_unused_cache_auto_flush() |
| */ |
| Eina_Bool ewk_tile_unused_cache_tile_put(Ewk_Tile_Unused_Cache* tileUnusedCache, Ewk_Tile* tile, void (* tileFreeCallback)(void* data, Ewk_Tile* tile), const void* data) |
| { |
| Ewk_Tile_Unused_Cache_Entry* unusedCacheEntry; |
| |
| if (tile->visible) { |
| ERR("tile=%p is not unused (visible=%d)", tile, tile->visible); |
| return false; |
| } |
| |
| unusedCacheEntry = new Ewk_Tile_Unused_Cache_Entry; |
| |
| tileUnusedCache->entries.list = eina_list_append(tileUnusedCache->entries.list, unusedCacheEntry); |
| if (eina_error_get()) { |
| ERR("List allocation failed"); |
| return false; |
| } |
| |
| unusedCacheEntry->tile = tile; |
| unusedCacheEntry->weight = 0; /* calculated just before sort */ |
| unusedCacheEntry->tile_free.callback = tileFreeCallback; |
| unusedCacheEntry->tile_free.data = (void*)data; |
| |
| tileUnusedCache->entries.count++; |
| tileUnusedCache->memory.used += ewk_tile_memory_size_get(tile); |
| |
| return true; |
| } |
| |
| void ewk_tile_unused_cache_dbg(const Ewk_Tile_Unused_Cache* tileUnusedCache) |
| { |
| void* item; |
| Eina_List* list; |
| int count = 0; |
| printf("Cache of unused tiles: entries: %zu/%zu, memory: %zu/%zu\n", |
| tileUnusedCache->entries.count, tileUnusedCache->entries.allocated, |
| tileUnusedCache->memory.used, tileUnusedCache->memory.max); |
| |
| EINA_LIST_FOREACH(tileUnusedCache->entries.list, list, item) { |
| const Ewk_Tile* tile = static_cast<Ewk_Tile_Unused_Cache_Entry*>(item)->tile; |
| printf(" [%3lu,%3lu + %dx%d @ %0.3f]%c", |
| tile->column, tile->row, tile->width, tile->height, tile->zoom, |
| tile->visible ? '*' : ' '); |
| |
| if (!(count % 4)) |
| printf("\n"); |
| } |
| |
| printf("\n"); |
| } |