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