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#define __STDC_FORMAT_MACROS
22#include "config.h"
23
24#include "ewk_private.h"
25#include "ewk_tiled_backing_store_private.h"
26#include "ewk_tiled_matrix_private.h"
27#include "ewk_tiled_model_private.h"
28#include <Eina.h>
29#include <errno.h>
30#include <inttypes.h>
31#include <math.h>
32#include <stdlib.h>
33#include <string.h>
34#include <wtf/OwnPtr.h>
35#include <wtf/PassOwnPtr.h>
36
37struct Ewk_Tile_Matrix_Entry {
38    EINA_INLIST;
39    float zoom;
40    unsigned long count;
41    Eina_Matrixsparse* matrix;
42};
43
44struct _Ewk_Tile_Matrix {
45    Eina_Matrixsparse* matrix;
46    Eina_Inlist* matrices;
47    Ewk_Tile_Unused_Cache* tileUnusedCache;
48    Evas_Colorspace cspace;
49    struct {
50        void (*callback)(void* data, Ewk_Tile* tile, const Eina_Rectangle* update);
51        void* data;
52    } render;
53    unsigned int frozen;
54    Eina_List* updates;
55    struct {
56        Evas_Coord width, height;
57    } tile;
58#ifdef DEBUG_MEM_LEAKS
59    struct {
60        struct {
61            uint64_t allocated, freed;
62        } tiles, bytes;
63    } stats;
64#endif
65};
66
67// Default 40 MB size of newly created cache
68static const size_t DEFAULT_CACHE_SIZE = 40 * 1024 * 1024;
69
70#ifdef DEBUG_MEM_LEAKS
71static uint64_t tiles_leaked = 0;
72static uint64_t bytes_leaked = 0;
73#endif
74
75static Ewk_Tile_Matrix_Entry* ewk_tile_matrix_entry_get(Ewk_Tile_Matrix* tileMatrix, float zoom)
76{
77    EINA_SAFETY_ON_NULL_RETURN_VAL(tileMatrix, 0);
78
79    Ewk_Tile_Matrix_Entry* it;
80    EINA_INLIST_FOREACH(tileMatrix->matrices, it) {
81        if (it->zoom == zoom)
82            return it;
83    }
84
85    return 0;
86}
87
88/* called when matrixsparse is resized or freed */
89static void _ewk_tile_matrix_cell_free(void* userData, void* cellData)
90{
91    Ewk_Tile_Matrix* tileMatrix = static_cast<Ewk_Tile_Matrix*>(userData);
92    Ewk_Tile* tile = static_cast<Ewk_Tile*>(cellData);
93
94    if (!tile)
95        return;
96
97    ewk_tile_unused_cache_freeze(tileMatrix->tileUnusedCache);
98
99    if (tile->updates || tile->stats.full_update)
100        tileMatrix->updates = eina_list_remove(tileMatrix->updates, tile);
101
102    if (tile->visible)
103        ERR("freeing cell that is visible, leaking tile %p", tile);
104    else {
105        if (ewk_tile_unused_cache_tile_get(tileMatrix->tileUnusedCache, tile)) {
106            DBG("tile cell does not exist anymore, free it %p", tile);
107#ifdef DEBUG_MEM_LEAKS
108            tileMatrix->stats.bytes.freed += tile->bytes;
109            tileMatrix->stats.tiles.freed++;
110#endif
111            Ewk_Tile_Matrix_Entry* entry = ewk_tile_matrix_entry_get(tileMatrix, tile->zoom);
112            if (!entry)
113                ERR("can't find matrix for zoom %0.3f", tile->zoom);
114            else
115                --entry->count;
116
117            ewk_tile_free(tile);
118        } else
119            ERR("tile %p was not in cache %p? leaking...", tile, tileMatrix->tileUnusedCache);
120    }
121
122    ewk_tile_unused_cache_thaw(tileMatrix->tileUnusedCache);
123}
124
125/* called when cache of unused tile is flushed */
126static void _ewk_tile_matrix_tile_free(void* data, Ewk_Tile* tile)
127{
128    Ewk_Tile_Matrix* tileMatrix = static_cast<Ewk_Tile_Matrix*>(data);
129
130    Ewk_Tile_Matrix_Entry* entry = ewk_tile_matrix_entry_get(tileMatrix, tile->zoom);
131    if (!entry) {
132        ERR("removing tile %p that was not in any matrix? Leaking...", tile);
133        return;
134    }
135
136    Eina_Matrixsparse_Cell* cell;
137    if (!eina_matrixsparse_cell_idx_get(entry->matrix, tile->row, tile->column, &cell)) {
138        ERR("removing tile %p that was not in the matrix? Leaking...", tile);
139        return;
140    }
141
142    if (!cell) {
143        ERR("removing tile %p that was not in the matrix? Leaking...", tile);
144        return;
145    }
146
147    if (tile->updates || tile->stats.full_update)
148        tileMatrix->updates = eina_list_remove(tileMatrix->updates, tile);
149
150    /* set to null to avoid double free */
151    eina_matrixsparse_cell_data_replace(cell, 0, 0);
152    eina_matrixsparse_cell_clear(cell);
153
154    if (EINA_UNLIKELY(!!tile->visible)) {
155        ERR("cache of unused tiles requesting deletion of used tile %p? "
156            "Leaking...", tile);
157        return;
158    }
159
160#ifdef DEBUG_MEM_LEAKS
161    tileMatrix->stats.bytes.freed += tile->bytes;
162    tileMatrix->stats.tiles.freed++;
163#endif
164
165    --entry->count;
166    if (!entry->count && entry->matrix != tileMatrix->matrix) {
167        eina_matrixsparse_free(entry->matrix);
168        tileMatrix->matrices = eina_inlist_remove(tileMatrix->matrices, EINA_INLIST_GET(entry));
169        delete entry;
170    }
171
172    ewk_tile_free(tile);
173}
174
175/**
176 * Creates a new matrix of tiles.
177 *
178 * The tile matrix is responsible for keeping tiles around and
179 * providing fast access to them. One can use it to retrieve new or
180 * existing tiles and give them back, allowing them to be
181 * freed/replaced by the cache.
182 *
183 * @param tileUnusedCache cache of unused tiles or @c 0 to create one
184 *        automatically.
185 * @param columns number of columns in the matrix.
186 * @param rows number of rows in the matrix.
187 * @param zoomLevel zoom level for the matrix.
188 * @param cspace the color space used to create tiles in this matrix.
189 * @param render_cb function used to render given tile update.
190 * @param render_data context to give back to @a render_cb.
191 *
192 * @return newly allocated instance on success, @c 0 on failure.
193 */
194Ewk_Tile_Matrix* ewk_tile_matrix_new(Ewk_Tile_Unused_Cache* tileUnusedCache, unsigned long columns, unsigned long rows, float zoomLevel, Evas_Colorspace colorSpace, void (*renderCallback)(void* data, Ewk_Tile* tile, const Eina_Rectangle* update), const void* renderData)
195{
196    OwnPtr<Ewk_Tile_Matrix> tileMatrix = adoptPtr(new Ewk_Tile_Matrix);
197
198    tileMatrix->matrices = 0;
199    if (!ewk_tile_matrix_zoom_level_set(tileMatrix.get(), zoomLevel))
200        ewk_tile_matrix_entry_new(tileMatrix.get(), zoomLevel);
201    ewk_tile_matrix_resize(tileMatrix.get(), columns, rows);
202
203    if (tileUnusedCache)
204        tileMatrix->tileUnusedCache = ewk_tile_unused_cache_ref(tileUnusedCache);
205    else {
206        tileMatrix->tileUnusedCache = ewk_tile_unused_cache_new(DEFAULT_CACHE_SIZE);
207        if (!tileMatrix->tileUnusedCache) {
208            ERR("no cache of unused tile!");
209            eina_matrixsparse_free(tileMatrix->matrix);
210            return 0;
211        }
212    }
213
214    tileMatrix->cspace = colorSpace;
215    tileMatrix->render.callback = renderCallback;
216    tileMatrix->render.data = (void*)renderData;
217    tileMatrix->tile.width = defaultTileWidth;
218    tileMatrix->tile.height = defaultTileHeigth;
219    tileMatrix->frozen = 0;
220    tileMatrix->updates = 0;
221
222#ifdef DEBUG_MEM_LEAKS
223    tileMatrix->stats.tiles.allocated = 0;
224    tileMatrix->stats.tiles.freed = 0;
225    tileMatrix->stats.bytes.allocated = 0;
226    tileMatrix->stats.bytes.freed = 0;
227#endif
228    return tileMatrix.leakPtr();
229}
230
231/**
232 * Find the matrix with the same zoom and set it as current matrix.
233 *
234 * @param tileMatrix tile matrix to search the matrix in.
235 * @param zoom zoom factor to find the same matrix with it in matrices.
236 *
237 * @return @c true if found, @c false otherwise.
238 */
239bool ewk_tile_matrix_zoom_level_set(Ewk_Tile_Matrix* tileMatrix, float zoom)
240{
241    EINA_SAFETY_ON_NULL_RETURN_VAL(tileMatrix, false);
242
243    Ewk_Tile_Matrix_Entry* it;
244    EINA_INLIST_FOREACH(tileMatrix->matrices, it) {
245        if (it->zoom == zoom) {
246            tileMatrix->matrices = eina_inlist_promote(tileMatrix->matrices, EINA_INLIST_GET(it));
247            tileMatrix->matrix = it->matrix;
248
249            return true;
250        }
251    }
252    return false;
253}
254
255void ewk_tile_matrix_entry_new(Ewk_Tile_Matrix* tileMatrix, float zoom)
256{
257    EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
258
259    OwnPtr<Ewk_Tile_Matrix_Entry> entry = adoptPtr(new Ewk_Tile_Matrix_Entry);
260    entry->zoom = zoom;
261    entry->count = 0;
262    entry->matrix = eina_matrixsparse_new(1, 1, _ewk_tile_matrix_cell_free, tileMatrix);
263    if (!entry->matrix) {
264        ERR("could not create sparse matrix.");
265        return;
266    }
267    tileMatrix->matrix = entry->matrix;
268    tileMatrix->matrices = eina_inlist_prepend(tileMatrix->matrices, EINA_INLIST_GET(entry.leakPtr()));
269}
270
271void ewk_tile_matrix_invalidate(Ewk_Tile_Matrix* tileMatrix)
272{
273    EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
274
275    Eina_Inlist* matrixList = tileMatrix->matrices;
276    while (matrixList) {
277        Ewk_Tile_Matrix_Entry* it = EINA_INLIST_CONTAINER_GET(matrixList, Ewk_Tile_Matrix_Entry);
278        Eina_Inlist* next = matrixList->next;
279
280        if (it->matrix != tileMatrix->matrix) {
281            eina_matrixsparse_free(it->matrix);
282            tileMatrix->matrices = eina_inlist_remove(tileMatrix->matrices, matrixList);
283            delete it;
284        }
285
286        matrixList = next;
287    }
288}
289
290/**
291 * Destroys tiles matrix, releasing its resources.
292 *
293 * The cache instance is unreferenced, possibly freeing it.
294 */
295void ewk_tile_matrix_free(Ewk_Tile_Matrix* tileMatrix)
296{
297    EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
298
299    ewk_tile_unused_cache_freeze(tileMatrix->tileUnusedCache);
300    ewk_tile_matrix_invalidate(tileMatrix);
301    Ewk_Tile_Matrix_Entry* entry = EINA_INLIST_CONTAINER_GET(tileMatrix->matrices, Ewk_Tile_Matrix_Entry);
302    eina_matrixsparse_free(entry->matrix);
303    tileMatrix->matrices = eina_inlist_remove(tileMatrix->matrices, reinterpret_cast<Eina_Inlist*>(entry));
304    tileMatrix->matrices = 0;
305    delete entry;
306
307    ewk_tile_unused_cache_thaw(tileMatrix->tileUnusedCache);
308    ewk_tile_unused_cache_unref(tileMatrix->tileUnusedCache);
309
310#ifdef DEBUG_MEM_LEAKS
311    uint64_t tiles = tileMatrix->stats.tiles.allocated - tileMatrix->stats.tiles.freed;
312    uint64_t bytes = tileMatrix->stats.bytes.allocated - tileMatrix->stats.bytes.freed;
313
314    tiles_leaked += tiles;
315    bytes_leaked += bytes;
316
317    if (tiles || bytes)
318        ERR("tiled matrix leaked: tiles[+%" PRIu64 ",-%" PRIu64 ":%" PRIu64 "] "
319            "bytes[+%" PRIu64 ",-%" PRIu64 ":%" PRIu64 "]",
320            tileMatrix->stats.tiles.allocated, tileMatrix->stats.tiles.freed, tiles,
321            tileMatrix->stats.bytes.allocated, tileMatrix->stats.bytes.freed, bytes);
322    else if (tiles_leaked || bytes_leaked)
323        WARN("tiled matrix had no leaks: tiles[+%" PRIu64 ",-%" PRIu64 "] "
324            "bytes[+%" PRIu64 ",-%" PRIu64 "], but some other leaked "
325            "%" PRIu64 " tiles (%" PRIu64 " bytes)",
326            tileMatrix->stats.tiles.allocated, tileMatrix->stats.tiles.freed,
327            tileMatrix->stats.bytes.allocated, tileMatrix->stats.bytes.freed,
328            tiles_leaked, bytes_leaked);
329    else
330        INFO("tiled matrix had no leaks: tiles[+%" PRIu64 ",-%" PRIu64 "] "
331            "bytes[+%" PRIu64 ",-%" PRIu64 "]",
332            tileMatrix->stats.tiles.allocated, tileMatrix->stats.tiles.freed,
333            tileMatrix->stats.bytes.allocated, tileMatrix->stats.bytes.freed);
334#endif
335
336    delete tileMatrix;
337}
338
339/**
340 * Resize matrix to given number of rows and columns.
341 */
342void ewk_tile_matrix_resize(Ewk_Tile_Matrix* tileMatrix, unsigned long cols, unsigned long rows)
343{
344    EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
345
346    eina_matrixsparse_size_set(tileMatrix->matrix, rows, cols);
347}
348
349void ewk_tile_matrix_size_get(Ewk_Tile_Matrix* tileMatrix, unsigned long* columns, unsigned long* rows)
350{
351    EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
352
353    eina_matrixsparse_size_get(tileMatrix->matrix, rows, columns);
354}
355
356/**
357 * Get the cache of unused tiles in use by given matrix.
358 *
359 * No reference is taken to the cache.
360 */
361Ewk_Tile_Unused_Cache* ewk_tile_matrix_unused_cache_get(const Ewk_Tile_Matrix* tileMatrix)
362{
363    EINA_SAFETY_ON_NULL_RETURN_VAL(tileMatrix, 0);
364
365    return tileMatrix->tileUnusedCache;
366}
367
368/**
369 * Get the exact tile for the given position and zoom.
370 *
371 * If the tile.widthas unused then it's removed from the cache.
372 *
373 * After usage, please give it back using
374 * ewk_tile_matrix_tile_put(). If you just want to check if it exists,
375 * then use ewk_tile_matrix_tile_exact_exists().
376 *
377 * @param tileMatrix the tile matrix to get tile from.
378 * @param column the column number.
379 * @param row the row number.
380 * @param zoom the exact zoom to use.
381 *
382 * @return The tile instance or @c 0 if none is found. If the tile
383 *         was in the unused cache it will be @b removed (thus
384 *         considered used) and one should give it back with
385 *         ewk_tile_matrix_tile_put() afterwards.
386 *
387 * @see ewk_tile_matrix_tile_exact_get()
388 */
389Ewk_Tile* ewk_tile_matrix_tile_exact_get(Ewk_Tile_Matrix* tileMatrix, unsigned long column, unsigned long row, float zoom)
390{
391    Ewk_Tile* tile = static_cast<Ewk_Tile*>(eina_matrixsparse_data_idx_get(tileMatrix->matrix, row, column));
392    if (!tile)
393        return 0;
394
395    if (tile->zoom != zoom)
396        return 0;
397
398#ifndef NDEBUG
399    if (!tile->visible) {
400        if (!ewk_tile_unused_cache_tile_get(tileMatrix->tileUnusedCache, tile))
401            WARN("Ewk_Tile was unused but not in cache? bug!");
402    }
403#endif
404
405    return tile;
406}
407
408/**
409 * Checks if tile of given zoom exists in matrix.
410 *
411 * @param tileMatrix the tile matrix to check tile existence.
412 * @param column the column number.
413 * @param row the row number.
414 * @param zoom the exact zoom to query.
415 *
416 * @return @c true if found, @c false otherwise.
417 *
418 * @see ewk_tile_matrix_tile_exact_get()
419 */
420bool ewk_tile_matrix_tile_exact_exists(Ewk_Tile_Matrix* tileMatrix, unsigned long column, unsigned long row, float zoom)
421{
422    return ewk_tile_matrix_tile_exact_get(tileMatrix, column, row, zoom);
423}
424
425/**
426 * Create a new tile at given position and zoom level in the matrix.
427 *
428 * The newly created tile is considered in use and not put into cache
429 * of unused tiles. After it is used one should call
430 * ewk_tile_matrix_tile_put() to give it back to matrix.
431 *
432 * @param tileMatrix the tile matrix to create tile on.
433 * @param column the column number.
434 * @param row the row number.
435 * @param zoom the level to create tile, used to determine tile size.
436 */
437Ewk_Tile* ewk_tile_matrix_tile_new(Ewk_Tile_Matrix* tileMatrix, Evas* canvas, unsigned long column, unsigned long row, float zoom)
438{
439    EINA_SAFETY_ON_NULL_RETURN_VAL(tileMatrix, 0);
440    EINA_SAFETY_ON_FALSE_RETURN_VAL(zoom > 0.0, 0);
441
442    Ewk_Tile_Matrix_Entry* entry = ewk_tile_matrix_entry_get(tileMatrix, zoom);
443    if (!entry) {
444        ERR("could not get matrix at zoom %f for tile", zoom);
445        return 0;
446    }
447    ++entry->count;
448
449    Evas_Coord tileWidth = tileMatrix->tile.width;
450    Evas_Coord tileHeight = tileMatrix->tile.height;
451
452    Ewk_Tile* tile = ewk_tile_new(canvas, tileWidth, tileHeight, zoom, tileMatrix->cspace);
453    if (!tile) {
454        ERR("could not create tile %dx%d at %f, cspace=%d", tileWidth, tileHeight, (double)zoom, tileMatrix->cspace);
455        return 0;
456    }
457
458    if (!eina_matrixsparse_data_idx_set(tileMatrix->matrix, row, column, tile)) {
459        ERR("could not set matrix cell, row/col outside matrix dimensions!");
460        ewk_tile_free(tile);
461        return 0;
462    }
463
464    tile->column = column;
465    tile->row = row;
466    tile->x = column * tileWidth;
467    tile->y = row * tileHeight;
468    tile->stats.full_update = true;
469    tileMatrix->updates = eina_list_append(tileMatrix->updates, tile);
470
471#ifdef DEBUG_MEM_LEAKS
472    tileMatrix->stats.bytes.allocated += tile->bytes;
473    tileMatrix->stats.tiles.allocated++;
474#endif
475
476    return tile;
477}
478
479/**
480 * Gives back the tile to the tile matrix.
481 *
482 * This will report the tile is no longer in use by the one that got
483 * it with ewk_tile_matrix_tile_exact_get().
484 *
485 * Any previous reference to tile should be released
486 * (ewk_tile.heightide()) before calling this function, so it will
487 * be known if it is not visibile anymore and thus can be put into the
488 * unused cache.
489 *
490 * @param tileMatrix the tile matrix to return tile to.
491 * @param tile the tile instance to return, must @b not be @c 0.
492 * @param last_used time in which tile.widthas last used.
493 *
494 * @return #true on success or #false on failure.
495 */
496bool ewk_tile_matrix_tile_put(Ewk_Tile_Matrix* tileMatrix, Ewk_Tile* tile, double lastUsed)
497{
498    EINA_SAFETY_ON_NULL_RETURN_VAL(tileMatrix, false);
499    EINA_SAFETY_ON_NULL_RETURN_VAL(tile, false);
500
501    if (tile->visible)
502        return true;
503
504    tile->stats.last_used = lastUsed;
505    return ewk_tile_unused_cache_tile_put(tileMatrix->tileUnusedCache, tile, _ewk_tile_matrix_tile_free, tileMatrix);
506}
507
508void ewk_tile_matrix_tile_updates_clear(Ewk_Tile_Matrix* tileMatrix, Ewk_Tile* tile)
509{
510    EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
511
512    if (!tile->updates && !tile->stats.full_update)
513        return;
514
515    ewk_tile_updates_clear(tile);
516    tileMatrix->updates = eina_list_remove(tileMatrix->updates, tile);
517}
518
519static bool _ewk_tile_matrix_slicer_setup(Ewk_Tile_Matrix* tileMatrix, const Eina_Rectangle* area, float zoom, Eina_Tile_Grid_Slicer* slicer)
520{
521    UNUSED_PARAM(zoom);
522    if (area->w <= 0 || area->h <= 0) {
523        WARN("invalid area region: %d,%d+%dx%d.", area->x, area->y, area->w, area->h);
524        return false;
525    }
526
527    Evas_Coord x = area->x;
528    Evas_Coord y = area->y;
529    Evas_Coord width = area->w;
530    Evas_Coord height = area->h;
531
532    Evas_Coord tileWidth = tileMatrix->tile.width;
533    Evas_Coord tileHeight = tileMatrix->tile.height;
534
535    // cropping area region to fit matrix
536    unsigned long rows, cols;
537    eina_matrixsparse_size_get(tileMatrix->matrix, &rows, &cols);
538    if (x < 0) {
539        width += x;
540        x = 0;
541    }
542    if (y < 0) {
543        height += y;
544        y = 0;
545    }
546
547    if (y + height - 1 > static_cast<long>(rows * tileHeight))
548        height = rows * tileHeight - y;
549    if (x + width - 1 > static_cast<long>(cols * tileWidth))
550        width = cols * tileWidth - x;
551
552    return eina_tile_grid_slicer_setup(slicer, x, y, width, height, tileWidth, tileHeight);
553}
554
555
556bool ewk_tile_matrix_update(Ewk_Tile_Matrix* tileMatrix, const Eina_Rectangle* update, float zoom)
557{
558    EINA_SAFETY_ON_NULL_RETURN_VAL(tileMatrix, false);
559    EINA_SAFETY_ON_NULL_RETURN_VAL(update, false);
560
561    if (update->w < 1 || update->h < 1) {
562        DBG("Why we get updates with empty areas? %d,%d+%dx%d at zoom %f", update->x, update->y, update->w, update->h, zoom);
563        return true;
564    }
565
566    Eina_Tile_Grid_Slicer slicer;
567    if (!_ewk_tile_matrix_slicer_setup(tileMatrix, update, zoom, &slicer)) {
568        ERR("Could not setup slicer for update %d,%d+%dx%d at zoom %f", update->x, update->y, update->w, update->h, zoom);
569        return false;
570    }
571
572    const Eina_Tile_Grid_Info* info;
573    while (eina_tile_grid_slicer_next(&slicer, &info)) {
574        Ewk_Tile* tile = static_cast<Ewk_Tile*>(eina_matrixsparse_data_idx_get(tileMatrix->matrix, info->row, info->col));
575        if (!tile)
576            continue;
577
578        if (!tile->updates && !tile->stats.full_update)
579            tileMatrix->updates = eina_list_append(tileMatrix->updates, tile);
580        if (info->full)
581            ewk_tile_update_full(tile);
582        else
583            ewk_tile_update_area(tile, &info->rect);
584    }
585
586
587    return true;
588}
589
590void ewk_tile_matrix_updates_process(Ewk_Tile_Matrix* tileMatrix)
591{
592    EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
593
594    // process updates, unflag tiles
595    Eina_List* list, *listNext;
596    void* item;
597    EINA_LIST_FOREACH_SAFE(tileMatrix->updates, list, listNext, item) {
598        Ewk_Tile* tile = static_cast<Ewk_Tile*>(item);
599        ewk_tile_updates_process(tile, tileMatrix->render.callback, tileMatrix->render.data);
600        if (tile->visible) {
601            ewk_tile_updates_clear(tile);
602            tileMatrix->updates = eina_list_remove_list(tileMatrix->updates, list);
603        }
604    }
605}
606
607void ewk_tile_matrix_updates_clear(Ewk_Tile_Matrix* tileMatrix)
608{
609    EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
610
611    void* item;
612    EINA_LIST_FREE(tileMatrix->updates, item)
613        ewk_tile_updates_clear(static_cast<Ewk_Tile*>(item));
614    tileMatrix->updates = 0;
615}
616
617#ifdef DEBUG_MEM_LEAKS
618// remove me later!
619void ewk_tile_matrix_dbg(const Ewk_Tile_Matrix* tileMatrix)
620{
621    Eina_Iterator* it = eina_matrixsparse_iterator_complete_new(tileMatrix->matrix);
622    Eina_Matrixsparse_Cell* cell;
623    bool wasPreviousEmpty = false;
624
625    printf("Ewk_Tile Matrix: tiles[+%" PRIu64 ",-%" PRIu64 ":%" PRIu64 "] "
626           "bytes[+%" PRIu64 ",-%" PRIu64 ":%" PRIu64 "]\n",
627           tileMatrix->stats.tiles.allocated, tileMatrix->stats.tiles.freed,
628           tileMatrix->stats.tiles.allocated - tileMatrix->stats.tiles.freed,
629           tileMatrix->stats.bytes.allocated, tileMatrix->stats.bytes.freed,
630           tileMatrix->stats.bytes.allocated - tileMatrix->stats.bytes.freed);
631
632    EINA_ITERATOR_FOREACH(it, cell) {
633        unsigned long row, column;
634        eina_matrixsparse_cell_position_get(cell, &row, &column);
635
636        Ewk_Tile* tile = static_cast<Ewk_Tile*>(eina_matrixsparse_cell_data_get(cell));
637        if (!tile) {
638            if (!wasPreviousEmpty) {
639                wasPreviousEmpty = true;
640                printf("Empty:");
641            }
642            printf(" [%lu,%lu]", column, row);
643        } else {
644            if (wasPreviousEmpty) {
645                wasPreviousEmpty = false;
646                printf("\n");
647            }
648            printf("%3lu,%3lu %10p:", column, row, tile);
649            printf(" [%3lu,%3lu + %dx%d @ %0.3f]%c", tile->column, tile->row, tile->width, tile->height, tile->zoom, tile->visible ? '*' : ' ');
650            printf("\n");
651        }
652    }
653    if (wasPreviousEmpty)
654        printf("\n");
655    eina_iterator_free(it);
656
657    ewk_tile_unused_cache_dbg(tileMatrix->tileUnusedCache);
658}
659#endif
660
661/**
662 * Freeze matrix to not do maintenance tasks.
663 *
664 * Maintenance tasks optimize usage, but maybe we know we should hold
665 * on them until we do the last operation, in this case we freeze
666 * while operating and then thaw when we're done.
667 *
668 * @see ewk_tile_matrix_thaw()
669 */
670void ewk_tile_matrix_freeze(Ewk_Tile_Matrix* tileMatrix)
671{
672    EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
673
674    if (!tileMatrix->frozen)
675        ewk_tile_unused_cache_freeze(tileMatrix->tileUnusedCache);
676    ++tileMatrix->frozen;
677}
678
679/**
680 * Unfreezes maintenance tasks.
681 *
682 * If this is the last counterpart of freeze, then maintenance tasks
683 * will run immediately.
684 */
685void ewk_tile_matrix_thaw(Ewk_Tile_Matrix* tileMatrix)
686{
687    EINA_SAFETY_ON_NULL_RETURN(tileMatrix);
688
689    if (!tileMatrix->frozen) {
690        ERR("thawing more than freezing!");
691        return;
692    }
693
694    --tileMatrix->frozen;
695    if (!tileMatrix->frozen)
696        ewk_tile_unused_cache_thaw(tileMatrix->tileUnusedCache);
697}
698