90eea438c5e9bcb52cd86cb9fed0eb532f2e40cc
[blender.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 #include "BLI_threads.h"
45
46 #include "IMB_moviecache.h"
47
48 #include "IMB_imbuf_types.h"
49 #include "IMB_imbuf.h"
50
51 #ifdef DEBUG_MESSAGES
52 #  if defined __GNUC__ || defined __sun
53 #    define PRINT(format, args ...) printf(format, ##args)
54 #  else
55 #    define PRINT(format, ...) printf(__VA_ARGS__)
56 #  endif
57 #else
58 #  define PRINT(format, ...)
59 #endif
60
61 static MEM_CacheLimiterC *limitor = NULL;
62 static pthread_mutex_t limitor_lock = BLI_MUTEX_INITIALIZER;
63
64 typedef struct MovieCache {
65         char name[64];
66
67         GHash *hash;
68         GHashHashFP hashfp;
69         GHashCmpFP cmpfp;
70         MovieCacheGetKeyDataFP getdatafp;
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         BLI_mempool_free(key->cache_owner->userkeys_pool, key->userkey);
120
121         BLI_mempool_free(key->cache_owner->keys_pool, key);
122 }
123
124 static void moviecache_valfree(void *val)
125 {
126         MovieCacheItem *item = (MovieCacheItem *)val;
127         MovieCache *cache = item->cache_owner;
128
129         PRINT("%s: cache '%s' free item %p buffer %p\n", __func__, cache->name, item, item->ibuf);
130
131         if (item->ibuf) {
132                 MEM_CacheLimiter_unmanage(item->c_handle);
133                 IMB_freeImBuf(item->ibuf);
134         }
135
136         if (item->priority_data && cache->prioritydeleterfp) {
137                 cache->prioritydeleterfp(item->priority_data);
138         }
139
140         BLI_mempool_free(item->cache_owner->items_pool, item);
141 }
142
143 static void check_unused_keys(MovieCache *cache)
144 {
145         GHashIterator *iter;
146
147         iter = BLI_ghashIterator_new(cache->hash);
148         while (!BLI_ghashIterator_done(iter)) {
149                 MovieCacheKey *key = BLI_ghashIterator_getKey(iter);
150                 MovieCacheItem *item = BLI_ghashIterator_getValue(iter);
151                 int remove = 0;
152
153                 BLI_ghashIterator_step(iter);
154
155                 remove = !item->ibuf;
156
157                 if (remove) {
158                         PRINT("%s: cache '%s' remove item %p without buffer\n", __func__, cache->name, item);
159                 }
160
161                 if (remove)
162                         BLI_ghash_remove(cache->hash, key, moviecache_keyfree, moviecache_valfree);
163         }
164
165         BLI_ghashIterator_free(iter);
166 }
167
168 static int compare_int(const void *av, const void *bv)
169 {
170         const int *a = (int *)av;
171         const int *b = (int *)bv;
172         return *a - *b;
173 }
174
175 static void IMB_moviecache_destructor(void *p)
176 {
177         MovieCacheItem *item = (MovieCacheItem *)p;
178
179         if (item && item->ibuf) {
180                 MovieCache *cache = item->cache_owner;
181
182                 PRINT("%s: cache '%s' destroy item %p buffer %p\n", __func__, cache->name, item, item->ibuf);
183
184                 IMB_freeImBuf(item->ibuf);
185
186                 item->ibuf = NULL;
187                 item->c_handle = NULL;
188
189                 /* force cached segments to be updated */
190                 if (cache->points) {
191                         MEM_freeN(cache->points);
192                         cache->points = NULL;
193                 }
194         }
195 }
196
197 /* approximate size of ImBuf in memory */
198 static size_t IMB_get_size_in_memory(ImBuf *ibuf)
199 {
200         int a;
201         size_t size = 0, channel_size = 0;
202
203         size += sizeof(ImBuf);
204
205         if (ibuf->rect)
206                 channel_size += sizeof(char);
207
208         if (ibuf->rect_float)
209                 channel_size += sizeof(float);
210
211         size += channel_size * ibuf->x * ibuf->y * ibuf->channels;
212
213         if (ibuf->miptot) {
214                 for (a = 0; a < ibuf->miptot; a++) {
215                         if (ibuf->mipmap[a])
216                                 size += IMB_get_size_in_memory(ibuf->mipmap[a]);
217                 }
218         }
219
220         if (ibuf->tiles) {
221                 size += sizeof(unsigned int) * ibuf->ytiles * ibuf->xtiles;
222         }
223
224         return size;
225 }
226
227 static size_t get_item_size(void *p)
228 {
229         size_t size = sizeof(MovieCacheItem);
230         MovieCacheItem *item = (MovieCacheItem *) p;
231
232         if (item->ibuf)
233                 size += IMB_get_size_in_memory(item->ibuf);
234
235         return size;
236 }
237
238 static int get_item_priority(void *item_v, int default_priority)
239 {
240         MovieCacheItem *item = (MovieCacheItem *) item_v;
241         MovieCache *cache = item->cache_owner;
242         int priority;
243
244         if (!cache->getitempriorityfp) {
245                 PRINT("%s: cache '%s' item %p use default priority %d\n", __func__, cache-> name, item, default_priority);
246
247                 return default_priority;
248         }
249
250         priority = cache->getitempriorityfp(cache->last_userkey, item->priority_data);
251
252         PRINT("%s: cache '%s' item %p priority %d\n", __func__, cache-> name, item, priority);
253
254         return priority;
255 }
256
257 void IMB_moviecache_init(void)
258 {
259         limitor = new_MEM_CacheLimiter(IMB_moviecache_destructor, get_item_size);
260
261         MEM_CacheLimiter_ItemPriority_Func_set(limitor, get_item_priority);
262 }
263
264 void IMB_moviecache_destruct(void)
265 {
266         if (limitor)
267                 delete_MEM_CacheLimiter(limitor);
268 }
269
270 MovieCache *IMB_moviecache_create(const char *name, int keysize, GHashHashFP hashfp, GHashCmpFP cmpfp)
271 {
272         MovieCache *cache;
273
274         PRINT("%s: cache '%s' create\n", __func__, name);
275
276         cache = MEM_callocN(sizeof(MovieCache), "MovieCache");
277
278         BLI_strncpy(cache->name, name, sizeof(cache->name));
279
280         cache->keys_pool = BLI_mempool_create(sizeof(MovieCacheKey), 64, 64, 0);
281         cache->items_pool = BLI_mempool_create(sizeof(MovieCacheItem), 64, 64, 0);
282         cache->userkeys_pool = BLI_mempool_create(keysize, 64, 64, 0);
283         cache->hash = BLI_ghash_new(moviecache_hashhash, moviecache_hashcmp, "MovieClip ImBuf cache hash");
284
285         cache->keysize = keysize;
286         cache->hashfp = hashfp;
287         cache->cmpfp = cmpfp;
288         cache->proxy = -1;
289
290         return cache;
291 }
292
293 void IMB_moviecache_set_getdata_callback(MovieCache *cache, MovieCacheGetKeyDataFP getdatafp)
294 {
295         cache->getdatafp = getdatafp;
296 }
297
298 void IMB_moviecache_set_priority_callback(struct MovieCache *cache, MovieCacheGetPriorityDataFP getprioritydatafp,
299                                           MovieCacheGetItemPriorityFP getitempriorityfp,
300                                           MovieCachePriorityDeleterFP prioritydeleterfp)
301 {
302         cache->last_userkey = MEM_mallocN(cache->keysize, "movie cache last user key");
303
304         cache->getprioritydatafp = getprioritydatafp;
305         cache->getitempriorityfp = getitempriorityfp;
306         cache->prioritydeleterfp = prioritydeleterfp;
307 }
308
309 static void do_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf, int need_lock)
310 {
311         MovieCacheKey *key;
312         MovieCacheItem *item;
313
314         if (!limitor)
315                 IMB_moviecache_init();
316
317         IMB_refImBuf(ibuf);
318
319         key = BLI_mempool_alloc(cache->keys_pool);
320         key->cache_owner = cache;
321         key->userkey = BLI_mempool_alloc(cache->userkeys_pool);
322         memcpy(key->userkey, userkey, cache->keysize);
323
324         item = BLI_mempool_alloc(cache->items_pool);
325
326         PRINT("%s: cache '%s' put %p, item %p\n", __func__, cache-> name, ibuf, item);
327
328         item->ibuf = ibuf;
329         item->cache_owner = cache;
330         item->c_handle = NULL;
331         item->priority_data = NULL;
332
333         if (cache->getprioritydatafp) {
334                 item->priority_data = cache->getprioritydatafp(userkey);
335         }
336
337         BLI_ghash_remove(cache->hash, key, moviecache_keyfree, moviecache_valfree);
338         BLI_ghash_insert(cache->hash, key, item);
339
340         if (cache->last_userkey) {
341                 memcpy(cache->last_userkey, userkey, cache->keysize);
342         }
343
344         if (need_lock)
345                 BLI_mutex_lock(&limitor_lock);
346
347         item->c_handle = MEM_CacheLimiter_insert(limitor, item);
348
349         MEM_CacheLimiter_ref(item->c_handle);
350         MEM_CacheLimiter_enforce_limits(limitor);
351         MEM_CacheLimiter_unref(item->c_handle);
352
353         if (need_lock)
354                 BLI_mutex_unlock(&limitor_lock);
355
356         /* cache limiter can't remove unused keys which points to destoryed values */
357         check_unused_keys(cache);
358
359         if (cache->points) {
360                 MEM_freeN(cache->points);
361                 cache->points = NULL;
362         }
363 }
364
365 void IMB_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf)
366 {
367         do_moviecache_put(cache, userkey, ibuf, TRUE);
368 }
369
370 int IMB_moviecache_put_if_possible(MovieCache *cache, void *userkey, ImBuf *ibuf)
371 {
372         size_t mem_in_use, mem_limit, elem_size;
373         int result = FALSE;
374
375         elem_size = IMB_get_size_in_memory(ibuf);
376         mem_limit = MEM_CacheLimiter_get_maximum();
377
378         BLI_mutex_lock(&limitor_lock);
379         mem_in_use = MEM_CacheLimiter_get_memory_in_use(limitor);
380
381         if (mem_in_use + elem_size <= mem_limit) {
382                 do_moviecache_put(cache, userkey, ibuf, FALSE);
383                 result = TRUE;
384         }
385
386         BLI_mutex_unlock(&limitor_lock);
387
388         return result;
389 }
390
391 ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey)
392 {
393         MovieCacheKey key;
394         MovieCacheItem *item;
395
396         key.cache_owner = cache;
397         key.userkey = userkey;
398         item = (MovieCacheItem *)BLI_ghash_lookup(cache->hash, &key);
399
400         if (item) {
401                 if (item->ibuf) {
402                         BLI_mutex_lock(&limitor_lock);
403                         MEM_CacheLimiter_touch(item->c_handle);
404                         BLI_mutex_unlock(&limitor_lock);
405
406                         IMB_refImBuf(item->ibuf);
407
408                         return item->ibuf;
409                 }
410         }
411
412         return NULL;
413 }
414
415 int IMB_moviecache_has_frame(MovieCache *cache, void *userkey)
416 {
417         MovieCacheKey key;
418         MovieCacheItem *item;
419
420         key.cache_owner = cache;
421         key.userkey = userkey;
422         item = (MovieCacheItem *)BLI_ghash_lookup(cache->hash, &key);
423
424         return item != NULL;
425 }
426
427 void IMB_moviecache_free(MovieCache *cache)
428 {
429         PRINT("%s: cache '%s' free\n", __func__, cache->name);
430
431         BLI_ghash_free(cache->hash, moviecache_keyfree, moviecache_valfree);
432
433         BLI_mempool_destroy(cache->keys_pool);
434         BLI_mempool_destroy(cache->items_pool);
435         BLI_mempool_destroy(cache->userkeys_pool);
436
437         if (cache->points)
438                 MEM_freeN(cache->points);
439
440         if (cache->last_userkey)
441                 MEM_freeN(cache->last_userkey);
442
443         MEM_freeN(cache);
444 }
445
446 void IMB_moviecache_cleanup(MovieCache *cache, int (cleanup_check_cb) (void *userkey, void *userdata), void *userdata)
447 {
448         GHashIterator *iter;
449
450         iter = BLI_ghashIterator_new(cache->hash);
451         while (!BLI_ghashIterator_done(iter)) {
452                 MovieCacheKey *key = BLI_ghashIterator_getKey(iter);
453                 int remove;
454
455                 BLI_ghashIterator_step(iter);
456
457                 remove = cleanup_check_cb(key->userkey, userdata);
458
459                 if (remove) {
460                         MovieCacheItem *item = BLI_ghashIterator_getValue(iter);
461                         (void) item;  /* silence unused variable when not using debug */
462
463                         PRINT("%s: cache '%s' remove item %p\n", __func__, cache->name, item);
464
465                         BLI_ghash_remove(cache->hash, key, moviecache_keyfree, moviecache_valfree);
466                 }
467         }
468
469         BLI_ghashIterator_free(iter);
470 }
471
472 /* get segments of cached frames. useful for debugging cache policies */
473 void IMB_moviecache_get_cache_segments(MovieCache *cache, int proxy, int render_flags, int *totseg_r, int **points_r)
474 {
475         *totseg_r = 0;
476         *points_r = NULL;
477
478         if (!cache->getdatafp)
479                 return;
480
481         if (cache->proxy != proxy || cache->render_flags != render_flags) {
482                 if (cache->points)
483                         MEM_freeN(cache->points);
484
485                 cache->points = NULL;
486         }
487
488         if (cache->points) {
489                 *totseg_r = cache->totseg;
490                 *points_r = cache->points;
491         }
492         else {
493                 int totframe = BLI_ghash_size(cache->hash);
494                 int *frames = MEM_callocN(totframe * sizeof(int), "movieclip cache frames");
495                 int a, totseg = 0;
496                 GHashIterator *iter;
497
498                 iter = BLI_ghashIterator_new(cache->hash);
499                 a = 0;
500                 while (!BLI_ghashIterator_done(iter)) {
501                         MovieCacheKey *key = BLI_ghashIterator_getKey(iter);
502                         MovieCacheItem *item = BLI_ghashIterator_getValue(iter);
503                         int framenr, curproxy, curflags;
504
505                         if (item->ibuf) {
506                                 cache->getdatafp(key->userkey, &framenr, &curproxy, &curflags);
507
508                                 if (curproxy == proxy && curflags == render_flags)
509                                         frames[a++] = framenr;
510                         }
511
512                         BLI_ghashIterator_step(iter);
513                 }
514
515                 BLI_ghashIterator_free(iter);
516
517                 qsort(frames, totframe, sizeof(int), compare_int);
518
519                 /* count */
520                 for (a = 0; a < totframe; a++) {
521                         if (a && frames[a] - frames[a - 1] != 1)
522                                 totseg++;
523
524                         if (a == totframe - 1)
525                                 totseg++;
526                 }
527
528                 if (totseg) {
529                         int b, *points;
530
531                         points = MEM_callocN(2 * sizeof(int) * totseg, "movieclip cache segments");
532
533                         /* fill */
534                         for (a = 0, b = 0; a < totframe; a++) {
535                                 if (a == 0)
536                                         points[b++] = frames[a];
537
538                                 if (a && frames[a] - frames[a - 1] != 1) {
539                                         points[b++] = frames[a - 1];
540                                         points[b++] = frames[a];
541                                 }
542
543                                 if (a == totframe - 1)
544                                         points[b++] = frames[a];
545                         }
546
547                         *totseg_r = totseg;
548                         *points_r = points;
549
550                         cache->totseg = totseg;
551                         cache->points = points;
552                         cache->proxy = proxy;
553                         cache->render_flags = render_flags;
554                 }
555
556                 MEM_freeN(frames);
557         }
558 }