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