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