2 Copyright (C) 2009-2010 Samsung Electronics
3 Copyright (C) 2009-2010 ProFUSION embedded systems
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.
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.
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.
22 #include "ewk_tiled_model.h"
25 #include "ewk_tiled_backing_store.h"
26 #include "ewk_tiled_private.h"
27 #include <Ecore_Evas.h>
29 #include <eina_safety_checks.h>
32 #include <stdio.h> // XXX REMOVE ME LATER
36 #ifdef TILE_STATS_ACCOUNT_RENDER_TIME
40 #ifndef CAIRO_FORMAT_RGB16_565
41 #define CAIRO_FORMAT_RGB16_565 4
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)
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;
62 static size_t accounting_len = 0;
63 static struct tile_account *accounting = NULL;
65 static inline struct tile_account *_ewk_tile_account_get(const Ewk_Tile *t)
67 struct tile_account *acc;
70 for (i = 0; i < accounting_len; i++) {
71 if (accounting[i].size == t->w)
72 return accounting + i;
75 i = (accounting_len + 1) * sizeof(struct tile_account);
76 REALLOC_OR_OOM_RET(accounting, i, NULL);
78 acc = accounting + accounting_len;
80 acc->tiles.allocated = 0;
82 acc->bytes.allocated = 0;
90 static inline void _ewk_tile_account_allocated(const Ewk_Tile *t)
92 struct tile_account *acc = _ewk_tile_account_get(t);
95 acc->bytes.allocated += t->bytes;
96 acc->tiles.allocated++;
98 bytes_allocated += t->bytes;
102 static inline void _ewk_tile_account_freed(const Ewk_Tile *t)
104 struct tile_account *acc = _ewk_tile_account_get(t);
108 acc->bytes.freed += t->bytes;
111 bytes_freed += t->bytes;
115 void ewk_tile_accounting_dbg(void)
117 struct tile_account *acc;
118 struct tile_account *acc_end;
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);
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;
134 tiles = acc->tiles.allocated - acc->tiles.freed;
135 bytes = acc->bytes.allocated - acc->bytes.freed;
137 printf(" %4d: tiles[+%4"PRIu64",-%4"PRIu64":%4"PRIu64"] "
138 "bytes[+%8"PRIu64",-%8"PRIu64":%8"PRIu64"]%s\n",
140 acc->tiles.allocated, acc->tiles.freed, tiles,
141 acc->bytes.allocated, acc->bytes.freed, bytes,
142 (bytes || tiles) ? " POSSIBLE LEAK" : "");
144 printf("END: TILE BALANCE DETAILS (TO THIS MOMENT!):\n");
148 static inline void _ewk_tile_account_allocated(const Ewk_Tile *t) { }
149 static inline void _ewk_tile_account_freed(const Ewk_Tile *t) { }
151 void ewk_tile_accounting_dbg(void)
153 printf("compile webkit with DEBUG_MEM_LEAKS defined!\n");
157 static inline void _ewk_tile_paint_rgb888(Ewk_Tile *t, uint8_t r, uint8_t g, uint8_t b)
159 uint32_t *dst32, *dst32_end, c1;
160 uint64_t *dst64, *dst64_end, c2;
162 c1 = 0xff000000 | ((uint32_t)r << 16) | ((uint32_t)g << 8) | b;
163 c2 = ((uint64_t)c1 << 32) | c1;
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 */
179 dst32 = (uint32_t *)dst64_end;
180 dst32_end = (uint32_t *)(t->pixels + t->bytes);
181 for (; dst32 < dst32_end; dst32++)
185 static inline void _ewk_tile_paint_rgb565(Ewk_Tile *t, uint8_t r, uint8_t g, uint8_t b)
187 uint16_t *dst16, *dst16_end, c1;
188 uint64_t *dst64, *dst64_end, c2;
190 c1 = ((((r >> 3) & 0x1f) << 11) |
191 (((g >> 2) & 0x3f) << 5) |
194 c2 = (((uint64_t)c1 << 48) | ((uint64_t)c1 << 32) |
195 ((uint64_t)c1 << 16) | c1);
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 */
211 dst16 = (uint16_t *)dst16_end;
212 dst16_end = (uint16_t *)(t->pixels + t->bytes);
213 for (; dst16 < dst16_end; dst16++)
217 static inline void _ewk_tile_paint(Ewk_Tile *t, uint8_t r, uint8_t g, uint8_t b)
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);
224 ERR("unknown color space: %d", t->cspace);
228 * Create a new tile of given size, zoom level and colorspace.
230 * After created these properties are immutable as they're the basic
231 * characteristic of the tile and any change will lead to invalid
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.
242 Ewk_Tile *ewk_tile_new(Evas *evas, Evas_Coord w, Evas_Coord h, float zoom, Evas_Colorspace cspace)
245 Evas_Colorspace *ecs;
251 cairo_format_t format;
252 cairo_status_t status;
259 if (cspace == EVAS_COLORSPACE_ARGB8888) {
262 format = CAIRO_FORMAT_ARGB32;
263 } else if (cspace == EVAS_COLORSPACE_RGB565_A5P) {
266 format = CAIRO_FORMAT_RGB16_565;
268 ERR("unknown color space: %d", cspace);
272 DBG("size: %dx%d (%d), zoom: %f, cspace=%d", w, h, area, (double)zoom, cspace);
274 MALLOC_OR_OOM_RET(t, sizeof(Ewk_Tile), NULL);
275 t->image = evas_object_image_add(evas);
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);
285 memset(&t->stats, 0, sizeof(Ewk_Tile_Stats));
286 t->stats.area = area;
288 /* ugly, but let's avoid at all costs having users to modify those */
289 ec = (Evas_Coord *)&t->w;
292 ec = (Evas_Coord *)&t->h;
295 ecs = (Evas_Colorspace *)&t->cspace;
298 f = (float *)&t->zoom;
301 s = (size_t *)&t->bytes;
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));
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);
326 _ewk_tile_account_allocated(t);
334 void ewk_tile_free(Ewk_Tile *t)
336 _ewk_tile_account_freed(t);
339 eina_tiler_free(t->updates);
341 cairo_surface_destroy(t->surface);
342 cairo_destroy(t->cairo);
343 evas_object_del(t->image);
348 * Make the tile visible, incrementing its counter.
350 void ewk_tile_show(Ewk_Tile *t)
353 evas_object_show(t->image);
357 * Decrement the visibility counter, making it invisible if necessary.
359 void ewk_tile_hide(Ewk_Tile *t)
363 evas_object_hide(t->image);
367 * Returns EINA_TRUE if the tile is visible, EINA_FALSE otherwise.
369 Eina_Bool ewk_tile_visible_get(Ewk_Tile *t)
375 * Mark whole tile as dirty and requiring update.
377 void ewk_tile_update_full(Ewk_Tile *t)
379 /* TODO: list of tiles pending updates? */
382 if (!t->stats.full_update) {
383 t->stats.full_update = EINA_TRUE;
385 eina_tiler_free(t->updates);
392 * Mark the specific subarea as dirty and requiring update.
394 void ewk_tile_update_area(Ewk_Tile *t, const Eina_Rectangle *r)
396 /* TODO: list of tiles pending updates? */
399 if (t->stats.full_update)
402 if (!r->x && !r->y && r->w == t->w && r->h == t->h) {
403 t->stats.full_update = EINA_TRUE;
405 eina_tiler_free(t->updates);
412 t->updates = eina_tiler_new(t->w, t->h);
414 CRITICAL("could not create eina_tiler %dx%d.", t->w, t->h);
419 eina_tiler_rect_add(t->updates, r);
423 * For each updated region, call the given function.
425 * This will not change the tile statistics or clear the processed
426 * updates, use ewk_tile_updates_clear() for that.
428 void ewk_tile_updates_process(Ewk_Tile *t, void (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update), const void *data)
430 if (t->stats.full_update) {
436 #ifdef TILE_STATS_ACCOUNT_RENDER_TIME
437 struct timeval timev;
439 gettimeofday(&timev, NULL);
440 render_start = (double)timev.tv_sec +
441 (((double)timev.tv_usec) / 1000000);
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;
449 } else if (t->updates) {
450 Eina_Iterator *itr = eina_tiler_iterator_new(t->updates);
453 CRITICAL("could not create tiler iterator!");
456 EINA_ITERATOR_FOREACH(itr, r)
457 cb((void *)data, t, r);
458 eina_iterator_free(itr);
463 * Clear all updates in region, if any.
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
469 * This function is usually called after ewk_tile_updates_process() is
472 void ewk_tile_updates_clear(Ewk_Tile *t)
474 /* TODO: remove from list of pending updates? */
477 if (t->stats.full_update)
478 t->stats.full_update = 0;
479 else if (t->updates) {
480 eina_tiler_free(t->updates);
485 typedef struct _Ewk_Tile_Unused_Cache_Entry Ewk_Tile_Unused_Cache_Entry;
486 struct _Ewk_Tile_Unused_Cache_Entry {
490 void (*cb)(void *data, Ewk_Tile *t);
495 struct _Ewk_Tile_Unused_Cache {
502 size_t max; /**< watermark (in bytes) to start freeing tiles */
503 size_t used; /**< in bytes, maybe more than max. */
506 Evas_Coord x, y, w, h;
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;
519 * Cache of unused tiles (those that are not visible).
521 * The cache of unused tiles.
523 * @param max cache size in bytes.
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.
529 Ewk_Tile_Unused_Cache *ewk_tile_unused_cache_new(size_t max)
531 Ewk_Tile_Unused_Cache *tuc;
533 CALLOC_OR_OOM_RET(tuc, sizeof(Ewk_Tile_Unused_Cache), NULL);
536 tuc->memory.max = max;
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)
543 EINA_SAFETY_ON_NULL_RETURN(tuc);
545 tuc->locked.locked = EINA_TRUE;
550 tuc->locked.zoom = zoom;
553 void ewk_tile_unused_cache_unlock_area(Ewk_Tile_Unused_Cache *tuc)
555 EINA_SAFETY_ON_NULL_RETURN(tuc);
557 tuc->locked.locked = EINA_FALSE;
561 * Free cache of unused tiles.
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.
567 * Those tiles that are still visible will remain live. The unused
568 * tiles will be freed.
570 * @see ewk_tile_unused_cache_unref()
572 static void _ewk_tile_unused_cache_free(Ewk_Tile_Unused_Cache *tuc)
574 EINA_SAFETY_ON_NULL_RETURN(tuc);
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);
582 ewk_tile_unused_cache_clear(tuc);
587 * Clear cache of unused tiles.
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).
592 void ewk_tile_unused_cache_clear(Ewk_Tile_Unused_Cache *tuc)
594 Ewk_Tile_Unused_Cache_Entry *itr;
595 EINA_SAFETY_ON_NULL_RETURN(tuc);
597 if (!tuc->entries.count)
600 EINA_LIST_FREE(tuc->entries.list, itr) {
601 itr->tile_free.cb(itr->tile_free.data, itr->tile);
605 tuc->memory.used = 0;
606 tuc->entries.count = 0;
610 * Hold reference to cache.
612 * @return same pointer as taken.
614 * @see ewk_tile_unused_cache_unref()
616 Ewk_Tile_Unused_Cache *ewk_tile_unused_cache_ref(Ewk_Tile_Unused_Cache *tuc)
618 EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, NULL);
624 * Release cache reference, freeing it if it drops to zero.
626 * @see ewk_tile_unused_cache_ref()
627 * @see ewk_tile_unused_cache_free()
629 void ewk_tile_unused_cache_unref(Ewk_Tile_Unused_Cache *tuc)
631 EINA_SAFETY_ON_NULL_RETURN(tuc);
633 if (!tuc->references)
634 _ewk_tile_unused_cache_free(tuc);
637 void ewk_tile_unused_cache_max_set(Ewk_Tile_Unused_Cache *tuc, size_t max)
639 EINA_SAFETY_ON_NULL_RETURN(tuc);
640 tuc->memory.max = max;
643 size_t ewk_tile_unused_cache_max_get(const Ewk_Tile_Unused_Cache *tuc)
645 EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, 0);
646 return tuc->memory.max;
649 size_t ewk_tile_unused_cache_used_get(const Ewk_Tile_Unused_Cache *tuc)
651 EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, 0);
652 return tuc->memory.used;
655 size_t ewk_tile_unused_cache_flush(Ewk_Tile_Unused_Cache *tuc, size_t bytes)
657 Ewk_Tile_Unused_Cache_Entry *itr;
658 Eina_List *l, *l_next;
659 EINA_SAFETY_ON_NULL_RETURN_VAL(tuc, 0);
663 if (!tuc->entries.count)
669 * NOTE: the cache is a FIFO queue currently.
670 * Don't need to sort any more.
675 EINA_LIST_FOREACH_SAFE(tuc->entries.list, l, l_next, itr) {
676 Ewk_Tile *t = itr->tile;
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) {
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);
694 tuc->memory.used -= done;
695 tuc->entries.count -= count;
700 void ewk_tile_unused_cache_auto_flush(Ewk_Tile_Unused_Cache *tuc)
702 EINA_SAFETY_ON_NULL_RETURN(tuc);
703 if (tuc->memory.used <= tuc->memory.max)
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);
712 * Freeze cache to not do maintenance tasks.
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.
718 * @see ewk_tile_unused_cache_thaw()
720 void ewk_tile_unused_cache_freeze(Ewk_Tile_Unused_Cache *tuc)
726 * Unfreezes maintenance tasks.
728 * If this is the last counterpart of freeze, then maintenance tasks
729 * will run immediately.
731 void ewk_tile_unused_cache_thaw(Ewk_Tile_Unused_Cache *tuc)
734 ERR("thawing more than freezing!");
742 * Get tile from cache of unused tiles, removing it from the cache.
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().
748 * @param tuc cache of unused tiles
749 * @param t the tile to be removed from Ewk_Tile_Unused_Cache.
751 * @return #EINA_TRUE on success, #EINA_FALSE otherwise.
753 Eina_Bool ewk_tile_unused_cache_tile_get(Ewk_Tile_Unused_Cache *tuc, Ewk_Tile *t)
755 Ewk_Tile_Unused_Cache_Entry *entry;
759 EINA_LIST_FOREACH(tuc->entries.list, l, entry)
761 if (entry->tile == t) {
767 ERR("tile %p not found in cache %p", t, tuc);
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);
780 * Put tile into cache of unused tiles, adding it to the cache.
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.
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.
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.
796 * @return #EINA_TRUE on success, #EINA_FALSE otherwise. If @c t->visible
797 * is not #EINA_FALSE, then it will return #EINA_FALSE.
799 * @see ewk_tile_unused_cache_auto_flush()
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)
803 Ewk_Tile_Unused_Cache_Entry *e;
806 ERR("tile=%p is not unused (visible=%d)", t, t->visible);
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");
818 e->weight = 0; /* calculated just before sort */
819 e->tile_free.cb = tile_free_cb;
820 e->tile_free.data = (void *)data;
822 tuc->entries.count++;
823 tuc->memory.used += sizeof(Ewk_Tile) + t->bytes;
828 void ewk_tile_unused_cache_dbg(const Ewk_Tile_Unused_Cache *tuc)
830 Ewk_Tile_Unused_Cache_Entry *itr;
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);
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 ? '*': ' ');