tornavis/source/blender/imbuf/intern/moviecache.c

587 lines
14 KiB
C
Raw Normal View History

/*
* ***** 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.
*
* The Original Code is Copyright (C) 2011 Blender Foundation.
* All rights reserved.
*
* Contributor(s): Blender Foundation,
* Sergey Sharybin,
* Peter Schlaile
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/imbuf/intern/moviecache.c
* \ingroup bke
*/
#undef DEBUG_MESSAGES
#include <stdlib.h> /* for qsort */
#include <memory.h>
#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"
#include "BLI_threads.h"
#include "IMB_moviecache.h"
#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
2012-05-04 01:47:39 +02:00
static MEM_CacheLimiterC *limitor = NULL;
static pthread_mutex_t limitor_lock = BLI_MUTEX_INITIALIZER;
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;
void *last_userkey;
2012-05-16 11:26:37 +02:00
int totseg, *points, proxy, render_flags; /* for visual statistics optimization */
int pad;
} MovieCache;
typedef struct MovieCacheKey {
MovieCache *cache_owner;
void *userkey;
} MovieCacheKey;
typedef struct MovieCacheItem {
MovieCache *cache_owner;
ImBuf *ibuf;
2012-05-16 11:26:37 +02:00
MEM_CacheLimiterHandleC *c_handle;
void *priority_data;
} MovieCacheItem;
static unsigned int moviecache_hashhash(const void *keyv)
{
2012-05-16 11:26:37 +02:00
MovieCacheKey *key = (MovieCacheKey *)keyv;
return key->cache_owner->hashfp(key->userkey);
}
static int moviecache_hashcmp(const void *av, const void *bv)
{
2012-05-16 11:26:37 +02:00
const MovieCacheKey *a = (MovieCacheKey *)av;
const MovieCacheKey *b = (MovieCacheKey *)bv;
return a->cache_owner->cmpfp(a->userkey, b->userkey);
}
static void moviecache_keyfree(void *val)
{
2012-05-16 11:26:37 +02:00
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)
{
2012-05-16 11:26:37 +02:00
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);
}
static void check_unused_keys(MovieCache *cache)
{
GHashIterator *iter;
2012-05-04 01:47:39 +02:00
iter = BLI_ghashIterator_new(cache->hash);
while (!BLI_ghashIterator_done(iter)) {
2012-03-26 18:04:10 +02:00
MovieCacheKey *key = BLI_ghashIterator_getKey(iter);
MovieCacheItem *item = BLI_ghashIterator_getValue(iter);
int remove = 0;
BLI_ghashIterator_step(iter);
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);
}
BLI_ghashIterator_free(iter);
}
static int compare_int(const void *av, const void *bv)
{
2012-03-26 18:04:10 +02:00
const int *a = (int *)av;
const int *b = (int *)bv;
return *a - *b;
}
static void IMB_moviecache_destructor(void *p)
{
MovieCacheItem *item = (MovieCacheItem *)p;
if (item && item->ibuf) {
MovieCache *cache = item->cache_owner;
PRINT("%s: cache '%s' destroy item %p buffer %p\n", __func__, cache->name, item, item->ibuf);
IMB_freeImBuf(item->ibuf);
2012-03-26 18:04:10 +02:00
item->ibuf = NULL;
item->c_handle = NULL;
/* force cached segments to be updated */
if (cache->points) {
MEM_freeN(cache->points);
cache->points = NULL;
}
}
}
/* approximate size of ImBuf in memory */
static size_t IMB_get_size_in_memory(ImBuf *ibuf)
{
int a;
2012-03-26 18:04:10 +02:00
size_t size = 0, channel_size = 0;
2012-03-26 18:04:10 +02:00
size += sizeof(ImBuf);
if (ibuf->rect)
2012-03-26 18:04:10 +02:00
channel_size += sizeof(char);
if (ibuf->rect_float)
2012-03-26 18:04:10 +02:00
channel_size += sizeof(float);
2012-05-16 11:26:37 +02:00
size += channel_size * ibuf->x * ibuf->y * ibuf->channels;
if (ibuf->miptot) {
2012-03-26 18:04:10 +02:00
for (a = 0; a < ibuf->miptot; a++) {
if (ibuf->mipmap[a])
2012-03-26 18:04:10 +02:00
size += IMB_get_size_in_memory(ibuf->mipmap[a]);
}
}
if (ibuf->tiles) {
2012-05-16 11:26:37 +02:00
size += sizeof(unsigned int) * ibuf->ytiles * ibuf->xtiles;
}
return size;
}
2012-05-16 11:26:37 +02:00
static size_t get_item_size(void *p)
{
2012-03-26 18:04:10 +02:00
size_t size = sizeof(MovieCacheItem);
MovieCacheItem *item = (MovieCacheItem *) p;
if (item->ibuf)
2012-03-26 18:04:10 +02:00
size += IMB_get_size_in_memory(item->ibuf);
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)
{
2012-03-26 18:04:10 +02:00
limitor = new_MEM_CacheLimiter(IMB_moviecache_destructor, get_item_size);
MEM_CacheLimiter_ItemPriority_Func_set(limitor, get_item_priority);
}
void IMB_moviecache_destruct(void)
{
if (limitor)
delete_MEM_CacheLimiter(limitor);
}
MovieCache *IMB_moviecache_create(const char *name, int keysize, GHashHashFP hashfp, GHashCmpFP cmpfp)
2012-05-18 01:21:11 +02:00
{
MovieCache *cache;
PRINT("%s: cache '%s' create\n", __func__, name);
2012-03-26 18:04:10 +02:00
cache = MEM_callocN(sizeof(MovieCache), "MovieCache");
BLI_strncpy(cache->name, name, sizeof(cache->name));
2012-03-26 18:04:10 +02:00
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);
cache->hash = BLI_ghash_new(moviecache_hashhash, moviecache_hashcmp, "MovieClip ImBuf cache hash");
2012-03-26 18:04:10 +02:00
cache->keysize = keysize;
cache->hashfp = hashfp;
cache->cmpfp = cmpfp;
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;
}
Prefetching for movie clips This commit basically implements frames prefetching for movie clip datablock. Number of frames to be prefetched is controlled in User Preferences, System tab, Prefetch Frames option. Currently prefetching is destructive-less for movie cache, meaning mo frames will be removed from the cache when while prefetching. This is because it's half of simplier to implement, but it also makes sense from tracking point of view -- we could want to playback in both directions and removing frames from behind time cursor is not always a good idea. Anyway, smarter prefetching strategy could be developed later. Some implementation notes: - Added MEM_CacheLimiter_get_memory_in_use function to get memory usage of specified memory limiter. - Fixed prototype of MEM_CacheLimiter_get_maximum which was simply wrong (used wrong data type for output). - Added some utility functions to movie clip and movie cache for direct cache interaction and obtaining cache statistics. - Prefetching is implemented using general jobs system. which is invoking from clip draw function. - Prefetcing will stop as soon other job or playback starts. This is done from performance point of view. Jobs will likely require lots of CPU power and better to provide whole CPU to it. Playback is a bit more complicated case. For jpeg sequence playback prefetching while paying back is nice. But trying to prefetch heavy exr images and doing color space conversion slows down both playback and prefetching. TODO: - Think of better policy of dealing with already cached frames (like when cached frames from other clips prevents frames from current clip to be prefetched) - Currently a bit funky redraw notification happens from prefetch job. Perhaps own ND_ is better to have here. - Hiding clip while prefetch is active in theory shall stop prefetching job. - Having multiple clips opened on file load will prefetch frames for only one of them.
2013-03-20 18:03:20 +01:00
static void do_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf, int need_lock)
{
MovieCacheKey *key;
MovieCacheItem *item;
if (!limitor)
IMB_moviecache_init();
IMB_refImBuf(ibuf);
2012-03-26 18:04:10 +02:00
key = BLI_mempool_alloc(cache->keys_pool);
key->cache_owner = cache;
key->userkey = BLI_mempool_alloc(cache->userkeys_pool);
memcpy(key->userkey, userkey, cache->keysize);
2012-03-26 18:04:10 +02:00
item = BLI_mempool_alloc(cache->items_pool);
PRINT("%s: cache '%s' put %p, item %p\n", __func__, cache-> name, ibuf, item);
2012-03-26 18:04:10 +02:00
item->ibuf = ibuf;
item->cache_owner = cache;
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);
if (cache->last_userkey) {
memcpy(cache->last_userkey, userkey, cache->keysize);
}
Prefetching for movie clips This commit basically implements frames prefetching for movie clip datablock. Number of frames to be prefetched is controlled in User Preferences, System tab, Prefetch Frames option. Currently prefetching is destructive-less for movie cache, meaning mo frames will be removed from the cache when while prefetching. This is because it's half of simplier to implement, but it also makes sense from tracking point of view -- we could want to playback in both directions and removing frames from behind time cursor is not always a good idea. Anyway, smarter prefetching strategy could be developed later. Some implementation notes: - Added MEM_CacheLimiter_get_memory_in_use function to get memory usage of specified memory limiter. - Fixed prototype of MEM_CacheLimiter_get_maximum which was simply wrong (used wrong data type for output). - Added some utility functions to movie clip and movie cache for direct cache interaction and obtaining cache statistics. - Prefetching is implemented using general jobs system. which is invoking from clip draw function. - Prefetcing will stop as soon other job or playback starts. This is done from performance point of view. Jobs will likely require lots of CPU power and better to provide whole CPU to it. Playback is a bit more complicated case. For jpeg sequence playback prefetching while paying back is nice. But trying to prefetch heavy exr images and doing color space conversion slows down both playback and prefetching. TODO: - Think of better policy of dealing with already cached frames (like when cached frames from other clips prevents frames from current clip to be prefetched) - Currently a bit funky redraw notification happens from prefetch job. Perhaps own ND_ is better to have here. - Hiding clip while prefetch is active in theory shall stop prefetching job. - Having multiple clips opened on file load will prefetch frames for only one of them.
2013-03-20 18:03:20 +01:00
if (need_lock)
BLI_mutex_lock(&limitor_lock);
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);
Prefetching for movie clips This commit basically implements frames prefetching for movie clip datablock. Number of frames to be prefetched is controlled in User Preferences, System tab, Prefetch Frames option. Currently prefetching is destructive-less for movie cache, meaning mo frames will be removed from the cache when while prefetching. This is because it's half of simplier to implement, but it also makes sense from tracking point of view -- we could want to playback in both directions and removing frames from behind time cursor is not always a good idea. Anyway, smarter prefetching strategy could be developed later. Some implementation notes: - Added MEM_CacheLimiter_get_memory_in_use function to get memory usage of specified memory limiter. - Fixed prototype of MEM_CacheLimiter_get_maximum which was simply wrong (used wrong data type for output). - Added some utility functions to movie clip and movie cache for direct cache interaction and obtaining cache statistics. - Prefetching is implemented using general jobs system. which is invoking from clip draw function. - Prefetcing will stop as soon other job or playback starts. This is done from performance point of view. Jobs will likely require lots of CPU power and better to provide whole CPU to it. Playback is a bit more complicated case. For jpeg sequence playback prefetching while paying back is nice. But trying to prefetch heavy exr images and doing color space conversion slows down both playback and prefetching. TODO: - Think of better policy of dealing with already cached frames (like when cached frames from other clips prevents frames from current clip to be prefetched) - Currently a bit funky redraw notification happens from prefetch job. Perhaps own ND_ is better to have here. - Hiding clip while prefetch is active in theory shall stop prefetching job. - Having multiple clips opened on file load will prefetch frames for only one of them.
2013-03-20 18:03:20 +01:00
if (need_lock)
BLI_mutex_unlock(&limitor_lock);
/* cache limiter can't remove unused keys which points to destoryed values */
check_unused_keys(cache);
if (cache->points) {
MEM_freeN(cache->points);
2012-05-04 01:47:39 +02:00
cache->points = NULL;
}
}
Prefetching for movie clips This commit basically implements frames prefetching for movie clip datablock. Number of frames to be prefetched is controlled in User Preferences, System tab, Prefetch Frames option. Currently prefetching is destructive-less for movie cache, meaning mo frames will be removed from the cache when while prefetching. This is because it's half of simplier to implement, but it also makes sense from tracking point of view -- we could want to playback in both directions and removing frames from behind time cursor is not always a good idea. Anyway, smarter prefetching strategy could be developed later. Some implementation notes: - Added MEM_CacheLimiter_get_memory_in_use function to get memory usage of specified memory limiter. - Fixed prototype of MEM_CacheLimiter_get_maximum which was simply wrong (used wrong data type for output). - Added some utility functions to movie clip and movie cache for direct cache interaction and obtaining cache statistics. - Prefetching is implemented using general jobs system. which is invoking from clip draw function. - Prefetcing will stop as soon other job or playback starts. This is done from performance point of view. Jobs will likely require lots of CPU power and better to provide whole CPU to it. Playback is a bit more complicated case. For jpeg sequence playback prefetching while paying back is nice. But trying to prefetch heavy exr images and doing color space conversion slows down both playback and prefetching. TODO: - Think of better policy of dealing with already cached frames (like when cached frames from other clips prevents frames from current clip to be prefetched) - Currently a bit funky redraw notification happens from prefetch job. Perhaps own ND_ is better to have here. - Hiding clip while prefetch is active in theory shall stop prefetching job. - Having multiple clips opened on file load will prefetch frames for only one of them.
2013-03-20 18:03:20 +01:00
void IMB_moviecache_put(MovieCache *cache, void *userkey, ImBuf *ibuf)
{
do_moviecache_put(cache, userkey, ibuf, TRUE);
}
int IMB_moviecache_put_if_possible(MovieCache *cache, void *userkey, ImBuf *ibuf)
{
size_t mem_in_use, mem_limit, elem_size;
int result = FALSE;
elem_size = IMB_get_size_in_memory(ibuf);
mem_limit = MEM_CacheLimiter_get_maximum();
BLI_mutex_lock(&limitor_lock);
mem_in_use = MEM_CacheLimiter_get_memory_in_use(limitor);
if (mem_in_use + elem_size <= mem_limit) {
do_moviecache_put(cache, userkey, ibuf, FALSE);
result = TRUE;
}
BLI_mutex_unlock(&limitor_lock);
return result;
}
2012-05-16 11:26:37 +02:00
ImBuf *IMB_moviecache_get(MovieCache *cache, void *userkey)
{
MovieCacheKey key;
MovieCacheItem *item;
2012-03-26 18:04:10 +02:00
key.cache_owner = cache;
key.userkey = userkey;
2012-05-16 11:26:37 +02:00
item = (MovieCacheItem *)BLI_ghash_lookup(cache->hash, &key);
if (item) {
if (item->ibuf) {
BLI_mutex_lock(&limitor_lock);
MEM_CacheLimiter_touch(item->c_handle);
BLI_mutex_unlock(&limitor_lock);
IMB_refImBuf(item->ibuf);
return item->ibuf;
}
}
return NULL;
}
Prefetching for movie clips This commit basically implements frames prefetching for movie clip datablock. Number of frames to be prefetched is controlled in User Preferences, System tab, Prefetch Frames option. Currently prefetching is destructive-less for movie cache, meaning mo frames will be removed from the cache when while prefetching. This is because it's half of simplier to implement, but it also makes sense from tracking point of view -- we could want to playback in both directions and removing frames from behind time cursor is not always a good idea. Anyway, smarter prefetching strategy could be developed later. Some implementation notes: - Added MEM_CacheLimiter_get_memory_in_use function to get memory usage of specified memory limiter. - Fixed prototype of MEM_CacheLimiter_get_maximum which was simply wrong (used wrong data type for output). - Added some utility functions to movie clip and movie cache for direct cache interaction and obtaining cache statistics. - Prefetching is implemented using general jobs system. which is invoking from clip draw function. - Prefetcing will stop as soon other job or playback starts. This is done from performance point of view. Jobs will likely require lots of CPU power and better to provide whole CPU to it. Playback is a bit more complicated case. For jpeg sequence playback prefetching while paying back is nice. But trying to prefetch heavy exr images and doing color space conversion slows down both playback and prefetching. TODO: - Think of better policy of dealing with already cached frames (like when cached frames from other clips prevents frames from current clip to be prefetched) - Currently a bit funky redraw notification happens from prefetch job. Perhaps own ND_ is better to have here. - Hiding clip while prefetch is active in theory shall stop prefetching job. - Having multiple clips opened on file load will prefetch frames for only one of them.
2013-03-20 18:03:20 +01:00
int IMB_moviecache_has_frame(MovieCache *cache, void *userkey)
{
MovieCacheKey key;
MovieCacheItem *item;
key.cache_owner = cache;
key.userkey = userkey;
item = (MovieCacheItem *)BLI_ghash_lookup(cache->hash, &key);
return item != NULL;
}
void IMB_moviecache_free(MovieCache *cache)
{
PRINT("%s: cache '%s' free\n", __func__, cache->name);
BLI_ghash_free(cache->hash, moviecache_keyfree, moviecache_valfree);
BLI_mempool_destroy(cache->keys_pool);
BLI_mempool_destroy(cache->items_pool);
BLI_mempool_destroy(cache->userkeys_pool);
if (cache->points)
MEM_freeN(cache->points);
if (cache->last_userkey)
MEM_freeN(cache->last_userkey);
MEM_freeN(cache);
}
Image cache rewrite to using generic movie cache Summary: Behaves very much the same as cache for Movie Clip datablock: - Image now have `MovieCache *cache` field which replaced legacy `ListBase ibufs`. This allows image datablock to easily keep of image buffers which are owned by itself. This field isn't saved to the file and getting restored on undo steps. However, cache limit is global for movies, sequences and image datablocks now. So overall cached image buffers size will not go above cache limit size in user preferences. - Image buffers which are marked as BITMAPDIRTY will never be freed from the cache. - Added utility function to iterate over image buffers saved in movie cache. - Movie cache cleanup check callback now have ImBuf argument which can be used in a condition of cleanup. - Added some utility functions which replaces legacy ibufs iterations with image cache iteration which happens from inside a lock. - Fixed `image_mem_size()` which was only counting one of the buffers if both float and byte buffer present. Additional notes: - `BKE_image_get_first_ibuf()` is rather stupid, but direct access to ibufs->first was also the same stupid idea. Would consider avoid this function is another project. - There are some places which doesn't look threadsafe, but they already were not so much threadsafe anyway before. So think not a big deal with solving this later. Finally solves infinite memory usage by image sequences! :) Reviewers: brecht, campbellbarton Reviewed By: brecht CC: sebastian_k Differential Revision: http://developer.blender.org/D95
2013-12-13 11:22:08 +01:00
void IMB_moviecache_cleanup(MovieCache *cache, bool (cleanup_check_cb) (ImBuf *ibuf, void *userkey, void *userdata), void *userdata)
{
GHashIterator *iter;
iter = BLI_ghashIterator_new(cache->hash);
while (!BLI_ghashIterator_done(iter)) {
MovieCacheKey *key = BLI_ghashIterator_getKey(iter);
Image cache rewrite to using generic movie cache Summary: Behaves very much the same as cache for Movie Clip datablock: - Image now have `MovieCache *cache` field which replaced legacy `ListBase ibufs`. This allows image datablock to easily keep of image buffers which are owned by itself. This field isn't saved to the file and getting restored on undo steps. However, cache limit is global for movies, sequences and image datablocks now. So overall cached image buffers size will not go above cache limit size in user preferences. - Image buffers which are marked as BITMAPDIRTY will never be freed from the cache. - Added utility function to iterate over image buffers saved in movie cache. - Movie cache cleanup check callback now have ImBuf argument which can be used in a condition of cleanup. - Added some utility functions which replaces legacy ibufs iterations with image cache iteration which happens from inside a lock. - Fixed `image_mem_size()` which was only counting one of the buffers if both float and byte buffer present. Additional notes: - `BKE_image_get_first_ibuf()` is rather stupid, but direct access to ibufs->first was also the same stupid idea. Would consider avoid this function is another project. - There are some places which doesn't look threadsafe, but they already were not so much threadsafe anyway before. So think not a big deal with solving this later. Finally solves infinite memory usage by image sequences! :) Reviewers: brecht, campbellbarton Reviewed By: brecht CC: sebastian_k Differential Revision: http://developer.blender.org/D95
2013-12-13 11:22:08 +01:00
MovieCacheItem *item = BLI_ghashIterator_getValue(iter);
BLI_ghashIterator_step(iter);
if (item->ibuf == NULL || cleanup_check_cb(item->ibuf, key->userkey, userdata)) {
PRINT("%s: cache '%s' remove item %p\n", __func__, cache->name, item);
BLI_ghash_remove(cache->hash, key, moviecache_keyfree, moviecache_valfree);
}
}
BLI_ghashIterator_free(iter);
}
/* get segments of cached frames. useful for debugging cache policies */
void IMB_moviecache_get_cache_segments(MovieCache *cache, int proxy, int render_flags, int *totseg_r, int **points_r)
{
2012-03-26 18:04:10 +02:00
*totseg_r = 0;
*points_r = NULL;
if (!cache->getdatafp)
return;
2012-03-26 18:04:10 +02:00
if (cache->proxy != proxy || cache->render_flags != render_flags) {
if (cache->points)
MEM_freeN(cache->points);
2012-03-26 18:04:10 +02:00
cache->points = NULL;
}
if (cache->points) {
2012-03-26 18:04:10 +02:00
*totseg_r = cache->totseg;
*points_r = cache->points;
}
else {
2012-03-26 18:04:10 +02:00
int totframe = BLI_ghash_size(cache->hash);
2012-05-16 11:26:37 +02:00
int *frames = MEM_callocN(totframe * sizeof(int), "movieclip cache frames");
2012-03-26 18:04:10 +02:00
int a, totseg = 0;
GHashIterator *iter;
2012-03-26 18:04:10 +02:00
iter = BLI_ghashIterator_new(cache->hash);
2012-05-04 01:47:39 +02:00
a = 0;
while (!BLI_ghashIterator_done(iter)) {
2012-03-26 18:04:10 +02:00
MovieCacheKey *key = BLI_ghashIterator_getKey(iter);
MovieCacheItem *item = BLI_ghashIterator_getValue(iter);
int framenr, curproxy, curflags;
if (item->ibuf) {
cache->getdatafp(key->userkey, &framenr, &curproxy, &curflags);
2012-03-26 18:04:10 +02:00
if (curproxy == proxy && curflags == render_flags)
frames[a++] = framenr;
}
BLI_ghashIterator_step(iter);
}
BLI_ghashIterator_free(iter);
qsort(frames, totframe, sizeof(int), compare_int);
/* count */
2012-03-26 18:04:10 +02:00
for (a = 0; a < totframe; a++) {
if (a && frames[a] - frames[a - 1] != 1)
totseg++;
2012-03-26 18:04:10 +02:00
if (a == totframe - 1)
totseg++;
}
if (totseg) {
int b, *points;
2012-05-16 11:26:37 +02:00
points = MEM_callocN(2 * sizeof(int) * totseg, "movieclip cache segments");
/* fill */
2012-03-26 18:04:10 +02:00
for (a = 0, b = 0; a < totframe; a++) {
if (a == 0)
points[b++] = frames[a];
2012-03-26 18:04:10 +02:00
if (a && frames[a] - frames[a - 1] != 1) {
points[b++] = frames[a - 1];
points[b++] = frames[a];
}
2012-03-26 18:04:10 +02:00
if (a == totframe - 1)
points[b++] = frames[a];
}
2012-03-26 18:04:10 +02:00
*totseg_r = totseg;
*points_r = points;
2012-03-26 18:04:10 +02:00
cache->totseg = totseg;
cache->points = points;
cache->proxy = proxy;
cache->render_flags = render_flags;
}
MEM_freeN(frames);
}
}
Image cache rewrite to using generic movie cache Summary: Behaves very much the same as cache for Movie Clip datablock: - Image now have `MovieCache *cache` field which replaced legacy `ListBase ibufs`. This allows image datablock to easily keep of image buffers which are owned by itself. This field isn't saved to the file and getting restored on undo steps. However, cache limit is global for movies, sequences and image datablocks now. So overall cached image buffers size will not go above cache limit size in user preferences. - Image buffers which are marked as BITMAPDIRTY will never be freed from the cache. - Added utility function to iterate over image buffers saved in movie cache. - Movie cache cleanup check callback now have ImBuf argument which can be used in a condition of cleanup. - Added some utility functions which replaces legacy ibufs iterations with image cache iteration which happens from inside a lock. - Fixed `image_mem_size()` which was only counting one of the buffers if both float and byte buffer present. Additional notes: - `BKE_image_get_first_ibuf()` is rather stupid, but direct access to ibufs->first was also the same stupid idea. Would consider avoid this function is another project. - There are some places which doesn't look threadsafe, but they already were not so much threadsafe anyway before. So think not a big deal with solving this later. Finally solves infinite memory usage by image sequences! :) Reviewers: brecht, campbellbarton Reviewed By: brecht CC: sebastian_k Differential Revision: http://developer.blender.org/D95
2013-12-13 11:22:08 +01:00
struct MovieCacheIter *IMB_moviecacheIter_new(MovieCache *cache)
{
GHashIterator *iter = BLI_ghashIterator_new(cache->hash);
return (struct MovieCacheIter *) iter;
}
void IMB_moviecacheIter_free(struct MovieCacheIter *iter)
{
BLI_ghashIterator_free((GHashIterator *) iter);
}
bool IMB_moviecacheIter_done(struct MovieCacheIter *iter)
{
return BLI_ghashIterator_done((GHashIterator *) iter);
}
void IMB_moviecacheIter_step(struct MovieCacheIter *iter)
{
BLI_ghashIterator_step((GHashIterator *) iter);
}
ImBuf *IMB_moviecacheIter_getImBuf(struct MovieCacheIter *iter)
{
MovieCacheItem *item = BLI_ghashIterator_getValue((GHashIterator *) iter);
return item->ibuf;
}
void *IMB_moviecacheIter_getUserKey(struct MovieCacheIter *iter)
{
MovieCacheKey *key = BLI_ghashIterator_getKey((GHashIterator *) iter);
return key->userkey;
}