Home | History | Annotate | Download | only in ewk
      1 /*
      2     Copyright (C) 2009-2010 Samsung Electronics
      3     Copyright (C) 2009-2010 ProFUSION embedded systems
      4 
      5     This library is free software; you can redistribute it and/or
      6     modify it under the terms of the GNU Library General Public
      7     License as published by the Free Software Foundation; either
      8     version 2 of the License, or (at your option) any later version.
      9 
     10     This library is distributed in the hope that it will be useful,
     11     but WITHOUT ANY WARRANTY; without even the implied warranty of
     12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13     Library General Public License for more details.
     14 
     15     You should have received a copy of the GNU Library General Public License
     16     along with this library; see the file COPYING.LIB.  If not, write to
     17     the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18     Boston, MA 02110-1301, USA.
     19 */
     20 
     21 #include "config.h"
     22 #include "ewk_tiled_model.h"
     23 
     24 #define _GNU_SOURCE
     25 #include "ewk_tiled_backing_store.h"
     26 #include "ewk_tiled_private.h"
     27 #include <Eina.h>
     28 #include <eina_safety_checks.h>
     29 #include <errno.h>
     30 #include <inttypes.h>
     31 #include <stdio.h> // XXX REMOVE ME LATER
     32 #include <stdlib.h>
     33 #include <string.h>
     34 
     35 #ifdef TILE_STATS_ACCOUNT_RENDER_TIME
     36 #include <sys/time.h>
     37 #endif
     38 
     39 #ifndef CAIRO_FORMAT_RGB16_565
     40 #define CAIRO_FORMAT_RGB16_565 4
     41 #endif
     42 
     43 #define IDX(col, row, rowspan) (col + (row * rowspan))
     44 #define MIN(a, b) ((a < b) ? a : b)
     45 #define MAX(a, b) ((a > b) ? a : b)
     46 
     47 #ifdef DEBUG_MEM_LEAKS
     48 static uint64_t tiles_allocated = 0;
     49 static uint64_t tiles_freed = 0;
     50 static uint64_t bytes_allocated = 0;
     51 static uint64_t bytes_freed = 0;
     52 
     53 struct tile_account {
     54     Evas_Coord size;
     55     struct {
     56         uint64_t allocated;
     57         uint64_t freed;
     58     } tiles, bytes;
     59 };
     60 
     61 static size_t accounting_len = 0;
     62 static struct tile_account *accounting = NULL;
     63 
     64 static inline struct tile_account *_ewk_tile_account_get(const Ewk_Tile *t)
     65 {
     66     struct tile_account *acc;
     67     size_t i;
     68 
     69     for (i = 0; i < accounting_len; i++) {
     70         if (accounting[i].size == t->w)
     71             return accounting + i;
     72     }
     73 
     74     i = (accounting_len + 1) * sizeof(struct tile_account);
     75     REALLOC_OR_OOM_RET(accounting, i, NULL);
     76 
     77     acc = accounting + accounting_len;
     78     acc->size = t->w;
     79     acc->tiles.allocated = 0;
     80     acc->tiles.freed = 0;
     81     acc->bytes.allocated = 0;
     82     acc->bytes.freed = 0;
     83 
     84     accounting_len++;
     85 
     86     return acc;
     87 }
     88 
     89 static inline void _ewk_tile_account_allocated(const Ewk_Tile *t)
     90 {
     91     struct tile_account *acc = _ewk_tile_account_get(t);
     92     if (!acc)
     93         return;
     94     acc->bytes.allocated += t->bytes;
     95     acc->tiles.allocated++;
     96 
     97     bytes_allocated += t->bytes;
     98     tiles_allocated++;
     99 }
    100 
    101 static inline void _ewk_tile_account_freed(const Ewk_Tile *t)
    102 {
    103     struct tile_account *acc = _ewk_tile_account_get(t);
    104     if (!acc)
    105         return;
    106 
    107     acc->bytes.freed += t->bytes;
    108     acc->tiles.freed++;
    109 
    110     bytes_freed += t->bytes;
    111     tiles_freed++;
    112 }
    113 
    114 void ewk_tile_accounting_dbg(void)
    115 {
    116     struct tile_account *acc;
    117     struct tile_account *acc_end;
    118 
    119     printf("TILE BALANCE: tiles[+%"PRIu64",-%"PRIu64":%"PRIu64"] "
    120            "bytes[+%"PRIu64",-%"PRIu64":%"PRIu64"]\n",
    121             tiles_allocated, tiles_freed, tiles_allocated - tiles_freed,
    122             bytes_allocated, bytes_freed, bytes_allocated - bytes_freed);
    123 
    124     if (!accounting_len)
    125         return;
    126 
    127     acc = accounting;
    128     acc_end = acc + accounting_len;
    129     printf("BEGIN: TILE BALANCE DETAILS (TO THIS MOMENT!):\n");
    130     for (; acc < acc_end; acc++) {
    131         uint64_t tiles, bytes;
    132 
    133         tiles = acc->tiles.allocated - acc->tiles.freed;
    134         bytes = acc->bytes.allocated - acc->bytes.freed;
    135 
    136         printf("   %4d: tiles[+%4"PRIu64",-%4"PRIu64":%4"PRIu64"] "
    137                "bytes[+%8"PRIu64",-%8"PRIu64":%8"PRIu64"]%s\n",
    138                acc->size,
    139                acc->tiles.allocated, acc->tiles.freed, tiles,
    140                acc->bytes.allocated, acc->bytes.freed, bytes,
    141                (bytes || tiles) ? " POSSIBLE LEAK" : "");
    142     }
    143     printf("END: TILE BALANCE DETAILS (TO THIS MOMENT!):\n");
    144 }
    145 #else
    146 
    147 static inline void _ewk_tile_account_allocated(const Ewk_Tile *t) { }
    148 static inline void _ewk_tile_account_freed(const Ewk_Tile *t) { }
    149 
    150 void ewk_tile_accounting_dbg(void)
    151 {
    152     printf("compile webkit with DEBUG_MEM_LEAKS defined!\n");
    153 }
    154 #endif
    155 
    156 static inline void _ewk_tile_paint_rgb888(Ewk_Tile *t, uint8_t r, uint8_t g, uint8_t b)
    157 {
    158     uint32_t *dst32, *dst32_end, c1;
    159     uint64_t *dst64, *dst64_end, c2;
    160 
    161     c1 = 0xff000000 | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
    162     c2 = ((uint64_t)c1 << 32) | c1;
    163 
    164     dst64 = (uint64_t *)t->pixels;
    165     dst64_end = dst64 + ((t->bytes / 8) & ~7);
    166     for (; dst64 < dst64_end; dst64 += 8) {
    167         /* TODO: ARM add pld or NEON instructions */
    168         dst64[0] = c2;
    169         dst64[1] = c2;
    170         dst64[2] = c2;
    171         dst64[3] = c2;
    172         dst64[4] = c2;
    173         dst64[5] = c2;
    174         dst64[6] = c2;
    175         dst64[7] = c2;
    176     }
    177 
    178     dst32 = (uint32_t *)dst64_end;
    179     dst32_end = (uint32_t *)(t->pixels + t->bytes);
    180     for (; dst32 < dst32_end; dst32++)
    181         *dst32 = c1;
    182 }
    183 
    184 static inline void _ewk_tile_paint_rgb565(Ewk_Tile *t, uint8_t r, uint8_t g, uint8_t b)
    185 {
    186     uint16_t *dst16, *dst16_end, c1;
    187     uint64_t *dst64, *dst64_end, c2;
    188 
    189     c1 = ((((r >> 3) & 0x1f) << 11) |
    190           (((g >> 2) & 0x3f) << 5) |
    191           ((b >> 3) & 0x1f));
    192 
    193     c2 = (((uint64_t)c1 << 48) | ((uint64_t)c1 << 32) |
    194           ((uint64_t)c1 << 16) | c1);
    195 
    196     dst64 = (uint64_t *)t->pixels;
    197     dst64_end = dst64 + ((t->bytes / 8) & ~7);
    198     for (; dst64 < dst64_end; dst64 += 8) {
    199         /* TODO: ARM add pld or NEON instructions */
    200         dst64[0] = c2;
    201         dst64[1] = c2;
    202         dst64[2] = c2;
    203         dst64[3] = c2;
    204         dst64[4] = c2;
    205         dst64[5] = c2;
    206         dst64[6] = c2;
    207         dst64[7] = c2;
    208     }
    209 
    210     dst16 = (uint16_t *)dst16_end;
    211     dst16_end = (uint16_t *)(t->pixels + t->bytes);
    212     for (; dst16 < dst16_end; dst16++)
    213         *dst16 = c1;
    214 }
    215 
    216 static inline void _ewk_tile_paint(Ewk_Tile *t, uint8_t r, uint8_t g, uint8_t b)
    217 {
    218     if (t->cspace == EVAS_COLORSPACE_ARGB8888)
    219         _ewk_tile_paint_rgb888(t, r, g, b);
    220     else if (t->cspace == EVAS_COLORSPACE_RGB565_A5P)
    221         _ewk_tile_paint_rgb565(t, r, g, b);
    222     else
    223         ERR("unknown color space: %d", t->cspace);
    224 }
    225 
    226 /**
    227  * Create a new tile of given size, zoom level and colorspace.
    228  *
    229  * After created these properties are immutable as they're the basic
    230  * characteristic of the tile and any change will lead to invalid
    231  * memory access.
    232  *
    233  * Other members are of free-access and no getters/setters are
    234  * provided in orderr to avoid expensive operations on those, however
    235  * some are better manipulated with provided functions, such as
    236  * ewk_tile_show() and ewk_tile_hide() to change
    237  * @c visible or ewk_tile_update_full(), ewk_tile_update_area(),
    238  * ewk_tile_updates_clear() to change @c stats.misses,
    239  * @c stats.full_update and @c updates.
    240  */
    241 Ewk_Tile *ewk_tile_new(Evas *evas, Evas_Coord w, Evas_Coord h, float zoom, Evas_Colorspace cspace)
    242 {
    243     Eina_Inlist *l;
    244     Evas_Coord *ec;
    245     Evas_Colorspace *ecs;
    246     float *f;
    247     size_t *s;
    248     Ewk_Tile *t;
    249     unsigned int area;
    250     size_t bytes;
    251     cairo_format_t format;
    252     cairo_status_t status;
    253     int stride;
    254 
    255     area = w * h;
    256 
    257     if (cspace == EVAS_COLORSPACE_ARGB8888) {
    258         bytes = area * 4;
    259         stride = w * 4;
    260         format = CAIRO_FORMAT_RGB24;
    261     } else if (cspace == EVAS_COLORSPACE_RGB565_A5P) {
    262         bytes = area * 2;
    263         stride = w * 2;
    264         format = CAIRO_FORMAT_RGB16_565;
    265     } else {
    266         ERR("unknown color space: %d", cspace);
    267         return NULL;
    268     }
    269 
    270     DBG("size: %dx%d (%d), zoom: %f, cspace=%d",
    271         w, h, area, (double)zoom, cspace);
    272 
    273     MALLOC_OR_OOM_RET(t, sizeof(Ewk_Tile), NULL);
    274     t->image = evas_object_image_add(evas);
    275 
    276     l = EINA_INLIST_GET(t);
    277     l->prev = NULL;
    278     l->next = NULL;
    279 
    280     t->visible = 0;
    281     t->updates = NULL;
    282 
    283     memset(&t->stats, 0, sizeof(Ewk_Tile_Stats));
    284     t->stats.area = area;
    285 
    286     /* ugly, but let's avoid at all costs having users to modify those */
    287     ec = (Evas_Coord *)&t->w;
    288     *ec = w;
    289 
    290     ec = (Evas_Coord *)&t->h;
    291     *ec = h;
    292 
    293     ecs = (Evas_Colorspace *)&t->cspace;
    294     *ecs = cspace;
    295 
    296     f = (float *)&t->zoom;
    297     *f = zoom;
    298 
    299     s = (size_t *)&t->bytes;
    300     *s = bytes;
    301 
    302     evas_object_image_size_set(t->image, t->w, t->h);
    303     evas_object_image_colorspace_set(t->image, t->cspace);
    304     t->pixels = evas_object_image_data_get(t->image, EINA_TRUE);
    305     t->surface = cairo_image_surface_create_for_data
    306         (t->pixels, format, w, h, stride);
    307     status = cairo_surface_status(t->surface);
    308     if (status != CAIRO_STATUS_SUCCESS) {
    309         ERR("failed to create cairo surface: %s",
    310             cairo_status_to_string(status));
    311         free(t);
    312         return NULL;
    313     }
    314 
    315     t->cairo = cairo_create(t->surface);
    316     status = cairo_status(t->cairo);
    317     if (status != CAIRO_STATUS_SUCCESS) {
    318         ERR("failed to create cairo: %s", cairo_status_to_string(status));
    319         cairo_surface_destroy(t->surface);
    320         evas_object_del(t->image);
    321         free(t);
    322         return NULL;
    323     }
    324 
    325     _ewk_tile_account_allocated(t);
    326 
    327     return t;
    328 }
    329 
    330 /**
    331  * Free tile memory.
    332  */
    333 void ewk_tile_free(Ewk_Tile *t)
    334 {
    335     _ewk_tile_account_freed(t);
    336 
    337     if (t->updates)
    338         eina_tiler_free(t->updates);
    339 
    340     cairo_surface_destroy(t->surface);
    341     cairo_destroy(t->cairo);
    342     evas_object_del(t->image);
    343     free(t);
    344 }
    345 
    346 /**
    347  * Make the tile visible, incrementing its counter.
    348  */
    349 void ewk_tile_show(Ewk_Tile *t)
    350 {
    351     t->visible++;
    352     evas_object_show(t->image);
    353 }
    354 
    355 /**
    356  * Decrement the visibility counter, making it invisible if necessary.
    357  */
    358 void ewk_tile_hide(Ewk_Tile *t)
    359 {
    360     t->visible--;
    361     if (!t->visible)
    362         evas_object_hide(t->image);
    363 }
    364 
    365 /**
    366  * Returns EINA_TRUE if the tile is visible, EINA_FALSE otherwise.
    367  */
    368 Eina_Bool ewk_tile_visible_get(Ewk_Tile *t)
    369 {
    370     return !!t->visible;
    371 }
    372 
    373 /**
    374  * Mark whole tile as dirty and requiring update.
    375  */
    376 void ewk_tile_update_full(Ewk_Tile *t)
    377 {
    378     /* TODO: list of tiles pending updates? */
    379     t->stats.misses++;
    380 
    381     if (!t->stats.full_update) {
    382         t->stats.full_update = EINA_TRUE;
    383         if (t->updates) {
    384             eina_tiler_free(t->updates);
    385             t->updates = NULL;
    386         }
    387     }
    388 }
    389 
    390 /**
    391  * Mark the specific subarea as dirty and requiring update.
    392  */
    393 void ewk_tile_update_area(Ewk_Tile *t, const Eina_Rectangle *r)
    394 {
    395     /* TODO: list of tiles pending updates? */
    396     t->stats.misses++;
    397 
    398     if (t->stats.full_update)
    399         return;
    400 
    401     if (!r->x && !r->y && r->w == t->w && r->h == t->h) {
    402         t->stats.full_update = EINA_TRUE;
    403         if (t->updates) {
    404             eina_tiler_free(t->updates);
    405             t->updates = NULL;
    406         }
    407         return;
    408     }
    409 
    410     if (!t->updates) {
    411         t->updates = eina_tiler_new(t->w, t->h);
    412         if (!t->updates) {
    413             CRITICAL("could not create eina_tiler %dx%d.", t->w, t->h);
    414             return;
    415         }
    416     }
    417 
    418     eina_tiler_rect_add(t->updates, r);
    419 }
    420 
    421 /**
    422  * For each updated region, call the given function.
    423  *
    424  * This will not change the tile statistics or clear the processed
    425  * updates, use ewk_tile_updates_clear() for that.
    426  */
    427 void ewk_tile_updates_process(Ewk_Tile *t, void (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update), const void *data)
    428 {
    429     if (t->stats.full_update) {
    430         Eina_Rectangle r;
    431         r.x = 0;
    432         r.y = 0;
    433         r.w = t->w;
    434         r.h = t->h;
    435 #ifdef TILE_STATS_ACCOUNT_RENDER_TIME
    436         struct timeval timev;
    437         double render_start;
    438         gettimeofday(&timev, NULL);
    439         render_start = (double)timev.tv_sec +
    440             (((double)timev.tv_usec) / 1000000);
    441 #endif
    442         cb((void *)data, t, &r);
    443 #ifdef TILE_STATS_ACCOUNT_RENDER_TIME
    444         gettimeofday(&timev, NULL);
    445         t->stats.render_time = (double)timev.tv_sec +
    446             (((double)timev.tv_usec) / 1000000) - render_start;
    447 #endif
    448     } else if (t->updates) {
    449         Eina_Iterator *itr = eina_tiler_iterator_new(t->updates);
    450         Eina_Rectangle *r;
    451         if (!itr) {
    452             CRITICAL("could not create tiler iterator!");
    453             return;
    454         }
    455         EINA_ITERATOR_FOREACH(itr, r)
    456             cb((void *)data, t, r);
    457         eina_iterator_free(itr);
    458     }
    459 }
    460 
    461 /**
    462  * Clear all updates in region, if any.
    463  *
    464  * This will change the tile statistics, specially zero stat.misses
    465  * and unset stats.full_update. If t->updates existed, then it will be
    466  * destroyed.
    467  *
    468  * This function is usually called after ewk_tile_updates_process() is
    469  * called.
    470  */
    471 void ewk_tile_updates_clear(Ewk_Tile *t)
    472 {
    473     /* TODO: remove from list of pending updates? */
    474     t->stats.misses = 0;
    475 
    476     if (t->stats.full_update)
    477         t->stats.full_update = 0;
    478     else if (t->updates) {
    479         eina_tiler_free(t->updates);
    480         t->updates = NULL;
    481     }
    482 }
    483 
    484 typedef struct _Ewk_Tile_Unused_Cache_Entry Ewk_Tile_Unused_Cache_Entry;
    485 struct _Ewk_Tile_Unused_Cache_Entry {
    486     Ewk_Tile *tile;
    487     int weight;
    488     struct {
    489         void (*cb)(void *data, Ewk_Tile *t);
    490         void *data;
    491     } tile_free;
    492 };
    493 
    494 struct _Ewk_Tile_Unused_Cache {
    495     struct {
    496         Eina_List *list;
    497         size_t count;
    498         size_t allocated;
    499     } entries;
    500     struct {
    501         size_t max;  /**< watermark (in bytes) to start freeing tiles */
    502         size_t used; /**< in bytes, maybe more than max. */
    503     } memory;
    504     struct {
    505         Evas_Coord x, y, w, h;
    506         float zoom;
    507         Eina_Bool locked;
    508     } locked;
    509     int references;
    510     unsigned int frozen;
    511     Eina_Bool dirty:1;
    512 };
    513 
    514 static const size_t TILE_UNUSED_CACHE_ALLOCATE_INITIAL = 128;
    515 static const size_t TILE_UNUSED_CACHE_ALLOCATE_STEP = 16;
    516 static const size_t TILE_UNUSED_CACHE_MAX_FREE = 32;
    517 
    518 /**
    519  * Cache of unused tiles (those that are not visible).
    520  *
    521  * The cache of unused tiles.
    522  *
    523  * @param max cache size in bytes.
    524  *
    525  * @return newly allocated cache of unused tiles, use
    526  *         ewk_tile_unused_cache_free() to release resources. If not
    527  *         possible to allocate memory, @c NULL is returned.
    528  */
    529 Ewk_Tile_Unused_Cache *ewk_tile_unused_cache_new(size_t max)
    530 {
    531     Ewk_Tile_Unused_Cache *tuc;
    532 
    533     CALLOC_OR_OOM_RET(tuc, sizeof(Ewk_Tile_Unused_Cache), NULL);
    534 
    535     DBG("tuc=%p", tuc);
    536     tuc->memory.max = max;
    537     tuc->references = 1;
    538     return tuc;
    539 }
    540 
    541 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)
    542 {
    543     EINA_SAFETY_ON_NULL_RETURN(tuc);
    544 
    545     tuc->locked.locked = EINA_TRUE;
    546     tuc->locked.x = x;
    547     tuc->locked.y = y;
    548     tuc->locked.w = w;
    549     tuc->locked.h = h;
    550     tuc->locked.zoom = zoom;
    551 }
    552 
    553 void ewk_tile_unused_cache_unlock_area(Ewk_Tile_Unused_Cache *tuc)
    554 {
    555     EINA_SAFETY_ON_NULL_RETURN(tuc);
    556 
    557     tuc->locked.locked = EINA_FALSE;
    558 }
    559 
    560 /**
    561  * Free cache of unused tiles.
    562  *
    563  * Those tiles that are still visible will remain live. The unused
    564  * tiles will be freed.
    565  *
    566  * @see ewk_tile_unused_cache_unref()
    567  */
    568 void ewk_tile_unused_cache_free(Ewk_Tile_Unused_Cache *tuc)
    569 {
    570     EINA_SAFETY_ON_NULL_RETURN(tuc);
    571 
    572     DBG("tuc=%p, "
    573         "entries=(count:%zd, allocated:%zd), "
    574         "memory=(max:%zd, used:%zd)",
    575         tuc, tuc->entries.count, tuc->entries.allocated,
    576         tuc->memory.max, tuc->memory.used);
    577 
    578     ewk_tile_unused_cache_clear(tuc);
    579     free(tuc);
    580 }
    581 
    582 /**
    583  * Clear cache of unused tiles.
    584  *
    585  * Any tiles that are in the cache are freed. The only tiles that are
    586  * kept are those that aren't in the cache (i.e. that are visible).
    587  */
    588 void ewk_tile_unused_cache_clear(Ewk_Tile_Unused_Cache *tuc)
    589 {
    590     Ewk_Tile_Unused_Cache_Entry *itr;
    591     EINA_SAFETY_ON_NULL_RETURN(tuc);
    592 
    593     if (!tuc->entries.count)
    594         return;
    595 
    596     EINA_LIST_FREE(tuc->entries.list, itr) {
    597         itr->tile_free.cb(itr->tile_free.data, itr->tile);
    598         free(itr);
    599     }
    600 
    601     tuc->memory.used = 0;
    602     tuc->entries.count = 0;
    603     tuc->dirty = EINA_FALSE;
    604 }
    605 
    606 /**
    607  * Hold reference to cache.
    608  *
    609  * @return same pointer as taken.
    610  *
    611  * @see ewk_tile_unused_cache_unref()
    612  */
    613 Ewk_Tile_Unused_Cache *ewk_tile_unused_cache_ref(Ewk_Tile_Unused_Cache *tuc)
    614 {
    615     EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, NULL);
    616     tuc->references++;
    617     return tuc;
    618 }
    619 
    620 /**
    621  * Release cache reference, freeing it if it drops to zero.
    622  *
    623  * @see ewk_tile_unused_cache_ref()
    624  * @see ewk_tile_unused_cache_free()
    625  */
    626 void ewk_tile_unused_cache_unref(Ewk_Tile_Unused_Cache *tuc)
    627 {
    628     EINA_SAFETY_ON_NULL_RETURN(tuc);
    629     tuc->references--;
    630     if (!tuc->references)
    631         ewk_tile_unused_cache_free(tuc);
    632 }
    633 
    634 /**
    635  * Change cache capacity, in bytes.
    636  *
    637  * This will not flush cache, use ewk_tile_unused_cache_flush() or
    638  * ewk_tile_unused_cache_auto_flush() to do so.
    639  */
    640 void ewk_tile_unused_cache_max_set(Ewk_Tile_Unused_Cache *tuc, size_t max)
    641 {
    642     EINA_SAFETY_ON_NULL_RETURN(tuc);
    643     tuc->memory.max = max;
    644 }
    645 
    646 /**
    647  * Retrieve maximum cache capacity, in bytes.
    648  */
    649 size_t ewk_tile_unused_cache_max_get(const Ewk_Tile_Unused_Cache *tuc)
    650 {
    651     EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, 0);
    652     return tuc->memory.max;
    653 }
    654 
    655 /**
    656  * Retrieve the used cache capacity, in bytes.
    657  */
    658 size_t ewk_tile_unused_cache_used_get(const Ewk_Tile_Unused_Cache *tuc)
    659 {
    660     EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, 0);
    661     return tuc->memory.used;
    662 }
    663 
    664 /**
    665  * Flush given amount of bytes from cache.
    666  *
    667  * After calling this function, near @a bytes are freed from cache. It
    668  * may be less if cache did not contain that amount of bytes (ie: an
    669  * empty cache has nothing to free!) or more if the cache just
    670  * contained objects that were larger than the requested amount (this
    671  * is usually the case).
    672  *
    673  * @param tuc cache of unused tiles.
    674  * @param bytes amount to free.
    675  *
    676  * @return amount really freed.
    677  *
    678  * @see ewk_tile_unused_cache_used_get()
    679  */
    680 size_t ewk_tile_unused_cache_flush(Ewk_Tile_Unused_Cache *tuc, size_t bytes)
    681 {
    682     Ewk_Tile_Unused_Cache_Entry *itr;
    683     Eina_List *l, *l_next;
    684     EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, 0);
    685     size_t done;
    686     unsigned int count;
    687 
    688     if (!tuc->entries.count)
    689         return 0;
    690     if (bytes < 1)
    691         return 0;
    692 
    693     /*
    694      * NOTE: the cache is a FIFO queue currently.
    695      * Don't need to sort any more.
    696      */
    697 
    698     if (tuc->dirty)
    699         tuc->dirty = EINA_FALSE;
    700 
    701     done = 0;
    702     count = 0;
    703     EINA_LIST_FOREACH_SAFE(tuc->entries.list, l, l_next, itr) {
    704         Ewk_Tile *t = itr->tile;
    705         if (done > bytes)
    706             break;
    707         if (tuc->locked.locked
    708             && t->x + t->w > tuc->locked.x
    709             && t->y + t->h > tuc->locked.y
    710             && t->x < tuc->locked.x + tuc->locked.w
    711             && t->y < tuc->locked.y + tuc->locked.h
    712             && t->zoom == tuc->locked.zoom) {
    713             continue;
    714         }
    715         done += sizeof(Ewk_Tile) + itr->tile->bytes;
    716         itr->tile_free.cb(itr->tile_free.data, itr->tile);
    717         tuc->entries.list = eina_list_remove_list(tuc->entries.list, l);
    718         free(itr);
    719         count++;
    720     }
    721 
    722     tuc->memory.used -= done;
    723     tuc->entries.count -= count;
    724 
    725     return done;
    726 }
    727 
    728 /**
    729  * Flush enough bytes to make cache usage lower than maximum.
    730  *
    731  * Just like ewk_tile_unused_cache_flush(), but this will make the cache
    732  * free enough tiles to respect maximum cache size as defined with
    733  * ewk_tile_unused_cache_max_set().
    734  *
    735  * This function is usually called when system becomes idle. This way
    736  * we keep memory low but do not impact performance when
    737  * creating/deleting tiles.
    738  */
    739 void ewk_tile_unused_cache_auto_flush(Ewk_Tile_Unused_Cache *tuc)
    740 {
    741     EINA_SAFETY_ON_NULL_RETURN(tuc);
    742     if (tuc->memory.used <= tuc->memory.max)
    743         return;
    744     ewk_tile_unused_cache_flush(tuc, tuc->memory.used - tuc->memory.max);
    745     if (tuc->memory.used > tuc->memory.max)
    746         CRITICAL("Cache still using too much memory: %zd KB; max: %zd KB",
    747                  tuc->memory.used, tuc->memory.max);
    748 }
    749 
    750 /**
    751  * Flag cache as dirty.
    752  *
    753  * If cache is dirty then next flush operations will have to recompute
    754  * weight and sort again to find the best tiles to expire.
    755  *
    756  * One must call this function when tile properties that may change
    757  * likeness of tile to be flushed change, like Tile::stats.
    758  */
    759 void ewk_tile_unused_cache_dirty(Ewk_Tile_Unused_Cache *tuc)
    760 {
    761     tuc->dirty = EINA_TRUE;
    762 }
    763 
    764 /**
    765  * Freeze cache to not do maintenance tasks.
    766  *
    767  * Maintenance tasks optimize cache usage, but maybe we know we should
    768  * hold on them until we do the last operation, in this case we freeze
    769  * while operating and then thaw when we're done.
    770  *
    771  * @see ewk_tile_unused_cache_thaw()
    772  */
    773 void ewk_tile_unused_cache_freeze(Ewk_Tile_Unused_Cache *tuc)
    774 {
    775     tuc->frozen++;
    776 }
    777 
    778 /**
    779  * Unfreezes maintenance tasks.
    780  *
    781  * If this is the last counterpart of freeze, then maintenance tasks
    782  * will run immediately.
    783  */
    784 void ewk_tile_unused_cache_thaw(Ewk_Tile_Unused_Cache *tuc)
    785 {
    786     if (!tuc->frozen) {
    787         ERR("thawing more than freezing!");
    788         return;
    789     }
    790 
    791     tuc->frozen--;
    792 }
    793 
    794 /**
    795  * Get tile from cache of unused tiles, removing it from the cache.
    796  *
    797  * If the tile is used, then it's not in cache of unused tiles, so it
    798  * is removed from the cache and may be given back with
    799  * ewk_tile_unused_cache_tile_put().
    800  *
    801  * @param tuc cache of unused tiles
    802  * @param t the tile to be removed from Ewk_Tile_Unused_Cache.
    803  *
    804  * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
    805  */
    806 Eina_Bool ewk_tile_unused_cache_tile_get(Ewk_Tile_Unused_Cache *tuc, Ewk_Tile *t)
    807 {
    808     Ewk_Tile_Unused_Cache_Entry *entry;
    809     Eina_List *e, *l;
    810 
    811     e = NULL;
    812     EINA_LIST_FOREACH(tuc->entries.list, l, entry)
    813     {
    814         if (entry->tile == t) {
    815             e = l;
    816             break;
    817         }
    818     }
    819     if (!e) {
    820         ERR("tile %p not found in cache %p", t, tuc);
    821         return EINA_FALSE;
    822     }
    823 
    824     tuc->entries.count--;
    825     tuc->memory.used -= sizeof(Ewk_Tile) + t->bytes;
    826     tuc->entries.list = eina_list_remove_list(tuc->entries.list, e);
    827     free(entry);
    828     // TODO assume dirty for now, but may it's not,
    829     // if the item was at the beginning of the queue
    830     tuc->dirty = EINA_TRUE;
    831 
    832     return EINA_TRUE;
    833 }
    834 
    835 /**
    836  * Put tile into cache of unused tiles, adding it to the cache.
    837  *
    838  * This should be called when @c t->visible is @c 0 and no objects are
    839  * using the tile anymore, making it available to be expired and have
    840  * its memory replaced.
    841  *
    842  * Note that tiles are not automatically deleted if cache is full,
    843  * instead the cache will have more bytes used than maximum and one
    844  * can call ewk_tile_unused_cache_auto_flush() to free them. This is done
    845  * because usually we want a lazy operation for better performance.
    846  *
    847  * @param tuc cache of unused tiles
    848  * @param t tile to be added to cache.
    849  * @param tile_free_cb function used to free tiles.
    850  * @param data context to give back to @a tile_free_cb as first argument.
    851  *
    852  * @return #EINA_TRUE on success, #EINA_FALSE otherwise. If @c t->visible
    853  *         is not #EINA_FALSE, then it will return #EINA_FALSE.
    854  *
    855  * @see ewk_tile_unused_cache_auto_flush()
    856  */
    857 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)
    858 {
    859     Ewk_Tile_Unused_Cache_Entry *e;
    860 
    861     if (t->visible) {
    862         ERR("tile=%p is not unused (visible=%d)", t, t->visible);
    863         return EINA_FALSE;
    864     }
    865 
    866     MALLOC_OR_OOM_RET(e, sizeof(Ewk_Tile_Unused_Cache_Entry), EINA_FALSE);
    867     tuc->entries.list = eina_list_append(tuc->entries.list, e);
    868     if (eina_error_get()) {
    869         ERR("List allocation failed");
    870         return EINA_FALSE;
    871     }
    872 
    873     e->tile = t;
    874     e->weight = 0; /* calculated just before sort */
    875     e->tile_free.cb = tile_free_cb;
    876     e->tile_free.data = (void *)data;
    877 
    878     tuc->entries.count++;
    879     tuc->memory.used += sizeof(Ewk_Tile) + t->bytes;
    880     tuc->dirty = EINA_TRUE;
    881 
    882     return EINA_TRUE;
    883 }
    884 
    885 void ewk_tile_unused_cache_dbg(const Ewk_Tile_Unused_Cache *tuc)
    886 {
    887     Ewk_Tile_Unused_Cache_Entry *itr;
    888     Eina_List *l;
    889     int count = 0;
    890     printf("Cache of unused tiles: entries: %zu/%zu, memory: %zu/%zu\n",
    891            tuc->entries.count, tuc->entries.allocated,
    892            tuc->memory.used, tuc->memory.max);
    893 
    894     EINA_LIST_FOREACH(tuc->entries.list, l, itr) {
    895         const Ewk_Tile *t = itr->tile;
    896         printf(" [%3lu,%3lu + %dx%d @ %0.3f]%c",
    897                t->col, t->row, t->w, t->h, t->zoom,
    898                t->visible ? '*': ' ');
    899 
    900         if (!(count % 4))
    901             printf("\n");
    902     }
    903 
    904     printf("\n");
    905 }
    906