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