Color management: refactor of cache to deal better with movie clip cache
[blender-staging.git] / source / blender / imbuf / intern / moviecache.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  * The Original Code is Copyright (C) 2011 Blender Foundation.
19  * All rights reserved.
20  *
21  * Contributor(s): Blender Foundation,
22  *                 Sergey Sharybin,
23  *                 Peter Schlaile
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/imbuf/intern/moviecache.c
29  *  \ingroup bke
30  */
31
32 #undef DEBUG_MESSAGES
33
34 #include <stdlib.h> /* for qsort */
35 #include <memory.h>
36
37 #include "MEM_guardedalloc.h"
38 #include "MEM_CacheLimiterC-Api.h"
39
40 #include "BLI_string.h"
41 #include "BLI_utildefines.h"
42 #include "BLI_ghash.h"
43 #include "BLI_mempool.h"
44
45 #include "IMB_moviecache.h"
46
47 #include "IMB_imbuf_types.h"
48 #include "IMB_imbuf.h"
49
50 #ifdef DEBUG_MESSAGES
51 #  if defined __GNUC__ || defined __sun
52 #    define PRINT(format, args ...) printf(format, ##args)
53 #  else
54 #    define PRINT(format, ...) printf(__VA_ARGS__)
55 #  endif
56 #else
57 #  define PRINT(format, ...)
58 #endif
59
60 static MEM_CacheLimiterC *limitor = NULL;
61
62 typedef struct MovieCache {
63         char name[64];
64
65         GHash *hash;
66         MovieCacheKeyDeleterFP keydeleterfp;
67         GHashHashFP hashfp;
68         GHashCmpFP cmpfp;
69         MovieCacheGetKeyDataFP getdatafp;
70         MovieCacheCheckKeyUnusedFP checkkeyunusedfp;
71
72         MovieCacheGetPriorityDataFP getprioritydatafp;
73         MovieCacheGetItemPriorityFP getitempriorityfp;
74         MovieCachePriorityDeleterFP prioritydeleterfp;
75
76         struct BLI_mempool *keys_pool;
77         struct BLI_mempool *items_pool;
78         struct BLI_mempool *userkeys_pool;
79
80         int keysize;
81
82         void *last_userkey;
83
84         int totseg, *points, proxy, render_flags;  /* for visual statistics optimization */
85         int pad;
86 } MovieCache;
87
88 typedef struct MovieCacheKey {
89         MovieCache *cache_owner;
90         void *userkey;
91 } MovieCacheKey;
92
93 typedef struct MovieCacheItem {
94         MovieCache *cache_owner;
95         ImBuf *ibuf;
96         MEM_CacheLimiterHandleC *c_handle;
97         void *priority_data;
98 } MovieCacheItem;
99
100 static unsigned int moviecache_hashhash(const void *keyv)
101 {
102         MovieCacheKey *key = (MovieCacheKey *)keyv;
103
104         return key->cache_owner->hashfp(key->userkey);
105 }
106
107 static int moviecache_hashcmp(const void *av, const void *bv)
108 {
109         const MovieCacheKey *a = (MovieCacheKey *)av;
110         const MovieCacheKey *b = (MovieCacheKey *)bv;
111
112         return a->cache_owner->cmpfp(a->userkey, b->userkey);
113 }
114
115 static void moviecache_keyfree(void *val)
116 {
117         MovieCacheKey *key = (MovieCacheKey *)val;
118
119         if (key->cache_owner->keydeleterfp) {
120                 key->cache_owner->keydeleterfp(key->userkey);
121         }
122
123         BLI_mempool_free(key->cache_owner->userkeys_pool, key->userkey);
124
125         BLI_mempool_free(key->cache_owner->keys_pool, key);
126 }
127
128 static void moviecache_valfree(void *val)
129 {
130         MovieCacheItem *item = (MovieCacheItem *)val;
131         MovieCache *cache = item->cache_owner;
132
133         PRINT("%s: cache '%s' free item %p buffer %p\n", __func__, cache->name, item, item->ibuf);
134
135         if (item->ibuf) {
136                 MEM_CacheLimiter_unmanage(item->c_handle);
137                 IMB_freeImBuf(item->ibuf);
138         }
139
140         if (item->priority_data && cache->prioritydeleterfp) {
141                 cache->prioritydeleterfp(item->priority_data);
142         }
143
144         BLI_mempool_free(item->cache_owner->items_pool, item);
145 }
146
147 static void check_unused_keys(MovieCache *cache)
148 {
149         GHashIterator *iter;
150
151         iter = BLI_ghashIterator_new(cache->hash);
152         while (!BLI_ghashIterator_isDone(iter)) {
153                 MovieCacheKey *key = BLI_ghashIterator_getKey(iter);
154                 MovieCacheItem *item = BLI_ghashIterator_getValue(iter);
155                 int remove = 0;
156
157                 BLI_ghashIterator_step(iter);
158
159                 remove = !item->ibuf;
160
161                 if (remove)
162                         PRINT("%s: cache '%s' remove item %p without buffer\n", __func__, cache->name, item);
163
164                 if (!remove && cache->checkkeyunusedfp) {
165                         remove = cache->checkkeyunusedfp(key->userkey);
166
167                         if (remove)
168                                 PRINT("%s: cache '%s' remove unused item %p\n", __func__, cache->name, item);
169                 }
170
171                 if (remove)
172                         BLI_ghash_remove(cache->hash, key, moviecache_keyfree, moviecache_valfree);
173         }
174
175         BLI_ghashIterator_free(iter);
176 }
177
178 static int compare_int(const void *av, const void *bv)
179 {
180         const int *a = (int *)av;
181         const int *b = (int *)bv;
182         return *a - *b;
183 }
184
185 static void IMB_moviecache_destructor(void *p)
186 {
187         MovieCacheItem *item = (MovieCacheItem *) p;
188         MovieCache *cache = item->cache_owner;
189
190         PRINT("%s: cache '%s' destroy item %p buffer %p\n", __func__, cache->name, item, item->ibuf);
191
192         if (item && item->ibuf) {
193                 IMB_freeImBuf(item->ibuf);
194
195                 item->ibuf = NULL;
196                 item->c_handle = NULL;
197
198                 /* force cached segments to be updated */
199                 if (cache->points) {
200                         MEM_freeN(cache->points);
201                         cache->points = NULL;
202                 }
203         }
204 }
205
206 /* approximate size of ImBuf in memory */
207 static size_t IMB_get_size_in_memory(ImBuf *ibuf)
208 {
209         int a;
210         size_t size = 0, channel_size = 0;
211
212         size += sizeof(ImBuf);
213
214         if (ibuf->rect)
215                 channel_size += sizeof(char);
216
217         if (ibuf->rect_float)
218                 channel_size += sizeof(float);
219
220         size += channel_size * ibuf->x * ibuf->y * ibuf->channels;
221
222         if (ibuf->miptot) {
223                 for (a = 0; a < ibuf->miptot; a++) {
224                         if (ibuf->mipmap[a])
225                                 size += IMB_get_size_in_memory(ibuf->mipmap[a]);
226                 }
227         }
228
229         if (ibuf->tiles) {
230                 size += sizeof(unsigned int) * ibuf->ytiles * ibuf->xtiles;
231         }
232
233         return size;
234 }
235
236 static size_t get_item_size(void *p)
237 {
238         size_t size = sizeof(MovieCacheItem);
239         MovieCacheItem *item = (MovieCacheItem *) p;
240
241         if (item->ibuf)
242                 size += IMB_get_size_in_memory(item->ibuf);
243
244         return size;
245 }
246
247 static int get_item_priority(void *item_v, int default_priority)
248 {
249         MovieCacheItem *item = (MovieCacheItem *) item_v;
250         MovieCache *cache = item->cache_owner;
251         int priority;
252
253         if (!cache->getitempriorityfp) {
254                 PRINT("%s: cache '%s' item %p use default priority %d\n", __func__, cache-> name, item, default_priority);
255
256                 return default_priority;
257         }
258
259         priority = cache->getitempriorityfp(cache->last_userkey, item->priority_data);
260
261         PRINT("%s: cache '%s' item %p priority %d\n", __func__, cache-> name, item, priority);
262
263         return priority;
264 }
265
266 void IMB_moviecache_init(void)
267 {
268         limitor = new_MEM_CacheLimiter(IMB_moviecache_destructor, get_item_size);
269
270         MEM_CacheLimiter_ItemPriority_Func_set(limitor, get_item_priority);
271 }
272
273 void IMB_moviecache_destruct(void)
274 {
275         if (limitor)
276                 delete_MEM_CacheLimiter(limitor);
277 }
278
279 MovieCache *IMB_moviecache_create(const char *name, int keysize, GHashHashFP hashfp, GHashCmpFP cmpfp)
280 {
281         MovieCache *cache;
282
283         PRINT("%s: cache '%s' create\n", __func__, name);
284
285         cache = MEM_callocN(sizeof(MovieCache), "MovieCache");
286
287         BLI_strncpy(cache->name, name, sizeof(cache->name));
288
289         cache->keys_pool = BLI_mempool_create(sizeof(MovieCacheKey), 64, 64, 0);
290         cache->items_pool = BLI_mempool_create(sizeof(MovieCacheItem), 64, 64, 0);
291         cache->userkeys_pool = BLI_mempool_create(keysize, 64, 64, 0);
292         cache->hash = BLI_ghash_new(moviecache_hashhash, moviecache_hashcmp, "MovieClip ImBuf cache hash");
293
294         cache->keysize = keysize;
295         cache->hashfp = hashfp;
296         cache->cmpfp = cmpfp;
297         cache->proxy = -1;
298
299         return cache;
300 }
301
302 void IMB_moviecache_set_key_deleter_callback(MovieCache *cache, MovieCacheKeyDeleterFP keydeleterfp)
303 {
304         cache->keydeleterfp = keydeleterfp;
305 }
306
307 void IMB_moviecache_set_getdata_callback(MovieCache *cache, MovieCacheGetKeyDataFP getdatafp)
308 {
309         cache->getdatafp = getdatafp;
310 }
311
312 void IMB_moviecache_set_check_unused_callback(MovieCache *cache, MovieCacheCheckKeyUnusedFP checkkeyunusedfp)
313 {
314         cache->checkkeyunusedfp = checkkeyunusedfp;
315 }
316
317 void IMB_moviecache_set_priority_callback(struct MovieCache *cache, MovieCacheGetPriorityDataFP getprioritydatafp,
318                                           MovieCacheGetItemPriorityFP getitempriorityfp,
319                                           MovieCachePriorityDeleterFP prioritydeleterfp)
320 {
321         cache->last_userkey = MEM_mallocN(cache->keysize, "movie cache last user key");
322
323         cache->getprioritydatafp = getprioritydatafp;
324         cache->getitempriorityfp = getitempriorityfp;
325         cache->prioritydeleterfp = prioritydeleterfp;
326 }
327
328 void IMB_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf)
329 {
330         MovieCacheKey *key;
331         MovieCacheItem *item;
332
333         if (!limitor)
334                 IMB_moviecache_init();
335
336         IMB_refImBuf(ibuf);
337
338         key = BLI_mempool_alloc(cache->keys_pool);
339         key->cache_owner = cache;
340         key->userkey = BLI_mempool_alloc(cache->userkeys_pool);
341         memcpy(key->userkey, userkey, cache->keysize);
342
343         item = BLI_mempool_alloc(cache->items_pool);
344
345         PRINT("%s: cache '%s' put %p, item %p\n", __func__, cache-> name, ibuf, item);
346
347         item->ibuf = ibuf;
348         item->cache_owner = cache;
349         item->c_handle = NULL;
350         item->priority_data = NULL;
351
352         if (cache->getprioritydatafp) {
353                 item->priority_data = cache->getprioritydatafp(userkey);
354         }
355
356         BLI_ghash_remove(cache->hash, key, moviecache_keyfree, moviecache_valfree);
357         BLI_ghash_insert(cache->hash, key, item);
358
359         item->c_handle = MEM_CacheLimiter_insert(limitor, item);
360
361         if (cache->last_userkey) {
362                 memcpy(cache->last_userkey, userkey, cache->keysize);
363         }
364
365         MEM_CacheLimiter_ref(item->c_handle);
366         MEM_CacheLimiter_enforce_limits(limitor);
367         MEM_CacheLimiter_unref(item->c_handle);
368
369         /* cache limiter can't remove unused keys which points to destoryed values */
370         check_unused_keys(cache);
371
372         if (cache->points) {
373                 MEM_freeN(cache->points);
374                 cache->points = NULL;
375         }
376 }
377
378 ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey)
379 {
380         MovieCacheKey key;
381         MovieCacheItem *item;
382
383         key.cache_owner = cache;
384         key.userkey = userkey;
385         item = (MovieCacheItem *)BLI_ghash_lookup(cache->hash, &key);
386
387         if (item) {
388                 if (item->ibuf) {
389                         MEM_CacheLimiter_touch(item->c_handle);
390                         IMB_refImBuf(item->ibuf);
391
392                         return item->ibuf;
393                 }
394         }
395
396         return NULL;
397 }
398
399 void IMB_moviecache_free(MovieCache *cache)
400 {
401         PRINT("%s: create '%s' free\n", __func__, cache->name);
402
403         BLI_ghash_free(cache->hash, moviecache_keyfree, moviecache_valfree);
404
405         BLI_mempool_destroy(cache->keys_pool);
406         BLI_mempool_destroy(cache->items_pool);
407         BLI_mempool_destroy(cache->userkeys_pool);
408
409         if (cache->points)
410                 MEM_freeN(cache->points);
411
412         if (cache->last_userkey)
413                 MEM_freeN(cache->last_userkey);
414
415         MEM_freeN(cache);
416 }
417
418 /* get segments of cached frames. useful for debugging cache policies */
419 void IMB_moviecache_get_cache_segments(MovieCache *cache, int proxy, int render_flags, int *totseg_r, int **points_r)
420 {
421         *totseg_r = 0;
422         *points_r = NULL;
423
424         if (!cache->getdatafp)
425                 return;
426
427         if (cache->proxy != proxy || cache->render_flags != render_flags) {
428                 if (cache->points)
429                         MEM_freeN(cache->points);
430
431                 cache->points = NULL;
432         }
433
434         if (cache->points) {
435                 *totseg_r = cache->totseg;
436                 *points_r = cache->points;
437         }
438         else {
439                 int totframe = BLI_ghash_size(cache->hash);
440                 int *frames = MEM_callocN(totframe * sizeof(int), "movieclip cache frames");
441                 int a, totseg = 0;
442                 GHashIterator *iter;
443
444                 iter = BLI_ghashIterator_new(cache->hash);
445                 a = 0;
446                 while (!BLI_ghashIterator_isDone(iter)) {
447                         MovieCacheKey *key = BLI_ghashIterator_getKey(iter);
448                         MovieCacheItem *item = BLI_ghashIterator_getValue(iter);
449                         int framenr, curproxy, curflags;
450
451                         if (item->ibuf) {
452                                 cache->getdatafp(key->userkey, &framenr, &curproxy, &curflags);
453
454                                 if (curproxy == proxy && curflags == render_flags)
455                                         frames[a++] = framenr;
456                         }
457
458                         BLI_ghashIterator_step(iter);
459                 }
460
461                 BLI_ghashIterator_free(iter);
462
463                 qsort(frames, totframe, sizeof(int), compare_int);
464
465                 /* count */
466                 for (a = 0; a < totframe; a++) {
467                         if (a && frames[a] - frames[a - 1] != 1)
468                                 totseg++;
469
470                         if (a == totframe - 1)
471                                 totseg++;
472                 }
473
474                 if (totseg) {
475                         int b, *points;
476
477                         points = MEM_callocN(2 * sizeof(int) * totseg, "movieclip cache segments");
478
479                         /* fill */
480                         for (a = 0, b = 0; a < totframe; a++) {
481                                 if (a == 0)
482                                         points[b++] = frames[a];
483
484                                 if (a && frames[a] - frames[a - 1] != 1) {
485                                         points[b++] = frames[a - 1];
486                                         points[b++] = frames[a];
487                                 }
488
489                                 if (a == totframe - 1)
490                                         points[b++] = frames[a];
491                         }
492
493                         *totseg_r = totseg;
494                         *points_r = points;
495
496                         cache->totseg = totseg;
497                         cache->points = points;
498                         cache->proxy = proxy;
499                         cache->render_flags = render_flags;
500                 }
501
502                 MEM_freeN(frames);
503         }
504 }