diff options
Diffstat (limited to 'Source/WebKit/efl/ewk/ewk_tiled_model.c')
-rw-r--r-- | Source/WebKit/efl/ewk/ewk_tiled_model.c | 905 |
1 files changed, 905 insertions, 0 deletions
diff --git a/Source/WebKit/efl/ewk/ewk_tiled_model.c b/Source/WebKit/efl/ewk/ewk_tiled_model.c new file mode 100644 index 0000000..4b81a15 --- /dev/null +++ b/Source/WebKit/efl/ewk/ewk_tiled_model.c @@ -0,0 +1,905 @@ +/* + 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. +*/ + +#include "config.h" +#include "ewk_tiled_model.h" + +#define _GNU_SOURCE +#include "ewk_tiled_backing_store.h" +#include "ewk_tiled_private.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 + +#ifndef CAIRO_FORMAT_RGB16_565 +#define CAIRO_FORMAT_RGB16_565 4 +#endif + +#define IDX(col, row, rowspan) (col + (row * rowspan)) +#define MIN(a, b) ((a < b) ? a : b) +#define MAX(a, b) ((a > b) ? a : b) + +#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 = NULL; + +static inline struct tile_account *_ewk_tile_account_get(const Ewk_Tile *t) +{ + struct tile_account *acc; + size_t i; + + for (i = 0; i < accounting_len; i++) { + if (accounting[i].size == t->w) + return accounting + i; + } + + i = (accounting_len + 1) * sizeof(struct tile_account); + REALLOC_OR_OOM_RET(accounting, i, NULL); + + acc = accounting + accounting_len; + acc->size = t->w; + 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 *t) +{ + struct tile_account *acc = _ewk_tile_account_get(t); + if (!acc) + return; + acc->bytes.allocated += t->bytes; + acc->tiles.allocated++; + + bytes_allocated += t->bytes; + tiles_allocated++; +} + +static inline void _ewk_tile_account_freed(const Ewk_Tile *t) +{ + struct tile_account *acc = _ewk_tile_account_get(t); + if (!acc) + return; + + acc->bytes.freed += t->bytes; + acc->tiles.freed++; + + bytes_freed += t->bytes; + tiles_freed++; +} + +void ewk_tile_accounting_dbg(void) +{ + 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 *t) { } +static inline void _ewk_tile_account_freed(const Ewk_Tile *t) { } + +void ewk_tile_accounting_dbg(void) +{ + printf("compile webkit with DEBUG_MEM_LEAKS defined!\n"); +} +#endif + +static inline void _ewk_tile_paint_rgb888(Ewk_Tile *t, uint8_t r, uint8_t g, uint8_t b) +{ + uint32_t *dst32, *dst32_end, c1; + uint64_t *dst64, *dst64_end, c2; + + c1 = 0xff000000 | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b; + c2 = ((uint64_t)c1 << 32) | c1; + + dst64 = (uint64_t *)t->pixels; + dst64_end = dst64 + ((t->bytes / 8) & ~7); + for (; dst64 < dst64_end; dst64 += 8) { + /* TODO: ARM add pld or NEON instructions */ + dst64[0] = c2; + dst64[1] = c2; + dst64[2] = c2; + dst64[3] = c2; + dst64[4] = c2; + dst64[5] = c2; + dst64[6] = c2; + dst64[7] = c2; + } + + dst32 = (uint32_t *)dst64_end; + dst32_end = (uint32_t *)(t->pixels + t->bytes); + for (; dst32 < dst32_end; dst32++) + *dst32 = c1; +} + +static inline void _ewk_tile_paint_rgb565(Ewk_Tile *t, uint8_t r, uint8_t g, uint8_t b) +{ + uint16_t *dst16, *dst16_end, c1; + uint64_t *dst64, *dst64_end, c2; + + c1 = ((((r >> 3) & 0x1f) << 11) | + (((g >> 2) & 0x3f) << 5) | + ((b >> 3) & 0x1f)); + + c2 = (((uint64_t)c1 << 48) | ((uint64_t)c1 << 32) | + ((uint64_t)c1 << 16) | c1); + + dst64 = (uint64_t *)t->pixels; + dst64_end = dst64 + ((t->bytes / 8) & ~7); + for (; dst64 < dst64_end; dst64 += 8) { + /* TODO: ARM add pld or NEON instructions */ + dst64[0] = c2; + dst64[1] = c2; + dst64[2] = c2; + dst64[3] = c2; + dst64[4] = c2; + dst64[5] = c2; + dst64[6] = c2; + dst64[7] = c2; + } + + dst16 = (uint16_t *)dst16_end; + dst16_end = (uint16_t *)(t->pixels + t->bytes); + for (; dst16 < dst16_end; dst16++) + *dst16 = c1; +} + +static inline void _ewk_tile_paint(Ewk_Tile *t, uint8_t r, uint8_t g, uint8_t b) +{ + if (t->cspace == EVAS_COLORSPACE_ARGB8888) + _ewk_tile_paint_rgb888(t, r, g, b); + else if (t->cspace == EVAS_COLORSPACE_RGB565_A5P) + _ewk_tile_paint_rgb565(t, r, g, b); + else + ERR("unknown color space: %d", t->cspace); +} + +/** + * 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 w, Evas_Coord h, float zoom, Evas_Colorspace cspace) +{ + Eina_Inlist *l; + Evas_Coord *ec; + Evas_Colorspace *ecs; + float *f; + size_t *s; + Ewk_Tile *t; + unsigned int area; + size_t bytes; + cairo_format_t format; + cairo_status_t status; + int stride; + + area = w * h; + + if (cspace == EVAS_COLORSPACE_ARGB8888) { + bytes = area * 4; + stride = w * 4; + format = CAIRO_FORMAT_RGB24; + } else if (cspace == EVAS_COLORSPACE_RGB565_A5P) { + bytes = area * 2; + stride = w * 2; + format = CAIRO_FORMAT_RGB16_565; + } else { + ERR("unknown color space: %d", cspace); + return NULL; + } + + DBG("size: %dx%d (%d), zoom: %f, cspace=%d", + w, h, area, (double)zoom, cspace); + + MALLOC_OR_OOM_RET(t, sizeof(Ewk_Tile), NULL); + t->image = evas_object_image_add(evas); + + l = EINA_INLIST_GET(t); + l->prev = NULL; + l->next = NULL; + + t->visible = 0; + t->updates = NULL; + + memset(&t->stats, 0, sizeof(Ewk_Tile_Stats)); + t->stats.area = area; + + /* ugly, but let's avoid at all costs having users to modify those */ + ec = (Evas_Coord *)&t->w; + *ec = w; + + ec = (Evas_Coord *)&t->h; + *ec = h; + + ecs = (Evas_Colorspace *)&t->cspace; + *ecs = cspace; + + f = (float *)&t->zoom; + *f = zoom; + + s = (size_t *)&t->bytes; + *s = bytes; + + evas_object_image_size_set(t->image, t->w, t->h); + evas_object_image_colorspace_set(t->image, t->cspace); + t->pixels = evas_object_image_data_get(t->image, EINA_TRUE); + t->surface = cairo_image_surface_create_for_data + (t->pixels, format, w, h, stride); + status = cairo_surface_status(t->surface); + if (status != CAIRO_STATUS_SUCCESS) { + ERR("failed to create cairo surface: %s", + cairo_status_to_string(status)); + free(t); + return NULL; + } + + t->cairo = cairo_create(t->surface); + status = cairo_status(t->cairo); + if (status != CAIRO_STATUS_SUCCESS) { + ERR("failed to create cairo: %s", cairo_status_to_string(status)); + cairo_surface_destroy(t->surface); + evas_object_del(t->image); + free(t); + return NULL; + } + + _ewk_tile_account_allocated(t); + + return t; +} + +/** + * Free tile memory. + */ +void ewk_tile_free(Ewk_Tile *t) +{ + _ewk_tile_account_freed(t); + + if (t->updates) + eina_tiler_free(t->updates); + + cairo_surface_destroy(t->surface); + cairo_destroy(t->cairo); + evas_object_del(t->image); + free(t); +} + +/** + * Make the tile visible, incrementing its counter. + */ +void ewk_tile_show(Ewk_Tile *t) +{ + t->visible++; + evas_object_show(t->image); +} + +/** + * Decrement the visibility counter, making it invisible if necessary. + */ +void ewk_tile_hide(Ewk_Tile *t) +{ + t->visible--; + if (!t->visible) + evas_object_hide(t->image); +} + +/** + * Returns EINA_TRUE if the tile is visible, EINA_FALSE otherwise. + */ +Eina_Bool ewk_tile_visible_get(Ewk_Tile *t) +{ + return !!t->visible; +} + +/** + * Mark whole tile as dirty and requiring update. + */ +void ewk_tile_update_full(Ewk_Tile *t) +{ + /* TODO: list of tiles pending updates? */ + t->stats.misses++; + + if (!t->stats.full_update) { + t->stats.full_update = EINA_TRUE; + if (t->updates) { + eina_tiler_free(t->updates); + t->updates = NULL; + } + } +} + +/** + * Mark the specific subarea as dirty and requiring update. + */ +void ewk_tile_update_area(Ewk_Tile *t, const Eina_Rectangle *r) +{ + /* TODO: list of tiles pending updates? */ + t->stats.misses++; + + if (t->stats.full_update) + return; + + if (!r->x && !r->y && r->w == t->w && r->h == t->h) { + t->stats.full_update = EINA_TRUE; + if (t->updates) { + eina_tiler_free(t->updates); + t->updates = NULL; + } + return; + } + + if (!t->updates) { + t->updates = eina_tiler_new(t->w, t->h); + if (!t->updates) { + CRITICAL("could not create eina_tiler %dx%d.", t->w, t->h); + return; + } + } + + eina_tiler_rect_add(t->updates, r); +} + +/** + * 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 *t, void (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update), const void *data) +{ + if (t->stats.full_update) { + Eina_Rectangle r; + r.x = 0; + r.y = 0; + r.w = t->w; + r.h = t->h; +#ifdef TILE_STATS_ACCOUNT_RENDER_TIME + struct timeval timev; + double render_start; + gettimeofday(&timev, NULL); + render_start = (double)timev.tv_sec + + (((double)timev.tv_usec) / 1000000); +#endif + cb((void *)data, t, &r); +#ifdef TILE_STATS_ACCOUNT_RENDER_TIME + gettimeofday(&timev, NULL); + t->stats.render_time = (double)timev.tv_sec + + (((double)timev.tv_usec) / 1000000) - render_start; +#endif + } else if (t->updates) { + Eina_Iterator *itr = eina_tiler_iterator_new(t->updates); + Eina_Rectangle *r; + if (!itr) { + CRITICAL("could not create tiler iterator!"); + return; + } + EINA_ITERATOR_FOREACH(itr, r) + cb((void *)data, t, r); + 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 t->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 *t) +{ + /* TODO: remove from list of pending updates? */ + t->stats.misses = 0; + + if (t->stats.full_update) + t->stats.full_update = 0; + else if (t->updates) { + eina_tiler_free(t->updates); + t->updates = NULL; + } +} + +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 (*cb)(void *data, Ewk_Tile *t); + 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, w, h; + float zoom; + Eina_Bool locked; + } locked; + int references; + unsigned int frozen; + Eina_Bool dirty:1; +}; + +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 NULL is returned. + */ +Ewk_Tile_Unused_Cache *ewk_tile_unused_cache_new(size_t max) +{ + Ewk_Tile_Unused_Cache *tuc; + + CALLOC_OR_OOM_RET(tuc, sizeof(Ewk_Tile_Unused_Cache), NULL); + + DBG("tuc=%p", tuc); + tuc->memory.max = max; + tuc->references = 1; + return tuc; +} + +void ewk_tile_unused_cache_lock_area(Ewk_Tile_Unused_Cache *tuc, Evas_Coord x, Evas_Coord y, Evas_Coord w, Evas_Coord h, float zoom) +{ + EINA_SAFETY_ON_NULL_RETURN(tuc); + + tuc->locked.locked = EINA_TRUE; + tuc->locked.x = x; + tuc->locked.y = y; + tuc->locked.w = w; + tuc->locked.h = h; + tuc->locked.zoom = zoom; +} + +void ewk_tile_unused_cache_unlock_area(Ewk_Tile_Unused_Cache *tuc) +{ + EINA_SAFETY_ON_NULL_RETURN(tuc); + + tuc->locked.locked = EINA_FALSE; +} + +/** + * Free cache of unused tiles. + * + * Those tiles that are still visible will remain live. The unused + * tiles will be freed. + * + * @see ewk_tile_unused_cache_unref() + */ +void ewk_tile_unused_cache_free(Ewk_Tile_Unused_Cache *tuc) +{ + EINA_SAFETY_ON_NULL_RETURN(tuc); + + DBG("tuc=%p, " + "entries=(count:%zd, allocated:%zd), " + "memory=(max:%zd, used:%zd)", + tuc, tuc->entries.count, tuc->entries.allocated, + tuc->memory.max, tuc->memory.used); + + ewk_tile_unused_cache_clear(tuc); + free(tuc); +} + +/** + * 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 *tuc) +{ + Ewk_Tile_Unused_Cache_Entry *itr; + EINA_SAFETY_ON_NULL_RETURN(tuc); + + if (!tuc->entries.count) + return; + + EINA_LIST_FREE(tuc->entries.list, itr) { + itr->tile_free.cb(itr->tile_free.data, itr->tile); + free(itr); + } + + tuc->memory.used = 0; + tuc->entries.count = 0; + tuc->dirty = EINA_FALSE; +} + +/** + * 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 *tuc) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, NULL); + tuc->references++; + return tuc; +} + +/** + * 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 *tuc) +{ + EINA_SAFETY_ON_NULL_RETURN(tuc); + tuc->references--; + if (!tuc->references) + ewk_tile_unused_cache_free(tuc); +} + +/** + * Change cache capacity, in bytes. + * + * This will not flush cache, use ewk_tile_unused_cache_flush() or + * ewk_tile_unused_cache_auto_flush() to do so. + */ +void ewk_tile_unused_cache_max_set(Ewk_Tile_Unused_Cache *tuc, size_t max) +{ + EINA_SAFETY_ON_NULL_RETURN(tuc); + tuc->memory.max = max; +} + +/** + * Retrieve maximum cache capacity, in bytes. + */ +size_t ewk_tile_unused_cache_max_get(const Ewk_Tile_Unused_Cache *tuc) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, 0); + return tuc->memory.max; +} + +/** + * Retrieve the used cache capacity, in bytes. + */ +size_t ewk_tile_unused_cache_used_get(const Ewk_Tile_Unused_Cache *tuc) +{ + EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, 0); + return tuc->memory.used; +} + +/** + * Flush given amount of bytes from cache. + * + * After calling this function, near @a bytes are freed from cache. It + * may be less if cache did not contain that amount of bytes (ie: an + * empty cache has nothing to free!) or more if the cache just + * contained objects that were larger than the requested amount (this + * is usually the case). + * + * @param tuc cache of unused tiles. + * @param bytes amount to free. + * + * @return amount really freed. + * + * @see ewk_tile_unused_cache_used_get() + */ +size_t ewk_tile_unused_cache_flush(Ewk_Tile_Unused_Cache *tuc, size_t bytes) +{ + Ewk_Tile_Unused_Cache_Entry *itr; + Eina_List *l, *l_next; + EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, 0); + size_t done; + unsigned int count; + + if (!tuc->entries.count) + return 0; + if (bytes < 1) + return 0; + + /* + * NOTE: the cache is a FIFO queue currently. + * Don't need to sort any more. + */ + + if (tuc->dirty) + tuc->dirty = EINA_FALSE; + + done = 0; + count = 0; + EINA_LIST_FOREACH_SAFE(tuc->entries.list, l, l_next, itr) { + Ewk_Tile *t = itr->tile; + if (done > bytes) + break; + if (tuc->locked.locked + && t->x + t->w > tuc->locked.x + && t->y + t->h > tuc->locked.y + && t->x < tuc->locked.x + tuc->locked.w + && t->y < tuc->locked.y + tuc->locked.h + && t->zoom == tuc->locked.zoom) { + continue; + } + done += sizeof(Ewk_Tile) + itr->tile->bytes; + itr->tile_free.cb(itr->tile_free.data, itr->tile); + tuc->entries.list = eina_list_remove_list(tuc->entries.list, l); + free(itr); + count++; + } + + tuc->memory.used -= done; + tuc->entries.count -= count; + + return done; +} + +/** + * Flush enough bytes to make cache usage lower than maximum. + * + * Just like ewk_tile_unused_cache_flush(), but this will make the cache + * free enough tiles to respect maximum cache size as defined with + * ewk_tile_unused_cache_max_set(). + * + * This function is usually called when system becomes idle. This way + * we keep memory low but do not impact performance when + * creating/deleting tiles. + */ +void ewk_tile_unused_cache_auto_flush(Ewk_Tile_Unused_Cache *tuc) +{ + EINA_SAFETY_ON_NULL_RETURN(tuc); + if (tuc->memory.used <= tuc->memory.max) + return; + ewk_tile_unused_cache_flush(tuc, tuc->memory.used - tuc->memory.max); + if (tuc->memory.used > tuc->memory.max) + CRITICAL("Cache still using too much memory: %zd KB; max: %zd KB", + tuc->memory.used, tuc->memory.max); +} + +/** + * Flag cache as dirty. + * + * If cache is dirty then next flush operations will have to recompute + * weight and sort again to find the best tiles to expire. + * + * One must call this function when tile properties that may change + * likeness of tile to be flushed change, like Tile::stats. + */ +void ewk_tile_unused_cache_dirty(Ewk_Tile_Unused_Cache *tuc) +{ + tuc->dirty = EINA_TRUE; +} + +/** + * 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 *tuc) +{ + tuc->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 *tuc) +{ + if (!tuc->frozen) { + ERR("thawing more than freezing!"); + return; + } + + tuc->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 tuc cache of unused tiles + * @param t the tile to be removed from Ewk_Tile_Unused_Cache. + * + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. + */ +Eina_Bool ewk_tile_unused_cache_tile_get(Ewk_Tile_Unused_Cache *tuc, Ewk_Tile *t) +{ + Ewk_Tile_Unused_Cache_Entry *entry; + Eina_List *e, *l; + + e = NULL; + EINA_LIST_FOREACH(tuc->entries.list, l, entry) + { + if (entry->tile == t) { + e = l; + break; + } + } + if (!e) { + ERR("tile %p not found in cache %p", t, tuc); + return EINA_FALSE; + } + + tuc->entries.count--; + tuc->memory.used -= sizeof(Ewk_Tile) + t->bytes; + tuc->entries.list = eina_list_remove_list(tuc->entries.list, e); + free(entry); + // TODO assume dirty for now, but may it's not, + // if the item was at the beginning of the queue + tuc->dirty = EINA_TRUE; + + return EINA_TRUE; +} + +/** + * Put tile into cache of unused tiles, adding it to the cache. + * + * This should be called when @c t->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 tuc cache of unused tiles + * @param t tile to be added to cache. + * @param tile_free_cb function used to free tiles. + * @param data context to give back to @a tile_free_cb as first argument. + * + * @return #EINA_TRUE on success, #EINA_FALSE otherwise. If @c t->visible + * is not #EINA_FALSE, then it will return #EINA_FALSE. + * + * @see ewk_tile_unused_cache_auto_flush() + */ +Eina_Bool ewk_tile_unused_cache_tile_put(Ewk_Tile_Unused_Cache *tuc, Ewk_Tile *t, void (*tile_free_cb)(void *data, Ewk_Tile *t), const void *data) +{ + Ewk_Tile_Unused_Cache_Entry *e; + + if (t->visible) { + ERR("tile=%p is not unused (visible=%d)", t, t->visible); + return EINA_FALSE; + } + + MALLOC_OR_OOM_RET(e, sizeof(Ewk_Tile_Unused_Cache_Entry), EINA_FALSE); + tuc->entries.list = eina_list_append(tuc->entries.list, e); + if (eina_error_get()) { + ERR("List allocation failed"); + return EINA_FALSE; + } + + e->tile = t; + e->weight = 0; /* calculated just before sort */ + e->tile_free.cb = tile_free_cb; + e->tile_free.data = (void *)data; + + tuc->entries.count++; + tuc->memory.used += sizeof(Ewk_Tile) + t->bytes; + tuc->dirty = EINA_TRUE; + + return EINA_TRUE; +} + +void ewk_tile_unused_cache_dbg(const Ewk_Tile_Unused_Cache *tuc) +{ + Ewk_Tile_Unused_Cache_Entry *itr; + Eina_List *l; + int count = 0; + printf("Cache of unused tiles: entries: %zu/%zu, memory: %zu/%zu\n", + tuc->entries.count, tuc->entries.allocated, + tuc->memory.used, tuc->memory.max); + + EINA_LIST_FOREACH(tuc->entries.list, l, itr) { + const Ewk_Tile *t = itr->tile; + printf(" [%3lu,%3lu + %dx%d @ %0.3f]%c", + t->col, t->row, t->w, t->h, t->zoom, + t->visible ? '*': ' '); + + if (!(count % 4)) + printf("\n"); + } + + printf("\n"); +} |