ewk_tiled_model.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760
  1. /*
  2. Copyright (C) 2009-2010 Samsung Electronics
  3. Copyright (C) 2009-2010 ProFUSION embedded systems
  4. This library is free software; you can redistribute it and/or
  5. modify it under the terms of the GNU Library General Public
  6. License as published by the Free Software Foundation; either
  7. version 2 of the License, or (at your option) any later version.
  8. This library is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. Library General Public License for more details.
  12. You should have received a copy of the GNU Library General Public License
  13. along with this library; see the file COPYING.LIB. If not, write to
  14. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  15. Boston, MA 02110-1301, USA.
  16. */
  17. #define __STDC_FORMAT_MACROS
  18. #include "config.h"
  19. #include "ewk_private.h"
  20. #include "ewk_tiled_backing_store_private.h"
  21. #include "ewk_tiled_model_private.h"
  22. #include <Ecore_Evas.h>
  23. #include <Eina.h>
  24. #include <eina_safety_checks.h>
  25. #include <errno.h>
  26. #include <inttypes.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #ifdef TILE_STATS_ACCOUNT_RENDER_TIME
  30. #include <sys/time.h>
  31. #endif
  32. #define IDX(col, row, rowspan) (col + (rowidth * rowspan))
  33. #ifdef DEBUG_MEM_LEAKS
  34. static uint64_t tiles_allocated = 0;
  35. static uint64_t tiles_freed = 0;
  36. static uint64_t bytes_allocated = 0;
  37. static uint64_t bytes_freed = 0;
  38. struct tile_account {
  39. Evas_Coord size;
  40. struct {
  41. uint64_t allocated;
  42. uint64_t freed;
  43. } tiles, bytes;
  44. };
  45. static size_t accounting_len = 0;
  46. static struct tile_account* accounting = 0;
  47. static inline struct tile_account* _ewk_tile_account_get(const Ewk_Tile* tile)
  48. {
  49. struct tile_account* acc;
  50. for (size_t i = 0; i < accounting_len; i++) {
  51. if (accounting[i].size == tile->width)
  52. return accounting + i;
  53. }
  54. accounting = static_cast<struct tile_account*>(realloc(accounting, sizeof(struct tile_account) * (accounting_len + 1)));
  55. acc = accounting + accounting_len;
  56. acc->size = tile->width;
  57. acc->tiles.allocated = 0;
  58. acc->tiles.freed = 0;
  59. acc->bytes.allocated = 0;
  60. acc->bytes.freed = 0;
  61. accounting_len++;
  62. return acc;
  63. }
  64. static inline void _ewk_tile_account_allocated(const Ewk_Tile* tile)
  65. {
  66. struct tile_account* acc = _ewk_tile_account_get(tile);
  67. if (!acc)
  68. return;
  69. acc->bytes.allocated += tile->bytes;
  70. acc->tiles.allocated++;
  71. bytes_allocated += tile->bytes;
  72. tiles_allocated++;
  73. }
  74. static inline void _ewk_tile_account_freed(const Ewk_Tile* tile)
  75. {
  76. struct tile_account* acc = _ewk_tile_account_get(tile);
  77. if (!acc)
  78. return;
  79. acc->bytes.freed += tile->bytes;
  80. acc->tiles.freed++;
  81. bytes_freed += tile->bytes;
  82. tiles_freed++;
  83. }
  84. void ewk_tile_accounting_dbg()
  85. {
  86. struct tile_account* acc;
  87. struct tile_account* acc_end;
  88. printf("TILE BALANCE: tiles[+%" PRIu64 ",-%" PRIu64 ":%" PRIu64 "] "
  89. "bytes[+%" PRIu64 ",-%" PRIu64 ":%" PRIu64 "]\n",
  90. tiles_allocated, tiles_freed, tiles_allocated - tiles_freed,
  91. bytes_allocated, bytes_freed, bytes_allocated - bytes_freed);
  92. if (!accounting_len)
  93. return;
  94. acc = accounting;
  95. acc_end = acc + accounting_len;
  96. printf("BEGIN: TILE BALANCE DETAILS (TO THIS MOMENT!):\n");
  97. for (; acc < acc_end; acc++) {
  98. uint64_t tiles, bytes;
  99. tiles = acc->tiles.allocated - acc->tiles.freed;
  100. bytes = acc->bytes.allocated - acc->bytes.freed;
  101. printf(" %4d: tiles[+%4" PRIu64 ",-%4" PRIu64 ":%4" PRIu64 "] "
  102. "bytes[+%8" PRIu64 ",-%8" PRIu64 ":%8" PRIu64 "]%s\n",
  103. acc->size,
  104. acc->tiles.allocated, acc->tiles.freed, tiles,
  105. acc->bytes.allocated, acc->bytes.freed, bytes,
  106. (bytes || tiles) ? " POSSIBLE LEAK" : "");
  107. }
  108. printf("END: TILE BALANCE DETAILS (TO THIS MOMENT!):\n");
  109. }
  110. #else
  111. static inline void _ewk_tile_account_allocated(const Ewk_Tile*) { }
  112. static inline void _ewk_tile_account_freed(const Ewk_Tile*) { }
  113. void ewk_tile_accounting_dbg()
  114. {
  115. printf("compile webkit with DEBUG_MEM_LEAKS defined!\n");
  116. }
  117. #endif
  118. /**
  119. * Create a new tile of given size, zoom level and colorspace.
  120. *
  121. * After created these properties are immutable as they're the basic
  122. * characteristic of the tile and any change will lead to invalid
  123. * memory access.
  124. *
  125. * Other members are of free-access and no getters/setters are
  126. * provided in orderr to avoid expensive operations on those, however
  127. * some are better manipulated with provided functions, such as
  128. * ewk_tile_show() and ewk_tile_hide() to change
  129. * @c visible or ewk_tile_update_full(), ewk_tile_update_area(),
  130. * ewk_tile_updates_clear() to change @c stats.misses,
  131. * @c stats.full_update and @c updates.
  132. */
  133. Ewk_Tile* ewk_tile_new(Evas* evas, Evas_Coord width, Evas_Coord height, float zoom, Evas_Colorspace colorSpace)
  134. {
  135. Evas_Coord* evasCoord;
  136. Evas_Colorspace* evasColorSpace;
  137. float* tileZoom;
  138. size_t* tileSize;
  139. Ewk_Tile* tile;
  140. unsigned int area;
  141. size_t bytes;
  142. Ecore_Evas* ecoreEvas;
  143. const char* engine;
  144. area = width * height;
  145. if (colorSpace == EVAS_COLORSPACE_ARGB8888)
  146. bytes = area * 4;
  147. else if (colorSpace == EVAS_COLORSPACE_RGB565_A5P)
  148. bytes = area * 2;
  149. else {
  150. ERR("unknown color space: %d", colorSpace);
  151. return 0;
  152. }
  153. DBG("size: %dx%d (%d), zoom: %f, cspace=%d", width, height, area, (double)zoom, colorSpace);
  154. tile = static_cast<Ewk_Tile*>(malloc(sizeof(Ewk_Tile)));
  155. if (!tile)
  156. return 0;
  157. tile->image = evas_object_image_add(evas);
  158. ecoreEvas = ecore_evas_ecore_evas_get(evas);
  159. engine = ecore_evas_engine_name_get(ecoreEvas);
  160. if (engine && !strcmp(engine, "opengl_x11"))
  161. evas_object_image_content_hint_set(tile->image, EVAS_IMAGE_CONTENT_HINT_DYNAMIC);
  162. tile->visible = 0;
  163. tile->updates = 0;
  164. memset(&tile->stats, 0, sizeof(Ewk_Tile_Stats));
  165. tile->stats.area = area;
  166. /* ugly, but let's avoid at all costs having users to modify those */
  167. evasCoord = (Evas_Coord*)&tile->width;
  168. *evasCoord = width;
  169. evasCoord = (Evas_Coord*)&tile->height;
  170. *evasCoord = height;
  171. evasColorSpace = (Evas_Colorspace*)&tile->cspace;
  172. *evasColorSpace = colorSpace;
  173. tileZoom = (float*)&tile->zoom;
  174. *tileZoom = zoom;
  175. tileSize = (size_t*)&tile->bytes;
  176. *tileSize = bytes;
  177. evas_object_image_size_set(tile->image, tile->width, tile->height);
  178. evas_object_image_colorspace_set(tile->image, tile->cspace);
  179. _ewk_tile_account_allocated(tile);
  180. return tile;
  181. }
  182. /**
  183. * Free tile memory.
  184. */
  185. void ewk_tile_free(Ewk_Tile* tile)
  186. {
  187. _ewk_tile_account_freed(tile);
  188. if (tile->updates)
  189. eina_tiler_free(tile->updates);
  190. evas_object_del(tile->image);
  191. free(tile);
  192. }
  193. /**
  194. * @internal
  195. * Returns memory size used by given tile
  196. *
  197. * @param t tile to size check
  198. * @return Returns used memory or zero if object is NULL.
  199. */
  200. size_t ewk_tile_memory_size_get(const Ewk_Tile* tile)
  201. {
  202. EINA_SAFETY_ON_NULL_RETURN_VAL(tile, 0);
  203. return sizeof(Ewk_Tile) + tile->bytes;
  204. }
  205. /**
  206. * Make the tile visible, incrementing its counter.
  207. */
  208. void ewk_tile_show(Ewk_Tile* tile)
  209. {
  210. tile->visible++;
  211. evas_object_show(tile->image);
  212. }
  213. /**
  214. * Decrement the visibility counter, making it invisible if necessary.
  215. */
  216. void ewk_tile_hide(Ewk_Tile* tile)
  217. {
  218. tile->visible--;
  219. if (!tile->visible)
  220. evas_object_hide(tile->image);
  221. }
  222. /**
  223. * Returns true if the tile is visible, false otherwise.
  224. */
  225. Eina_Bool ewk_tile_visible_get(Ewk_Tile* tile)
  226. {
  227. return !!tile->visible;
  228. }
  229. /**
  230. * Mark whole tile as dirty and requiring update.
  231. */
  232. void ewk_tile_update_full(Ewk_Tile* tile)
  233. {
  234. /* TODO: list of tiles pending updates? */
  235. tile->stats.misses++;
  236. if (!tile->stats.full_update) {
  237. tile->stats.full_update = true;
  238. if (tile->updates) {
  239. eina_tiler_free(tile->updates);
  240. tile->updates = 0;
  241. }
  242. }
  243. }
  244. /**
  245. * Mark the specific subarea as dirty and requiring update.
  246. */
  247. void ewk_tile_update_area(Ewk_Tile* tile, const Eina_Rectangle* rect)
  248. {
  249. /* TODO: list of tiles pending updates? */
  250. tile->stats.misses++;
  251. if (tile->stats.full_update)
  252. return;
  253. if (!rect->x && !rect->y && rect->w == tile->width && rect->h == tile->height) {
  254. tile->stats.full_update = true;
  255. if (tile->updates) {
  256. eina_tiler_free(tile->updates);
  257. tile->updates = 0;
  258. }
  259. return;
  260. }
  261. if (!tile->updates) {
  262. tile->updates = eina_tiler_new(tile->width, tile->height);
  263. if (!tile->updates) {
  264. CRITICAL("could not create eina_tiler %dx%d.", tile->width, tile->height);
  265. return;
  266. }
  267. }
  268. eina_tiler_rect_add(tile->updates, rect);
  269. }
  270. /**
  271. * For each updated region, call the given function.
  272. *
  273. * This will not change the tile statistics or clear the processed
  274. * updates, use ewk_tile_updates_clear() for that.
  275. */
  276. void ewk_tile_updates_process(Ewk_Tile* tile, void (*callback)(void* data, Ewk_Tile* tile, const Eina_Rectangle* update), const void* data)
  277. {
  278. if (tile->stats.full_update) {
  279. Eina_Rectangle rect;
  280. rect.x = 0;
  281. rect.y = 0;
  282. rect.w = tile->width;
  283. rect.h = tile->height;
  284. #ifdef TILE_STATS_ACCOUNT_RENDER_TIME
  285. struct timeval timev;
  286. double renderStartTime;
  287. gettimeofday(&timev, 0);
  288. renderStartTime = (double)timev.tv_sec +
  289. (((double)timev.tv_usec) / 1000000);
  290. #endif
  291. callback((void*)data, tile, &rect);
  292. #ifdef TILE_STATS_ACCOUNT_RENDER_TIME
  293. gettimeofday(&timev, 0);
  294. tile->stats.render_time = (double)timev.tv_sec +
  295. (((double)timev.tv_usec) / 1000000) - renderStartTime;
  296. #endif
  297. } else if (tile->updates) {
  298. Eina_Iterator* itr = eina_tiler_iterator_new(tile->updates);
  299. Eina_Rectangle* rect;
  300. if (!itr) {
  301. CRITICAL("could not create tiler iterator!");
  302. return;
  303. }
  304. EINA_ITERATOR_FOREACH(itr, rect)
  305. callback((void*)data, tile, rect);
  306. eina_iterator_free(itr);
  307. }
  308. }
  309. /**
  310. * Clear all updates in region, if any.
  311. *
  312. * This will change the tile statistics, specially zero stat.misses
  313. * and unset stats.full_update. If tile->updates existed, then it will be
  314. * destroyed.
  315. *
  316. * This function is usually called after ewk_tile_updates_process() is
  317. * called.
  318. */
  319. void ewk_tile_updates_clear(Ewk_Tile* tile)
  320. {
  321. /* TODO: remove from list of pending updates? */
  322. tile->stats.misses = 0;
  323. if (tile->stats.full_update)
  324. tile->stats.full_update = 0;
  325. else if (tile->updates) {
  326. eina_tiler_free(tile->updates);
  327. tile->updates = 0;
  328. }
  329. }
  330. typedef struct _Ewk_Tile_Unused_Cache_Entry Ewk_Tile_Unused_Cache_Entry;
  331. struct _Ewk_Tile_Unused_Cache_Entry {
  332. Ewk_Tile* tile;
  333. int weight;
  334. struct {
  335. void (*callback)(void* data, Ewk_Tile* tile);
  336. void* data;
  337. } tile_free;
  338. };
  339. struct _Ewk_Tile_Unused_Cache {
  340. struct {
  341. Eina_List* list;
  342. size_t count;
  343. size_t allocated;
  344. } entries;
  345. struct {
  346. size_t max; /**< watermark (in bytes) to start freeing tiles */
  347. size_t used; /**< in bytes, maybe more than max. */
  348. } memory;
  349. struct {
  350. Evas_Coord x, y, width, height;
  351. float zoom;
  352. Eina_Bool locked;
  353. } locked;
  354. int references;
  355. unsigned int frozen;
  356. };
  357. static const size_t TILE_UNUSED_CACHE_ALLOCATE_INITIAL = 128;
  358. static const size_t TILE_UNUSED_CACHE_ALLOCATE_STEP = 16;
  359. static const size_t TILE_UNUSED_CACHE_MAX_FREE = 32;
  360. /**
  361. * Cache of unused tiles (those that are not visible).
  362. *
  363. * The cache of unused tiles.
  364. *
  365. * @param max cache size in bytes.
  366. *
  367. * @return newly allocated cache of unused tiles, use
  368. * ewk_tile_unused_cache_free() to release resources. If not
  369. * possible to allocate memory, @c 0 is returned.
  370. */
  371. Ewk_Tile_Unused_Cache* ewk_tile_unused_cache_new(size_t max)
  372. {
  373. Ewk_Tile_Unused_Cache* tileUnusedCache;
  374. tileUnusedCache = new Ewk_Tile_Unused_Cache;
  375. memset(tileUnusedCache, 0, sizeof(Ewk_Tile_Unused_Cache));
  376. DBG("tileUnusedCache=%p", tileUnusedCache);
  377. tileUnusedCache->memory.max = max;
  378. tileUnusedCache->references = 1;
  379. return tileUnusedCache;
  380. }
  381. void ewk_tile_unused_cache_lock_area(Ewk_Tile_Unused_Cache* tileUnusedCache, Evas_Coord x, Evas_Coord y, Evas_Coord width, Evas_Coord height, float zoom)
  382. {
  383. EINA_SAFETY_ON_NULL_RETURN(tileUnusedCache);
  384. tileUnusedCache->locked.locked = true;
  385. tileUnusedCache->locked.x = x;
  386. tileUnusedCache->locked.y = y;
  387. tileUnusedCache->locked.width = width;
  388. tileUnusedCache->locked.height = height;
  389. tileUnusedCache->locked.zoom = zoom;
  390. }
  391. void ewk_tile_unused_cache_unlock_area(Ewk_Tile_Unused_Cache* tileUnusedCache)
  392. {
  393. EINA_SAFETY_ON_NULL_RETURN(tileUnusedCache);
  394. tileUnusedCache->locked.locked = false;
  395. }
  396. /**
  397. * Free cache of unused tiles.
  398. *
  399. * This function should be only called by ewk_tile_unused_cache_unref
  400. * function. Calling this function without considering reference counting
  401. * may lead to unknown results.
  402. *
  403. * Those tiles that are still visible will remain live. The unused
  404. * tiles will be freed.
  405. *
  406. * @see ewk_tile_unused_cache_unref()
  407. */
  408. static void _ewk_tile_unused_cache_free(Ewk_Tile_Unused_Cache* tileUnusedCache)
  409. {
  410. EINA_SAFETY_ON_NULL_RETURN(tileUnusedCache);
  411. DBG("tileUnusedCache=%p, "
  412. "entries=(count:%zd, allocated:%zd), "
  413. "memory=(max:%zd, used:%zd)",
  414. tileUnusedCache, tileUnusedCache->entries.count, tileUnusedCache->entries.allocated,
  415. tileUnusedCache->memory.max, tileUnusedCache->memory.used);
  416. ewk_tile_unused_cache_clear(tileUnusedCache);
  417. delete tileUnusedCache;
  418. }
  419. /**
  420. * Clear cache of unused tiles.
  421. *
  422. * Any tiles that are in the cache are freed. The only tiles that are
  423. * kept are those that aren't in the cache (i.e. that are visible).
  424. */
  425. void ewk_tile_unused_cache_clear(Ewk_Tile_Unused_Cache* tileUnusedCache)
  426. {
  427. EINA_SAFETY_ON_NULL_RETURN(tileUnusedCache);
  428. if (!tileUnusedCache->entries.count)
  429. return;
  430. void* item;
  431. EINA_LIST_FREE(tileUnusedCache->entries.list, item) {
  432. Ewk_Tile_Unused_Cache_Entry* itr = static_cast<Ewk_Tile_Unused_Cache_Entry*>(item);
  433. itr->tile_free.callback(itr->tile_free.data, itr->tile);
  434. delete itr;
  435. }
  436. tileUnusedCache->memory.used = 0;
  437. tileUnusedCache->entries.count = 0;
  438. }
  439. /**
  440. * Hold reference to cache.
  441. *
  442. * @return same pointer as taken.
  443. *
  444. * @see ewk_tile_unused_cache_unref()
  445. */
  446. Ewk_Tile_Unused_Cache* ewk_tile_unused_cache_ref(Ewk_Tile_Unused_Cache* tileUnusedCache)
  447. {
  448. EINA_SAFETY_ON_NULL_RETURN_VAL(tileUnusedCache, 0);
  449. tileUnusedCache->references++;
  450. return tileUnusedCache;
  451. }
  452. /**
  453. * Release cache reference, freeing it if it drops to zero.
  454. *
  455. * @see ewk_tile_unused_cache_ref()
  456. * @see ewk_tile_unused_cache_free()
  457. */
  458. void ewk_tile_unused_cache_unref(Ewk_Tile_Unused_Cache* tileUnusedCache)
  459. {
  460. EINA_SAFETY_ON_NULL_RETURN(tileUnusedCache);
  461. tileUnusedCache->references--;
  462. if (!tileUnusedCache->references)
  463. _ewk_tile_unused_cache_free(tileUnusedCache);
  464. }
  465. void ewk_tile_unused_cache_max_set(Ewk_Tile_Unused_Cache* tileUnusedCache, size_t max)
  466. {
  467. EINA_SAFETY_ON_NULL_RETURN(tileUnusedCache);
  468. size_t oldMax = tileUnusedCache->memory.max;
  469. tileUnusedCache->memory.max = max;
  470. /* Cache flush when new max is lower then old one */
  471. if (oldMax > max)
  472. ewk_tile_unused_cache_auto_flush(tileUnusedCache);
  473. }
  474. size_t ewk_tile_unused_cache_max_get(const Ewk_Tile_Unused_Cache* tileUnusedCache)
  475. {
  476. EINA_SAFETY_ON_NULL_RETURN_VAL(tileUnusedCache, 0);
  477. return tileUnusedCache->memory.max;
  478. }
  479. size_t ewk_tile_unused_cache_used_get(const Ewk_Tile_Unused_Cache* tileUnusedCache)
  480. {
  481. EINA_SAFETY_ON_NULL_RETURN_VAL(tileUnusedCache, 0);
  482. return tileUnusedCache->memory.used;
  483. }
  484. size_t ewk_tile_unused_cache_flush(Ewk_Tile_Unused_Cache* tileUnusedCache, size_t bytes)
  485. {
  486. Eina_List* list, * listNext;
  487. EINA_SAFETY_ON_NULL_RETURN_VAL(tileUnusedCache, 0);
  488. size_t done;
  489. unsigned int count;
  490. if (!tileUnusedCache->entries.count)
  491. return 0;
  492. if (bytes < 1)
  493. return 0;
  494. /*
  495. * NOTE: the cache is a FIFO queue currently.
  496. * Don't need to sort any more.
  497. */
  498. void* item;
  499. done = 0;
  500. count = 0;
  501. EINA_LIST_FOREACH_SAFE(tileUnusedCache->entries.list, list, listNext, item) {
  502. Ewk_Tile_Unused_Cache_Entry* itr = static_cast<Ewk_Tile_Unused_Cache_Entry*>(item);
  503. Ewk_Tile* tile = itr->tile;
  504. if (done > bytes)
  505. break;
  506. if (tileUnusedCache->locked.locked
  507. && tile->x + tile->width > tileUnusedCache->locked.x
  508. && tile->y + tile->height > tileUnusedCache->locked.y
  509. && tile->x < tileUnusedCache->locked.x + tileUnusedCache->locked.width
  510. && tile->y < tileUnusedCache->locked.y + tileUnusedCache->locked.height
  511. && tile->zoom == tileUnusedCache->locked.zoom) {
  512. continue;
  513. }
  514. done += ewk_tile_memory_size_get(itr->tile);
  515. itr->tile_free.callback(itr->tile_free.data, itr->tile);
  516. tileUnusedCache->entries.list = eina_list_remove_list(tileUnusedCache->entries.list, list);
  517. delete itr;
  518. count++;
  519. }
  520. tileUnusedCache->memory.used -= done;
  521. tileUnusedCache->entries.count -= count;
  522. return done;
  523. }
  524. void ewk_tile_unused_cache_auto_flush(Ewk_Tile_Unused_Cache* tileUnusedCache)
  525. {
  526. EINA_SAFETY_ON_NULL_RETURN(tileUnusedCache);
  527. if (tileUnusedCache->memory.used <= tileUnusedCache->memory.max)
  528. return;
  529. ewk_tile_unused_cache_flush(tileUnusedCache, tileUnusedCache->memory.used - tileUnusedCache->memory.max);
  530. if (tileUnusedCache->memory.used > tileUnusedCache->memory.max)
  531. CRITICAL("Cache still using too much memory: %zd KB; max: %zd KB",
  532. tileUnusedCache->memory.used, tileUnusedCache->memory.max);
  533. }
  534. /**
  535. * Freeze cache to not do maintenance tasks.
  536. *
  537. * Maintenance tasks optimize cache usage, but maybe we know we should
  538. * hold on them until we do the last operation, in this case we freeze
  539. * while operating and then thaw when we're done.
  540. *
  541. * @see ewk_tile_unused_cache_thaw()
  542. */
  543. void ewk_tile_unused_cache_freeze(Ewk_Tile_Unused_Cache* tileUnusedCache)
  544. {
  545. tileUnusedCache->frozen++;
  546. }
  547. /**
  548. * Unfreezes maintenance tasks.
  549. *
  550. * If this is the last counterpart of freeze, then maintenance tasks
  551. * will run immediately.
  552. */
  553. void ewk_tile_unused_cache_thaw(Ewk_Tile_Unused_Cache* tileUnusedCache)
  554. {
  555. if (!tileUnusedCache->frozen) {
  556. ERR("thawing more than freezing!");
  557. return;
  558. }
  559. tileUnusedCache->frozen--;
  560. }
  561. /**
  562. * Get tile from cache of unused tiles, removing it from the cache.
  563. *
  564. * If the tile is used, then it's not in cache of unused tiles, so it
  565. * is removed from the cache and may be given back with
  566. * ewk_tile_unused_cache_tile_put().
  567. *
  568. * @param tileUnusedCache cache of unused tiles
  569. * @param tile the tile to be removed from Ewk_Tile_Unused_Cache.
  570. *
  571. * @return #true on success, #false otherwise.
  572. */
  573. Eina_Bool ewk_tile_unused_cache_tile_get(Ewk_Tile_Unused_Cache* tileUnusedCache, Ewk_Tile* tile)
  574. {
  575. Eina_List* iterateEntry;
  576. void* item;
  577. EINA_LIST_FOREACH(tileUnusedCache->entries.list, iterateEntry, item) {
  578. Ewk_Tile_Unused_Cache_Entry* entry = static_cast<Ewk_Tile_Unused_Cache_Entry*>(item);
  579. if (entry->tile == tile) {
  580. tileUnusedCache->entries.count--;
  581. tileUnusedCache->memory.used -= ewk_tile_memory_size_get(tile);
  582. tileUnusedCache->entries.list = eina_list_remove_list(tileUnusedCache->entries.list, iterateEntry);
  583. delete entry;
  584. return true;
  585. }
  586. }
  587. ERR("tile %p not found in cache %p", tile, tileUnusedCache);
  588. return false;
  589. }
  590. /**
  591. * Put tile into cache of unused tiles, adding it to the cache.
  592. *
  593. * This should be called when @c tile->visible is @c 0 and no objects are
  594. * using the tile anymore, making it available to be expired and have
  595. * its memory replaced.
  596. *
  597. * Note that tiles are not automatically deleted if cache is full,
  598. * instead the cache will have more bytes used than maximum and one
  599. * can call ewk_tile_unused_cache_auto_flush() to free them. This is done
  600. * because usually we want a lazy operation for better performance.
  601. *
  602. * @param tileUnusedCache cache of unused tiles
  603. * @param tile tile to be added to cache.
  604. * @param tileFreeCallback function used to free tiles.
  605. * @param data context to give back to @a tile_free_cb as first argument.
  606. *
  607. * @return #true on success, #false otherwise. If @c tile->visible
  608. * is not #false, then it will return #false.
  609. *
  610. * @see ewk_tile_unused_cache_auto_flush()
  611. */
  612. Eina_Bool ewk_tile_unused_cache_tile_put(Ewk_Tile_Unused_Cache* tileUnusedCache, Ewk_Tile* tile, void (* tileFreeCallback)(void* data, Ewk_Tile* tile), const void* data)
  613. {
  614. Ewk_Tile_Unused_Cache_Entry* unusedCacheEntry;
  615. if (tile->visible) {
  616. ERR("tile=%p is not unused (visible=%d)", tile, tile->visible);
  617. return false;
  618. }
  619. unusedCacheEntry = new Ewk_Tile_Unused_Cache_Entry;
  620. tileUnusedCache->entries.list = eina_list_append(tileUnusedCache->entries.list, unusedCacheEntry);
  621. if (eina_error_get()) {
  622. ERR("List allocation failed");
  623. return false;
  624. }
  625. unusedCacheEntry->tile = tile;
  626. unusedCacheEntry->weight = 0; /* calculated just before sort */
  627. unusedCacheEntry->tile_free.callback = tileFreeCallback;
  628. unusedCacheEntry->tile_free.data = (void*)data;
  629. tileUnusedCache->entries.count++;
  630. tileUnusedCache->memory.used += ewk_tile_memory_size_get(tile);
  631. return true;
  632. }
  633. void ewk_tile_unused_cache_dbg(const Ewk_Tile_Unused_Cache* tileUnusedCache)
  634. {
  635. void* item;
  636. Eina_List* list;
  637. int count = 0;
  638. printf("Cache of unused tiles: entries: %zu/%zu, memory: %zu/%zu\n",
  639. tileUnusedCache->entries.count, tileUnusedCache->entries.allocated,
  640. tileUnusedCache->memory.used, tileUnusedCache->memory.max);
  641. EINA_LIST_FOREACH(tileUnusedCache->entries.list, list, item) {
  642. const Ewk_Tile* tile = static_cast<Ewk_Tile_Unused_Cache_Entry*>(item)->tile;
  643. printf(" [%3lu,%3lu + %dx%d @ %0.3f]%c",
  644. tile->column, tile->row, tile->width, tile->height, tile->zoom,
  645. tile->visible ? '*' : ' ');
  646. if (!(count % 4))
  647. printf("\n");
  648. }
  649. printf("\n");
  650. }