diff --git a/intern/memutil/MEM_CacheLimiter.h b/intern/memutil/MEM_CacheLimiter.h index 0b657104a05..498e6d29381 100644 --- a/intern/memutil/MEM_CacheLimiter.h +++ b/intern/memutil/MEM_CacheLimiter.h @@ -126,6 +126,10 @@ class MEM_CacheLimiter { public: typedef typename std::list *, MEM_Allocator *> >::iterator iterator; + typedef intptr_t (*MEM_CacheLimiter_DataSize_Func) (void *data); + MEM_CacheLimiter(MEM_CacheLimiter_DataSize_Func getDataSize_) + : getDataSize(getDataSize_) { + } ~MEM_CacheLimiter() { for (iterator it = queue.begin(); it != queue.end(); it++) { delete *it; @@ -144,17 +148,36 @@ public: } void enforce_limits() { intptr_t max = MEM_CacheLimiter_get_maximum(); - intptr_t mem_in_use= MEM_get_memory_in_use(); - intptr_t mmap_in_use= MEM_get_mapped_memory_in_use(); + intptr_t mem_in_use, cur_size; if (max == 0) { return; } + + if(getDataSize) { + mem_in_use = total_size(); + } else { + mem_in_use = MEM_get_memory_in_use(); + } + for (iterator it = queue.begin(); - it != queue.end() && mem_in_use + mmap_in_use > max;) { + it != queue.end() && mem_in_use > max;) { iterator jt = it; ++it; + + if(getDataSize) { + cur_size= getDataSize((*jt)->get()->get_data()); + } else { + 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(); + } } } void touch(MEM_CacheLimiterHandle * handle) { @@ -165,8 +188,17 @@ public: handle->me = it; } private: + intptr_t total_size() { + intptr_t size = 0; + for (iterator it = queue.begin(); it != queue.end(); it++) { + size+= getDataSize((*it)->get()->get_data()); + } + return size; + } + std::list*, MEM_Allocator *> > queue; + MEM_CacheLimiter_DataSize_Func getDataSize; }; #endif // MEM_CACHELIMITER_H diff --git a/intern/memutil/MEM_CacheLimiterC-Api.h b/intern/memutil/MEM_CacheLimiterC-Api.h index 4f267f7ddf0..b3e67c9434d 100644 --- a/intern/memutil/MEM_CacheLimiterC-Api.h +++ b/intern/memutil/MEM_CacheLimiterC-Api.h @@ -42,6 +42,9 @@ typedef struct MEM_CacheLimiterHandle_s MEM_CacheLimiterHandleC; /* function used to remove data from memory */ typedef void(*MEM_CacheLimiter_Destruct_Func)(void*); +/* function used to measure stored data element size */ +typedef intptr_t(*MEM_CacheLimiter_DataSize_Func) (void*); + #ifndef MEM_CACHELIMITER_H extern void MEM_CacheLimiter_set_maximum(int m); extern int MEM_CacheLimiter_get_maximum(void); @@ -55,7 +58,8 @@ extern int MEM_CacheLimiter_get_maximum(void); */ extern MEM_CacheLimiterC * new_MEM_CacheLimiter( - MEM_CacheLimiter_Destruct_Func data_destructor); + MEM_CacheLimiter_Destruct_Func data_destructor, + MEM_CacheLimiter_DataSize_Func data_size); /** * Delete MEM_CacheLimiter diff --git a/intern/memutil/intern/MEM_CacheLimiterC-Api.cpp b/intern/memutil/intern/MEM_CacheLimiterC-Api.cpp index 1bc011a5be0..cc8d2497f37 100644 --- a/intern/memutil/intern/MEM_CacheLimiterC-Api.cpp +++ b/intern/memutil/intern/MEM_CacheLimiterC-Api.cpp @@ -54,8 +54,8 @@ typedef std::listibuf); + if (item->ibuf) { + MEM_CacheLimiter_unmanage(item->c_handle); + IMB_freeImBuf(item->ibuf); + } BLI_mempool_free(item->cache_owner->items_pool, item); } -static MovieCacheKey *get_lru_key(MovieCache *cache) +static void check_unused_keys(MovieCache *cache) { GHashIterator *iter; - MovieCacheKey *lru_key= NULL; - MovieCacheItem *lru_item= NULL; iter= BLI_ghashIterator_new(cache->hash); while(!BLI_ghashIterator_isDone(iter)) { MovieCacheKey *key= BLI_ghashIterator_getKey(iter); MovieCacheItem *item= BLI_ghashIterator_getValue(iter); - if(lru_item==NULL || item->last_accesslast_access) { - lru_key= key; - lru_item= item; - } - BLI_ghashIterator_step(iter); + + if(!item->ibuf) + BLI_ghash_remove(cache->hash, key, moviecache_keyfree, moviecache_valfree); } BLI_ghashIterator_free(iter); - - return lru_key; } static int compare_int(const void *av, const void *bv) @@ -131,6 +132,70 @@ static int compare_int(const void *av, const void *bv) return *a-*b; } +static void IMB_moviecache_destructor(void *p) +{ + MovieCacheItem *item= (MovieCacheItem *) p; + + if (item && item->ibuf) { + IMB_freeImBuf(item->ibuf); + + item->ibuf= NULL; + item->c_handle= NULL; + } +} + +/* approximate size of ImBuf in memory */ +static intptr_t IMB_get_size_in_memory(ImBuf *ibuf) +{ + int a; + intptr_t size= 0, channel_size= 0; + + size+= sizeof(ImBuf); + + if(ibuf->rect) + channel_size+= sizeof(char); + + if(ibuf->rect_float) + channel_size= sizeof(float); + + size+= channel_size*ibuf->x*ibuf->y*ibuf->channels; + + if(ibuf->miptot) { + for(a= 0; amiptot; a++) { + if(ibuf->mipmap[a]) + size+= IMB_get_size_in_memory(ibuf->mipmap[a]); + } + } + + if(ibuf->tiles) { + size+= sizeof(unsigned int)*ibuf->ytiles*ibuf->xtiles; + } + + return size; +} + +static intptr_t get_item_size (void *p) +{ + intptr_t size= sizeof(MovieCacheItem); + MovieCacheItem *item= (MovieCacheItem *) p; + + if(item->ibuf) + size+= IMB_get_size_in_memory(item->ibuf); + + return size; +} + +void BKE_moviecache_init(void) +{ + limitor= new_MEM_CacheLimiter(IMB_moviecache_destructor, get_item_size); +} + +void BKE_moviecache_destruct(void) +{ + if(limitor) + delete_MEM_CacheLimiter(limitor); +} + struct MovieCache *BKE_moviecache_create(int keysize, GHashHashFP hashfp, GHashCmpFP cmpfp, MovieCacheGetKeyDataFP getdatafp) { @@ -155,11 +220,8 @@ void BKE_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf) MovieCacheKey *key; MovieCacheItem *item; - /* TODO: implement better limiters */ - if(BLI_ghash_size(cache->hash) > 250) { - MovieCacheKey *lru_key= get_lru_key(cache); - BLI_ghash_remove(cache->hash, lru_key, moviecache_keyfree, moviecache_valfree); - } + if(!limitor) + BKE_moviecache_init(); IMB_refImBuf(ibuf); @@ -172,10 +234,20 @@ void BKE_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf) item->ibuf= ibuf; item->cache_owner= cache; item->last_access= cache->curtime++; + item->c_handle= NULL; 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); + + MEM_CacheLimiter_ref(item->c_handle); + MEM_CacheLimiter_enforce_limits(limitor); + MEM_CacheLimiter_unref(item->c_handle); + + /* cache limiter can't remove unused keys which points to destoryed values */ + check_unused_keys(cache); + if(cache->points) { MEM_freeN(cache->points); cache->points= NULL; @@ -195,6 +267,7 @@ ImBuf* BKE_moviecache_get(MovieCache *cache, void *userkey) item->last_access= cache->curtime++; if(item->ibuf) { + MEM_CacheLimiter_touch(item->c_handle); IMB_refImBuf(item->ibuf); return item->ibuf; } @@ -239,11 +312,14 @@ void BKE_moviecache_get_cache_segments(MovieCache *cache, int *totseg_r, int **p a= 0; while(!BLI_ghashIterator_isDone(iter)) { MovieCacheKey *key= BLI_ghashIterator_getKey(iter); + MovieCacheItem *item= BLI_ghashIterator_getValue(iter); int framenr; - cache->getdatafp(key->userkey, &framenr); + if(item->ibuf) { + cache->getdatafp(key->userkey, &framenr); - frames[a++]= framenr; + frames[a++]= framenr; + } BLI_ghashIterator_step(iter); } diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c index 00f88fb6202..0077c52b9f8 100644 --- a/source/blender/blenkernel/intern/seqcache.c +++ b/source/blender/blenkernel/intern/seqcache.c @@ -148,7 +148,7 @@ static void IMB_seq_cache_destructor(void * p) void seq_stripelem_cache_init(void) { hash = BLI_ghash_new(HashHash, HashCmp, "seq stripelem cache hash"); - limitor = new_MEM_CacheLimiter( IMB_seq_cache_destructor ); + limitor = new_MEM_CacheLimiter( IMB_seq_cache_destructor, NULL ); entrypool = BLI_mempool_create(sizeof(seqCacheEntry), 64, 64, 0); keypool = BLI_mempool_create(sizeof(seqCacheKey), 64, 64, 0); diff --git a/source/blender/imbuf/intern/allocimbuf.c b/source/blender/imbuf/intern/allocimbuf.c index 59772771f3b..f71235b0a9d 100644 --- a/source/blender/imbuf/intern/allocimbuf.c +++ b/source/blender/imbuf/intern/allocimbuf.c @@ -455,7 +455,7 @@ static MEM_CacheLimiterC **get_imbuf_cache_limiter(void) static MEM_CacheLimiterC *c = NULL; if(!c) - c = new_MEM_CacheLimiter(imbuf_cache_destructor); + c = new_MEM_CacheLimiter(imbuf_cache_destructor, NULL); return &c; }