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
This commit is contained in:
Julian Eisel 2020-12-14 13:21:58 +01:00
parent c25e031049
commit 9363132c86
Notes: blender-bot 2024-03-22 15:57:27 +01:00
Referenced by issue #83829, Crash when opening the Add > Mesh menu or pressing SHIFT+A
Referenced by issue #82819, Merge Asset Browser Milestone 1 (Not Necessarily Feature-Complete)
9 changed files with 275 additions and 100 deletions

View File

@ -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,

View File

@ -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

View File

@ -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;
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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. */

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 */