diff --git a/source/blender/blenkernel/BKE_image.h b/source/blender/blenkernel/BKE_image.h index 7b5abbba7f8..b5171f8e0c2 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 82ea3cb80b1..1e55b5ee962 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; + return imagecache_get(ima, index); } -/* 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); - } -} - - /* 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); - 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); + while (!IMB_moviecacheIter_done(iter)) { + ImBuf *ibuf = IMB_moviecacheIter_getImBuf(iter); + ImBuf *ibufm; + int level; + + 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; - - for (ibuf = ima->ibufs.first; ibuf; ibuf = ibuf->next) { - /* escape when image is painted on */ - if (ibuf->userflags & IB_BITMAPDIRTY) - break; - -#if 0 - if (ibuf->mipmap[0]) - totsize += 1.33 * ibuf->x * ibuf->y * 4; - else - totsize += ibuf->x * ibuf->y * 4; + if (ima->cache && (ima->id.flag & LIB_DOIT)) { +#ifdef CHECK_FREED_SIZE + uintptr_t old_size = image_mem_size(ima); +#endif + + IMB_moviecache_cleanup(ima->cache, imagecache_check_dirty, NULL); + +#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_frame is weak, only works for seqs without offset... */ void BKE_image_free_anim_ibufs(Image *ima, int except_frame) { - ImBuf *ibuf, *nbuf; - - for (ibuf = ima->ibufs.first; ibuf; ibuf = nbuf) { - nbuf = ibuf->next; - if (ibuf->userflags & IB_BITMAPDIRTY) - continue; - if (ibuf->index == IMA_NO_INDEX) - continue; - if (except_frame != IMA_INDEX_FRAME(ibuf->index)) { - BLI_remlink(&ima->ibufs, ibuf); - - if (ibuf->userdata) { - MEM_freeN(ibuf->userdata); - ibuf->userdata = NULL; - } - IMB_freeImBuf(ibuf); - } - } + IMB_moviecache_cleanup(ima->cache, imagecache_check_free_anim, &except_frame); } void BKE_image_all_free_anim_ibufs(int cfra) @@ -2219,10 +2256,11 @@ void BKE_image_signal(Image *ima, ImageUser *iuser, int signal) if (ima->source == IMA_SRC_GENERATED) { if (ima->gen_x == 0 || ima->gen_y == 0) { - 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) { ima->gen_x = ibuf->x; ima->gen_y = ibuf->y; + IMB_freeImBuf(ibuf); } } } @@ -2784,7 +2822,7 @@ static ImBuf *image_get_render_result(Image *ima, ImageUser *iuser, void **lock_ } } - ibuf = image_get_ibuf(ima, IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); /* make ibuf if needed, and initialize it */ if (ibuf == NULL) { @@ -2870,7 +2908,13 @@ static void image_get_frame_and_index(Image *ima, ImageUser *iuser, int *frame_r *index_r = index; } -static ImBuf *image_get_ibuf_threadsafe(Image *ima, ImageUser *iuser, int *frame_r, int *index_r) +/* Get the ibuf from an image cache for a given image user. + * + * 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(Image *ima, ImageUser *iuser, int *frame_r, int *index_r) { ImBuf *ibuf = NULL; int frame = 0, index = 0; @@ -2878,7 +2922,7 @@ static ImBuf *image_get_ibuf_threadsafe(Image *ima, ImageUser *iuser, int *frame /* see if we already have an appropriate ibuf, with image source and type */ if (ima->source == IMA_SRC_MOVIE) { frame = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_ibuf(ima, 0, frame); + ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame); /* XXX temp stuff? */ if (ima->lastframe != frame) ima->tpageflag |= IMA_TPAGE_REFRESH; @@ -2887,7 +2931,7 @@ static ImBuf *image_get_ibuf_threadsafe(Image *ima, ImageUser *iuser, int *frame else if (ima->source == IMA_SRC_SEQUENCE) { if (ima->type == IMA_TYPE_IMAGE) { frame = iuser ? iuser->framenr : ima->lastframe; - ibuf = image_get_ibuf(ima, 0, frame); + ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame); /* XXX temp stuff? */ if (ima->lastframe != frame) { @@ -2898,17 +2942,17 @@ static ImBuf *image_get_ibuf_threadsafe(Image *ima, ImageUser *iuser, int *frame else if (ima->type == IMA_TYPE_MULTILAYER) { frame = iuser ? iuser->framenr : ima->lastframe; index = iuser ? iuser->multi_index : IMA_NO_INDEX; - ibuf = image_get_ibuf(ima, index, frame); + ibuf = image_get_cached_ibuf_for_index_frame(ima, index, frame); } } else if (ima->source == IMA_SRC_FILE) { if (ima->type == IMA_TYPE_IMAGE) - ibuf = image_get_ibuf(ima, IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); else if (ima->type == IMA_TYPE_MULTILAYER) - ibuf = image_get_ibuf(ima, iuser ? iuser->multi_index : IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_frame(ima, iuser ? iuser->multi_index : IMA_NO_INDEX, 0); } else if (ima->source == IMA_SRC_GENERATED) { - ibuf = image_get_ibuf(ima, IMA_NO_INDEX, 0); + ibuf = image_get_cached_ibuf_for_index_frame(ima, IMA_NO_INDEX, 0); } else if (ima->source == IMA_SRC_VIEWER) { /* always verify entirely, not that this shouldn't happen @@ -2957,7 +3001,7 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r) if (!image_quick_test(ima, iuser)) return NULL; - ibuf = image_get_ibuf_threadsafe(ima, iuser, &frame, &index); + ibuf = image_get_cached_ibuf(ima, iuser, &frame, &index); if (ibuf == NULL) { /* we are sure we have to load the ibuf, using source and type */ @@ -3012,7 +3056,7 @@ static ImBuf *image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r) /* XXX anim play for viewer nodes not yet supported */ frame = 0; // XXX iuser ? iuser->framenr : 0; - ibuf = image_get_ibuf(ima, 0, frame); + ibuf = image_get_cached_ibuf_for_index_frame(ima, 0, frame); if (!ibuf) { /* Composite Viewer, all handled in compositor */ @@ -3045,9 +3089,6 @@ ImBuf *BKE_image_acquire_ibuf(Image *ima, ImageUser *iuser, void **lock_r) ibuf = image_acquire_ibuf(ima, iuser, lock_r); - if (ibuf) - IMB_refImBuf(ibuf); - BLI_spin_unlock(&image_spin); return ibuf; @@ -3082,18 +3123,16 @@ int BKE_image_has_ibuf(Image *ima, ImageUser *iuser) if (!image_quick_test(ima, iuser)) return FALSE; - ibuf = image_get_ibuf_threadsafe(ima, iuser, NULL, NULL); + BLI_spin_lock(&image_spin); - if (!ibuf) { - BLI_spin_lock(&image_spin); + ibuf = image_get_cached_ibuf(ima, iuser, NULL, NULL); - ibuf = image_get_ibuf_threadsafe(ima, iuser, NULL, NULL); + if (!ibuf) + ibuf = image_acquire_ibuf(ima, iuser, NULL); - if (!ibuf) - ibuf = image_acquire_ibuf(ima, iuser, NULL); + BLI_spin_unlock(&image_spin); - BLI_spin_unlock(&image_spin); - } + IMB_freeImBuf(ibuf); return ibuf != NULL; } @@ -3187,9 +3226,6 @@ ImBuf *BKE_image_pool_acquire_ibuf(Image *ima, ImageUser *iuser, ImagePool *pool ibuf = image_acquire_ibuf(ima, iuser, NULL); - if (ibuf) - IMB_refImBuf(ibuf); - entry = MEM_callocN(sizeof(ImagePoolEntry), "Image Pool Entry"); entry->image = ima; entry->frame = frame; @@ -3455,3 +3491,120 @@ int BKE_image_sequence_guess_offset(Image *image) return atoi(num); } + +bool BKE_image_is_dirty(Image *image) +{ + bool is_dirty = false; + + 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); + if (ibuf->userflags & IB_BITMAPDIRTY) { + is_dirty = true; + break; + } + IMB_moviecacheIter_step(iter); + } + IMB_moviecacheIter_free(iter); + } + BLI_spin_unlock(&image_spin); + + return is_dirty; +} + +void BKE_image_file_format_set(Image *image, int ftype) +{ +#if 0 + ImBuf *ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); + if (ibuf) { + ibuf->ftype = ftype; + } + BKE_image_release_ibuf(image, ibuf, NULL); +#endif + + 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); + ibuf->ftype = ftype; + IMB_moviecacheIter_step(iter); + } + IMB_moviecacheIter_free(iter); + } + BLI_spin_unlock(&image_spin); +} + +bool BKE_image_has_loaded_ibuf(Image *image) +{ + bool has_loaded_ibuf = false; + + BLI_spin_lock(&image_spin); + if (image->cache != NULL) { + struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); + + while (!IMB_moviecacheIter_done(iter)) { + has_loaded_ibuf = true; + break; + } + IMB_moviecacheIter_free(iter); + } + BLI_spin_unlock(&image_spin); + + return has_loaded_ibuf; +} + +/* References the result, IMB_freeImBuf is to be called to de-reference. */ +ImBuf *BKE_image_get_ibuf_with_name(Image *image, const char *name) +{ + ImBuf *ibuf = NULL; + + BLI_spin_lock(&image_spin); + if (image->cache != NULL) { + struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); + + while (!IMB_moviecacheIter_done(iter)) { + ImBuf *current_ibuf = IMB_moviecacheIter_getImBuf(iter); + if (STREQ(current_ibuf->name, name)) { + ibuf = current_ibuf; + IMB_refImBuf(ibuf); + break; + } + } + IMB_moviecacheIter_free(iter); + } + BLI_spin_unlock(&image_spin); + + return ibuf; +} + +/* References the result, IMB_freeImBuf is to be called to de-reference. + * + * TODO(sergey): This is actually "get first entry from the cache", which is + * not so much predictable. But using first loaded image buffer + * was also malicious logic and all the areas which uses this + * function are to be re-considered. + */ +ImBuf *BKE_image_get_first_ibuf(Image *image) +{ + ImBuf *ibuf = NULL; + + BLI_spin_lock(&image_spin); + if (image->cache != NULL) { + struct MovieCacheIter *iter = IMB_moviecacheIter_new(image->cache); + + while (!IMB_moviecacheIter_done(iter)) { + ibuf = IMB_moviecacheIter_getImBuf(iter); + IMB_refImBuf(ibuf); + break; + } + IMB_moviecacheIter_free(iter); + } + BLI_spin_unlock(&image_spin); + + return ibuf; +} diff --git a/source/blender/blenkernel/intern/seqcache.c b/source/blender/blenkernel/intern/seqcache.c index 33a76d0c03d..919d38be9e0 100644 --- a/source/blender/blenkernel/intern/seqcache.c +++ b/source/blender/blenkernel/intern/seqcache.c @@ -195,7 +195,7 @@ void BKE_sequencer_cache_cleanup(void) BKE_sequencer_preprocessed_cache_cleanup(); } -static int seqcache_key_check_seq(void *userkey, void *userdata) +static bool seqcache_key_check_seq(ImBuf *UNUSED(ibuf), void *userkey, void *userdata) { SeqCacheKey *key = (SeqCacheKey *) userkey; Sequence *seq = (Sequence *) userdata; diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c index 37d2ec787ed..6e64dcc0560 100644 --- a/source/blender/blenloader/intern/readfile.c +++ b/source/blender/blenloader/intern/readfile.c @@ -1317,9 +1317,8 @@ void blo_make_image_pointer_map(FileData *fd, Main *oldmain) fd->imamap = oldnewmap_new(); for (; ima; ima = ima->id.next) { - Link *ibuf = ima->ibufs.first; - for (; ibuf; ibuf = ibuf->next) - oldnewmap_insert(fd->imamap, ibuf, ibuf, 0); + if (ima->cache) + oldnewmap_insert(fd->imamap, ima->cache, ima->cache, 0); if (ima->gputexture) oldnewmap_insert(fd->imamap, ima->gputexture, ima->gputexture, 0); if (ima->rr) @@ -1355,19 +1354,7 @@ void blo_end_image_pointer_map(FileData *fd, Main *oldmain) } for (; ima; ima = ima->id.next) { - Link *ibuf, *next; - - /* this mirrors direct_link_image */ - for (ibuf = ima->ibufs.first; ibuf; ibuf = next) { - next = ibuf->next; - if (NULL == newimaadr(fd, ibuf)) { /* so was restored */ - BLI_remlink(&ima->ibufs, ibuf); - ima->bindcode = 0; - ima->tpageflag &= ~IMA_GLBIND_IS_DATA; - ima->gputexture = NULL; - ima->rr = NULL; - } - } + ima->cache = newmclipadr(fd, ima->cache); for (i = 0; i < IMA_MAX_RENDER_SLOT; i++) ima->renders[i] = newimaadr(fd, ima->renders[i]); @@ -3281,34 +3268,16 @@ static void lib_link_image(FileData *fd, Main *main) } } -static void link_ibuf_list(FileData *fd, ListBase *lb) -{ - Link *ln, *prev; - - if (lb->first == NULL) return; - - lb->first = newimaadr(fd, lb->first); - ln = lb->first; - prev = NULL; - while (ln) { - ln->next = newimaadr(fd, ln->next); - ln->prev = prev; - prev = ln; - ln = ln->next; - } - lb->last = prev; -} - static void direct_link_image(FileData *fd, Image *ima) { /* for undo system, pointers could be restored */ if (fd->imamap) - link_ibuf_list(fd, &ima->ibufs); + ima->cache = newmclipadr(fd, ima->cache); else - ima->ibufs.first = ima->ibufs.last = NULL; + ima->cache = NULL; /* if not restored, we keep the binded opengl index */ - if (ima->ibufs.first == NULL) { + if (!fd->imamap) { ima->bindcode = 0; ima->tpageflag &= ~IMA_GLBIND_IS_DATA; ima->gputexture = NULL; diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 47ca3e5ce0c..5b323a0a396 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -250,6 +250,7 @@ void ED_image_undo_restore(bContext *C, ListBase *lb) for (tile = lb->first; tile; tile = tile->next) { short use_float; + bool need_release = true; /* find image based on name, pointer becomes invalid with global undo */ if (ima && strcmp(tile->idname, ima->id.name) == 0) { @@ -268,8 +269,9 @@ void ED_image_undo_restore(bContext *C, ListBase *lb) * matched file name in list of already loaded images */ BKE_image_release_ibuf(ima, ibuf, NULL); + need_release = false; - ibuf = BLI_findstring(&ima->ibufs, tile->ibufname, offsetof(ImBuf, name)); + ibuf = BKE_image_get_ibuf_with_name(ima, tile->ibufname); } if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) { @@ -298,7 +300,12 @@ void ED_image_undo_restore(bContext *C, ListBase *lb) ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; - BKE_image_release_ibuf(ima, ibuf, NULL); + if (need_release) { + BKE_image_release_ibuf(ima, ibuf, NULL); + } + else { + IMB_freeImBuf(ibuf); + } } IMB_freeImBuf(tmpibuf); diff --git a/source/blender/editors/sculpt_paint/paint_image_proj.c b/source/blender/editors/sculpt_paint/paint_image_proj.c index d6989c082a1..4b402bc1741 100644 --- a/source/blender/editors/sculpt_paint/paint_image_proj.c +++ b/source/blender/editors/sculpt_paint/paint_image_proj.c @@ -542,8 +542,7 @@ static bool project_paint_PickColor(const ProjPaintState *ps, const float pt[2], } ima = project_paint_face_image(ps, ps->dm_mtface, face_index); - ibuf = ima->ibufs.first; /* we must have got the imbuf before getting here */ - if (!ibuf) return 0; + ibuf = BKE_image_get_first_ibuf(ima); /* we must have got the imbuf before getting here */ if (interp) { float x, y; @@ -599,6 +598,7 @@ static bool project_paint_PickColor(const ProjPaintState *ps, const float pt[2], } } } + IMB_freeImBuf(ibuf); return 1; } diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c index bda4be40c5c..a2f49af1730 100644 --- a/source/blender/editors/space_image/image_ops.c +++ b/source/blender/editors/space_image/image_ops.c @@ -63,6 +63,7 @@ #include "IMB_colormanagement.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" +#include "IMB_moviecache.h" #include "RE_pipeline.h" @@ -1628,9 +1629,10 @@ static int image_save_sequence_exec(bContext *C, wmOperator *op) { Main *bmain = CTX_data_main(C); SpaceImage *sima = CTX_wm_space_image(C); - ImBuf *ibuf; + ImBuf *ibuf, *first_ibuf = NULL; int tot = 0; char di[FILE_MAX]; + struct MovieCacheIter *iter; if (sima->image == NULL) return OPERATOR_CANCELLED; @@ -1645,10 +1647,22 @@ static int image_save_sequence_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - /* get total */ - for (ibuf = sima->image->ibufs.first; ibuf; ibuf = ibuf->next) - if (ibuf->userflags & IB_BITMAPDIRTY) - tot++; + /* get total dirty buffers and first dirty buffer which is used for menu */ + ibuf = NULL; + if (sima->image->cache != NULL) { + iter = IMB_moviecacheIter_new(sima->image->cache); + while (!IMB_moviecacheIter_done(iter)) { + ibuf = IMB_moviecacheIter_getImBuf(iter); + if (ibuf->userflags & IB_BITMAPDIRTY) { + if (first_ibuf == NULL) { + first_ibuf = ibuf; + } + tot++; + } + IMB_moviecacheIter_step(iter); + } + IMB_moviecacheIter_free(iter); + } if (tot == 0) { BKE_report(op->reports, RPT_WARNING, "No images have been changed"); @@ -1656,18 +1670,17 @@ static int image_save_sequence_exec(bContext *C, wmOperator *op) } /* get a filename for menu */ - for (ibuf = sima->image->ibufs.first; ibuf; ibuf = ibuf->next) - if (ibuf->userflags & IB_BITMAPDIRTY) - break; - - BLI_split_dir_part(ibuf->name, di, sizeof(di)); + BLI_split_dir_part(first_ibuf->name, di, sizeof(di)); BKE_reportf(op->reports, RPT_INFO, "%d image(s) will be saved in %s", tot, di); - for (ibuf = sima->image->ibufs.first; ibuf; ibuf = ibuf->next) { + iter = IMB_moviecacheIter_new(sima->image->cache); + while (!IMB_moviecacheIter_done(iter)) { + ibuf = IMB_moviecacheIter_getImBuf(iter); + if (ibuf->userflags & IB_BITMAPDIRTY) { char name[FILE_MAX]; BLI_strncpy(name, ibuf->name, sizeof(name)); - + BLI_path_abs(name, bmain->name); if (0 == IMB_saveiff(ibuf, name, IB_rect | IB_zbuf | IB_zbuffloat)) { @@ -1678,7 +1691,10 @@ static int image_save_sequence_exec(bContext *C, wmOperator *op) BKE_reportf(op->reports, RPT_INFO, "Saved %s", ibuf->name); ibuf->userflags &= ~IB_BITMAPDIRTY; } + + IMB_moviecacheIter_step(iter); } + IMB_moviecacheIter_free(iter); return OPERATOR_FINISHED; } diff --git a/source/blender/editors/space_info/info_ops.c b/source/blender/editors/space_info/info_ops.c index 4e367c1d48d..6c76ba64893 100644 --- a/source/blender/editors/space_info/info_ops.c +++ b/source/blender/editors/space_info/info_ops.c @@ -145,7 +145,7 @@ static int pack_all_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(ev // first check for dirty images for (ima = bmain->image.first; ima; ima = ima->id.next) { - if (ima->ibufs.first) { /* XXX FIX */ + if (BKE_image_has_loaded_ibuf(ima)) { /* XXX FIX */ ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); if (ibuf && (ibuf->userflags & IB_BITMAPDIRTY)) { diff --git a/source/blender/gpu/intern/gpu_draw.c b/source/blender/gpu/intern/gpu_draw.c index 0dba2cd50e8..ee59cf418bf 100644 --- a/source/blender/gpu/intern/gpu_draw.c +++ b/source/blender/gpu/intern/gpu_draw.c @@ -93,6 +93,7 @@ void GPU_render_text(MTFace *tface, int mode, { if ((mode & GEMAT_TEXT) && (textlen>0) && tface->tpage) { Image* ima = (Image *)tface->tpage; + ImBuf *first_ibuf; int index, character; float centerx, centery, sizex, sizey, transx, transy, movex, movey, advance; float advance_tab; @@ -117,7 +118,8 @@ void GPU_render_text(MTFace *tface, int mode, glPushMatrix(); /* get the tab width */ - matrixGlyph((ImBuf *)ima->ibufs.first, ' ', & centerx, ¢ery, + first_ibuf = BKE_image_get_first_ibuf(ima); + matrixGlyph(first_ibuf, ' ', ¢erx, ¢ery, &sizex, &sizey, &transx, &transy, &movex, &movey, &advance); advance_tab= advance * 4; /* tab width could also be an option */ @@ -143,7 +145,7 @@ void GPU_render_text(MTFace *tface, int mode, // space starts at offset 1 // character = character - ' ' + 1; - matrixGlyph((ImBuf *)ima->ibufs.first, character, & centerx, ¢ery, + matrixGlyph(first_ibuf, character, & centerx, ¢ery, &sizex, &sizey, &transx, &transy, &movex, &movey, &advance); uv[0][0] = (tface->uv[0][0] - centerx) * sizex + transx; @@ -184,6 +186,8 @@ void GPU_render_text(MTFace *tface, int mode, line_start -= advance; /* so we can go back to the start of the line */ } glPopMatrix(); + + IMB_freeImBuf(first_ibuf); } } diff --git a/source/blender/imbuf/IMB_moviecache.h b/source/blender/imbuf/IMB_moviecache.h index 1c569712968..3432741596f 100644 --- a/source/blender/imbuf/IMB_moviecache.h +++ b/source/blender/imbuf/IMB_moviecache.h @@ -63,8 +63,18 @@ struct ImBuf *IMB_moviecache_get(struct MovieCache *cache, void *userkey); int IMB_moviecache_has_frame(struct MovieCache *cache, void *userkey); void IMB_moviecache_free(struct MovieCache *cache); -void IMB_moviecache_cleanup(struct MovieCache *cache, int (cleanup_check_cb) (void *userkey, void *userdata), void *userdata); +void IMB_moviecache_cleanup(struct MovieCache *cache, + bool (cleanup_check_cb) (struct ImBuf *ibuf, void *userkey, void *userdata), + void *userdata); void IMB_moviecache_get_cache_segments(struct MovieCache *cache, int proxy, int render_flags, int *totseg_r, int **points_r); +struct MovieCacheIter; +struct MovieCacheIter *IMB_moviecacheIter_new(struct MovieCache *cache); +void IMB_moviecacheIter_free(struct MovieCacheIter *iter); +bool IMB_moviecacheIter_done(struct MovieCacheIter *iter); +void IMB_moviecacheIter_step(struct MovieCacheIter *iter); +struct ImBuf *IMB_moviecacheIter_getImBuf(struct MovieCacheIter *iter); +void *IMB_moviecacheIter_getUserKey(struct MovieCacheIter *iter); + #endif diff --git a/source/blender/imbuf/intern/moviecache.c b/source/blender/imbuf/intern/moviecache.c index 90eea438c5e..c287cf4c005 100644 --- a/source/blender/imbuf/intern/moviecache.c +++ b/source/blender/imbuf/intern/moviecache.c @@ -443,23 +443,18 @@ void IMB_moviecache_free(MovieCache *cache) MEM_freeN(cache); } -void IMB_moviecache_cleanup(MovieCache *cache, int (cleanup_check_cb) (void *userkey, void *userdata), void *userdata) +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); - int remove; + MovieCacheItem *item = BLI_ghashIterator_getValue(iter); BLI_ghashIterator_step(iter); - remove = cleanup_check_cb(key->userkey, userdata); - - if (remove) { - MovieCacheItem *item = BLI_ghashIterator_getValue(iter); - (void) item; /* silence unused variable when not using debug */ - + if (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); @@ -556,3 +551,36 @@ void IMB_moviecache_get_cache_segments(MovieCache *cache, int proxy, int render_ MEM_freeN(frames); } } + +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; +} diff --git a/source/blender/makesdna/DNA_image_types.h b/source/blender/makesdna/DNA_image_types.h index 6b0299e6d50..0b9dddd0ea5 100644 --- a/source/blender/makesdna/DNA_image_types.h +++ b/source/blender/makesdna/DNA_image_types.h @@ -40,6 +40,7 @@ struct PackedFile; struct Scene; struct anim; struct ImBuf; +struct MovieCache; struct RenderResult; struct GPUTexture; @@ -74,7 +75,7 @@ typedef struct Image { char name[1024]; /* file path, 1024 = FILE_MAX */ - ListBase ibufs; /* not written in file */ + struct MovieCache *cache; /* not written in file */ struct GPUTexture *gputexture; /* not written in file */ /* sources from: */ diff --git a/source/blender/makesrna/intern/rna_image.c b/source/blender/makesrna/intern/rna_image.c index c901abc834e..69b2d2d0227 100644 --- a/source/blender/makesrna/intern/rna_image.c +++ b/source/blender/makesrna/intern/rna_image.c @@ -79,14 +79,7 @@ static void rna_Image_animated_update(Main *UNUSED(bmain), Scene *UNUSED(scene), static int rna_Image_dirty_get(PointerRNA *ptr) { - Image *ima = (Image *)ptr->data; - ImBuf *ibuf; - - for (ibuf = ima->ibufs.first; ibuf; ibuf = ibuf->next) - if (ibuf->userflags & IB_BITMAPDIRTY) - return 1; - - return 0; + return BKE_image_is_dirty((Image *)ptr->data); } static void rna_Image_source_set(PointerRNA *ptr, int value) @@ -210,31 +203,16 @@ static void rna_Image_file_format_set(PointerRNA *ptr, int value) { Image *image = (Image *)ptr->data; if (BKE_imtype_is_movie(value) == 0) { /* should be able to throw an error here */ - ImBuf *ibuf; int ftype = BKE_imtype_to_ftype(value); - -#if 0 - ibuf = BKE_image_acquire_ibuf(image, NULL, NULL); - if (ibuf) - ibuf->ftype = ftype; -#endif - - /* to be safe change all buffer file types */ - /* TODO: this is never threadsafe */ - for (ibuf = image->ibufs.first; ibuf; ibuf = ibuf->next) { - ibuf->ftype = ftype; - } + BKE_image_file_format_set(image, ftype); } } static int rna_Image_has_data_get(PointerRNA *ptr) { - Image *im = (Image *)ptr->data; + Image *image = (Image *)ptr->data; - if (im->ibufs.first) - return 1; - - return 0; + return BKE_image_has_loaded_ibuf(image); } static void rna_Image_size_get(PointerRNA *ptr, int *values) diff --git a/source/blender/render/intern/source/imagetexture.c b/source/blender/render/intern/source/imagetexture.c index bfc13bf6151..37c6a5ffdd1 100644 --- a/source/blender/render/intern/source/imagetexture.c +++ b/source/blender/render/intern/source/imagetexture.c @@ -127,7 +127,7 @@ int imagewrap(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], TexResul if (ima) { /* hack for icon render */ - if (ima->ibufs.first==NULL && (R.r.scemode & R_NO_IMAGE_LOAD)) + if ((R.r.scemode & R_NO_IMAGE_LOAD) && !BKE_image_has_loaded_ibuf(ima)) return retval; ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool); @@ -1094,7 +1094,9 @@ static int imagewraposa_aniso(Tex *tex, Image *ima, ImBuf *ibuf, const float tex if (ibuf==NULL && ima==NULL) return retval; if (ima) { /* hack for icon render */ - if ((ima->ibufs.first == NULL) && (R.r.scemode & R_NO_IMAGE_LOAD)) return retval; + if ((R.r.scemode & R_NO_IMAGE_LOAD) && !BKE_image_has_loaded_ibuf(ima)) { + return retval; + } ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool); } @@ -1515,7 +1517,7 @@ int imagewraposa(Tex *tex, Image *ima, ImBuf *ibuf, const float texvec[3], const if (ima) { /* hack for icon render */ - if (ima->ibufs.first==NULL && (R.r.scemode & R_NO_IMAGE_LOAD)) + if ((R.r.scemode & R_NO_IMAGE_LOAD) && !BKE_image_has_loaded_ibuf(ima)) return retval; ibuf = BKE_image_pool_acquire_ibuf(ima, &tex->iuser, pool);