Move allocation of imbuf from array to allocimbuf.
[blender.git] / source / blender / imbuf / intern / cache.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * ***** END GPL LICENSE BLOCK *****
19  */
20
21 /** \file blender/imbuf/intern/cache.c
22  *  \ingroup imbuf
23  */
24
25 #include "MEM_guardedalloc.h"
26
27 #include "BLI_utildefines.h"
28 #include "BLI_ghash.h"
29 #include "BLI_listbase.h"
30 #include "BLI_memarena.h"
31 #include "BLI_threads.h"
32
33 #include "IMB_imbuf.h"
34 #include "IMB_imbuf_types.h"
35 #include "IMB_filetype.h"
36
37 #include "imbuf.h"
38
39 /* We use a two level cache here. A per-thread cache with limited number of
40  * tiles. This can be accessed without locking and so is hoped to lead to most
41  * tile access being lock-free. The global cache is shared between all threads
42  * and requires slow locking to access, and contains all tiles.
43  *
44  * The per-thread cache should be big enough that one might hope to not fall
45  * back to the global cache every pixel, but not to big to keep too many tiles
46  * locked and using memory. */
47
48 #define IB_THREAD_CACHE_SIZE    100
49
50 typedef struct ImGlobalTile {
51         struct ImGlobalTile *next, *prev;
52
53         ImBuf *ibuf;
54         int tx, ty;
55         int refcount;
56         volatile int loading;
57 } ImGlobalTile;
58
59 typedef struct ImThreadTile {
60         struct ImThreadTile *next, *prev;
61
62         ImBuf *ibuf;
63         int tx, ty;
64
65         ImGlobalTile *global;
66 } ImThreadTile;
67
68 typedef struct ImThreadTileCache {
69         ListBase tiles;
70         ListBase unused;
71         GHash *tilehash;
72 } ImThreadTileCache;
73
74 typedef struct ImGlobalTileCache {
75         ListBase tiles;
76         ListBase unused;
77         GHash *tilehash;
78
79         MemArena *memarena;
80         uintptr_t totmem, maxmem;
81
82         ImThreadTileCache thread_cache[BLENDER_MAX_THREADS + 1];
83         int totthread;
84
85         ThreadMutex mutex;
86
87         int initialized;
88 } ImGlobalTileCache;
89
90 static ImGlobalTileCache GLOBAL_CACHE;
91
92 /***************************** Hash Functions ********************************/
93
94 static unsigned int imb_global_tile_hash(const void *gtile_p)
95 {
96         const ImGlobalTile *gtile = gtile_p;
97
98         return ((unsigned int)(intptr_t)gtile->ibuf) * 769 + gtile->tx * 53 + gtile->ty * 97;
99 }
100
101 static bool imb_global_tile_cmp(const void *a_p, const void *b_p)
102 {
103         const ImGlobalTile *a = a_p;
104         const ImGlobalTile *b = b_p;
105
106         return ((a->ibuf != b->ibuf) ||
107                 (a->tx != b->tx) ||
108                 (a->ty != b->ty));
109 }
110
111 static unsigned int imb_thread_tile_hash(const void *ttile_p)
112 {
113         const ImThreadTile *ttile = ttile_p;
114
115         return ((unsigned int)(intptr_t)ttile->ibuf) * 769 + ttile->tx * 53 + ttile->ty * 97;
116 }
117
118 static bool imb_thread_tile_cmp(const void *a_p, const void *b_p)
119 {
120         const ImThreadTile *a = a_p;
121         const ImThreadTile *b = b_p;
122
123         return ((a->ibuf != b->ibuf) ||
124                 (a->tx != b->tx) ||
125                 (a->ty != b->ty));
126 }
127
128 /******************************** Load/Unload ********************************/
129
130 static void imb_global_cache_tile_load(ImGlobalTile *gtile)
131 {
132         ImBuf *ibuf = gtile->ibuf;
133         int toffs = ibuf->xtiles * gtile->ty + gtile->tx;
134         unsigned int *rect;
135
136         rect = MEM_callocN(sizeof(unsigned int) * ibuf->tilex * ibuf->tiley, "imb_tile");
137         imb_loadtile(ibuf, gtile->tx, gtile->ty, rect);
138         ibuf->tiles[toffs] = rect;
139 }
140
141 static void imb_global_cache_tile_unload(ImGlobalTile *gtile)
142 {
143         ImBuf *ibuf = gtile->ibuf;
144         int toffs = ibuf->xtiles * gtile->ty + gtile->tx;
145
146         MEM_freeN(ibuf->tiles[toffs]);
147         ibuf->tiles[toffs] = NULL;
148
149         GLOBAL_CACHE.totmem -= sizeof(unsigned int) * ibuf->tilex * ibuf->tiley;
150 }
151
152 /* external free */
153 void imb_tile_cache_tile_free(ImBuf *ibuf, int tx, int ty)
154 {
155         ImGlobalTile *gtile, lookuptile;
156
157         BLI_mutex_lock(&GLOBAL_CACHE.mutex);
158
159         lookuptile.ibuf = ibuf;
160         lookuptile.tx = tx;
161         lookuptile.ty = ty;
162         gtile = BLI_ghash_lookup(GLOBAL_CACHE.tilehash, &lookuptile);
163
164         if (gtile) {
165                 /* in case another thread is loading this */
166                 while (gtile->loading)
167                         ;
168
169                 BLI_ghash_remove(GLOBAL_CACHE.tilehash, gtile, NULL, NULL);
170                 BLI_remlink(&GLOBAL_CACHE.tiles, gtile);
171                 BLI_addtail(&GLOBAL_CACHE.unused, gtile);
172         }
173
174         BLI_mutex_unlock(&GLOBAL_CACHE.mutex);
175 }
176
177 /******************************* Init/Exit ***********************************/
178
179 static void imb_thread_cache_init(ImThreadTileCache *cache)
180 {
181         ImThreadTile *ttile;
182         int a;
183
184         memset(cache, 0, sizeof(ImThreadTileCache));
185
186         cache->tilehash = BLI_ghash_new(imb_thread_tile_hash, imb_thread_tile_cmp, "imb_thread_cache_init gh");
187
188         /* pre-allocate all thread local tiles in unused list */
189         for (a = 0; a < IB_THREAD_CACHE_SIZE; a++) {
190                 ttile = BLI_memarena_alloc(GLOBAL_CACHE.memarena, sizeof(ImThreadTile));
191                 BLI_addtail(&cache->unused, ttile);
192         }
193 }
194
195 static void imb_thread_cache_exit(ImThreadTileCache *cache)
196 {
197         BLI_ghash_free(cache->tilehash, NULL, NULL);
198 }
199
200 void imb_tile_cache_init(void)
201 {
202         memset(&GLOBAL_CACHE, 0, sizeof(ImGlobalTileCache));
203
204         BLI_mutex_init(&GLOBAL_CACHE.mutex);
205
206         /* initialize for one thread, for places that access textures
207          * outside of rendering (displace modifier, painting, ..) */
208         IMB_tile_cache_params(0, 0);
209
210         GLOBAL_CACHE.initialized = 1;
211 }
212
213 void imb_tile_cache_exit(void)
214 {
215         ImGlobalTile *gtile;
216         int a;
217
218         if (GLOBAL_CACHE.initialized) {
219                 for (gtile = GLOBAL_CACHE.tiles.first; gtile; gtile = gtile->next)
220                         imb_global_cache_tile_unload(gtile);
221
222                 for (a = 0; a < GLOBAL_CACHE.totthread; a++)
223                         imb_thread_cache_exit(&GLOBAL_CACHE.thread_cache[a]);
224
225                 if (GLOBAL_CACHE.memarena)
226                         BLI_memarena_free(GLOBAL_CACHE.memarena);
227
228                 if (GLOBAL_CACHE.tilehash)
229                         BLI_ghash_free(GLOBAL_CACHE.tilehash, NULL, NULL);
230
231                 BLI_mutex_end(&GLOBAL_CACHE.mutex);
232
233                 memset(&GLOBAL_CACHE, 0, sizeof(ImGlobalTileCache));
234         }
235 }
236
237 /* presumed to be called when no threads are running */
238 void IMB_tile_cache_params(int totthread, int maxmem)
239 {
240         int a;
241
242         /* always one cache for non-threaded access */
243         totthread++;
244
245         /* lazy initialize cache */
246         if (GLOBAL_CACHE.totthread == totthread && GLOBAL_CACHE.maxmem == maxmem)
247                 return;
248
249         imb_tile_cache_exit();
250
251         memset(&GLOBAL_CACHE, 0, sizeof(ImGlobalTileCache));
252
253         GLOBAL_CACHE.tilehash = BLI_ghash_new(imb_global_tile_hash, imb_global_tile_cmp, "tile_cache_params gh");
254
255         GLOBAL_CACHE.memarena = BLI_memarena_new(BLI_MEMARENA_STD_BUFSIZE, "ImTileCache arena");
256         BLI_memarena_use_calloc(GLOBAL_CACHE.memarena);
257
258         GLOBAL_CACHE.maxmem = maxmem * 1024 * 1024;
259
260         GLOBAL_CACHE.totthread = totthread;
261         for (a = 0; a < totthread; a++)
262                 imb_thread_cache_init(&GLOBAL_CACHE.thread_cache[a]);
263
264         BLI_mutex_init(&GLOBAL_CACHE.mutex);
265 }
266
267 /***************************** Global Cache **********************************/
268
269 static ImGlobalTile *imb_global_cache_get_tile(ImBuf *ibuf, int tx, int ty, ImGlobalTile *replacetile)
270 {
271         ImGlobalTile *gtile, lookuptile;
272
273         BLI_mutex_lock(&GLOBAL_CACHE.mutex);
274
275         if (replacetile)
276                 replacetile->refcount--;
277
278         /* find tile in global cache */
279         lookuptile.ibuf = ibuf;
280         lookuptile.tx = tx;
281         lookuptile.ty = ty;
282         gtile = BLI_ghash_lookup(GLOBAL_CACHE.tilehash, &lookuptile);
283         
284         if (gtile) {
285                 /* found tile. however it may be in the process of being loaded
286                  * by another thread, in that case we do stupid busy loop waiting
287                  * for the other thread to load the tile */
288                 gtile->refcount++;
289
290                 BLI_mutex_unlock(&GLOBAL_CACHE.mutex);
291
292                 while (gtile->loading)
293                         ;
294         }
295         else {
296                 /* not found, let's load it from disk */
297
298                 /* first check if we hit the memory limit */
299                 if (GLOBAL_CACHE.maxmem && GLOBAL_CACHE.totmem > GLOBAL_CACHE.maxmem) {
300                         /* find an existing tile to unload */
301                         for (gtile = GLOBAL_CACHE.tiles.last; gtile; gtile = gtile->prev)
302                                 if (gtile->refcount == 0 && gtile->loading == 0)
303                                         break;
304                 }
305
306                 if (gtile) {
307                         /* found a tile to unload */
308                         imb_global_cache_tile_unload(gtile);
309                         BLI_ghash_remove(GLOBAL_CACHE.tilehash, gtile, NULL, NULL);
310                         BLI_remlink(&GLOBAL_CACHE.tiles, gtile);
311                 }
312                 else {
313                         /* allocate a new tile or reuse unused */
314                         if (GLOBAL_CACHE.unused.first) {
315                                 gtile = GLOBAL_CACHE.unused.first;
316                                 BLI_remlink(&GLOBAL_CACHE.unused, gtile);
317                         }
318                         else
319                                 gtile = BLI_memarena_alloc(GLOBAL_CACHE.memarena, sizeof(ImGlobalTile));
320                 }
321
322                 /* setup new tile */
323                 gtile->ibuf = ibuf;
324                 gtile->tx = tx;
325                 gtile->ty = ty;
326                 gtile->refcount = 1;
327                 gtile->loading = 1;
328
329                 BLI_ghash_insert(GLOBAL_CACHE.tilehash, gtile, gtile);
330                 BLI_addhead(&GLOBAL_CACHE.tiles, gtile);
331
332                 /* mark as being loaded and unlock to allow other threads to load too */
333                 GLOBAL_CACHE.totmem += sizeof(unsigned int) * ibuf->tilex * ibuf->tiley;
334
335                 BLI_mutex_unlock(&GLOBAL_CACHE.mutex);
336
337                 /* load from disk */
338                 imb_global_cache_tile_load(gtile);
339
340                 /* mark as done loading */
341                 gtile->loading = 0;
342         }
343
344         return gtile;
345 }
346
347 /***************************** Per-Thread Cache ******************************/
348
349 static unsigned int *imb_thread_cache_get_tile(ImThreadTileCache *cache, ImBuf *ibuf, int tx, int ty)
350 {
351         ImThreadTile *ttile, lookuptile;
352         ImGlobalTile *gtile, *replacetile;
353         int toffs = ibuf->xtiles * ty + tx;
354
355         /* test if it is already in our thread local cache */
356         if ((ttile = cache->tiles.first)) {
357                 /* check last used tile before going to hash */
358                 if (ttile->ibuf == ibuf && ttile->tx == tx && ttile->ty == ty)
359                         return ibuf->tiles[toffs];
360
361                 /* find tile in hash */
362                 lookuptile.ibuf = ibuf;
363                 lookuptile.tx = tx;
364                 lookuptile.ty = ty;
365
366                 if ((ttile = BLI_ghash_lookup(cache->tilehash, &lookuptile))) {
367                         BLI_remlink(&cache->tiles, ttile);
368                         BLI_addhead(&cache->tiles, ttile);
369
370                         return ibuf->tiles[toffs];
371                 }
372         }
373
374         /* not found, have to do slow lookup in global cache */
375         if (BLI_listbase_is_empty(&cache->unused)) {
376                 ttile = cache->tiles.last;
377                 replacetile = ttile->global;
378                 BLI_remlink(&cache->tiles, ttile);
379                 BLI_ghash_remove(cache->tilehash, ttile, NULL, NULL);
380         }
381         else {
382                 ttile = cache->unused.first;
383                 replacetile = NULL;
384                 BLI_remlink(&cache->unused, ttile);
385         }
386
387         BLI_addhead(&cache->tiles, ttile);
388         BLI_ghash_insert(cache->tilehash, ttile, ttile);
389
390         gtile = imb_global_cache_get_tile(ibuf, tx, ty, replacetile);
391
392         ttile->ibuf = gtile->ibuf;
393         ttile->tx = gtile->tx;
394         ttile->ty = gtile->ty;
395         ttile->global = gtile;
396
397         return ibuf->tiles[toffs];
398 }
399
400 unsigned int *IMB_gettile(ImBuf *ibuf, int tx, int ty, int thread)
401 {
402         return imb_thread_cache_get_tile(&GLOBAL_CACHE.thread_cache[thread + 1], ibuf, tx, ty);
403 }
404
405 void IMB_tiles_to_rect(ImBuf *ibuf)
406 {
407         ImBuf *mipbuf;
408         ImGlobalTile *gtile;
409         unsigned int *to, *from;
410         int a, tx, ty, y, w, h;
411
412         for (a = 0; a < ibuf->miptot; a++) {
413                 mipbuf = IMB_getmipmap(ibuf, a);
414
415                 /* don't call imb_addrectImBuf, it frees all mipmaps */
416                 if (!mipbuf->rect) {
417                         if ((mipbuf->rect = MEM_mapallocN(ibuf->x * ibuf->y * sizeof(unsigned int), "imb_addrectImBuf"))) {
418                                 mipbuf->mall |= IB_rect;
419                                 mipbuf->flags |= IB_rect;
420                         }
421                         else
422                                 break;
423                 }
424
425                 for (ty = 0; ty < mipbuf->ytiles; ty++) {
426                         for (tx = 0; tx < mipbuf->xtiles; tx++) {
427                                 /* acquire tile through cache, this assumes cache is initialized,
428                                  * which it is always now but it's a weak assumption ... */
429                                 gtile = imb_global_cache_get_tile(mipbuf, tx, ty, NULL);
430
431                                 /* setup pointers */
432                                 from = mipbuf->tiles[mipbuf->xtiles * ty + tx];
433                                 to = mipbuf->rect + mipbuf->x * ty * mipbuf->tiley + tx * mipbuf->tilex;
434
435                                 /* exception in tile width/height for tiles at end of image */
436                                 w = (tx == mipbuf->xtiles - 1) ? mipbuf->x - tx * mipbuf->tilex : mipbuf->tilex;
437                                 h = (ty == mipbuf->ytiles - 1) ? mipbuf->y - ty * mipbuf->tiley : mipbuf->tiley;
438
439                                 for (y = 0; y < h; y++) {
440                                         memcpy(to, from, sizeof(unsigned int) * w);
441                                         from += mipbuf->tilex;
442                                         to += mipbuf->x;
443                                 }
444
445                                 /* decrease refcount for tile again */
446                                 BLI_mutex_lock(&GLOBAL_CACHE.mutex);
447                                 gtile->refcount--;
448                                 BLI_mutex_unlock(&GLOBAL_CACHE.mutex);
449                         }
450                 }
451         }
452 }
453