[Bf-blender-cvs] [ce89355] master: Image cache rewrite to using generic movie cache

Sergey Sharybin noreply at git.blender.org
Fri Dec 13 11:30:47 CET 2013


Commit: ce893552c359d11cfa93709f239a3c93f4cdb244
Author: Sergey Sharybin
Date:   Fri Dec 13 16:22:08 2013 +0600
http://developer.blender.org/rBce893552c359d11cfa93709f239a3c93f4cdb244

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

===================================================================

M	source/blender/blenkernel/BKE_image.h
M	source/blender/blenkernel/intern/image.c
M	source/blender/blenkernel/intern/seqcache.c
M	source/blender/blenloader/intern/readfile.c
M	source/blender/editors/sculpt_paint/paint_image.c
M	source/blender/editors/sculpt_paint/paint_image_proj.c
M	source/blender/editors/space_image/image_ops.c
M	source/blender/editors/space_info/info_ops.c
M	source/blender/gpu/intern/gpu_draw.c
M	source/blender/imbuf/IMB_moviecache.h
M	source/blender/imbuf/intern/moviecache.c
M	source/blender/makesdna/DNA_image_types.h
M	source/blender/makesrna/intern/rna_image.c
M	source/blender/render/intern/source/imagetexture.c

===================================================================

diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h
index 7b5abbb..b5171f8 100644
--- a/source/blender/blenkernel/BKE_image.h
+++ b/source/blender/blenkernel/BKE_image.h
@@ -235,6 +235,12 @@ float *BKE_image_get_float_pixels_for_frame(struct Image *image, int frame);
 
 /* Guess offset for the first frame in the sequence */
 int BKE_image_sequence_guess_offset(struct Image *image);
+
+bool BKE_image_is_dirty(struct Image *image);
+void BKE_image_file_format_set(struct Image *image, int ftype);
+bool BKE_image_has_loaded_ibuf(struct Image *image);
+struct ImBuf *BKE_image_get_ibuf_with_name(struct Image *image, const char *name);
+struct ImBuf *BKE_image_get_first_ibuf(struct Image *image);
 #ifdef __cplusplus
 }
 #endif
diff --git a/source/blender/blenkernel/intern/image.c b/source/blender/blenkernel/intern/image.c
index 82ea3cb..1e55b5e 100644
--- a/source/blender/blenkernel/intern/image.c
+++ b/source/blender/blenkernel/intern/image.c
@@ -50,6 +50,7 @@
 #include "IMB_colormanagement.h"
 #include "IMB_imbuf_types.h"
 #include "IMB_imbuf.h"
+#include "IMB_moviecache.h"
 
 #ifdef WITH_OPENEXR
 #  include "intern/openexr/openexr_multi.h"
@@ -109,6 +110,51 @@ static SpinLock image_spin;
 #define IMA_INDEX_FRAME(index)          (index >> 10)
 #define IMA_INDEX_PASS(index)           (index & ~1023)
 
+/* ******** IMAGE CACHE ************* */
+
+typedef struct ImageCacheKey {
+	int index;
+} ImageCacheKey;
+
+static unsigned int imagecache_hashhash(const void *key_v)
+{
+	const ImageCacheKey *key = (ImageCacheKey *) key_v;
+	return key->index;
+}
+
+static int imagecache_hashcmp(const void *a_v, const void *b_v)
+{
+	const ImageCacheKey *a = (ImageCacheKey *) a_v;
+	const ImageCacheKey *b = (ImageCacheKey *) b_v;
+
+	return a->index - b->index;
+}
+
+static void imagecache_put(Image *image, int index, ImBuf *ibuf)
+{
+	ImageCacheKey key;
+
+	if (image->cache == NULL) {
+		image->cache = IMB_moviecache_create("Image Datablock Cache", sizeof(ImageCacheKey),
+		                                     imagecache_hashhash, imagecache_hashcmp);
+	}
+
+	key.index = index;
+
+	IMB_moviecache_put(image->cache, &key, ibuf);
+}
+
+static struct ImBuf* imagecache_get(Image *image, int index)
+{
+	if (image->cache) {
+		ImageCacheKey key;
+		key.index = index;
+		return IMB_moviecache_get(image->cache, &key);
+	}
+
+	return NULL;
+}
+
 void BKE_images_init(void)
 {
 	BLI_spin_init(&image_spin);
@@ -193,13 +239,9 @@ void BKE_image_de_interlace(Image *ima, int odd)
 
 static void image_free_cahced_frames(Image *image)
 {
-	ImBuf *ibuf;
-	while ((ibuf = BLI_pophead(&image->ibufs))) {
-		if (ibuf->userdata) {
-			MEM_freeN(ibuf->userdata);
-			ibuf->userdata = NULL;
-		}
-		IMB_freeImBuf(ibuf);
+	if (image->cache) {
+		IMB_moviecache_free(image->cache);
+		image->cache = NULL;
 	}
 }
 
@@ -268,59 +310,30 @@ static Image *image_alloc(Main *bmain, const char *name, short source, short typ
 	return ima;
 }
 
-/* get the ibuf from an image cache, local use here only */
-static ImBuf *image_get_ibuf(Image *ima, int index, int frame)
+/* Get the ibuf from an image cache by it's index and frame.
+ * Local use here only.
+ *
+ * Returns referenced image buffer if it exists, callee is to
+ * call IMB_freeImBuf to de-reference the image buffer after
+ * it's done handling it.
+ */
+static ImBuf *image_get_cached_ibuf_for_index_frame(Image *ima, int index, int frame)
 {
-	/* this function is intended to be thread safe. with IMA_NO_INDEX this
-	 * should be OK, but when iterating over the list this is more tricky
-	 * */
-	if (index == IMA_NO_INDEX) {
-		return ima->ibufs.first;
-	}
-	else {
-		ImBuf *ibuf;
-
+	if (index != IMA_NO_INDEX) {
 		index = IMA_MAKE_INDEX(frame, index);
-		for (ibuf = ima->ibufs.first; ibuf; ibuf = ibuf->next)
-			if (ibuf->index == index)
-				return ibuf;
 	}
 
-	return NULL;
-}
-
-/* no ima->ibuf anymore, but listbase */
-static void image_remove_ibuf(Image *ima, ImBuf *ibuf)
-{
-	if (ibuf) {
-		BLI_remlink(&ima->ibufs, ibuf);
-		IMB_freeImBuf(ibuf);
-	}
+	return imagecache_get(ima, index);
 }
 
-
 /* no ima->ibuf anymore, but listbase */
 static void image_assign_ibuf(Image *ima, ImBuf *ibuf, int index, int frame)
 {
 	if (ibuf) {
-		ImBuf *link;
-
 		if (index != IMA_NO_INDEX)
 			index = IMA_MAKE_INDEX(frame, index);
 
-		/* insert based on index */
-		for (link = ima->ibufs.first; link; link = link->next)
-			if (link->index >= index)
-				break;
-
-		ibuf->index = index;
-
-		/* this function accepts (link == NULL) */
-		BLI_insertlinkbefore(&ima->ibufs, link, ibuf);
-
-		/* now we don't want copies? */
-		if (link && ibuf->index == link->index)
-			image_remove_ibuf(ima, link);
+		imagecache_put(ima, index, ibuf);
 	}
 }
 
@@ -521,14 +534,21 @@ void BKE_image_make_local(struct Image *ima)
 
 void BKE_image_merge(Image *dest, Image *source)
 {
-	ImBuf *ibuf;
-
 	/* sanity check */
 	if (dest && source && dest != source) {
-
-		while ((ibuf = BLI_pophead(&source->ibufs))) {
-			image_assign_ibuf(dest, ibuf, IMA_INDEX_PASS(ibuf->index), IMA_INDEX_FRAME(ibuf->index));
+		BLI_spin_lock(&image_spin);
+		if (source->cache != NULL) {
+			struct MovieCacheIter *iter;
+			iter = IMB_moviecacheIter_new(source->cache);
+			while (!IMB_moviecacheIter_done(iter)) {
+				ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter);
+				ImageCacheKey *key = IMB_moviecacheIter_getUserKey(iter);
+				imagecache_put(dest, key->index, ibuf);
+				IMB_moviecacheIter_step(iter);
+			}
+			IMB_moviecacheIter_free(iter);
 		}
+		BLI_spin_unlock(&image_spin);
 
 		BKE_libblock_free(&G.main->image, source);
 	}
@@ -729,6 +749,9 @@ Image *BKE_image_add_generated(Main *bmain, unsigned int width, unsigned int hei
 		ibuf = add_ibuf_size(width, height, ima->name, depth, floatbuf, gen_type, color, &ima->colorspace_settings);
 		image_assign_ibuf(ima, ibuf, IMA_NO_INDEX, 0);
 
+		/* image_assign_ibuf puts buffer to the cache, which increments user counter. */
+		IMB_freeImBuf(ibuf);
+
 		ima->ok = IMA_OK_LOADED;
 	}
 
@@ -755,7 +778,7 @@ Image *BKE_image_add_from_imbuf(ImBuf *ibuf)
 /* packs rect from memory as PNG */
 void BKE_image_memorypack(Image *ima)
 {
-	ImBuf *ibuf = image_get_ibuf(ima, IMA_NO_INDEX, 0);
+	ImBuf *ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0);
 
 	if (ibuf == NULL)
 		return;
@@ -786,6 +809,8 @@ void BKE_image_memorypack(Image *ima)
 			ima->type = IMA_TYPE_IMAGE;
 		}
 	}
+
+	IMB_freeImBuf(ibuf);
 }
 
 void BKE_image_tag_time(Image *ima)
@@ -838,7 +863,7 @@ void free_old_images(void)
 				ima->lastused = ctime;
 			}
 			/* Otherwise, just kill the buffers */
-			else if (ima->ibufs.first) {
+			else {
 				image_free_buffers(ima);
 			}
 		}
@@ -846,30 +871,47 @@ void free_old_images(void)
 	}
 }
 
-static uintptr_t image_mem_size(Image *ima)
+static uintptr_t image_mem_size(Image *image)
 {
-	ImBuf *ibuf, *ibufm;
-	int level;
 	uintptr_t size = 0;
 
-	size = 0;
-
 	/* viewers have memory depending on other rules, has no valid rect pointer */
-	if (ima->source == IMA_SRC_VIEWER)
+	if (image->source == IMA_SRC_VIEWER)
 		return 0;
 
-	for (ibuf = ima->ibufs.first; ibuf; ibuf = ibuf->next) {
-		if (ibuf->rect) size += MEM_allocN_len(ibuf->rect);
-		else if (ibuf->rect_float) size += MEM_allocN_len(ibuf->rect_float);
+	BLI_spin_lock(&image_spin);
+	if (image->cache != NULL) {
+		struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache);
+
+		while (!IMB_moviecacheIter_done(iter)) {
+			ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter);
+			ImBuf *ibufm;
+			int level;
 
-		for (level = 0; level < IB_MIPMAP_LEVELS; level++) {
-			ibufm = ibuf->mipmap[level];
-			if (ibufm) {
-				if (ibufm->rect) size += MEM_allocN_len(ibufm->rect);
-				else if (ibufm->rect_float) size += MEM_allocN_len(ibufm->rect_float);
+			if (ibuf->rect) {
+				size += MEM_allocN_len(ibuf->rect);
+			}
+			if (ibuf->rect_float) {
+				size += MEM_allocN_len(ibuf->rect_float);
 			}
+
+			for (level = 0; level < IB_MIPMAP_LEVELS; level++) {
+				ibufm = ibuf->mipmap[level];
+				if (ibufm) {
+					if (ibufm->rect) {
+						size += MEM_allocN_len(ibufm->rect);
+					}
+					if (ibufm->rect_float) {
+						size += MEM_allocN_len(ibufm->rect_float);
+					}
+				}
+			}
+
+			IMB_moviecacheIter_step(iter);
 		}
+		IMB_moviecacheIter_free(iter);
 	}
+	BLI_spin_unlock(&image_spin);
 
 	return size;
 }
@@ -892,11 +934,20 @@ void BKE_image_print_memlist(void)
 	}
 }
 
+static bool imagecache_check_dirty(ImBuf *ibuf, void *UNUSED(userkey), void *UNUSED(userdata))
+{
+	return (ibuf->userflags & IB_BITMAPDIRTY) == 0;
+}
+
 void BKE_image_free_all_textures(void)
 {
+#undef CHECK_FREED_SIZE
+
 	Tex *tex;
 	Image *ima;
-	/* unsigned int totsize = 0; */
+#ifdef CHECK_FREED_SIZE
+	uintptr_t tot_freed_size = 0;
+#endif
 
 	for (ima = G.main->image.first; ima; ima = ima->id.next)
 		ima->id.flag &= ~LIB_DOIT;
@@ -906,49 +957,35 @@ void BKE_image_free_all_textures(void)
 			tex->ima->id.flag |= LIB_DOIT;
 
 	for (ima = G.main->image.first; ima; ima = ima->id.next) {
-		if (ima->ibufs.first && (ima->id.flag & LIB_DOIT)) {
-			ImBuf *ibuf;
+		if (ima->cache && (ima->id.flag & LIB_DOIT)) {
+#ifdef CHECK_FREED_SIZE
+			uintptr_t old_size = image_mem_size(ima);
+#endif
 
-			for (ibuf = ima->ibufs.first; ibuf; ibuf = ibuf->next) {
-				/* escape when image is painted on */
-				if (ibuf->userflags & IB_BITMAPDIRTY)
-					break;
+			IMB_moviecache_cleanup(ima->cache, imagecache_check_dirty, NULL);
 
-#if 0
-				if (ibuf->mipmap[0])
-					totsize += 1.33 * ibuf->x * ibuf->y * 4;
-				else
-					totsize += ibuf->x * ibuf->y * 4;
+#ifdef CHECK_FREED_SIZE
+			tot_freed_size += old_size - image_mem_size(ima);
 #endif
-			}
-			if (ibuf == NULL)
-				image_free_buffers(ima);
 		}
 	}
-	/* printf("freed total %d MB\n", totsize / (1024 * 1024)); */
+#ifdef CHECK_FREED_SIZE
+	printf("%s: freed total %lu MB\n", __func__, tot_freed_size / (1024 * 1024));
+#endif
+}
+
+static bool imagecache_check_free_anim(ImBuf *ibuf, void *UNUSED(userkey), void *userdata)
+{
+	int except_frame = *(int *)userdata;
+	return (ibuf->userflags & IB_BITMAPDIRTY) == 0 &&
+	       (ibuf->index != IMA_NO_INDEX) &&
+	       (except_frame != IMA_INDEX_FRAME(ibuf->index));
 }
 
 /* except_fr

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list