svn merge ^/trunk/blender -r41230:41266
[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/blenkernel/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         GHashHashFP hashfp;
52         GHashCmpFP cmpfp;
53         MovieCacheGetKeyDataFP getdatafp;
54
55         struct BLI_mempool *keys_pool;
56         struct BLI_mempool *items_pool;
57         struct BLI_mempool *userkeys_pool;
58
59         int keysize;
60         unsigned long curtime;
61
62         int totseg, *points, proxy, render_flags;       /* for visual statistics optimization */
63         int pad;
64 } MovieCache;
65
66 typedef struct MovieCacheKey {
67         MovieCache *cache_owner;
68         void *userkey;
69 } MovieCacheKey;
70
71 typedef struct MovieCacheItem {
72         MovieCache *cache_owner;
73         ImBuf *ibuf;
74         MEM_CacheLimiterHandleC * c_handle;
75         unsigned long last_access;
76 } MovieCacheItem;
77
78 static unsigned int moviecache_hashhash(const void *keyv)
79 {
80         MovieCacheKey *key= (MovieCacheKey*)keyv;
81
82         return key->cache_owner->hashfp(key->userkey);
83 }
84
85 static int moviecache_hashcmp(const void *av, const void *bv)
86 {
87         const MovieCacheKey *a= (MovieCacheKey*)av;
88         const MovieCacheKey *b= (MovieCacheKey*)bv;
89
90         return a->cache_owner->cmpfp(a->userkey, b->userkey);
91 }
92
93 static void moviecache_keyfree(void *val)
94 {
95         MovieCacheKey *key= (MovieCacheKey*)val;
96
97         BLI_mempool_free(key->cache_owner->keys_pool, key);
98 }
99
100 static void moviecache_valfree(void *val)
101 {
102         MovieCacheItem *item= (MovieCacheItem*)val;
103
104         if (item->ibuf) {
105                 MEM_CacheLimiter_unmanage(item->c_handle);
106                 IMB_freeImBuf(item->ibuf);
107         }
108
109         BLI_mempool_free(item->cache_owner->items_pool, item);
110 }
111
112 static void check_unused_keys(MovieCache *cache)
113 {
114         GHashIterator *iter;
115
116         iter= BLI_ghashIterator_new(cache->hash);
117         while(!BLI_ghashIterator_isDone(iter)) {
118                 MovieCacheKey *key= BLI_ghashIterator_getKey(iter);
119                 MovieCacheItem *item= BLI_ghashIterator_getValue(iter);
120
121                 BLI_ghashIterator_step(iter);
122
123                 if(!item->ibuf)
124                         BLI_ghash_remove(cache->hash, key, moviecache_keyfree, moviecache_valfree);
125         }
126
127         BLI_ghashIterator_free(iter);
128 }
129
130 static int compare_int(const void *av, const void *bv)
131 {
132         const int *a= (int *)av;
133         const int *b= (int *)bv;
134         return *a-*b;
135 }
136
137 static void IMB_moviecache_destructor(void *p)
138 {
139         MovieCacheItem *item= (MovieCacheItem *) p;
140
141         if (item && item->ibuf) {
142                 IMB_freeImBuf(item->ibuf);
143
144                 item->ibuf= NULL;
145                 item->c_handle= NULL;
146         }
147 }
148
149 /* approximate size of ImBuf in memory */
150 static intptr_t IMB_get_size_in_memory(ImBuf *ibuf)
151 {
152         int a;
153         intptr_t size= 0, channel_size= 0;
154
155         size+= sizeof(ImBuf);
156
157         if(ibuf->rect)
158                 channel_size+= sizeof(char);
159
160         if(ibuf->rect_float)
161                 channel_size+= sizeof(float);
162
163         size+= channel_size*ibuf->x*ibuf->y*ibuf->channels;
164
165         if(ibuf->miptot) {
166                 for(a= 0; a<ibuf->miptot; a++) {
167                         if(ibuf->mipmap[a])
168                                 size+= IMB_get_size_in_memory(ibuf->mipmap[a]);
169                 }
170         }
171
172         if(ibuf->tiles) {
173                 size+= sizeof(unsigned int)*ibuf->ytiles*ibuf->xtiles;
174         }
175
176         return size;
177 }
178
179 static intptr_t get_item_size (void *p)
180 {
181         intptr_t size= sizeof(MovieCacheItem);
182         MovieCacheItem *item= (MovieCacheItem *) p;
183
184         if(item->ibuf)
185                 size+= IMB_get_size_in_memory(item->ibuf);
186
187         return size;
188 }
189
190 void IMB_moviecache_init(void)
191 {
192         limitor= new_MEM_CacheLimiter(IMB_moviecache_destructor, get_item_size);
193 }
194
195 void IMB_moviecache_destruct(void)
196 {
197         if(limitor)
198                 delete_MEM_CacheLimiter(limitor);
199 }
200
201 struct MovieCache *IMB_moviecache_create(int keysize, GHashHashFP hashfp, GHashCmpFP cmpfp,
202                 MovieCacheGetKeyDataFP getdatafp)
203 {
204         MovieCache *cache;
205
206         cache= MEM_callocN(sizeof(MovieCache), "MovieCache");
207         cache->keys_pool= BLI_mempool_create(sizeof(MovieCacheKey), 64, 64, 0, 0);
208         cache->items_pool= BLI_mempool_create(sizeof(MovieCacheItem), 64, 64, 0, 0);
209         cache->userkeys_pool= BLI_mempool_create(keysize, 64, 64, 0, 0);
210         cache->hash= BLI_ghash_new(moviecache_hashhash, moviecache_hashcmp, "MovieClip ImBuf cache hash");
211
212         cache->keysize= keysize;
213         cache->hashfp= hashfp;
214         cache->cmpfp= cmpfp;
215         cache->getdatafp= getdatafp;
216         cache->proxy= -1;
217
218         return cache;
219 }
220
221 void IMB_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf)
222 {
223         MovieCacheKey *key;
224         MovieCacheItem *item;
225
226         if(!limitor)
227                 IMB_moviecache_init();
228
229         IMB_refImBuf(ibuf);
230
231         key= BLI_mempool_alloc(cache->keys_pool);
232         key->cache_owner= cache;
233         key->userkey= BLI_mempool_alloc(cache->userkeys_pool);;
234         memcpy(key->userkey, userkey, cache->keysize);
235
236         item= BLI_mempool_alloc(cache->items_pool);
237         item->ibuf= ibuf;
238         item->cache_owner= cache;
239         item->last_access= cache->curtime++;
240         item->c_handle= NULL;
241
242         BLI_ghash_remove(cache->hash, key, moviecache_keyfree, moviecache_valfree);
243         BLI_ghash_insert(cache->hash, key, item);
244
245         item->c_handle= MEM_CacheLimiter_insert(limitor, item);
246
247         MEM_CacheLimiter_ref(item->c_handle);
248         MEM_CacheLimiter_enforce_limits(limitor);
249         MEM_CacheLimiter_unref(item->c_handle);
250
251         /* cache limiter can't remove unused keys which points to destoryed values */
252         check_unused_keys(cache);
253
254         if(cache->points) {
255                 MEM_freeN(cache->points);
256                 cache->points= NULL;
257         }
258 }
259
260 ImBuf* IMB_moviecache_get(MovieCache *cache, void *userkey)
261 {
262         MovieCacheKey key;
263         MovieCacheItem *item;
264
265         key.cache_owner= cache;
266         key.userkey= userkey;
267         item= (MovieCacheItem*)BLI_ghash_lookup(cache->hash, &key);
268
269         if(item) {
270                 item->last_access= cache->curtime++;
271
272                 if(item->ibuf) {
273                         MEM_CacheLimiter_touch(item->c_handle);
274                         IMB_refImBuf(item->ibuf);
275
276                         return item->ibuf;
277                 }
278         }
279
280         return NULL;
281 }
282
283 void IMB_moviecache_free(MovieCache *cache)
284 {
285         BLI_ghash_free(cache->hash, moviecache_keyfree, moviecache_valfree);
286
287         BLI_mempool_destroy(cache->keys_pool);
288         BLI_mempool_destroy(cache->items_pool);
289         BLI_mempool_destroy(cache->userkeys_pool);
290
291         if(cache->points)
292                 MEM_freeN(cache->points);
293
294         MEM_freeN(cache);
295 }
296
297 /* get segments of cached frames. useful for debugging cache policies */
298 void IMB_moviecache_get_cache_segments(MovieCache *cache, int proxy, int render_flags, int *totseg_r, int **points_r)
299 {
300         *totseg_r= 0;
301         *points_r= NULL;
302
303         if(!cache->getdatafp)
304                 return;
305
306         if(cache->proxy!=proxy || cache->render_flags!=render_flags) {
307                 if(cache->points)
308                         MEM_freeN(cache->points);
309
310                 cache->points= NULL;
311         }
312
313         if(cache->points) {
314                 *totseg_r= cache->totseg;
315                 *points_r= cache->points;
316         } else {
317                 int totframe= BLI_ghash_size(cache->hash);
318                 int *frames= MEM_callocN(totframe*sizeof(int), "movieclip cache frames");
319                 int a, totseg= 0;
320                 GHashIterator *iter;
321
322                 iter= BLI_ghashIterator_new(cache->hash);
323                 a= 0;
324                 while(!BLI_ghashIterator_isDone(iter)) {
325                         MovieCacheKey *key= BLI_ghashIterator_getKey(iter);
326                         MovieCacheItem *item= BLI_ghashIterator_getValue(iter);
327                         int framenr, curproxy, curflags;
328
329                         if(item->ibuf) {
330                                 cache->getdatafp(key->userkey, &framenr, &curproxy, &curflags);
331
332                                 if(curproxy==proxy && curflags==render_flags)
333                                         frames[a++]= framenr;
334                         }
335
336                         BLI_ghashIterator_step(iter);
337                 }
338
339                 BLI_ghashIterator_free(iter);
340
341                 qsort(frames, totframe, sizeof(int), compare_int);
342
343                 /* count */
344                 for(a= 0; a<totframe; a++) {
345                         if(a && frames[a]-frames[a-1]!=1)
346                                 totseg++;
347
348                         if(a==totframe-1)
349                                 totseg++;
350                 }
351
352                 if(totseg) {
353                         int b, *points;
354
355                         points= MEM_callocN(2*sizeof(int)*totseg, "movieclip cache segments");
356
357                         /* fill */
358                         for(a= 0, b= 0; a<totframe; a++) {
359                                 if(a==0)
360                                         points[b++]= frames[a];
361
362                                 if(a && frames[a]-frames[a-1]!=1) {
363                                         points[b++]= frames[a-1];
364                                         points[b++]= frames[a];
365                                 }
366
367                                 if(a==totframe-1)
368                                         points[b++]= frames[a];
369                         }
370
371                         *totseg_r= totseg;
372                         *points_r= points;
373
374                         cache->totseg= totseg;
375                         cache->points= points;
376                         cache->proxy= proxy;
377                         cache->render_flags= render_flags;
378                 }
379
380                 MEM_freeN(frames);
381         }
382 }