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_matrix.h"
24 #include "ewk_tiled_backing_store.h"
25 #include "ewk_tiled_private.h"
30 #include <stdio.h> // XXX remove me later
34 struct _Ewk_Tile_Matrix {
35 Eina_Matrixsparse *matrix;
36 Ewk_Tile_Unused_Cache *tuc;
37 Evas_Colorspace cspace;
39 void (*cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update);
47 #ifdef DEBUG_MEM_LEAKS
50 uint64_t allocated, freed;
56 #ifdef DEBUG_MEM_LEAKS
57 static uint64_t tiles_leaked = 0;
58 static uint64_t bytes_leaked = 0;
61 /* called when matrixsparse is resized or freed */
62 static void _ewk_tile_matrix_cell_free(void *user_data, void *cell_data)
64 Ewk_Tile_Matrix* tm = static_cast<Ewk_Tile_Matrix*>(user_data);
65 Ewk_Tile* t = static_cast<Ewk_Tile*>(cell_data);
70 ewk_tile_unused_cache_freeze(tm->tuc);
72 if (t->updates || t->stats.full_update)
73 tm->updates = eina_list_remove(tm->updates, t);
76 ERR("freeing cell that is visible, leaking tile %p", t);
78 if (!ewk_tile_unused_cache_tile_get(tm->tuc, t))
79 ERR("tile %p was not in cache %p? leaking...", t, tm->tuc);
81 DBG("tile cell does not exist anymore, free it %p", t);
82 #ifdef DEBUG_MEM_LEAKS
83 tm->stats.bytes.freed += t->bytes;
84 tm->stats.tiles.freed++;
90 ewk_tile_unused_cache_thaw(tm->tuc);
93 /* called when cache of unused tile is flushed */
94 static void _ewk_tile_matrix_tile_free(void *data, Ewk_Tile *t)
96 Ewk_Tile_Matrix* tm = static_cast<Ewk_Tile_Matrix*>(data);
97 Eina_Matrixsparse_Cell *cell;
99 if (!eina_matrixsparse_cell_idx_get(tm->matrix, t->row, t->col, &cell)) {
100 ERR("removing tile %p that was not in the matrix? Leaking...", t);
105 ERR("removing tile %p that was not in the matrix? Leaking...", t);
109 if (t->updates || t->stats.full_update)
110 tm->updates = eina_list_remove(tm->updates, t);
112 /* set to null to avoid double free */
113 eina_matrixsparse_cell_data_replace(cell, 0, 0);
114 eina_matrixsparse_cell_clear(cell);
116 if (EINA_UNLIKELY(!!t->visible)) {
117 ERR("cache of unused tiles requesting deletion of used tile %p? "
122 #ifdef DEBUG_MEM_LEAKS
123 tm->stats.bytes.freed += t->bytes;
124 tm->stats.tiles.freed++;
131 * Creates a new matrix of tiles.
133 * The tile matrix is responsible for keeping tiles around and
134 * providing fast access to them. One can use it to retrieve new or
135 * existing tiles and give them back, allowing them to be
136 * freed/replaced by the cache.
138 * @param tuc cache of unused tiles or @c 0 to create one
140 * @param cols number of columns in the matrix.
141 * @param rows number of rows in the matrix.
142 * @param cspace the color space used to create tiles in this matrix.
143 * @param render_cb function used to render given tile update.
144 * @param render_data context to give back to @a render_cb.
146 * @return newly allocated instance on success, @c 0 on failure.
148 Ewk_Tile_Matrix *ewk_tile_matrix_new(Ewk_Tile_Unused_Cache *tuc, unsigned long cols, unsigned long rows, Evas_Colorspace cspace, void (*render_cb)(void *data, Ewk_Tile *t, const Eina_Rectangle *update), const void *render_data)
150 Ewk_Tile_Matrix* tm = static_cast<Ewk_Tile_Matrix*>(calloc(1, sizeof(Ewk_Tile_Matrix)));
154 tm->matrix = eina_matrixsparse_new(rows, cols, _ewk_tile_matrix_cell_free, tm);
156 ERR("could not create sparse matrix.");
162 tm->tuc = ewk_tile_unused_cache_ref(tuc);
164 tm->tuc = ewk_tile_unused_cache_new(40960000);
166 ERR("no cache of unused tile!");
167 eina_matrixsparse_free(tm->matrix);
174 tm->render.cb = render_cb;
175 tm->render.data = (void *)render_data;
176 tm->tile.w = DEFAULT_TILE_W;
177 tm->tile.h = DEFAULT_TILE_H;
183 * Destroys tiles matrix, releasing its resources.
185 * The cache instance is unreferenced, possibly freeing it.
187 void ewk_tile_matrix_free(Ewk_Tile_Matrix *tm)
189 #ifdef DEBUG_MEM_LEAKS
190 uint64_t tiles, bytes;
193 EINA_SAFETY_ON_NULL_RETURN(tm);
194 ewk_tile_unused_cache_freeze(tm->tuc);
196 eina_matrixsparse_free(tm->matrix);
198 ewk_tile_unused_cache_thaw(tm->tuc);
199 ewk_tile_unused_cache_unref(tm->tuc);
201 #ifdef DEBUG_MEM_LEAKS
202 tiles = tm->stats.tiles.allocated - tm->stats.tiles.freed;
203 bytes = tm->stats.bytes.allocated - tm->stats.bytes.freed;
205 tiles_leaked += tiles;
206 bytes_leaked += bytes;
209 ERR("tiled matrix leaked: tiles[+%"PRIu64",-%"PRIu64":%"PRIu64"] "
210 "bytes[+%"PRIu64",-%"PRIu64":%"PRIu64"]",
211 tm->stats.tiles.allocated, tm->stats.tiles.freed, tiles,
212 tm->stats.bytes.allocated, tm->stats.bytes.freed, bytes);
213 else if (tiles_leaked || bytes_leaked)
214 WRN("tiled matrix had no leaks: tiles[+%"PRIu64",-%"PRIu64"] "
215 "bytes[+%"PRIu64",-%"PRIu64"], but some other leaked "
216 "%"PRIu64" tiles (%"PRIu64" bytes)",
217 tm->stats.tiles.allocated, tm->stats.tiles.freed,
218 tm->stats.bytes.allocated, tm->stats.bytes.freed,
219 tiles_leaked, bytes_leaked);
221 INF("tiled matrix had no leaks: tiles[+%"PRIu64",-%"PRIu64"] "
222 "bytes[+%"PRIu64",-%"PRIu64"]",
223 tm->stats.tiles.allocated, tm->stats.tiles.freed,
224 tm->stats.bytes.allocated, tm->stats.bytes.freed);
231 * Resize matrix to given number of rows and columns.
233 void ewk_tile_matrix_resize(Ewk_Tile_Matrix *tm, unsigned long cols, unsigned long rows)
235 EINA_SAFETY_ON_NULL_RETURN(tm);
236 eina_matrixsparse_size_set(tm->matrix, rows, cols);
240 * Get the cache of unused tiles in use by given matrix.
242 * No reference is taken to the cache.
244 Ewk_Tile_Unused_Cache *ewk_tile_matrix_unused_cache_get(const Ewk_Tile_Matrix *tm)
246 EINA_SAFETY_ON_NULL_RETURN_VAL(tm, 0);
251 * Get the exact tile for the given position and zoom.
253 * If the tile was unused then it's removed from the cache.
255 * After usage, please give it back using
256 * ewk_tile_matrix_tile_put(). If you just want to check if it exists,
257 * then use ewk_tile_matrix_tile_exact_exists().
259 * @param tm the tile matrix to get tile from.
260 * @param col the column number.
261 * @param row the row number.
262 * @param zoom the exact zoom to use.
264 * @return The tile instance or @c 0 if none is found. If the tile
265 * was in the unused cache it will be @b removed (thus
266 * considered used) and one should give it back with
267 * ewk_tile_matrix_tile_put() afterwards.
269 * @see ewk_tile_matrix_tile_exact_get()
271 Ewk_Tile *ewk_tile_matrix_tile_exact_get(Ewk_Tile_Matrix *tm, unsigned long col, unsigned long row, float zoom)
273 Ewk_Tile* t = static_cast<Ewk_Tile*>(eina_matrixsparse_data_idx_get(tm->matrix, row, col));
282 if (!ewk_tile_unused_cache_tile_get(tm->tuc, t))
283 WRN("Ewk_Tile was unused but not in cache? bug!");
290 * Checks if tile of given zoom exists in matrix.
292 * @param tm the tile matrix to check tile existence.
293 * @param col the column number.
294 * @param row the row number.
295 * @param zoom the exact zoom to query.
297 * @return @c EINA_TRUE if found, @c EINA_FALSE otherwise.
299 * @see ewk_tile_matrix_tile_exact_get()
301 Eina_Bool ewk_tile_matrix_tile_exact_exists(Ewk_Tile_Matrix *tm, unsigned long col, unsigned long row, float zoom)
303 if (!eina_matrixsparse_data_idx_get(tm->matrix, row, col))
310 * Create a new tile at given position and zoom level in the matrix.
312 * The newly created tile is considered in use and not put into cache
313 * of unused tiles. After it is used one should call
314 * ewk_tile_matrix_tile_put() to give it back to matrix.
316 * @param tm the tile matrix to create tile on.
317 * @param col the column number.
318 * @param row the row number.
319 * @param zoom the level to create tile, used to determine tile size.
321 Ewk_Tile *ewk_tile_matrix_tile_new(Ewk_Tile_Matrix *tm, Evas *evas, unsigned long col, unsigned long row, float zoom)
326 EINA_SAFETY_ON_NULL_RETURN_VAL(tm, 0);
327 EINA_SAFETY_ON_FALSE_RETURN_VAL(zoom > 0.0, 0);
332 t = ewk_tile_new(evas, tw, th, zoom, tm->cspace);
334 ERR("could not create tile %dx%d at %f, cspace=%d", tw, th, (double)zoom, tm->cspace);
338 if (!eina_matrixsparse_data_idx_set(tm->matrix, row, col, t)) {
339 ERR("could not set matrix cell, row/col outside matrix dimensions!");
349 cairo_translate(t->cairo, -t->x, -t->y);
351 t->stats.full_update = EINA_TRUE;
352 tm->updates = eina_list_append(tm->updates, t);
354 #ifdef DEBUG_MEM_LEAKS
355 tm->stats.bytes.allocated += t->bytes;
356 tm->stats.tiles.allocated++;
363 * Gives back the tile to the tile matrix.
365 * This will report the tile is no longer in use by the one that got
366 * it with ewk_tile_matrix_tile_exact_get().
368 * Any previous reference to tile should be released
369 * (ewk_tile_hide()) before calling this function, so it will
370 * be known if it is not visibile anymore and thus can be put into the
373 * @param tm the tile matrix to return tile to.
374 * @param t the tile instance to return, must @b not be @c 0.
375 * @param last_used time in which tile was last used.
377 * @return #EINA_TRUE on success or #EINA_FALSE on failure.
379 Eina_Bool ewk_tile_matrix_tile_put(Ewk_Tile_Matrix *tm, Ewk_Tile *t, double last_used)
381 EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
382 EINA_SAFETY_ON_NULL_RETURN_VAL(t, EINA_FALSE);
387 t->stats.last_used = last_used;
388 return ewk_tile_unused_cache_tile_put(tm->tuc, t, _ewk_tile_matrix_tile_free, tm);
391 Eina_Bool ewk_tile_matrix_tile_update(Ewk_Tile_Matrix *tm, unsigned long col, unsigned long row, const Eina_Rectangle *update)
393 Eina_Rectangle new_update;
394 EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
395 EINA_SAFETY_ON_NULL_RETURN_VAL(update, EINA_FALSE);
397 memcpy(&new_update, update, sizeof(new_update));
398 // check update is valid, otherwise return EINA_FALSE
399 if (update->x < 0 || update->y < 0 || update->w <= 0 || update->h <= 0) {
400 ERR("invalid update region.");
404 if (update->x + update->w - 1 >= tm->tile.w)
405 new_update.w = tm->tile.w - update->x;
406 if (update->y + update->h - 1 >= tm->tile.h)
407 new_update.h = tm->tile.h - update->y;
409 Ewk_Tile* t = static_cast<Ewk_Tile*>(eina_matrixsparse_data_idx_get(tm->matrix, row, col));
413 if (!t->updates && !t->stats.full_update)
414 tm->updates = eina_list_append(tm->updates, t);
415 ewk_tile_update_area(t, &new_update);
420 Eina_Bool ewk_tile_matrix_tile_update_full(Ewk_Tile_Matrix *tm, unsigned long col, unsigned long row)
422 Eina_Matrixsparse_Cell *cell;
423 EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
425 if (!eina_matrixsparse_cell_idx_get(tm->matrix, row, col, &cell))
431 Ewk_Tile* t = static_cast<Ewk_Tile*>(eina_matrixsparse_cell_data_get(cell));
433 CRITICAL("matrix cell with no tile!");
437 if (!t->updates && !t->stats.full_update)
438 tm->updates = eina_list_append(tm->updates, t);
439 ewk_tile_update_full(t);
444 void ewk_tile_matrix_tile_updates_clear(Ewk_Tile_Matrix *tm, Ewk_Tile *t)
446 EINA_SAFETY_ON_NULL_RETURN(tm);
447 if (!t->updates && !t->stats.full_update)
449 ewk_tile_updates_clear(t);
450 tm->updates = eina_list_remove(tm->updates, t);
453 static Eina_Bool _ewk_tile_matrix_slicer_setup(Ewk_Tile_Matrix *tm, const Eina_Rectangle *area, float zoom, Eina_Tile_Grid_Slicer *slicer)
455 unsigned long rows, cols;
456 Evas_Coord x, y, w, h, tw, th;
458 if (area->w <= 0 || area->h <= 0) {
459 WRN("invalid area region: %d,%d+%dx%d.",
460 area->x, area->y, area->w, area->h);
472 // cropping area region to fit matrix
473 eina_matrixsparse_size_get(tm->matrix, &rows, &cols);
483 if (y + h - 1 > (long)(rows * th))
485 if (x + w - 1 > (long)(cols * tw))
488 return eina_tile_grid_slicer_setup(slicer, x, y, w, h, tw, th);
492 Eina_Bool ewk_tile_matrix_update(Ewk_Tile_Matrix *tm, const Eina_Rectangle *update, float zoom)
494 const Eina_Tile_Grid_Info *info;
495 Eina_Tile_Grid_Slicer slicer;
496 EINA_SAFETY_ON_NULL_RETURN_VAL(tm, EINA_FALSE);
497 EINA_SAFETY_ON_NULL_RETURN_VAL(update, EINA_FALSE);
499 if (update->w < 1 || update->h < 1) {
500 DBG("Why we get updates with empty areas? %d,%d+%dx%d at zoom %f",
501 update->x, update->y, update->w, update->h, zoom);
505 if (!_ewk_tile_matrix_slicer_setup(tm, update, zoom, &slicer)) {
506 ERR("Could not setup slicer for update %d,%d+%dx%d at zoom %f",
507 update->x, update->y, update->w, update->h, zoom);
511 while (eina_tile_grid_slicer_next(&slicer, &info)) {
512 unsigned long col, row;
516 Ewk_Tile* t = static_cast<Ewk_Tile*>(eina_matrixsparse_data_idx_get(tm->matrix, row, col));
520 if (!t->updates && !t->stats.full_update)
521 tm->updates = eina_list_append(tm->updates, t);
523 ewk_tile_update_full(t);
525 ewk_tile_update_area(t, &info->rect);
532 void ewk_tile_matrix_updates_process(Ewk_Tile_Matrix *tm)
534 Eina_List *l, *l_next;
536 EINA_SAFETY_ON_NULL_RETURN(tm);
538 // process updates, unflag tiles
539 EINA_LIST_FOREACH_SAFE(tm->updates, l, l_next, item) {
540 Ewk_Tile* tile = static_cast<Ewk_Tile*>(item);
541 ewk_tile_updates_process(tile, tm->render.cb, tm->render.data);
543 ewk_tile_updates_clear(tile);
544 tm->updates = eina_list_remove_list(tm->updates, l);
549 void ewk_tile_matrix_updates_clear(Ewk_Tile_Matrix *tm)
552 EINA_SAFETY_ON_NULL_RETURN(tm);
554 EINA_LIST_FREE(tm->updates, item)
555 ewk_tile_updates_clear(static_cast<Ewk_Tile*>(item));
560 void ewk_tile_matrix_dbg(const Ewk_Tile_Matrix *tm)
562 Eina_Iterator *it = eina_matrixsparse_iterator_complete_new(tm->matrix);
563 Eina_Matrixsparse_Cell *cell;
564 Eina_Bool last_empty = EINA_FALSE;
566 #ifdef DEBUG_MEM_LEAKS
567 printf("Ewk_Tile Matrix: tiles[+%"PRIu64",-%"PRIu64":%"PRIu64"] "
568 "bytes[+%"PRIu64",-%"PRIu64":%"PRIu64"]\n",
569 tm->stats.tiles.allocated, tm->stats.tiles.freed,
570 tm->stats.tiles.allocated - tm->stats.tiles.freed,
571 tm->stats.bytes.allocated, tm->stats.bytes.freed,
572 tm->stats.bytes.allocated - tm->stats.bytes.freed);
574 printf("Ewk_Tile Matrix:\n");
577 EINA_ITERATOR_FOREACH(it, cell) {
578 unsigned long row, col;
579 eina_matrixsparse_cell_position_get(cell, &row, &col);
581 Ewk_Tile* t = static_cast<Ewk_Tile*>(eina_matrixsparse_cell_data_get(cell));
584 last_empty = EINA_TRUE;
587 printf(" [%lu,%lu]", col, row);
590 last_empty = EINA_FALSE;
593 printf("%3lu,%3lu %10p:", col, row, t);
594 printf(" [%3lu,%3lu + %dx%d @ %0.3f]%c", t->col, t->row, t->w, t->h, t->zoom, t->visible ? '*': ' ');
600 eina_iterator_free(it);
602 ewk_tile_unused_cache_dbg(tm->tuc);
606 * Freeze matrix to not do maintenance tasks.
608 * Maintenance tasks optimize usage, but maybe we know we should hold
609 * on them until we do the last operation, in this case we freeze
610 * while operating and then thaw when we're done.
612 * @see ewk_tile_matrix_thaw()
614 void ewk_tile_matrix_freeze(Ewk_Tile_Matrix *tm)
616 EINA_SAFETY_ON_NULL_RETURN(tm);
618 ewk_tile_unused_cache_freeze(tm->tuc);
623 * Unfreezes maintenance tasks.
625 * If this is the last counterpart of freeze, then maintenance tasks
626 * will run immediately.
628 void ewk_tile_matrix_thaw(Ewk_Tile_Matrix *tm)
630 EINA_SAFETY_ON_NULL_RETURN(tm);
632 ERR("thawing more than freezing!");
638 ewk_tile_unused_cache_thaw(tm->tuc);