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