/* * ***** BEGIN GPL LICENSE BLOCK ***** * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software Foundation, * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * * Peter Schlaile 2010 * * Contributor(s): Sergey Sharybin * * ***** END GPL LICENSE BLOCK ***** */ /** \file blender/blenkernel/intern/seqcache.c * \ingroup bke */ #include #include #include "BLI_sys_types.h" /* for intptr_t */ #include "BKE_global.h" #include "MEM_guardedalloc.h" #include "DNA_sequence_types.h" #include "DNA_scene_types.h" #include "IMB_moviecache.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" #include "BLI_listbase.h" #include "BKE_sequencer.h" #include "BKE_scene.h" typedef struct SeqCacheKey { struct Sequence *seq; SeqRenderData context; float cfra; eSeqStripElemIBuf type; } SeqCacheKey; typedef struct SeqPreprocessCacheElem { struct SeqPreprocessCacheElem *next, *prev; struct Sequence *seq; SeqRenderData context; eSeqStripElemIBuf type; ImBuf *ibuf; } SeqPreprocessCacheElem; typedef struct SeqPreprocessCache { int cfra; ListBase elems; } SeqPreprocessCache; static struct MovieCache *moviecache = NULL; static struct SeqPreprocessCache *preprocess_cache = NULL; static void preprocessed_cache_destruct(void); static bool seq_cmp_render_data(const SeqRenderData *a, const SeqRenderData *b) { return ((a->preview_render_size != b->preview_render_size) || (a->rectx != b->rectx) || (a->recty != b->recty) || (a->bmain != b->bmain) || (a->scene != b->scene) || (a->motion_blur_shutter != b->motion_blur_shutter) || (a->motion_blur_samples != b->motion_blur_samples) || (a->scene->r.views_format != b->scene->r.views_format) || (a->view_id != b->view_id)); } static unsigned int seq_hash_render_data(const SeqRenderData *a) { unsigned int rval = a->rectx + a->recty; rval ^= a->preview_render_size; rval ^= ((intptr_t) a->bmain) << 6; rval ^= ((intptr_t) a->scene) << 6; rval ^= (int)(a->motion_blur_shutter * 100.0f) << 10; rval ^= a->motion_blur_samples << 24; rval ^= ((a->scene->r.views_format * 2) + a->view_id) << 32; return rval; } static unsigned int seqcache_hashhash(const void *key_) { const SeqCacheKey *key = key_; unsigned int rval = seq_hash_render_data(&key->context); rval ^= *(const unsigned int *) &key->cfra; rval += key->type; rval ^= ((intptr_t) key->seq) << 6; return rval; } static bool seqcache_hashcmp(const void *a_, const void *b_) { const SeqCacheKey *a = a_; const SeqCacheKey *b = b_; return ((a->seq != b->seq) || (a->cfra != b->cfra) || (a->type != b->type) || seq_cmp_render_data(&a->context, &b->context)); } typedef struct { int framenr; } SeqCachePriorityData; static void *moviecache_getprioritydata(void *key_v) { SeqCacheKey *key = (SeqCacheKey *) key_v; SeqCachePriorityData *priority_data; priority_data = MEM_callocN(sizeof(*priority_data), "movie cache clip priority data"); priority_data->framenr = key->cfra; return priority_data; } static int moviecache_getitempriority(void *last_userkey_v, void *priority_data_v) { SeqCacheKey *last_userkey = (SeqCacheKey *) last_userkey_v; SeqCachePriorityData *priority_data = (SeqCachePriorityData *) priority_data_v; return -abs(last_userkey->cfra - priority_data->framenr); } static void moviecache_prioritydeleter(void *priority_data_v) { SeqCachePriorityData *priority_data = (SeqCachePriorityData *) priority_data_v; MEM_freeN(priority_data); } void BKE_sequencer_cache_destruct(void) { if (moviecache) IMB_moviecache_free(moviecache); preprocessed_cache_destruct(); } void BKE_sequencer_cache_cleanup(void) { if (moviecache) { IMB_moviecache_free(moviecache); moviecache = IMB_moviecache_create("seqcache", sizeof(SeqCacheKey), seqcache_hashhash, seqcache_hashcmp); IMB_moviecache_set_priority_callback(moviecache, moviecache_getprioritydata, moviecache_getitempriority, moviecache_prioritydeleter); } BKE_sequencer_preprocessed_cache_cleanup(); } static bool seqcache_key_check_seq(ImBuf *UNUSED(ibuf), void *userkey, void *userdata) { SeqCacheKey *key = (SeqCacheKey *) userkey; Sequence *seq = (Sequence *) userdata; return key->seq == seq; } void BKE_sequencer_cache_cleanup_sequence(Sequence *seq) { if (moviecache) IMB_moviecache_cleanup(moviecache, seqcache_key_check_seq, seq); } struct ImBuf *BKE_sequencer_cache_get(const SeqRenderData *context, Sequence *seq, float cfra, eSeqStripElemIBuf type) { if (moviecache && seq) { SeqCacheKey key; key.seq = seq; key.context = *context; key.cfra = cfra - seq->start; key.type = type; return IMB_moviecache_get(moviecache, &key); } return NULL; } void BKE_sequencer_cache_put(const SeqRenderData *context, Sequence *seq, float cfra, eSeqStripElemIBuf type, ImBuf *i) { SeqCacheKey key; if (i == NULL || context->skip_cache || G.debug_value == 314) { return; } if (!moviecache) { moviecache = IMB_moviecache_create("seqcache", sizeof(SeqCacheKey), seqcache_hashhash, seqcache_hashcmp); IMB_moviecache_set_priority_callback(moviecache, moviecache_getprioritydata, moviecache_getitempriority, moviecache_prioritydeleter); } key.seq = seq; key.context = *context; key.cfra = cfra - seq->start; key.type = type; IMB_moviecache_put(moviecache, &key, i); } void BKE_sequencer_preprocessed_cache_cleanup(void) { SeqPreprocessCacheElem *elem; if (!preprocess_cache) return; for (elem = preprocess_cache->elems.first; elem; elem = elem->next) { IMB_freeImBuf(elem->ibuf); } BLI_freelistN(&preprocess_cache->elems); BLI_listbase_clear(&preprocess_cache->elems); } static void preprocessed_cache_destruct(void) { if (!preprocess_cache) return; BKE_sequencer_preprocessed_cache_cleanup(); MEM_freeN(preprocess_cache); preprocess_cache = NULL; } ImBuf *BKE_sequencer_preprocessed_cache_get(const SeqRenderData *context, Sequence *seq, float cfra, eSeqStripElemIBuf type) { SeqPreprocessCacheElem *elem; if (!preprocess_cache || G.debug_value == 314) return NULL; if (preprocess_cache->cfra != cfra) return NULL; for (elem = preprocess_cache->elems.first; elem; elem = elem->next) { if (elem->seq != seq) continue; if (elem->type != type) continue; if (seq_cmp_render_data(&elem->context, context) != 0) continue; IMB_refImBuf(elem->ibuf); return elem->ibuf; } return NULL; } void BKE_sequencer_preprocessed_cache_put(const SeqRenderData *context, Sequence *seq, float cfra, eSeqStripElemIBuf type, ImBuf *ibuf) { SeqPreprocessCacheElem *elem; if (!preprocess_cache) { preprocess_cache = MEM_callocN(sizeof(SeqPreprocessCache), "sequencer preprocessed cache"); } else { if (preprocess_cache->cfra != cfra) BKE_sequencer_preprocessed_cache_cleanup(); } elem = MEM_callocN(sizeof(SeqPreprocessCacheElem), "sequencer preprocessed cache element"); elem->seq = seq; elem->type = type; elem->context = *context; elem->ibuf = ibuf; preprocess_cache->cfra = cfra; IMB_refImBuf(ibuf); BLI_addtail(&preprocess_cache->elems, elem); } void BKE_sequencer_preprocessed_cache_cleanup_sequence(Sequence *seq) { SeqPreprocessCacheElem *elem, *elem_next; if (!preprocess_cache) return; for (elem = preprocess_cache->elems.first; elem; elem = elem_next) { elem_next = elem->next; if (elem->seq == seq) { IMB_freeImBuf(elem->ibuf); BLI_freelinkN(&preprocess_cache->elems, elem); } } }