/*********************** Color managed cache *************************/
-/* Currently it's original ImBuf pointer is used to distinguish which
- * datablock, frame number, possible postprocessing display buffer was
- * created for.
+/* Cache Implementation Notes
+ * ==========================
*
- * This makes it's possible to easy define key for color managed cache
- * which would work for Images, Movie Clips, Sequencer Strips and so.
+ * All color management cache stuff is stored in two properties of
+ * image buffers:
*
- * This also allows to easily control memory usage -- all color managed
- * buffers are concentrated in single cache and it's really easy to
- * control maximal memory usage for all color management related stuff
- * (currently supports only maximal memory usage, but it could be
- * improved further to support removing buffers when they are not needed
- * anymore but memory usage didn't exceed it's limit).
+ * 1. display_buffer_flags
*
- * This ImBuf is being referenced by cache key, so it could accessed
- * anytime on runtime while cache element is valid. This is needed to
- * support removing display buffers from cache when ImBuf they were
- * created for is being freed.
+ * This is a bit field which used to mark calculated transformations
+ * for particular image buffer. Index inside of this array means index
+ * of a color managed display. Element with given index matches view
+ * transformations applied for a given display. So if bit B of array
+ * element B is set to 1, this means display buffer with display index
+ * of A and view transform of B was ever calculated for this imbuf.
*
- * Technically it works in the following way:
- * - ImBuf is being referenced first time when display buffer is
- * creating for it and being put into the cache
- * - On any further display buffer created for this ImBuf user
- * reference counter is not being incremented
- * - There's count of color management users in ImBuf which is
- * being incremented every time display buffer is creating for
- * giver ImBuf.
- * - Hence, we always know how many display buffers is created
- * for the ImBuf and if there's any display buffers created
- * this ImBuf would be referenced by color management stuff and
- * actual data for it wouldn't be freed even when this ImBuf is
- * being freed by user, who created it.
- * - When all external users finished working with this ImBuf it's
- * reference counter would be 0.
- * - On every new display buffer adding to the cache review of
- * the cache happens and all cached display buffers who's ImBuf's
- * user counter is zero are being removed from the cache.
- * - On every display buffer removed from the cache ImBuf's color
- * management user counter is being decremented. As soon as it's
- * becoming zero, original ImBuf is being freed completely.
+ * In contrast with indices in global lists of displays and views this
+ * indices are 0-based, not 1-based. This is needed to save some bytes
+ * of memory.
+ *
+ * 2. colormanage_cache
+ *
+ * This is a pointer to a structure which holds all data which is
+ * needed for color management cache to work.
+ *
+ * It contains two parts:
+ * - data
+ * - moviecache
+ *
+ * Data field is used to store additional information about cached
+ * buffers which affects on whether cached buffer could be used.
+ * This data can't go to cache key because changes in this data
+ * shouldn't lead extra buffers adding to cache, it shall
+ * invalidate cached images.
+ *
+ * Currently such a data contains only exposure and gamma, but
+ * would likely extended further.
+ *
+ * data field is not null only for elements of cache, not used for
+ * original image buffers.
+ *
+ * Color management cache is using generic MovieCache implementation
+ * to make it easier to deal with memory limitation.
+ *
+ * Currently color management is using the same memory limitation
+ * pool as sequencer and clip editor are using which means color
+ * managed buffers would be removed from the cache as soon as new
+ * frames are loading for the movie clip and there's no space in
+ * cache.
+ *
+ * Every image buffer has got own movie cache instance, which
+ * means keys for color managed buffers could be really simple
+ * and look up in this cache would be fast and independent from
+ * overall amount of color managed images.
*/
/* NOTE: ColormanageCacheViewSettings and ColormanageCacheDisplaySettings are
} ColormanageCacheDisplaySettings;
typedef struct ColormanageCacheKey {
- ImBuf *ibuf; /* image buffer for which display buffer was created */
int view; /* view transformation used for display buffer */
int display; /* display device name */
} ColormanageCacheKey;
-typedef struct ColormnaageCacheImBufData {
+typedef struct ColormnaageCacheData {
float exposure; /* exposure value cached buffer is calculated with */
float gamma; /* gamma value cached buffer is calculated with */
-} ColormnaageCacheImBufData;
+} ColormnaageCacheData;
+
+typedef struct ColormanageCache {
+ struct MovieCache *moviecache;
+
+ ColormnaageCacheData *data;
+} ColormanageCache;
+
+static struct MovieCache *colormanage_moviecache_get(const ImBuf *ibuf)
+{
+ if (!ibuf->colormanage_cache)
+ return NULL;
-static struct MovieCache *colormanage_cache = NULL;
+ return ibuf->colormanage_cache->moviecache;
+}
+
+static ColormnaageCacheData *colormanage_cachedata_get(const ImBuf *ibuf)
+{
+ if (!ibuf->colormanage_cache)
+ return NULL;
+ return ibuf->colormanage_cache->data;
+}
+
+#ifdef WITH_OCIO
static unsigned int colormanage_hashhash(const void *key_v)
{
ColormanageCacheKey *key = (ColormanageCacheKey *)key_v;
- unsigned int rval = *(unsigned int *) key->ibuf;
+ unsigned int rval = (key->display << 16) | (key->view % 0xffff);
return rval;
}
const ColormanageCacheKey *a = (ColormanageCacheKey *) av;
const ColormanageCacheKey *b = (ColormanageCacheKey *) bv;
- if (a->ibuf < b->ibuf)
- return -1;
- else if (a->ibuf > b->ibuf)
- return 1;
-
if (a->view < b->view)
return -1;
else if (a->view > b->view)
return 0;
}
-static int colormanage_checkkeyunused(void *key_v)
+static struct MovieCache *colormanage_moviecache_ensure(ImBuf *ibuf)
{
- ColormanageCacheKey *key = (ColormanageCacheKey *)key_v;
+ if (!ibuf->colormanage_cache) {
+ ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage ca cache");
+ }
- return key->ibuf->refcounter == 0;
-}
+ if (!ibuf->colormanage_cache->moviecache) {
+ struct MovieCache *moviecache;
-static void colormanage_keydeleter(void *key_v)
-{
- ColormanageCacheKey *key = (ColormanageCacheKey *)key_v;
- ImBuf *cache_ibuf = key->ibuf;
-
- cache_ibuf->colormanage_refcounter--;
+ moviecache = IMB_moviecache_create("colormanage cache", sizeof(ColormanageCacheKey), colormanage_hashhash, colormanage_hashcmp);
- if (cache_ibuf->colormanage_refcounter == 0) {
- IMB_freeImBuf(key->ibuf);
+ ibuf->colormanage_cache->moviecache = moviecache;
}
-}
-static void colormanage_cache_init(void)
-{
- colormanage_cache = IMB_moviecache_create(sizeof(ColormanageCacheKey), colormanage_hashhash, colormanage_hashcmp);
-
- IMB_moviecache_set_key_deleter_callback(colormanage_cache, colormanage_keydeleter);
- IMB_moviecache_set_check_unused_callback(colormanage_cache, colormanage_checkkeyunused);
+ return ibuf->colormanage_cache->moviecache;
}
-static void colormanage_cache_exit(void)
+static void colormanage_cachedata_set(ImBuf *ibuf, ColormnaageCacheData *data)
{
- IMB_moviecache_free(colormanage_cache);
+ if (!ibuf->colormanage_cache) {
+ ibuf->colormanage_cache = MEM_callocN(sizeof(ColormanageCache), "imbuf colormanage ca cache");
+ }
+
+ ibuf->colormanage_cache->data = data;
}
-#ifdef WITH_OCIO
static void colormanage_view_settings_to_cache(ColormanageCacheViewSettings *cache_view_settings,
const ColorManagedViewSettings *view_settings)
{
cache_display_settings->display = display;
}
-static void colormanage_settings_to_key(ColormanageCacheKey *key, ImBuf *ibuf,
+static void colormanage_settings_to_key(ColormanageCacheKey *key,
const ColormanageCacheViewSettings *view_settings,
const ColormanageCacheDisplaySettings *display_settings)
{
- key->ibuf = ibuf;
key->view = view_settings->view;
key->display = display_settings->display;
}
-static ImBuf *colormanage_cache_get_ibuf(ColormanageCacheKey *key, void **cache_handle)
+static ImBuf *colormanage_cache_get_ibuf(ImBuf *ibuf, ColormanageCacheKey *key, void **cache_handle)
{
ImBuf *cache_ibuf;
+ struct MovieCache *moviecache = colormanage_moviecache_get(ibuf);
+
+ if (!moviecache) {
+ /* if there's no moviecache it means no color management was applied before */
+
+ return NULL;
+ }
*cache_handle = NULL;
- cache_ibuf = IMB_moviecache_get(colormanage_cache, key);
+ cache_ibuf = IMB_moviecache_get(moviecache, key);
*cache_handle = cache_ibuf;
ImBuf *cache_ibuf;
int view_flag = 1 << (view_settings->view - 1);
- colormanage_settings_to_key(&key, ibuf, view_settings, display_settings);
+ colormanage_settings_to_key(&key, view_settings, display_settings);
/* check whether image was marked as dirty for requested transform */
if ((ibuf->display_buffer_flags[display_settings->display - 1] & view_flag) == 0) {
return NULL;
}
- cache_ibuf = colormanage_cache_get_ibuf(&key, cache_handle);
+ cache_ibuf = colormanage_cache_get_ibuf(ibuf, &key, cache_handle);
if (cache_ibuf) {
- ColormnaageCacheImBufData *cache_data;
+ ColormnaageCacheData *cache_data;
/* only buffers with different color space conversions are being stored
* in cache separately. buffer which were used only different exposure/gamma
* check here which exposure/gamma was used for cached buffer and if they're
* different from requested buffer should be re-generated
*/
- cache_data = (ColormnaageCacheImBufData *) cache_ibuf->colormanage_cache_data;
+ cache_data = colormanage_cachedata_get(cache_ibuf);
if (cache_data->exposure != view_settings->exposure ||
cache_data->gamma != view_settings->gamma)
{
ColormanageCacheKey key;
ImBuf *cache_ibuf;
- ColormnaageCacheImBufData *cache_data;
+ ColormnaageCacheData *cache_data;
int view_flag = 1 << (view_settings->view - 1);
+ struct MovieCache *moviecache = colormanage_moviecache_ensure(ibuf);
- colormanage_settings_to_key(&key, ibuf, view_settings, display_settings);
+ colormanage_settings_to_key(&key, view_settings, display_settings);
/* mark display buffer as valid */
ibuf->display_buffer_flags[display_settings->display - 1] |= view_flag;
cache_ibuf->flags |= IB_rect;
/* store data which is needed to check whether cached buffer could be used for color managed display settings */
- cache_data = MEM_callocN(sizeof(ColormnaageCacheImBufData), "color manage cache imbuf data");
+ cache_data = MEM_callocN(sizeof(ColormnaageCacheData), "color manage cache imbuf data");
cache_data->exposure = view_settings->exposure;
cache_data->gamma = view_settings->gamma;
- cache_ibuf->colormanage_cache_data = cache_data;
+ colormanage_cachedata_set(cache_ibuf, cache_data);
*cache_handle = cache_ibuf;
- /* mark source buffer as having color managed buffer and increment color managed buffers count for it */
- if ((ibuf->colormanage_flags & IMB_COLORMANAGED) == 0) {
- ibuf->colormanage_flags |= IMB_COLORMANAGED;
-
- IMB_refImBuf(ibuf);
- }
-
- ibuf->colormanage_refcounter++;
-
- IMB_moviecache_put(colormanage_cache, &key, cache_ibuf);
+ IMB_moviecache_put(moviecache, &key, cache_ibuf);
}
/* validation function checks whether there's buffer with given display transform
ColormanageCacheKey key;
ImBuf *cache_ibuf;
- colormanage_settings_to_key(&key, ibuf, view_settings, display_settings);
+ colormanage_settings_to_key(&key, view_settings, display_settings);
- cache_ibuf = colormanage_cache_get_ibuf(&key, cache_handle);
+ cache_ibuf = colormanage_cache_get_ibuf(ibuf, &key, cache_handle);
if (cache_ibuf) {
if (cache_ibuf->x != ibuf->x || cache_ibuf->y != ibuf->y) {
ColormanageCacheViewSettings new_view_settings = *view_settings;
- ColormnaageCacheImBufData *cache_data;
+ ColormnaageCacheData *cache_data;
unsigned char *display_buffer;
int buffer_size;
/* use the same settings as original cached buffer */
- cache_data = (ColormnaageCacheImBufData *) cache_ibuf->colormanage_cache_data;
+ cache_data = colormanage_cachedata_get(cache_ibuf);
new_view_settings.exposure = cache_data->exposure;
new_view_settings.gamma = cache_data->gamma;
static void colormanage_cache_get_cache_data(void *cache_handle, float *exposure, float *gamma)
{
ImBuf *cache_ibuf = (ImBuf *) cache_handle;
- ColormnaageCacheImBufData *cache_data;
+ ColormnaageCacheData *cache_data;
- cache_data = (ColormnaageCacheImBufData *) cache_ibuf->colormanage_cache_data;
+ cache_data = colormanage_cachedata_get(cache_ibuf);
*exposure = cache_data->exposure;
*gamma = cache_data->gamma;
/* special views, which does not depend on OCIO */
colormanage_view_add("ACES ODT Tonecurve");
#endif
-
- colormanage_cache_init();
}
void IMB_colormanagement_exit(void)
#ifdef WITH_OCIO
colormanage_free_config();
#endif
-
- colormanage_cache_exit();
}
/*********************** Public display buffers interfaces *************************/
}
}
-#endif
-void IMB_colormanage_flags_allocate(ImBuf *ibuf)
+static void colormanage_flags_allocate(ImBuf *ibuf)
{
if (global_tot_display == 0)
return;
ibuf->display_buffer_flags = MEM_callocN(sizeof(unsigned int) * global_tot_display, "imbuf display_buffer_flags");
}
+#endif
-void IMB_colormanage_flags_free(ImBuf *ibuf)
+void IMB_colormanage_cache_free(ImBuf *ibuf)
{
if (ibuf->display_buffer_flags) {
MEM_freeN(ibuf->display_buffer_flags);
ibuf->display_buffer_flags = NULL;
}
-}
-void IMB_colormanage_cache_data_free(ImBuf *ibuf)
-{
- if (ibuf->colormanage_cache_data) {
- MEM_freeN(ibuf->colormanage_cache_data);
+ if (ibuf->colormanage_cache) {
+ ColormnaageCacheData *cache_data = colormanage_cachedata_get(ibuf);
+ struct MovieCache *moviecache = colormanage_moviecache_get(ibuf);
+
+ if (cache_data) {
+ MEM_freeN(cache_data);
+ }
+
+ if (moviecache) {
+ IMB_moviecache_free(moviecache);
+ }
+
+ MEM_freeN(ibuf->colormanage_cache);
- ibuf->colormanage_cache_data = NULL;
+ ibuf->colormanage_cache = NULL;
}
}
/* ensure color management bit fields exists */
if (!ibuf->display_buffer_flags)
- IMB_colormanage_flags_allocate(ibuf);
+ colormanage_flags_allocate(ibuf);
display_buffer = colormanage_cache_get(ibuf, &cache_view_settings, &cache_display_settings, cache_handle);
* \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 "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;
MovieCacheKeyDeleterFP keydeleterfp;
GHashHashFP hashfp;
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);
remove = !item->ibuf;
- if (!remove && cache->checkkeyunusedfp)
+ if (remove)
+ PRINT("%s: cache '%s' remove item %p without buffer\n", __func__, cache->name, item);
+
+ if (!remove && cache->checkkeyunusedfp) {
remove = cache->checkkeyunusedfp(key->userkey);
+ if (remove)
+ PRINT("%s: cache '%s' remove unused item %p\n", __func__, cache->name, item);
+ }
+
if (remove)
BLI_ghash_remove(cache->hash, key, moviecache_keyfree, moviecache_valfree);
}
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;
+ }
}
}
{
MovieCacheItem *item = (MovieCacheItem *) item_v;
MovieCache *cache = item->cache_owner;
+ int priority;
- if (!cache->getitempriorityfp)
- return default_priority;
-
- if (!cache->last_userkey) {
- /* happens when cache was overflow when adding element to one cache
- * and elements from other cache are being measured as well
- */
+ if (!cache->getitempriorityfp) {
+ PRINT("%s: cache '%s' item %p use default priority %d\n", __func__, cache-> name, item, default_priority);
return default_priority;
}
- return cache->getitempriorityfp(cache->last_userkey, item->priority_data);
+ 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)
delete_MEM_CacheLimiter(limitor);
}
-MovieCache *IMB_moviecache_create(int keysize, GHashHashFP hashfp, GHashCmpFP cmpfp)
+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);
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;
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->c_handle = NULL;
item->c_handle = MEM_CacheLimiter_insert(limitor, item);
- cache->last_userkey = userkey;
+ 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);
- cache->last_userkey = NULL;
-
/* cache limiter can't remove unused keys which points to destoryed values */
check_unused_keys(cache);
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);
if (cache->points)
MEM_freeN(cache->points);
+ if (cache->last_userkey)
+ MEM_freeN(cache->last_userkey);
+
MEM_freeN(cache);
}