diff options
-rw-r--r-- | source/blender/blenkernel/BKE_icons.h | 25 | ||||
-rw-r--r-- | source/blender/blenkernel/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/icons.cc (renamed from source/blender/blenkernel/intern/icons.c) | 249 | ||||
-rw-r--r-- | source/blender/blenloader/intern/readblenentry.c | 4 | ||||
-rw-r--r-- | source/blender/editors/interface/interface_icons.c | 45 | ||||
-rw-r--r-- | source/blender/editors/render/render_preview.c | 37 | ||||
-rw-r--r-- | source/blender/editors/util/ed_util.c | 2 | ||||
-rw-r--r-- | source/blender/imbuf/intern/thumbs_blend.c | 10 | ||||
-rw-r--r-- | source/blender/makesdna/DNA_ID.h | 1 |
9 files changed, 275 insertions, 100 deletions
diff --git a/source/blender/blenkernel/BKE_icons.h b/source/blender/blenkernel/BKE_icons.h index cb2755d8706..5ceba4c717f 100644 --- a/source/blender/blenkernel/BKE_icons.h +++ b/source/blender/blenkernel/BKE_icons.h @@ -23,17 +23,26 @@ * \ingroup bke * * Resizable Icons for Blender + * + * There is some thread safety for this API but it is rather weak. Registering or unregistering + * icons is thread safe, changing data of icons from multiple threads is not. Practically this + * should be fine since only the main thread modifies icons. Should that change, more locks or a + * different design need to be introduced. */ #ifdef __cplusplus extern "C" { #endif +#include "BLI_compiler_attrs.h" + typedef void (*DrawInfoFreeFP)(void *drawinfo); enum { /** ID preview: obj is #ID. */ ICON_DATA_ID = 0, + /** Arbitrary Image buffer: obj is #ImBuf */ + ICON_DATA_IMBUF, /** Preview: obj is #PreviewImage */ ICON_DATA_PREVIEW, /** 2D triangles: obj is #Icon_Geom */ @@ -44,6 +53,9 @@ enum { ICON_DATA_GPLAYER, }; +/** + * \note See comment at the top regarding thread safety. + */ struct Icon { void *drawinfo; /** @@ -93,6 +105,9 @@ int BKE_icon_gplayer_color_ensure(struct bGPDlayer *gpl); int BKE_icon_preview_ensure(struct ID *id, struct PreviewImage *preview); +int BKE_icon_imbuf_create(struct ImBuf *ibuf) ATTR_WARN_UNUSED_RESULT; +struct ImBuf *BKE_icon_imbuf_get_buffer(int icon_id) ATTR_WARN_UNUSED_RESULT; + /* retrieve icon for id */ struct Icon *BKE_icon_get(const int icon_id); @@ -131,6 +146,9 @@ void BKE_previewimg_clear_single(struct PreviewImage *prv, enum eIconSizes size) struct PreviewImage **BKE_previewimg_id_get_p(const struct ID *id); struct PreviewImage *BKE_previewimg_id_get(const struct ID *id); +void BKE_previewimg_id_custom_set(struct ID *id, const char *path); + +bool BKE_previewimg_id_supports_jobs(const struct ID *id); /* Trigger deferred loading of a custom image file into the preview buffer. */ void BKE_previewimg_id_custom_set(struct ID *id, const char *path); @@ -151,6 +169,11 @@ struct PreviewImage *BKE_previewimg_id_ensure(struct ID *id); void BKE_previewimg_ensure(struct PreviewImage *prv, const int size); +struct ImBuf *BKE_previewimg_to_imbuf(struct PreviewImage *prv, const int size); + +void BKE_previewimg_finish(struct PreviewImage *prv, const int size); +bool BKE_previewimg_is_finished(const struct PreviewImage *prv, const int size); + struct PreviewImage *BKE_previewimg_cached_get(const char *name); struct PreviewImage *BKE_previewimg_cached_ensure(const char *name); @@ -168,7 +191,7 @@ void BKE_previewimg_blend_write(struct BlendWriter *writer, const struct Preview void BKE_previewimg_blend_read(struct BlendDataReader *reader, struct PreviewImage *prv); int BKE_icon_geom_ensure(struct Icon_Geom *geom); -struct Icon_Geom *BKE_icon_geom_from_memory(const uchar *data, size_t data_len); +struct Icon_Geom *BKE_icon_geom_from_memory(uchar *data, size_t data_len); struct Icon_Geom *BKE_icon_geom_from_file(const char *filename); struct ImBuf *BKE_icon_geom_rasterize(const struct Icon_Geom *geom, diff --git a/source/blender/blenkernel/CMakeLists.txt b/source/blender/blenkernel/CMakeLists.txt index 156d731d4a7..23ba4eb4235 100644 --- a/source/blender/blenkernel/CMakeLists.txt +++ b/source/blender/blenkernel/CMakeLists.txt @@ -133,7 +133,7 @@ set(SRC intern/gpencil_geom.c intern/gpencil_modifier.c intern/hair.c - intern/icons.c + intern/icons.cc intern/icons_rasterize.c intern/idprop.c intern/idprop_utils.c diff --git a/source/blender/blenkernel/intern/icons.c b/source/blender/blenkernel/intern/icons.cc index 0abf5d7279b..b92b2259eeb 100644 --- a/source/blender/blenkernel/intern/icons.c +++ b/source/blender/blenkernel/intern/icons.cc @@ -1,4 +1,4 @@ -/* +/* * 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 @@ -21,9 +21,10 @@ * \ingroup bke */ -#include <math.h> -#include <stdlib.h> -#include <string.h> +#include <cmath> +#include <cstdlib> +#include <cstring> +#include <mutex> #include "CLG_log.h" @@ -46,6 +47,7 @@ #include "BLI_string.h" #include "BLI_threads.h" #include "BLI_utildefines.h" +#include "BLI_vector.hh" #include "BKE_global.h" /* only for G.background test */ #include "BKE_icons.h" @@ -61,6 +63,8 @@ #include "BLO_read_write.h" +#include "atomic_ops.h" + /** * Only allow non-managed icons to be removed (by Python for eg). * Previews & ID's have their own functions to remove icons. @@ -73,12 +77,18 @@ enum { static CLG_LogRef LOG = {"bke.icons"}; +/* Protected by gIconMutex. */ static GHash *gIcons = NULL; +/* Protected by gIconMutex. */ static int gNextIconId = 1; +/* Protected by gIconMutex. */ static int gFirstIconId = 1; +std::mutex gIconMutex; + +/* Not mutex-protected! */ static GHash *gCachedPreviews = NULL; /* Queue of icons for deferred deletion. */ @@ -86,15 +96,16 @@ typedef struct DeferredIconDeleteNode { struct DeferredIconDeleteNode *next; int icon_id; } DeferredIconDeleteNode; +/* Protected by gIconMutex. */ static LockfreeLinkList g_icon_delete_queue; static void icon_free(void *val) { - Icon *icon = val; + Icon *icon = (Icon *)val; if (icon) { if (icon->obj_type == ICON_DATA_GEOM) { - struct Icon_Geom *obj = icon->obj; + struct Icon_Geom *obj = (struct Icon_Geom *)icon->obj; if (obj->mem) { /* coords & colors are part of this memory. */ MEM_freeN((void *)obj->mem); @@ -121,6 +132,12 @@ static void icon_free_data(int icon_id, Icon *icon) if (icon->obj_type == ICON_DATA_ID) { ((ID *)(icon->obj))->icon_id = 0; } + else if (icon->obj_type == ICON_DATA_IMBUF) { + ImBuf *imbuf = (ImBuf *)icon->obj; + if (imbuf) { + IMB_freeImBuf(imbuf); + } + } else if (icon->obj_type == ICON_DATA_PREVIEW) { ((PreviewImage *)(icon->obj))->icon_id = 0; } @@ -131,7 +148,7 @@ static void icon_free_data(int icon_id, Icon *icon) ((struct Icon_Geom *)(icon->obj))->icon_id = 0; } else if (icon->obj_type == ICON_DATA_STUDIOLIGHT) { - StudioLight *sl = icon->obj; + StudioLight *sl = (StudioLight *)icon->obj; if (sl != NULL) { BKE_studiolight_unset_icon_id(sl, icon_id); } @@ -141,19 +158,27 @@ static void icon_free_data(int icon_id, Icon *icon) } } +static Icon *icon_ghash_lookup(int icon_id) +{ + std::scoped_lock lock(gIconMutex); + return (Icon *)BLI_ghash_lookup(gIcons, POINTER_FROM_INT(icon_id)); +} + /* create an id for a new icon and make sure that ids from deleted icons get reused * after the integer number range is used up */ static int get_next_free_id(void) { - BLI_assert(BLI_thread_is_main()); + std::scoped_lock lock(gIconMutex); int startId = gFirstIconId; /* if we haven't used up the int number range, we just return the next int */ if (gNextIconId >= gFirstIconId) { - return gNextIconId++; + int next_id = gNextIconId++; + return next_id; } - /* now we try to find the smallest icon id not stored in the gIcons hash */ + /* Now we try to find the smallest icon id not stored in the gIcons hash. + * Don't use icon_ghash_lookup here, it would lock recursively (dead-lock). */ while (BLI_ghash_lookup(gIcons, POINTER_FROM_INT(startId)) && startId >= gFirstIconId) { startId++; } @@ -203,7 +228,7 @@ void BKE_icons_free(void) void BKE_icons_deferred_free(void) { - BLI_assert(BLI_thread_is_main()); + std::scoped_lock lock(gIconMutex); for (DeferredIconDeleteNode *node = (DeferredIconDeleteNode *)BLI_linklist_lockfree_begin(&g_icon_delete_queue); @@ -216,7 +241,8 @@ void BKE_icons_deferred_free(void) static PreviewImage *previewimg_create_ex(size_t deferred_data_size) { - PreviewImage *prv_img = MEM_mallocN(sizeof(PreviewImage) + deferred_data_size, "img_prv"); + PreviewImage *prv_img = (PreviewImage *)MEM_mallocN(sizeof(PreviewImage) + deferred_data_size, + "img_prv"); memset(prv_img, 0, sizeof(*prv_img)); /* leave deferred data dirty */ if (deferred_data_size) { @@ -224,7 +250,7 @@ static PreviewImage *previewimg_create_ex(size_t deferred_data_size) } for (int i = 0; i < NUM_ICON_SIZES; i++) { - prv_img->flag[i] |= PRV_CHANGED; + prv_img->flag[i] |= (PRV_CHANGED | PRV_UNFINISHED); prv_img->changed_timestamp[i] = 0; } return prv_img; @@ -281,7 +307,7 @@ void BKE_previewimg_clear_single(struct PreviewImage *prv, enum eIconSizes size) GPU_texture_free(prv->gputexture[size]); } prv->h[size] = prv->w[size] = 0; - prv->flag[size] |= PRV_CHANGED; + prv->flag[size] |= (PRV_CHANGED | PRV_UNFINISHED); prv->flag[size] &= ~PRV_USER_EDITED; prv->changed_timestamp[size] = 0; } @@ -289,7 +315,7 @@ void BKE_previewimg_clear_single(struct PreviewImage *prv, enum eIconSizes size) void BKE_previewimg_clear(struct PreviewImage *prv) { for (int i = 0; i < NUM_ICON_SIZES; i++) { - BKE_previewimg_clear_single(prv, i); + BKE_previewimg_clear_single(prv, (eIconSizes)i); } } @@ -298,10 +324,10 @@ PreviewImage *BKE_previewimg_copy(const PreviewImage *prv) PreviewImage *prv_img = NULL; if (prv) { - prv_img = MEM_dupallocN(prv); + prv_img = (PreviewImage *)MEM_dupallocN(prv); for (int i = 0; i < NUM_ICON_SIZES; i++) { if (prv->rect[i]) { - prv_img->rect[i] = MEM_dupallocN(prv->rect[i]); + prv_img->rect[i] = (uint *)MEM_dupallocN(prv->rect[i]); } prv_img->gputexture[i] = NULL; } @@ -344,7 +370,7 @@ PreviewImage **BKE_previewimg_id_get_p(const ID *id) ID_PRV_CASE(ID_LA, Light); ID_PRV_CASE(ID_IM, Image); ID_PRV_CASE(ID_BR, Brush); - // ID_PRV_CASE(ID_OB, Object); + ID_PRV_CASE(ID_OB, Object); ID_PRV_CASE(ID_GR, Collection); ID_PRV_CASE(ID_SCE, Scene); ID_PRV_CASE(ID_SCR, bScreen); @@ -384,21 +410,6 @@ PreviewImage *BKE_previewimg_id_ensure(ID *id) return NULL; } -void BKE_previewimg_deferred_release(PreviewImage *prv) -{ - if (prv) { - if (prv->tag & PRV_TAG_DEFFERED_RENDERING) { - /* We cannot delete the preview while it is being loaded in another thread... */ - prv->tag |= PRV_TAG_DEFFERED_DELETE; - return; - } - if (prv->icon_id) { - BKE_icon_delete(prv->icon_id); - } - BKE_previewimg_freefunc(prv); - } -} - void BKE_previewimg_id_custom_set(ID *id, const char *path) { PreviewImage **prv = BKE_previewimg_id_get_p(id); @@ -420,9 +431,30 @@ void BKE_previewimg_id_custom_set(ID *id, const char *path) } } +bool BKE_previewimg_id_supports_jobs(const ID *id) +{ + return ELEM(GS(id->name), ID_OB, ID_MA, ID_TE, ID_LA, ID_WO, ID_IM, ID_BR); +} + +void BKE_previewimg_deferred_release(PreviewImage *prv) +{ + if (prv) { + if (prv->tag & PRV_TAG_DEFFERED_RENDERING) { + /* We cannot delete the preview while it is being loaded in another thread... */ + prv->tag |= PRV_TAG_DEFFERED_DELETE; + return; + } + if (prv->icon_id) { + BKE_icon_delete(prv->icon_id); + } + BKE_previewimg_freefunc(prv); + } +} + PreviewImage *BKE_previewimg_cached_get(const char *name) { - return BLI_ghash_lookup(gCachedPreviews, name); + BLI_assert(BLI_thread_is_main()); + return (PreviewImage *)BLI_ghash_lookup(gCachedPreviews, name); } /** @@ -430,6 +462,8 @@ PreviewImage *BKE_previewimg_cached_get(const char *name) */ PreviewImage *BKE_previewimg_cached_ensure(const char *name) { + BLI_assert(BLI_thread_is_main()); + PreviewImage *prv = NULL; void **key_p, **prv_p; @@ -437,7 +471,7 @@ PreviewImage *BKE_previewimg_cached_ensure(const char *name) *key_p = BLI_strdup(name); *prv_p = BKE_previewimg_create(); } - prv = *prv_p; + prv = *(PreviewImage **)prv_p; BLI_assert(prv); return prv; @@ -445,24 +479,27 @@ PreviewImage *BKE_previewimg_cached_ensure(const char *name) /** * Generate a PreviewImage from given file path, using thumbnails management, if not yet existing. + * Does not actually generate the preview, #BKE_previewimg_ensure() must be called for that. */ PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name, const char *path, const int source, bool force_update) { + BLI_assert(BLI_thread_is_main()); + PreviewImage *prv = NULL; void **prv_p; prv_p = BLI_ghash_lookup_p(gCachedPreviews, name); if (prv_p) { - prv = *prv_p; + prv = *(PreviewImage **)prv_p; BLI_assert(prv); } if (prv && force_update) { - const char *prv_deferred_data = PRV_DEFERRED_DATA(prv); + const char *prv_deferred_data = (char *)PRV_DEFERRED_DATA(prv); if (((int)prv_deferred_data[0] == source) && STREQ(&prv_deferred_data[1], path)) { /* If same path, no need to re-allocate preview, just clear it up. */ BKE_previewimg_clear(prv); @@ -491,7 +528,9 @@ PreviewImage *BKE_previewimg_cached_thumbnail_read(const char *name, void BKE_previewimg_cached_release(const char *name) { - PreviewImage *prv = BLI_ghash_popkey(gCachedPreviews, name, MEM_freeN); + BLI_assert(BLI_thread_is_main()); + + PreviewImage *prv = (PreviewImage *)BLI_ghash_popkey(gCachedPreviews, name, MEM_freeN); BKE_previewimg_deferred_release(prv); } @@ -508,12 +547,12 @@ void BKE_previewimg_ensure(PreviewImage *prv, const int size) if (do_icon || do_preview) { ImBuf *thumb; - char *prv_deferred_data = PRV_DEFERRED_DATA(prv); + char *prv_deferred_data = (char *)PRV_DEFERRED_DATA(prv); int source = prv_deferred_data[0]; char *path = &prv_deferred_data[1]; int icon_w, icon_h; - thumb = IMB_thumb_manage(path, THB_LARGE, source); + thumb = IMB_thumb_manage(path, THB_LARGE, (ThumbSource)source); if (thumb) { /* PreviewImage assumes premultiplied alhpa... */ @@ -522,8 +561,8 @@ void BKE_previewimg_ensure(PreviewImage *prv, const int size) if (do_preview) { prv->w[ICON_SIZE_PREVIEW] = thumb->x; prv->h[ICON_SIZE_PREVIEW] = thumb->y; - prv->rect[ICON_SIZE_PREVIEW] = MEM_dupallocN(thumb->rect); - prv->flag[ICON_SIZE_PREVIEW] &= ~(PRV_CHANGED | PRV_USER_EDITED); + prv->rect[ICON_SIZE_PREVIEW] = (uint *)MEM_dupallocN(thumb->rect); + prv->flag[ICON_SIZE_PREVIEW] &= ~(PRV_CHANGED | PRV_USER_EDITED | PRV_UNFINISHED); } if (do_icon) { if (thumb->x > thumb->y) { @@ -541,8 +580,8 @@ void BKE_previewimg_ensure(PreviewImage *prv, const int size) IMB_scaleImBuf(thumb, icon_w, icon_h); prv->w[ICON_SIZE_ICON] = icon_w; prv->h[ICON_SIZE_ICON] = icon_h; - prv->rect[ICON_SIZE_ICON] = MEM_dupallocN(thumb->rect); - prv->flag[ICON_SIZE_ICON] &= ~(PRV_CHANGED | PRV_USER_EDITED); + prv->rect[ICON_SIZE_ICON] = (uint *)MEM_dupallocN(thumb->rect); + prv->flag[ICON_SIZE_ICON] &= ~(PRV_CHANGED | PRV_USER_EDITED | PRV_UNFINISHED); } IMB_freeImBuf(thumb); } @@ -550,6 +589,38 @@ void BKE_previewimg_ensure(PreviewImage *prv, const int size) } } +/** + * Create an #ImBuf holding a copy of the preview image buffer in \a prv. + * \note The returned image buffer has to be free'd (#IMB_freeImBuf()). + */ +ImBuf *BKE_previewimg_to_imbuf(PreviewImage *prv, const int size) +{ + const unsigned int w = prv->w[size]; + const unsigned int h = prv->h[size]; + const unsigned int *rect = prv->rect[size]; + + ImBuf *ima = NULL; + + if (w > 0 && h > 0 && rect) { + /* first allocate imbuf for copying preview into it */ + ima = IMB_allocImBuf(w, h, 32, IB_rect); + memcpy(ima->rect, rect, w * h * sizeof(*ima->rect)); + } + + return ima; +} + +void BKE_previewimg_finish(PreviewImage *prv, const int size) +{ + /* Previews may be calculated on a thread. */ + atomic_fetch_and_and_int16(&prv->flag[size], ~PRV_UNFINISHED); +} + +bool BKE_previewimg_is_finished(const PreviewImage *prv, const int size) +{ + return (prv->flag[size] & PRV_UNFINISHED) == 0; +} + void BKE_previewimg_blend_write(BlendWriter *writer, const PreviewImage *prv) { /* Note we write previews also for undo steps. It takes up some memory, @@ -587,6 +658,7 @@ void BKE_previewimg_blend_read(BlendDataReader *reader, PreviewImage *prv) BLO_read_data_address(reader, &prv->rect[i]); } prv->gputexture[i] = NULL; + prv->flag[i] |= PRV_UNFINISHED; } prv->icon_id = 0; prv->tag = 0; @@ -594,15 +666,13 @@ void BKE_previewimg_blend_read(BlendDataReader *reader, PreviewImage *prv) void BKE_icon_changed(const int icon_id) { - BLI_assert(BLI_thread_is_main()); - Icon *icon = NULL; if (!icon_id || G.background) { return; } - icon = BLI_ghash_lookup(gIcons, POINTER_FROM_INT(icon_id)); + icon = icon_ghash_lookup(icon_id); if (icon) { /* We *only* expect ID-tied icons here, not non-ID icon/preview! */ @@ -617,7 +687,7 @@ void BKE_icon_changed(const int icon_id) /* If we have previews, they all are now invalid changed. */ if (p_prv && *p_prv) { for (int i = 0; i < NUM_ICON_SIZES; i++) { - (*p_prv)->flag[i] |= PRV_CHANGED; + (*p_prv)->flag[i] |= (PRV_CHANGED | PRV_UNFINISHED); (*p_prv)->changed_timestamp[i]++; } } @@ -626,7 +696,7 @@ void BKE_icon_changed(const int icon_id) static Icon *icon_create(int icon_id, int obj_type, void *obj) { - Icon *new_icon = MEM_mallocN(sizeof(Icon), __func__); + Icon *new_icon = (Icon *)MEM_mallocN(sizeof(Icon), __func__); new_icon->obj_type = obj_type; new_icon->obj = obj; @@ -637,7 +707,10 @@ static Icon *icon_create(int icon_id, int obj_type, void *obj) new_icon->drawinfo = NULL; new_icon->drawinfo_free = NULL; - BLI_ghash_insert(gIcons, POINTER_FROM_INT(icon_id), new_icon); + { + std::scoped_lock lock(gIconMutex); + BLI_ghash_insert(gIcons, POINTER_FROM_INT(icon_id), new_icon); + } return new_icon; } @@ -764,13 +837,43 @@ int BKE_icon_preview_ensure(ID *id, PreviewImage *preview) return preview->icon_id; } +/** + * Create an icon as owner or \a ibuf. The icon-ID is not stored in \a ibuf, it needs to be stored + * separately. + * \note Transforms ownership of \a ibuf to the newly created icon. + */ +int BKE_icon_imbuf_create(ImBuf *ibuf) +{ + int icon_id = get_next_free_id(); + + Icon *icon = icon_create(icon_id, ICON_DATA_IMBUF, ibuf); + icon->flag = ICON_FLAG_MANAGED; + + return icon_id; +} + +ImBuf *BKE_icon_imbuf_get_buffer(int icon_id) +{ + Icon *icon = icon_ghash_lookup(icon_id); + if (!icon) { + CLOG_ERROR(&LOG, "no icon for icon ID: %d", icon_id); + return NULL; + } + if (icon->obj_type != ICON_DATA_IMBUF) { + CLOG_ERROR(&LOG, "icon ID does not refer to an imbuf icon: %d", icon_id); + return NULL; + } + + return (ImBuf *)icon->obj; +} + Icon *BKE_icon_get(const int icon_id) { BLI_assert(BLI_thread_is_main()); Icon *icon = NULL; - icon = BLI_ghash_lookup(gIcons, POINTER_FROM_INT(icon_id)); + icon = icon_ghash_lookup(icon_id); if (!icon) { CLOG_ERROR(&LOG, "no icon for icon ID: %d", icon_id); @@ -782,10 +885,9 @@ Icon *BKE_icon_get(const int icon_id) void BKE_icon_set(const int icon_id, struct Icon *icon) { - BLI_assert(BLI_thread_is_main()); - void **val_p; + std::scoped_lock lock(gIconMutex); if (BLI_ghash_ensure_p(gIcons, POINTER_FROM_INT(icon_id), &val_p)) { CLOG_ERROR(&LOG, "icon already set: %d", icon_id); return; @@ -796,8 +898,10 @@ void BKE_icon_set(const int icon_id, struct Icon *icon) static void icon_add_to_deferred_delete_queue(int icon_id) { - DeferredIconDeleteNode *node = MEM_mallocN(sizeof(DeferredIconDeleteNode), __func__); + DeferredIconDeleteNode *node = (DeferredIconDeleteNode *)MEM_mallocN( + sizeof(DeferredIconDeleteNode), __func__); node->icon_id = icon_id; + /* Doesn't need lock. */ BLI_linklist_lockfree_insert(&g_icon_delete_queue, (LockfreeLinkNode *)node); } @@ -815,6 +919,7 @@ void BKE_icon_id_delete(struct ID *id) } BKE_icons_deferred_free(); + std::scoped_lock lock(gIconMutex); BLI_ghash_remove(gIcons, POINTER_FROM_INT(icon_id), NULL, icon_free); } @@ -828,8 +933,8 @@ bool BKE_icon_delete(const int icon_id) return false; } - Icon *icon = BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), NULL); - if (icon) { + std::scoped_lock lock(gIconMutex); + if (Icon *icon = (Icon *)BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), NULL)) { icon_free_data(icon_id, icon); icon_free(icon); return true; @@ -845,7 +950,9 @@ bool BKE_icon_delete_unmanaged(const int icon_id) return false; } - Icon *icon = BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), NULL); + std::scoped_lock lock(gIconMutex); + + Icon *icon = (Icon *)BLI_ghash_popkey(gIcons, POINTER_FROM_INT(icon_id), NULL); if (icon) { if (UNLIKELY(icon->flag & ICON_FLAG_MANAGED)) { BLI_ghash_insert(gIcons, POINTER_FROM_INT(icon_id), icon); @@ -880,50 +987,50 @@ int BKE_icon_geom_ensure(struct Icon_Geom *geom) return geom->icon_id; } -struct Icon_Geom *BKE_icon_geom_from_memory(const uchar *data, size_t data_len) +struct Icon_Geom *BKE_icon_geom_from_memory(uchar *data, size_t data_len) { BLI_assert(BLI_thread_is_main()); if (data_len <= 8) { - goto fail; + return nullptr; } + /* Wrapper for RAII early exit cleanups. */ + std::unique_ptr<uchar> data_wrapper(std::move(data)); + /* Skip the header. */ data_len -= 8; const int div = 3 * 2 * 3; const int coords_len = data_len / div; if (coords_len * div != data_len) { - goto fail; + return nullptr; } const uchar header[4] = {'V', 'C', 'O', 0}; - const uchar *p = data; + uchar *p = data_wrapper.get(); if (memcmp(p, header, ARRAY_SIZE(header)) != 0) { - goto fail; + return nullptr; } p += 4; - struct Icon_Geom *geom = MEM_mallocN(sizeof(*geom), __func__); + struct Icon_Geom *geom = (struct Icon_Geom *)MEM_mallocN(sizeof(*geom), __func__); geom->coords_range[0] = (int)*p++; geom->coords_range[1] = (int)*p++; /* x, y ignored for now */ p += 2; geom->coords_len = coords_len; - geom->coords = (void *)p; - geom->colors = (void *)(p + (data_len / 3)); + geom->coords = reinterpret_cast<decltype(geom->coords)>(p); + geom->colors = reinterpret_cast<decltype(geom->colors)>(p + (data_len / 3)); geom->icon_id = 0; - geom->mem = data; + /* Move buffer ownership to C buffer. */ + geom->mem = data_wrapper.release(); return geom; - -fail: - MEM_freeN((void *)data); - return NULL; } struct Icon_Geom *BKE_icon_geom_from_file(const char *filename) { BLI_assert(BLI_thread_is_main()); size_t data_len; - uchar *data = BLI_file_read_binary_as_mem(filename, 0, &data_len); + uchar *data = (uchar *)BLI_file_read_binary_as_mem(filename, 0, &data_len); if (data == NULL) { return NULL; } diff --git a/source/blender/blenloader/intern/readblenentry.c b/source/blender/blenloader/intern/readblenentry.c index 3a1ccbeda01..296480fc2e4 100644 --- a/source/blender/blenloader/intern/readblenentry.c +++ b/source/blender/blenloader/intern/readblenentry.c @@ -40,6 +40,7 @@ #include "DNA_genfile.h" #include "DNA_sdna_types.h" +#include "BKE_icons.h" #include "BKE_idtype.h" #include "BKE_main.h" @@ -248,6 +249,7 @@ LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *to if (looking) { if (bhead->SDNAnr == DNA_struct_find_nr(fd->filesdna, "PreviewImage")) { prv = BLO_library_read_struct(fd, bhead, "PreviewImage"); + if (prv) { memcpy(new_prv, prv, sizeof(PreviewImage)); if (prv->rect[0] && prv->w[0] && prv->h[0]) { @@ -262,6 +264,7 @@ LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *to new_prv->rect[0] = NULL; new_prv->w[0] = new_prv->h[0] = 0; } + BKE_previewimg_finish(new_prv, 0); if (prv->rect[1] && prv->w[1] && prv->h[1]) { bhead = blo_bhead_next(fd, bhead); @@ -275,6 +278,7 @@ LinkNode *BLO_blendhandle_get_previews(BlendHandle *bh, int ofblocktype, int *to new_prv->rect[1] = NULL; new_prv->w[1] = new_prv->h[1] = 0; } + BKE_previewimg_finish(new_prv, 1); MEM_freeN(prv); } } diff --git a/source/blender/editors/interface/interface_icons.c b/source/blender/editors/interface/interface_icons.c index 90f5172f6ec..1c09dc6f4df 100644 --- a/source/blender/editors/interface/interface_icons.c +++ b/source/blender/editors/interface/interface_icons.c @@ -100,11 +100,12 @@ typedef void (*VectorDrawFunc)(int x, int y, int w, int h, float alpha); #define ICON_TYPE_COLOR_TEXTURE 1 #define ICON_TYPE_MONO_TEXTURE 2 #define ICON_TYPE_BUFFER 3 -#define ICON_TYPE_VECTOR 4 -#define ICON_TYPE_GEOM 5 -#define ICON_TYPE_EVENT 6 /* draw keymap entries using custom renderer. */ -#define ICON_TYPE_GPLAYER 7 -#define ICON_TYPE_BLANK 8 +#define ICON_TYPE_IMBUF 4 +#define ICON_TYPE_VECTOR 5 +#define ICON_TYPE_GEOM 6 +#define ICON_TYPE_EVENT 7 /* draw keymap entries using custom renderer. */ +#define ICON_TYPE_GPLAYER 8 +#define ICON_TYPE_BLANK 9 typedef struct DrawInfo { int type; @@ -1147,6 +1148,9 @@ static DrawInfo *icon_create_drawinfo(Icon *icon) if (ELEM(icon_data_type, ICON_DATA_ID, ICON_DATA_PREVIEW)) { di->type = ICON_TYPE_PREVIEW; } + else if (icon_data_type == ICON_DATA_IMBUF) { + di->type = ICON_TYPE_IMBUF; + } else if (icon_data_type == ICON_DATA_GEOM) { di->type = ICON_TYPE_GEOM; } @@ -1262,7 +1266,7 @@ static void icon_create_rect(struct PreviewImage *prv_img, enum eIconSizes size) else if (!prv_img->rect[size]) { prv_img->w[size] = render_size; prv_img->h[size] = render_size; - prv_img->flag[size] |= PRV_CHANGED; + prv_img->flag[size] |= (PRV_CHANGED | PRV_UNFINISHED); prv_img->changed_timestamp[size] = 0; prv_img->rect[size] = MEM_callocN(render_size * render_size * sizeof(uint), "prv_rect"); } @@ -1384,8 +1388,12 @@ void ui_icon_ensure_deferred(const bContext *C, const int icon_id, const bool bi } } -/* only called when icon has changed */ -/* only call with valid pointer from UI_icon_draw */ +/** + * * Only call with valid pointer from UI_icon_draw. + * * Only called when icon has changed. + * + * Note that if an ID doesn't support jobs for preview creation, \a use_job will be ignored. + */ static void icon_set_image(const bContext *C, Scene *scene, ID *id, @@ -1408,7 +1416,7 @@ static void icon_set_image(const bContext *C, const bool delay = prv_img->rect[size] != NULL; icon_create_rect(prv_img, size); - if (use_job) { + if (use_job && BKE_previewimg_id_supports_jobs(id)) { /* Job (background) version */ ED_preview_icon_job( C, prv_img, id, prv_img->rect[size], prv_img->w[size], prv_img->h[size], delay); @@ -1790,7 +1798,14 @@ static void icon_draw_size(float x, /* We need to flush widget base first to ensure correct ordering. */ UI_widgetbase_draw_cache_flush(); - if (di->type == ICON_TYPE_VECTOR) { + if (di->type == ICON_TYPE_IMBUF) { + ImBuf *ibuf = icon->obj; + + GPU_blend(GPU_BLEND_ALPHA_PREMULT); + icon_draw_rect(x, y, w, h, aspect, ibuf->x, ibuf->y, ibuf->rect, alpha, desaturate); + GPU_blend(GPU_BLEND_ALPHA); + } + else if (di->type == ICON_TYPE_VECTOR) { /* vector icons use the uiBlock transformation, they are not drawn * with untransformed coordinates like the other icons */ di->data.vector.func((int)x, (int)y, w, h, 1.0f); @@ -1937,6 +1952,9 @@ static void ui_id_preview_image_render_size( } } +/** + * Note that if an ID doesn't support jobs for preview creation, \a use_job will be ignored. + */ void UI_icon_render_id(const bContext *C, Scene *scene, ID *id, const bool big, const bool use_job) { PreviewImage *pi = BKE_previewimg_id_ensure(id); @@ -1964,12 +1982,7 @@ static void ui_id_icon_render(const bContext *C, ID *id, bool use_jobs) } for (enum eIconSizes i = 0; i < NUM_ICON_SIZES; i++) { - /* check if rect needs to be created; changed - * only set by dynamic icons */ - if (((pi->flag[i] & PRV_CHANGED) || !pi->rect[i])) { - icon_set_image(C, NULL, id, pi, i, use_jobs); - pi->flag[i] &= ~PRV_CHANGED; - } + ui_id_preview_image_render_size(C, NULL, id, pi, i, use_jobs); } } diff --git a/source/blender/editors/render/render_preview.c b/source/blender/editors/render/render_preview.c index 3c103f28d93..579fd86077e 100644 --- a/source/blender/editors/render/render_preview.c +++ b/source/blender/editors/render/render_preview.c @@ -354,13 +354,15 @@ static ID *duplicate_ids(ID *id, const bool allow_failure) case ID_TE: case ID_LA: case ID_WO: { + BLI_assert(BKE_previewimg_id_supports_jobs(id)); ID *id_copy = BKE_id_copy_ex( NULL, id, NULL, LIB_ID_CREATE_LOCAL | LIB_ID_COPY_LOCALIZE | LIB_ID_COPY_NO_ANIMDATA); return id_copy; } + /* These support threading, but don't need duplicating. */ case ID_IM: case ID_BR: - case ID_SCR: + BLI_assert(BKE_previewimg_id_supports_jobs(id)); return NULL; default: if (!allow_failure) { @@ -1374,6 +1376,24 @@ static void other_id_types_preview_render(IconPreview *ip, shader_preview_free(sp); } +/* exported functions */ + +/** + * Find the index to map \a icon_size to data in \a preview_image. + */ +static int icon_previewimg_size_index_get(const IconPreviewSize *icon_size, + const PreviewImage *preview_image) +{ + for (int i = 0; i < NUM_ICON_SIZES; i++) { + if ((preview_image->w[i] == icon_size->sizex) && (preview_image->h[i] == icon_size->sizey)) { + return i; + } + } + + BLI_assert(!"The searched icon size does not match any in the preview image"); + return -1; +} + static void icon_preview_startjob_all_sizes(void *customdata, short *stop, short *do_update, @@ -1398,6 +1418,13 @@ static void icon_preview_startjob_all_sizes(void *customdata, continue; } +#ifndef NDEBUG + { + int size_index = icon_previewimg_size_index_get(cur_size, prv); + BLI_assert(!BKE_previewimg_is_finished(prv, size_index)); + } +#endif + if (ip->id && ELEM(GS(ip->id->name), ID_OB)) { /* Much simpler than the ShaderPreview mess used for other ID types. */ object_preview_render(ip, cur_size); @@ -1460,6 +1487,12 @@ static void icon_preview_endjob(void *customdata) if (ip->owner) { PreviewImage *prv_img = ip->owner; prv_img->tag &= ~PRV_TAG_DEFFERED_RENDERING; + + LISTBASE_FOREACH (IconPreviewSize *, icon_size, &ip->sizes) { + int size_index = icon_previewimg_size_index_get(icon_size, prv_img); + BKE_previewimg_finish(prv_img, size_index); + } + if (prv_img->tag & PRV_TAG_DEFFERED_DELETE) { BLI_assert(prv_img->tag & PRV_TAG_DEFFERED); BKE_previewimg_deferred_release(prv_img); @@ -1576,6 +1609,8 @@ void ED_preview_shader_job(const bContext *C, Scene *scene = CTX_data_scene(C); short id_type = GS(id->name); + BLI_assert(BKE_previewimg_id_supports_jobs(id)); + /* Use workspace render only for buttons Window, * since the other previews are related to the datablock. */ diff --git a/source/blender/editors/util/ed_util.c b/source/blender/editors/util/ed_util.c index 5e3f443ce9c..6547e42f410 100644 --- a/source/blender/editors/util/ed_util.c +++ b/source/blender/editors/util/ed_util.c @@ -546,7 +546,7 @@ static int lib_id_load_custom_preview_exec(bContext *C, wmOperator *op) BKE_previewimg_id_custom_set(id, path); - // WM_event_add_notifier(C, NC_ASSET, NULL); + WM_event_add_notifier(C, NC_ASSET, NULL); return OPERATOR_FINISHED; } diff --git a/source/blender/imbuf/intern/thumbs_blend.c b/source/blender/imbuf/intern/thumbs_blend.c index d5ded02be62..486db07597f 100644 --- a/source/blender/imbuf/intern/thumbs_blend.c +++ b/source/blender/imbuf/intern/thumbs_blend.c @@ -78,15 +78,7 @@ ImBuf *IMB_thumb_load_blend(const char *blen_path, const char *blen_group, const if (STREQ(blockname, blen_id)) { if (img) { - unsigned int w = img->w[ICON_SIZE_PREVIEW]; - unsigned int h = img->h[ICON_SIZE_PREVIEW]; - unsigned int *rect = img->rect[ICON_SIZE_PREVIEW]; - - if (w > 0 && h > 0 && rect) { - /* first allocate imbuf for copying preview into it */ - ima = IMB_allocImBuf(w, h, 32, IB_rect); - memcpy(ima->rect, rect, w * h * sizeof(unsigned int)); - } + ima = BKE_previewimg_to_imbuf(img, ICON_SIZE_PREVIEW); } break; } diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h index 84757e313ed..af8e83a2c44 100644 --- a/source/blender/makesdna/DNA_ID.h +++ b/source/blender/makesdna/DNA_ID.h @@ -360,6 +360,7 @@ enum eIconSizes { enum ePreviewImage_Flag { PRV_CHANGED = (1 << 0), PRV_USER_EDITED = (1 << 1), /* if user-edited, do not auto-update this anymore! */ + PRV_UNFINISHED = (1 << 2), /* The preview is not done rendering yet. */ }; /* for PreviewImage->tag */ |