diff --git a/intern/memutil/MEM_CacheLimiter.h b/intern/memutil/MEM_CacheLimiter.h index 9a36b67aa2f..801ee154d40 100644 --- a/intern/memutil/MEM_CacheLimiter.h +++ b/intern/memutil/MEM_CacheLimiter.h @@ -32,7 +32,7 @@ * @section MEM_CacheLimiter * This class defines a generic memory cache management system * to limit memory usage to a fixed global maximum. - * + * * Please use the C-API in MEM_CacheLimiterC-Api.h for code written in C. * * Usage example: @@ -41,12 +41,12 @@ * public: * ~BigFatImage() { tell_everyone_we_are_gone(this); } * }; - * + * * void doit() { * MEM_Cache BigFatImages; * * MEM_Cache_Handle* h = BigFatImages.insert(new BigFatImage); - * + * * BigFatImages.enforce_limits(); * h->ref(); * @@ -58,6 +58,8 @@ */ #include +#include +#include #include "MEM_Allocator.h" template @@ -65,36 +67,44 @@ class MEM_CacheLimiter; #ifndef __MEM_CACHELIMITERC_API_H__ extern "C" { - extern void MEM_CacheLimiter_set_maximum(size_t m); - extern size_t MEM_CacheLimiter_get_maximum(); + void MEM_CacheLimiter_set_maximum(size_t m); + size_t MEM_CacheLimiter_get_maximum(); }; #endif template class MEM_CacheLimiterHandle { public: - explicit MEM_CacheLimiterHandle(T * data_, - MEM_CacheLimiter * parent_) - : data(data_), refcount(0), parent(parent_) { } + explicit MEM_CacheLimiterHandle(T * data_,MEM_CacheLimiter *parent_) : + data(data_), + refcount(0), + parent(parent_) + { } - void ref() { - refcount++; + void ref() { + refcount++; } - void unref() { - refcount--; + + void unref() { + refcount--; } - T * get() { - return data; + + T *get() { + return data; } - const T * get() const { - return data; + + const T *get() const { + return data; } - int get_refcount() const { - return refcount; + + int get_refcount() const { + return refcount; } - bool can_destroy() const { - return !data || !refcount; + + bool can_destroy() const { + return !data || !refcount; } + bool destroy_if_possible() { if (can_destroy()) { delete data; @@ -104,48 +114,64 @@ public: } return false; } + void unmanage() { parent->unmanage(this); } + void touch() { parent->touch(this); } + + void set_priority(int priority) { + this->priority = priority; + } + + int get_priority(void) { + return this->priority; + } + private: friend class MEM_CacheLimiter; T * data; int refcount; - typename std::list *, - MEM_Allocator *> >::iterator me; + int priority; + typename std::list *, MEM_Allocator *> >::iterator me; MEM_CacheLimiter * parent; }; template class MEM_CacheLimiter { public: - typedef typename std::list *, - MEM_Allocator *> >::iterator iterator; typedef size_t (*MEM_CacheLimiter_DataSize_Func) (void *data); + typedef int (*MEM_CacheLimiter_ItemPriority_Func) (void *item, int default_priority); + MEM_CacheLimiter(MEM_CacheLimiter_DataSize_Func getDataSize_) : getDataSize(getDataSize_) { } + ~MEM_CacheLimiter() { for (iterator it = queue.begin(); it != queue.end(); it++) { delete *it; } } - MEM_CacheLimiterHandle * insert(T * elem) { + + MEM_CacheLimiterHandle *insert(T * elem) { queue.push_back(new MEM_CacheLimiterHandle(elem, this)); iterator it = queue.end(); --it; queue.back()->me = it; return queue.back(); } - void unmanage(MEM_CacheLimiterHandle * handle) { + + void unmanage(MEM_CacheLimiterHandle *handle) { queue.erase(handle->me); delete handle; } + void enforce_limits() { + MEM_CachePriorityQueue priority_queue; size_t max = MEM_CacheLimiter_get_maximum(); size_t mem_in_use, cur_size; @@ -159,27 +185,33 @@ public: mem_in_use = MEM_get_memory_in_use(); } - for (iterator it = queue.begin(); - it != queue.end() && mem_in_use > max;) - { - iterator jt = it; - ++it; + if (mem_in_use <= max) { + return; + } + + priority_queue = get_priority_queue(); + + while (!priority_queue.empty() && mem_in_use > max) { + MEM_CacheElementPtr elem = priority_queue.top(); + + priority_queue.pop(); if(getDataSize) { - cur_size= getDataSize((*jt)->get()->get_data()); + cur_size = getDataSize(elem->get()->get_data()); } else { - cur_size= mem_in_use; + cur_size = mem_in_use; } - (*jt)->destroy_if_possible(); - - if(getDataSize) { - mem_in_use-= cur_size; - } else { - mem_in_use-= cur_size - MEM_get_memory_in_use(); + if (elem->destroy_if_possible()) { + if (getDataSize) { + mem_in_use -= cur_size; + } else { + mem_in_use -= cur_size - MEM_get_memory_in_use(); + } } } } + void touch(MEM_CacheLimiterHandle * handle) { queue.push_back(handle); queue.erase(handle->me); @@ -187,7 +219,24 @@ public: --it; handle->me = it; } + + void set_item_priority_func(MEM_CacheLimiter_ItemPriority_Func item_priority_func) { + getItemPriority = item_priority_func; + } + private: + typedef MEM_CacheLimiterHandle *MEM_CacheElementPtr; + typedef std::list > MEM_CacheQueue; + typedef typename MEM_CacheQueue::iterator iterator; + + struct compare_element_priority : public std::binary_function { + bool operator()(const MEM_CacheElementPtr left_elem, const MEM_CacheElementPtr right_elem) const { + return left_elem->get_priority() > right_elem->get_priority(); + } + }; + + typedef std::priority_queue, compare_element_priority > MEM_CachePriorityQueue; + size_t total_size() { size_t size = 0; for (iterator it = queue.begin(); it != queue.end(); it++) { @@ -196,9 +245,33 @@ private: return size; } - std::list*, - MEM_Allocator *> > queue; + MEM_CachePriorityQueue get_priority_queue(void) { + MEM_CachePriorityQueue priority_queue; + iterator it; + int i; + + for (it = queue.begin(), i = 0; it != queue.end(); it++, i++) { + MEM_CacheElementPtr elem = *it; + int priority; + + /* by default 0 means higherst priority element */ + priority = -(queue.size() - i - 1); + + if (getItemPriority) { + priority = getItemPriority(elem->get()->get_data(), priority); + } + + elem->set_priority(priority); + + priority_queue.push(elem); + } + + return priority_queue; + } + + MEM_CacheQueue queue; MEM_CacheLimiter_DataSize_Func getDataSize; + MEM_CacheLimiter_ItemPriority_Func getItemPriority; }; #endif // __MEM_CACHELIMITER_H__ diff --git a/intern/memutil/MEM_CacheLimiterC-Api.h b/intern/memutil/MEM_CacheLimiterC-Api.h index 4ed692fb55f..1ae5e9df1c6 100644 --- a/intern/memutil/MEM_CacheLimiterC-Api.h +++ b/intern/memutil/MEM_CacheLimiterC-Api.h @@ -31,7 +31,7 @@ #ifdef __cplusplus extern "C" { #endif - + struct MEM_CacheLimiter_s; struct MEM_CacheLimiterHandle_s; @@ -39,106 +39,111 @@ typedef struct MEM_CacheLimiter_s MEM_CacheLimiterC; typedef struct MEM_CacheLimiterHandle_s MEM_CacheLimiterHandleC; /* function used to remove data from memory */ -typedef void(*MEM_CacheLimiter_Destruct_Func)(void*); +typedef void (*MEM_CacheLimiter_Destruct_Func)(void*); /* function used to measure stored data element size */ -typedef size_t(*MEM_CacheLimiter_DataSize_Func) (void*); +typedef size_t (*MEM_CacheLimiter_DataSize_Func) (void*); + +/* function used to measure priority of item when freeing memory */ +typedef int (*MEM_CacheLimiter_ItemPriority_Func) (void*, int); #ifndef __MEM_CACHELIMITER_H__ -extern void MEM_CacheLimiter_set_maximum(size_t m); -extern int MEM_CacheLimiter_get_maximum(void); +void MEM_CacheLimiter_set_maximum(size_t m); +int MEM_CacheLimiter_get_maximum(void); #endif /* __MEM_CACHELIMITER_H__ */ -/** - * Create new MEM_CacheLimiter object + +/** + * Create new MEM_CacheLimiter object * managed objects are destructed with the data_destructor * * @param data_destructor * @return A new MEM_CacheLimter object */ -extern MEM_CacheLimiterC * new_MEM_CacheLimiter( - MEM_CacheLimiter_Destruct_Func data_destructor, - MEM_CacheLimiter_DataSize_Func data_size); +MEM_CacheLimiterC *new_MEM_CacheLimiter(MEM_CacheLimiter_Destruct_Func data_destructor, + MEM_CacheLimiter_DataSize_Func data_size); -/** +/** * Delete MEM_CacheLimiter - * + * * Frees the memory of the CacheLimiter but does not touch managed objects! * * @param This "This" pointer */ -extern void delete_MEM_CacheLimiter(MEM_CacheLimiterC * This); +void delete_MEM_CacheLimiter(MEM_CacheLimiterC *This); -/** +/** * Manage object - * + * * @param This "This" pointer, data data object to manage * @return CacheLimiterHandle to ref, unref, touch the managed object */ - -extern MEM_CacheLimiterHandleC * MEM_CacheLimiter_insert( - MEM_CacheLimiterC * This, void * data); -/** +MEM_CacheLimiterHandleC *MEM_CacheLimiter_insert(MEM_CacheLimiterC * This, void * data); + +/** * Free objects until memory constraints are satisfied - * + * * @param This "This" pointer */ -extern void MEM_CacheLimiter_enforce_limits(MEM_CacheLimiterC * This); +void MEM_CacheLimiter_enforce_limits(MEM_CacheLimiterC *This); -/** - * Unmanage object previously inserted object. +/** + * Unmanage object previously inserted object. * Does _not_ delete managed object! - * + * * @param This "This" pointer, handle of object */ - -extern void MEM_CacheLimiter_unmanage(MEM_CacheLimiterHandleC * handle); + +void MEM_CacheLimiter_unmanage(MEM_CacheLimiterHandleC *handle); -/** +/** * Raise priority of object (put it at the tail of the deletion chain) - * + * * @param handle of object */ - -extern void MEM_CacheLimiter_touch(MEM_CacheLimiterHandleC * handle); -/** +void MEM_CacheLimiter_touch(MEM_CacheLimiterHandleC *handle); + +/** * Increment reference counter. Objects with reference counter != 0 are _not_ * deleted. - * + * * @param handle of object */ - -extern void MEM_CacheLimiter_ref(MEM_CacheLimiterHandleC * handle); -/** +void MEM_CacheLimiter_ref(MEM_CacheLimiterHandleC *handle); + +/** * Decrement reference counter. Objects with reference counter != 0 are _not_ * deleted. - * + * * @param handle of object */ - -extern void MEM_CacheLimiter_unref(MEM_CacheLimiterHandleC * handle); -/** +void MEM_CacheLimiter_unref(MEM_CacheLimiterHandleC *handle); + +/** * Get reference counter. - * + * * @param This "This" pointer, handle of object */ - -extern int MEM_CacheLimiter_get_refcount(MEM_CacheLimiterHandleC * handle); -/** +int MEM_CacheLimiter_get_refcount(MEM_CacheLimiterHandleC *handle); + +/** * Get pointer to managed object - * + * * @param handle of object */ - -extern void * MEM_CacheLimiter_get(MEM_CacheLimiterHandleC * handle); + +void * MEM_CacheLimiter_get(MEM_CacheLimiterHandleC *handle); + +void MEM_CacheLimiter_ItemPriority_Func_set(MEM_CacheLimiterC *This, + MEM_CacheLimiter_ItemPriority_Func item_priority_func); #ifdef __cplusplus } diff --git a/intern/memutil/intern/MEM_CacheLimiterC-Api.cpp b/intern/memutil/intern/MEM_CacheLimiterC-Api.cpp index cfa6a207e1c..81a1ce670ae 100644 --- a/intern/memutil/intern/MEM_CacheLimiterC-Api.cpp +++ b/intern/memutil/intern/MEM_CacheLimiterC-Api.cpp @@ -31,7 +31,7 @@ static size_t & get_max() { - static size_t m = 32*1024*1024; + static size_t m = 32 * 1024 * 1024; return m; } @@ -50,8 +50,7 @@ class MEM_CacheLimiterCClass; typedef MEM_CacheLimiterHandle handle_t; typedef MEM_CacheLimiter cache_t; -typedef std::list > list_t; +typedef std::list > list_t; class MEM_CacheLimiterCClass { public: @@ -59,11 +58,10 @@ public: : data_destructor(data_destructor_), cache(data_size) { } ~MEM_CacheLimiterCClass(); - + handle_t * insert(void * data); - void destruct(void * data, - list_t::iterator it); + void destruct(void * data, list_t::iterator it); cache_t * get_cache() { return &cache; @@ -72,42 +70,48 @@ private: MEM_CacheLimiter_Destruct_Func data_destructor; MEM_CacheLimiter cache; - + list_t cclass_list; }; class MEM_CacheLimiterHandleCClass { public: - MEM_CacheLimiterHandleCClass(void * data_, - MEM_CacheLimiterCClass * parent_) - : data(data_), parent(parent_) { } + MEM_CacheLimiterHandleCClass(void * data_, MEM_CacheLimiterCClass * parent_) : + data(data_), + parent(parent_) + { } + ~MEM_CacheLimiterHandleCClass(); + void set_iter(list_t::iterator it_) { it = it_; } + void set_data(void * data_) { data = data_; } - void * get_data() const { + + void *get_data() const { return data; } + private: - void * data; - MEM_CacheLimiterCClass * parent; + void *data; + MEM_CacheLimiterCClass *parent; list_t::iterator it; }; -handle_t * MEM_CacheLimiterCClass::insert(void * data) +handle_t *MEM_CacheLimiterCClass::insert(void * data) { cclass_list.push_back(new MEM_CacheLimiterHandleCClass(data, this)); list_t::iterator it = cclass_list.end(); --it; cclass_list.back()->set_iter(it); - + return cache.insert(cclass_list.back()); } -void MEM_CacheLimiterCClass::destruct(void * data, list_t::iterator it) +void MEM_CacheLimiterCClass::destruct(void * data, list_t::iterator it) { data_destructor(data); cclass_list.erase(it); @@ -123,77 +127,78 @@ MEM_CacheLimiterHandleCClass::~MEM_CacheLimiterHandleCClass() MEM_CacheLimiterCClass::~MEM_CacheLimiterCClass() { // should not happen, but don't leak memory in this case... - for (list_t::iterator it = cclass_list.begin(); - it != cclass_list.end(); it++) { + for (list_t::iterator it = cclass_list.begin(); it != cclass_list.end(); it++) { (*it)->set_data(0); + delete *it; } } // ---------------------------------------------------------------------- -static inline MEM_CacheLimiterCClass* cast(MEM_CacheLimiterC * l) +static inline MEM_CacheLimiterCClass *cast(MEM_CacheLimiterC *l) { - return (MEM_CacheLimiterCClass*) l; + return (MEM_CacheLimiterCClass *) l; } -static inline handle_t* cast(MEM_CacheLimiterHandleC * l) +static inline handle_t *cast(MEM_CacheLimiterHandleC *l) { - return (handle_t*) l; + return (handle_t *) l; } -MEM_CacheLimiterC * new_MEM_CacheLimiter( - MEM_CacheLimiter_Destruct_Func data_destructor, - MEM_CacheLimiter_DataSize_Func data_size) +MEM_CacheLimiterC *new_MEM_CacheLimiter(MEM_CacheLimiter_Destruct_Func data_destructor, + MEM_CacheLimiter_DataSize_Func data_size) { - return (MEM_CacheLimiterC*) new MEM_CacheLimiterCClass( - data_destructor, - data_size); + return (MEM_CacheLimiterC *) new MEM_CacheLimiterCClass(data_destructor, data_size); } -void delete_MEM_CacheLimiter(MEM_CacheLimiterC * This) +void delete_MEM_CacheLimiter(MEM_CacheLimiterC *This) { delete cast(This); } -MEM_CacheLimiterHandleC * MEM_CacheLimiter_insert( - MEM_CacheLimiterC * This, void * data) +MEM_CacheLimiterHandleC *MEM_CacheLimiter_insert(MEM_CacheLimiterC *This, void *data) { return (MEM_CacheLimiterHandleC *) cast(This)->insert(data); } -void MEM_CacheLimiter_enforce_limits(MEM_CacheLimiterC * This) +void MEM_CacheLimiter_enforce_limits(MEM_CacheLimiterC *This) { cast(This)->get_cache()->enforce_limits(); } - -void MEM_CacheLimiter_unmanage(MEM_CacheLimiterHandleC * handle) + +void MEM_CacheLimiter_unmanage(MEM_CacheLimiterHandleC *handle) { cast(handle)->unmanage(); } - -void MEM_CacheLimiter_touch(MEM_CacheLimiterHandleC * handle) + +void MEM_CacheLimiter_touch(MEM_CacheLimiterHandleC *handle) { cast(handle)->touch(); } - -void MEM_CacheLimiter_ref(MEM_CacheLimiterHandleC * handle) + +void MEM_CacheLimiter_ref(MEM_CacheLimiterHandleC *handle) { cast(handle)->ref(); } - -void MEM_CacheLimiter_unref(MEM_CacheLimiterHandleC * handle) + +void MEM_CacheLimiter_unref(MEM_CacheLimiterHandleC *handle) { cast(handle)->unref(); } -int MEM_CacheLimiter_get_refcount(MEM_CacheLimiterHandleC * handle) +int MEM_CacheLimiter_get_refcount(MEM_CacheLimiterHandleC *handle) { return cast(handle)->get_refcount(); } - -void * MEM_CacheLimiter_get(MEM_CacheLimiterHandleC * handle) +void *MEM_CacheLimiter_get(MEM_CacheLimiterHandleC *handle) { return cast(handle)->get()->get_data(); } + +void MEM_CacheLimiter_ItemPriority_Func_set(MEM_CacheLimiterC *This, + MEM_CacheLimiter_ItemPriority_Func item_priority_func) +{ + cast(This)->get_cache()->set_item_priority_func(item_priority_func); +} diff --git a/source/blender/blenkernel/intern/movieclip.c b/source/blender/blenkernel/intern/movieclip.c index 2d83a748767..de367b6b4d0 100644 --- a/source/blender/blenkernel/intern/movieclip.c +++ b/source/blender/blenkernel/intern/movieclip.c @@ -340,6 +340,10 @@ typedef struct MovieClipImBufCacheKey { short render_flag; } MovieClipImBufCacheKey; +typedef struct MovieClipCachePriorityData { + int framenr; +} MovieClipCachePriorityData; + static void moviecache_keydata(void *userkey, int *framenr, int *proxy, int *render_flags) { MovieClipImBufCacheKey *key = (MovieClipImBufCacheKey *)userkey; @@ -380,6 +384,32 @@ static int moviecache_hashcmp(const void *av, const void *bv) return 0; } +void *moviecache_getprioritydata(void *key_v) +{ + MovieClipImBufCacheKey *key = (MovieClipImBufCacheKey *) key_v; + MovieClipCachePriorityData *priority_data; + + priority_data = MEM_callocN(sizeof(priority_data), "movie cache clip priority data"); + priority_data->framenr = key->framenr; + + return priority_data; +} + +int moviecache_getitempriority(void *last_userkey_v, void *priority_data_v) +{ + MovieClipImBufCacheKey *last_userkey = (MovieClipImBufCacheKey *) last_userkey_v; + MovieClipCachePriorityData *priority_data = (MovieClipCachePriorityData *) priority_data_v; + + return -abs(last_userkey->framenr - priority_data->framenr); +} + +void moviecache_prioritydeleter(void *priority_data_v) +{ + MovieClipCachePriorityData *priority_data = (MovieClipCachePriorityData *) priority_data_v; + + MEM_freeN(priority_data); +} + static ImBuf *get_imbuf_cache(MovieClip *clip, MovieClipUser *user, int flag) { if (clip->cache) { @@ -407,10 +437,20 @@ static void put_imbuf_cache(MovieClip *clip, MovieClipUser *user, ImBuf *ibuf, i MovieClipImBufCacheKey key; if (!clip->cache) { + struct MovieCache *moviecache; + + // char cache_name[64]; + // BLI_snprintf(cache_name, sizeof(cache_name), "movie %s", clip->id.name); + clip->cache = MEM_callocN(sizeof(MovieClipCache), "movieClipCache"); - clip->cache->moviecache = IMB_moviecache_create(sizeof(MovieClipImBufCacheKey), moviecache_hashhash, - moviecache_hashcmp, moviecache_keydata); + moviecache = IMB_moviecache_create("movieclip", sizeof(MovieClipImBufCacheKey), moviecache_hashhash, moviecache_hashcmp); + + IMB_moviecache_set_getdata_callback(moviecache, moviecache_keydata); + IMB_moviecache_set_priority_callback(moviecache, moviecache_getprioritydata, moviecache_getitempriority, + moviecache_prioritydeleter); + + clip->cache->moviecache = moviecache; } key.framenr = user->framenr; diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c index 582034ae623..0d91dcb7faa 100644 --- a/source/blender/blenkernel/intern/seqcache.c +++ b/source/blender/blenkernel/intern/seqcache.c @@ -98,8 +98,7 @@ void seq_stripelem_cache_cleanup(void) { if (moviecache) { IMB_moviecache_free(moviecache); - moviecache = IMB_moviecache_create(sizeof(SeqCacheKey), seqcache_hashhash, - seqcache_hashcmp, NULL); + moviecache = IMB_moviecache_create("seqcache", sizeof(SeqCacheKey), seqcache_hashhash, seqcache_hashcmp); } } @@ -133,8 +132,7 @@ void seq_stripelem_cache_put( } if (!moviecache) { - moviecache = IMB_moviecache_create(sizeof(SeqCacheKey), seqcache_hashhash, - seqcache_hashcmp, NULL); + moviecache = IMB_moviecache_create("seqcache", sizeof(SeqCacheKey), seqcache_hashhash, seqcache_hashcmp); } key.seq = seq; diff --git a/source/blender/imbuf/IMB_moviecache.h b/source/blender/imbuf/IMB_moviecache.h index f6927d4f31d..d1d34a01433 100644 --- a/source/blender/imbuf/IMB_moviecache.h +++ b/source/blender/imbuf/IMB_moviecache.h @@ -44,10 +44,19 @@ struct MovieCache; typedef void (*MovieCacheGetKeyDataFP) (void *userkey, int *framenr, int *proxy, int *render_flags); +typedef void *(*MovieCacheGetPriorityDataFP) (void *userkey); +typedef int (*MovieCacheGetItemPriorityFP) (void *last_userkey, void *priority_data); +typedef void (*MovieCachePriorityDeleterFP) (void *priority_data); + void IMB_moviecache_init(void); void IMB_moviecache_destruct(void); -struct MovieCache *IMB_moviecache_create(int keysize, GHashHashFP hashfp, GHashCmpFP cmpfp, MovieCacheGetKeyDataFP getdatafp); +struct MovieCache *IMB_moviecache_create(const char *name, int keysize, GHashHashFP hashfp, GHashCmpFP cmpfp); +void IMB_moviecache_set_getdata_callback(struct MovieCache *cache, MovieCacheGetKeyDataFP getdatafp); +void IMB_moviecache_set_priority_callback(struct MovieCache *cache, MovieCacheGetPriorityDataFP getprioritydatafp, + MovieCacheGetItemPriorityFP getitempriorityfp, + MovieCachePriorityDeleterFP prioritydeleterfp); + void IMB_moviecache_put(struct MovieCache *cache, void *userkey, struct ImBuf *ibuf); struct ImBuf* IMB_moviecache_get(struct MovieCache *cache, void *userkey); void IMB_moviecache_free(struct MovieCache *cache); diff --git a/source/blender/imbuf/intern/moviecache.c b/source/blender/imbuf/intern/moviecache.c index 2992e4a0aa7..e398d1c7b16 100644 --- a/source/blender/imbuf/intern/moviecache.c +++ b/source/blender/imbuf/intern/moviecache.c @@ -29,12 +29,15 @@ * \ingroup bke */ +#undef DEBUG_MESSAGES + #include /* for qsort */ #include #include "MEM_guardedalloc.h" #include "MEM_CacheLimiterC-Api.h" +#include "BLI_string.h" #include "BLI_utildefines.h" #include "BLI_ghash.h" #include "BLI_mempool.h" @@ -44,20 +47,37 @@ #include "IMB_imbuf_types.h" #include "IMB_imbuf.h" +#ifdef DEBUG_MESSAGES +# if defined __GNUC__ || defined __sun +# define PRINT(format, args ...) printf(format, ##args) +# else +# define PRINT(format, ...) printf(__VA_ARGS__) +# endif +#else +# define PRINT(format, ...) +#endif + static MEM_CacheLimiterC *limitor = NULL; typedef struct MovieCache { + char name[64]; + GHash *hash; GHashHashFP hashfp; GHashCmpFP cmpfp; MovieCacheGetKeyDataFP getdatafp; + MovieCacheGetPriorityDataFP getprioritydatafp; + MovieCacheGetItemPriorityFP getitempriorityfp; + MovieCachePriorityDeleterFP prioritydeleterfp; + struct BLI_mempool *keys_pool; struct BLI_mempool *items_pool; struct BLI_mempool *userkeys_pool; int keysize; - unsigned long curtime; + + void *last_userkey; int totseg, *points, proxy, render_flags; /* for visual statistics optimization */ int pad; @@ -72,7 +92,7 @@ typedef struct MovieCacheItem { MovieCache *cache_owner; ImBuf *ibuf; MEM_CacheLimiterHandleC *c_handle; - unsigned long last_access; + void *priority_data; } MovieCacheItem; static unsigned int moviecache_hashhash(const void *keyv) @@ -94,18 +114,27 @@ static void moviecache_keyfree(void *val) { MovieCacheKey *key = (MovieCacheKey *)val; + BLI_mempool_free(key->cache_owner->userkeys_pool, key->userkey); + BLI_mempool_free(key->cache_owner->keys_pool, key); } static void moviecache_valfree(void *val) { MovieCacheItem *item = (MovieCacheItem *)val; + MovieCache *cache = item->cache_owner; + + PRINT("%s: cache '%s' free item %p buffer %p\n", __func__, cache->name, item, item->ibuf); if (item->ibuf) { MEM_CacheLimiter_unmanage(item->c_handle); IMB_freeImBuf(item->ibuf); } + if (item->priority_data && cache->prioritydeleterfp) { + cache->prioritydeleterfp(item->priority_data); + } + BLI_mempool_free(item->cache_owner->items_pool, item); } @@ -117,10 +146,16 @@ static void check_unused_keys(MovieCache *cache) while (!BLI_ghashIterator_isDone(iter)) { MovieCacheKey *key = BLI_ghashIterator_getKey(iter); MovieCacheItem *item = BLI_ghashIterator_getValue(iter); + int remove = 0; BLI_ghashIterator_step(iter); - if (!item->ibuf) + remove = !item->ibuf; + + if (remove) + PRINT("%s: cache '%s' remove item %p without buffer\n", __func__, cache->name, item); + + if (remove) BLI_ghash_remove(cache->hash, key, moviecache_keyfree, moviecache_valfree); } @@ -137,12 +172,21 @@ static int compare_int(const void *av, const void *bv) static void IMB_moviecache_destructor(void *p) { MovieCacheItem *item = (MovieCacheItem *) p; + MovieCache *cache = item->cache_owner; + + PRINT("%s: cache '%s' destroy item %p buffer %p\n", __func__, cache->name, item, item->ibuf); if (item && item->ibuf) { IMB_freeImBuf(item->ibuf); item->ibuf = NULL; item->c_handle = NULL; + + /* force cached segments to be updated */ + if (cache->points) { + MEM_freeN(cache->points); + cache->points = NULL; + } } } @@ -187,9 +231,30 @@ static size_t get_item_size(void *p) return size; } +static int get_item_priority(void *item_v, int default_priority) +{ + MovieCacheItem *item = (MovieCacheItem *) item_v; + MovieCache *cache = item->cache_owner; + int priority; + + if (!cache->getitempriorityfp) { + PRINT("%s: cache '%s' item %p use default priority %d\n", __func__, cache-> name, item, default_priority); + + return default_priority; + } + + priority = cache->getitempriorityfp(cache->last_userkey, item->priority_data); + + PRINT("%s: cache '%s' item %p priority %d\n", __func__, cache-> name, item, priority); + + return priority; +} + void IMB_moviecache_init(void) { limitor = new_MEM_CacheLimiter(IMB_moviecache_destructor, get_item_size); + + MEM_CacheLimiter_ItemPriority_Func_set(limitor, get_item_priority); } void IMB_moviecache_destruct(void) @@ -198,12 +263,16 @@ void IMB_moviecache_destruct(void) delete_MEM_CacheLimiter(limitor); } -MovieCache *IMB_moviecache_create(int keysize, GHashHashFP hashfp, GHashCmpFP cmpfp, - MovieCacheGetKeyDataFP getdatafp) +MovieCache *IMB_moviecache_create(const char *name, int keysize, GHashHashFP hashfp, GHashCmpFP cmpfp) { MovieCache *cache; + PRINT("%s: cache '%s' create\n", __func__, name); + cache = MEM_callocN(sizeof(MovieCache), "MovieCache"); + + BLI_strncpy(cache->name, name, sizeof(cache->name)); + cache->keys_pool = BLI_mempool_create(sizeof(MovieCacheKey), 64, 64, 0); cache->items_pool = BLI_mempool_create(sizeof(MovieCacheItem), 64, 64, 0); cache->userkeys_pool = BLI_mempool_create(keysize, 64, 64, 0); @@ -212,12 +281,27 @@ MovieCache *IMB_moviecache_create(int keysize, GHashHashFP hashfp, GHashCmpFP cm cache->keysize = keysize; cache->hashfp = hashfp; cache->cmpfp = cmpfp; - cache->getdatafp = getdatafp; cache->proxy = -1; return cache; } +void IMB_moviecache_set_getdata_callback(MovieCache *cache, MovieCacheGetKeyDataFP getdatafp) +{ + cache->getdatafp = getdatafp; +} + +void IMB_moviecache_set_priority_callback(struct MovieCache *cache, MovieCacheGetPriorityDataFP getprioritydatafp, + MovieCacheGetItemPriorityFP getitempriorityfp, + MovieCachePriorityDeleterFP prioritydeleterfp) +{ + cache->last_userkey = MEM_mallocN(cache->keysize, "movie cache last user key"); + + cache->getprioritydatafp = getprioritydatafp; + cache->getitempriorityfp = getitempriorityfp; + cache->prioritydeleterfp = prioritydeleterfp; +} + void IMB_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf) { MovieCacheKey *key; @@ -234,16 +318,27 @@ void IMB_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf) memcpy(key->userkey, userkey, cache->keysize); item = BLI_mempool_alloc(cache->items_pool); + + PRINT("%s: cache '%s' put %p, item %p\n", __func__, cache-> name, ibuf, item); + item->ibuf = ibuf; item->cache_owner = cache; - item->last_access = cache->curtime++; item->c_handle = NULL; + item->priority_data = NULL; + + if (cache->getprioritydatafp) { + item->priority_data = cache->getprioritydatafp(userkey); + } BLI_ghash_remove(cache->hash, key, moviecache_keyfree, moviecache_valfree); BLI_ghash_insert(cache->hash, key, item); item->c_handle = MEM_CacheLimiter_insert(limitor, item); + if (cache->last_userkey) { + memcpy(cache->last_userkey, userkey, cache->keysize); + } + MEM_CacheLimiter_ref(item->c_handle); MEM_CacheLimiter_enforce_limits(limitor); MEM_CacheLimiter_unref(item->c_handle); @@ -267,8 +362,6 @@ ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey) item = (MovieCacheItem *)BLI_ghash_lookup(cache->hash, &key); if (item) { - item->last_access = cache->curtime++; - if (item->ibuf) { MEM_CacheLimiter_touch(item->c_handle); IMB_refImBuf(item->ibuf); @@ -282,6 +375,8 @@ ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey) void IMB_moviecache_free(MovieCache *cache) { + PRINT("%s: create '%s' free\n", __func__, cache->name); + BLI_ghash_free(cache->hash, moviecache_keyfree, moviecache_valfree); BLI_mempool_destroy(cache->keys_pool); @@ -291,6 +386,9 @@ void IMB_moviecache_free(MovieCache *cache) if (cache->points) MEM_freeN(cache->points); + if (cache->last_userkey) + MEM_freeN(cache->last_userkey); + MEM_freeN(cache); }