Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/source
diff options
context:
space:
mode:
authorJulian Eisel <julian@blender.org>2020-12-14 15:21:58 +0300
committerJulian Eisel <julian@blender.org>2020-12-15 19:03:45 +0300
commit9363132c8601ebca6d89168e09bb10f81d6cb03a (patch)
tree59afa11ef38f56060f1d4c7a35bf23adab31424d /source
parentc25e0310497f5228bd04992d6bcd84481d0b0c5b (diff)
Asset System: Various changes to previews in preparation for Asset Browser
* Support defining (not necessarily rendering) icons in threads. Needed so the File Browser can expose file previews with an icon-id to scripts. ** For that, ported `icons.c` to C++, to be able to use scope based mutex locks (cleaner & safer code). Had to do some cleanups and minor refactoring for that. * Added support for ImBuf icons, as a decent way for icons to hold the file preview buffers. * Tag previews as "unfinished" while they render in a thread, for the File Browser to dynamically load previews as they get finished. * Better handle cases where threaded preview generation is requested, but the ID type doesn't support it (fallback to single threaded). This is for general sanity of the code (as in, safety and cleanness) * Enabled asset notifier for custom preview loading operator, was just disabled because `NC_ASSET` wasn't defined in master yet. Part of the first Asset Browser milestone. Check the #asset_browser_milestone_1 project milestone on developer.blender.org. Differential Revision: https://developer.blender.org/D9719 Reviewed by: Bastien Montagne, Brecht Van Lommel
Diffstat (limited to 'source')
-rw-r--r--source/blender/blenkernel/BKE_icons.h25
-rw-r--r--source/blender/blenkernel/CMakeLists.txt2
-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.c4
-rw-r--r--source/blender/editors/interface/interface_icons.c45
-rw-r--r--source/blender/editors/render/render_preview.c37
-rw-r--r--source/blender/editors/util/ed_util.c2
-rw-r--r--source/blender/imbuf/intern/thumbs_blend.c10
-rw-r--r--source/blender/makesdna/DNA_ID.h1
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 */