Add an session-wise uuid integer to IDs.
"session-wise" here mean while editing a same .blend file. So creating or opening a new one will reset the uuid counter. This should avoid any overflow in practice. Only IDs added to Main database get an uuid, runtime-only ones are not affected. This is intended to provide undo with a way to find IDs across several 'memory realms' (undo speedup project). No behavior change is expected from this commit itself. Part of T60695. Differential Revision: https://developer.blender.org/D7007
This commit is contained in:
parent
88db9a17ce
commit
c43725e189
Notes:
blender-bot
2023-02-14 05:43:04 +01:00
Referenced by issue #76646, Crash when changing Size in Add Cube pop-up
|
@ -67,6 +67,14 @@ void *BKE_libblock_alloc(struct Main *bmain, short type, const char *name, const
|
|||
ATTR_WARN_UNUSED_RESULT;
|
||||
void BKE_libblock_init_empty(struct ID *id) ATTR_NONNULL(1);
|
||||
|
||||
/* *** ID's session_uuid management. *** */
|
||||
|
||||
/* When an ID's uuid is of that value, it is unset/invalid (e.g. for runtime IDs, etc.). */
|
||||
#define MAIN_ID_SESSION_UUID_UNSET 0
|
||||
|
||||
void BKE_lib_libblock_session_uuid_reset(void);
|
||||
void BKE_lib_libblock_session_uuid_ensure(struct ID *id);
|
||||
|
||||
void *BKE_id_new(struct Main *bmain, const short type, const char *name);
|
||||
void *BKE_id_new_nomain(const short type, const char *name);
|
||||
|
||||
|
|
|
@ -40,22 +40,32 @@ struct ID;
|
|||
struct IDNameLib_Map;
|
||||
struct Main;
|
||||
|
||||
enum {
|
||||
MAIN_IDMAP_TYPE_NAME = 1 << 0,
|
||||
MAIN_IDMAP_TYPE_UUID = 1 << 1,
|
||||
};
|
||||
|
||||
struct IDNameLib_Map *BKE_main_idmap_create(struct Main *bmain,
|
||||
const bool create_valid_ids_set,
|
||||
struct Main *old_bmain) ATTR_WARN_UNUSED_RESULT
|
||||
struct Main *old_bmain,
|
||||
const int idmap_types) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL(1);
|
||||
void BKE_main_idmap_destroy(struct IDNameLib_Map *id_typemap) ATTR_NONNULL();
|
||||
struct Main *BKE_main_idmap_main_get(struct IDNameLib_Map *id_typemap) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL();
|
||||
struct ID *BKE_main_idmap_lookup(struct IDNameLib_Map *id_typemap,
|
||||
short id_type,
|
||||
const char *name,
|
||||
const struct Library *lib) ATTR_WARN_UNUSED_RESULT
|
||||
struct ID *BKE_main_idmap_lookup_name(struct IDNameLib_Map *id_typemap,
|
||||
short id_type,
|
||||
const char *name,
|
||||
const struct Library *lib) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL(1, 3);
|
||||
struct ID *BKE_main_idmap_lookup_id(struct IDNameLib_Map *id_typemap,
|
||||
const struct ID *id) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL(1, 2);
|
||||
|
||||
struct ID *BKE_main_idmap_lookup_uuid(struct IDNameLib_Map *id_typemap,
|
||||
const uint session_uuid) ATTR_WARN_UNUSED_RESULT
|
||||
ATTR_NONNULL(1);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1027,6 +1027,8 @@ void BKE_libblock_management_main_add(Main *bmain, void *idv)
|
|||
id->tag &= ~(LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT);
|
||||
bmain->is_memfile_undo_written = false;
|
||||
BKE_main_unlock(bmain);
|
||||
|
||||
BKE_lib_libblock_session_uuid_ensure(id);
|
||||
}
|
||||
|
||||
/** Remove a data-block from given main (set it to 'NO_MAIN' status). */
|
||||
|
@ -1313,6 +1315,8 @@ void *BKE_libblock_alloc(Main *bmain, short type, const char *name, const int fl
|
|||
/* alphabetic insertion: is in new_id */
|
||||
BKE_main_unlock(bmain);
|
||||
|
||||
BKE_lib_libblock_session_uuid_ensure(id);
|
||||
|
||||
/* TODO to be removed from here! */
|
||||
if ((flag & LIB_ID_CREATE_NO_DEG_TAG) == 0) {
|
||||
DEG_id_type_tag(bmain, type);
|
||||
|
@ -1456,6 +1460,33 @@ void BKE_libblock_init_empty(ID *id)
|
|||
}
|
||||
}
|
||||
|
||||
/* ********** ID session-wise UUID management. ********** */
|
||||
static uint global_session_uuid = 0;
|
||||
|
||||
/** Reset the session-wise uuid counter (used when reading a new file e.g.). */
|
||||
void BKE_lib_libblock_session_uuid_reset()
|
||||
{
|
||||
global_session_uuid = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a session-wise uuid for the given \a id.
|
||||
*
|
||||
* \note "session-wise" here means while editing a given .blend file. Once a new .blend file is
|
||||
* loaded or created, undo history is cleared/reset, and so is the uuid counter.
|
||||
*/
|
||||
void BKE_lib_libblock_session_uuid_ensure(ID *id)
|
||||
{
|
||||
if (id->session_uuid == MAIN_ID_SESSION_UUID_UNSET) {
|
||||
id->session_uuid = atomic_add_and_fetch_uint32(&global_session_uuid, 1);
|
||||
/* In case overflow happens, still assign a valid ID. This way opening files many times works
|
||||
* correctly. */
|
||||
if (UNLIKELY(id->session_uuid == MAIN_ID_SESSION_UUID_UNSET)) {
|
||||
id->session_uuid = atomic_add_and_fetch_uint32(&global_session_uuid, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generic helper to create a new empty data-block of given type in given \a bmain database.
|
||||
*
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
#include "DNA_ID.h"
|
||||
|
||||
#include "BKE_idcode.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_main_idmap.h" /* own include */
|
||||
|
||||
|
@ -65,16 +66,20 @@ struct IDNameLib_TypeMap {
|
|||
*/
|
||||
struct IDNameLib_Map {
|
||||
struct IDNameLib_TypeMap type_maps[MAX_LIBARRAY];
|
||||
struct GHash *uuid_map;
|
||||
struct Main *bmain;
|
||||
struct GSet *valid_id_pointers;
|
||||
int idmap_types;
|
||||
};
|
||||
|
||||
static struct IDNameLib_TypeMap *main_idmap_from_idcode(struct IDNameLib_Map *id_map,
|
||||
short id_type)
|
||||
{
|
||||
for (int i = 0; i < MAX_LIBARRAY; i++) {
|
||||
if (id_map->type_maps[i].id_type == id_type) {
|
||||
return &id_map->type_maps[i];
|
||||
if (id_map->idmap_types & MAIN_IDMAP_TYPE_NAME) {
|
||||
for (int i = 0; i < MAX_LIBARRAY; i++) {
|
||||
if (id_map->type_maps[i].id_type == id_type) {
|
||||
return &id_map->type_maps[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
|
@ -94,9 +99,12 @@ static struct IDNameLib_TypeMap *main_idmap_from_idcode(struct IDNameLib_Map *id
|
|||
*/
|
||||
struct IDNameLib_Map *BKE_main_idmap_create(struct Main *bmain,
|
||||
const bool create_valid_ids_set,
|
||||
struct Main *old_bmain)
|
||||
struct Main *old_bmain,
|
||||
const int idmap_types)
|
||||
{
|
||||
struct IDNameLib_Map *id_map = MEM_mallocN(sizeof(*id_map), __func__);
|
||||
id_map->bmain = bmain;
|
||||
id_map->idmap_types = idmap_types;
|
||||
|
||||
int index = 0;
|
||||
while (index < MAX_LIBARRAY) {
|
||||
|
@ -107,7 +115,22 @@ struct IDNameLib_Map *BKE_main_idmap_create(struct Main *bmain,
|
|||
}
|
||||
BLI_assert(index == MAX_LIBARRAY);
|
||||
|
||||
id_map->bmain = bmain;
|
||||
if (idmap_types & MAIN_IDMAP_TYPE_UUID) {
|
||||
ID *id;
|
||||
id_map->uuid_map = BLI_ghash_int_new(__func__);
|
||||
FOREACH_MAIN_ID_BEGIN (bmain, id) {
|
||||
BLI_assert(id->session_uuid != MAIN_ID_SESSION_UUID_UNSET);
|
||||
void **id_ptr_v;
|
||||
const bool existing_key = BLI_ghash_ensure_p(
|
||||
id_map->uuid_map, POINTER_FROM_UINT(id->session_uuid), &id_ptr_v);
|
||||
BLI_assert(existing_key == false);
|
||||
*id_ptr_v = id;
|
||||
}
|
||||
FOREACH_MAIN_ID_END;
|
||||
}
|
||||
else {
|
||||
id_map->uuid_map = NULL;
|
||||
}
|
||||
|
||||
if (create_valid_ids_set) {
|
||||
id_map->valid_id_pointers = BKE_main_gset_create(bmain, NULL);
|
||||
|
@ -144,10 +167,10 @@ static bool idkey_cmp(const void *a, const void *b)
|
|||
return strcmp(idkey_a->name, idkey_b->name) || (idkey_a->lib != idkey_b->lib);
|
||||
}
|
||||
|
||||
ID *BKE_main_idmap_lookup(struct IDNameLib_Map *id_map,
|
||||
short id_type,
|
||||
const char *name,
|
||||
const Library *lib)
|
||||
ID *BKE_main_idmap_lookup_name(struct IDNameLib_Map *id_map,
|
||||
short id_type,
|
||||
const char *name,
|
||||
const Library *lib)
|
||||
{
|
||||
struct IDNameLib_TypeMap *type_map = main_idmap_from_idcode(id_map, id_type);
|
||||
|
||||
|
@ -188,21 +211,34 @@ ID *BKE_main_idmap_lookup_id(struct IDNameLib_Map *id_map, const ID *id)
|
|||
* when trying to get ID name).
|
||||
*/
|
||||
if (id_map->valid_id_pointers == NULL || BLI_gset_haskey(id_map->valid_id_pointers, id)) {
|
||||
return BKE_main_idmap_lookup(id_map, GS(id->name), id->name + 2, id->lib);
|
||||
return BKE_main_idmap_lookup_name(id_map, GS(id->name), id->name + 2, id->lib);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
ID *BKE_main_idmap_lookup_uuid(struct IDNameLib_Map *id_map, const uint session_uuid)
|
||||
{
|
||||
if (id_map->idmap_types & MAIN_IDMAP_TYPE_UUID) {
|
||||
return BLI_ghash_lookup(id_map->uuid_map, POINTER_FROM_UINT(session_uuid));
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void BKE_main_idmap_destroy(struct IDNameLib_Map *id_map)
|
||||
{
|
||||
struct IDNameLib_TypeMap *type_map = id_map->type_maps;
|
||||
for (int i = 0; i < MAX_LIBARRAY; i++, type_map++) {
|
||||
if (type_map->map) {
|
||||
BLI_ghash_free(type_map->map, NULL, NULL);
|
||||
type_map->map = NULL;
|
||||
MEM_freeN(type_map->keys);
|
||||
if (id_map->idmap_types & MAIN_IDMAP_TYPE_NAME) {
|
||||
struct IDNameLib_TypeMap *type_map = id_map->type_maps;
|
||||
for (int i = 0; i < MAX_LIBARRAY; i++, type_map++) {
|
||||
if (type_map->map) {
|
||||
BLI_ghash_free(type_map->map, NULL, NULL);
|
||||
type_map->map = NULL;
|
||||
MEM_freeN(type_map->keys);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (id_map->idmap_types & MAIN_IDMAP_TYPE_UUID) {
|
||||
BLI_ghash_free(id_map->uuid_map, NULL, NULL);
|
||||
}
|
||||
|
||||
if (id_map->valid_id_pointers != NULL) {
|
||||
BLI_gset_free(id_map->valid_id_pointers, NULL);
|
||||
|
|
|
@ -8056,7 +8056,8 @@ void blo_lib_link_restore(Main *oldmain,
|
|||
Scene *curscene,
|
||||
ViewLayer *cur_view_layer)
|
||||
{
|
||||
struct IDNameLib_Map *id_map = BKE_main_idmap_create(newmain, true, oldmain);
|
||||
struct IDNameLib_Map *id_map = BKE_main_idmap_create(
|
||||
newmain, true, oldmain, MAIN_IDMAP_TYPE_NAME);
|
||||
|
||||
for (WorkSpace *workspace = newmain->workspaces.first; workspace;
|
||||
workspace = workspace->id.next) {
|
||||
|
@ -8801,6 +8802,8 @@ static ID *create_placeholder(Main *mainvar, const short idcode, const char *idn
|
|||
BLI_addtail(lb, ph_id);
|
||||
id_sort_by_name(lb, ph_id, NULL);
|
||||
|
||||
BKE_lib_libblock_session_uuid_ensure(ph_id);
|
||||
|
||||
return ph_id;
|
||||
}
|
||||
|
||||
|
@ -9013,6 +9016,14 @@ static BHead *read_libblock(FileData *fd,
|
|||
oldnewmap_insert(fd->libmap, bhead->old, id, bhead->code);
|
||||
|
||||
BLI_addtail(lb, id);
|
||||
|
||||
if (fd->memfile == NULL) {
|
||||
/* When actually reading a file , we do want to reset/re-generate session uuids.
|
||||
* In unod case, we want to re-use existing ones. */
|
||||
id->session_uuid = MAIN_ID_SESSION_UUID_UNSET;
|
||||
}
|
||||
|
||||
BKE_lib_libblock_session_uuid_ensure(id);
|
||||
}
|
||||
else {
|
||||
/* unknown ID type */
|
||||
|
|
|
@ -446,6 +446,8 @@ static void versions_gpencil_add_main(ListBase *lb, ID *id, const char *name)
|
|||
BKE_id_new_name_validate(lb, id, name);
|
||||
/* alphabetic insertion: is in BKE_id_new_name_validate */
|
||||
|
||||
BKE_lib_libblock_session_uuid_ensure(id);
|
||||
|
||||
if (G.debug & G_DEBUG) {
|
||||
printf("Converted GPencil to ID: %s\n", id->name + 2);
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@
|
|||
extern "C" {
|
||||
#endif
|
||||
|
||||
#include "DNA_defs.h"
|
||||
|
||||
struct FileData;
|
||||
struct GHash;
|
||||
struct GPUTexture;
|
||||
|
@ -243,7 +245,13 @@ typedef struct ID {
|
|||
int us;
|
||||
int icon_id;
|
||||
int recalc;
|
||||
char _pad[4];
|
||||
|
||||
/**
|
||||
* A session-wide unique identifier for a given ID, that remain the same across potential
|
||||
* re-allocations (e.g. due to undo/redo steps).
|
||||
*/
|
||||
unsigned int session_uuid;
|
||||
|
||||
IDProperty *properties;
|
||||
|
||||
/** Reference linked ID which this one overrides. */
|
||||
|
|
|
@ -77,6 +77,7 @@
|
|||
#include "BKE_context.h"
|
||||
#include "BKE_global.h"
|
||||
#include "BKE_idprop.h"
|
||||
#include "BKE_lib_id.h"
|
||||
#include "BKE_lib_override.h"
|
||||
#include "BKE_main.h"
|
||||
#include "BKE_packedFile.h"
|
||||
|
@ -611,6 +612,9 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports)
|
|||
|
||||
UI_view2d_zoom_cache_reset();
|
||||
|
||||
/* Reset session-wise ID UUID counter. */
|
||||
BKE_lib_libblock_session_uuid_reset();
|
||||
|
||||
/* first try to append data from exotic file formats... */
|
||||
/* it throws error box when file doesn't exist and returns -1 */
|
||||
/* note; it should set some error message somewhere... (ton) */
|
||||
|
@ -917,6 +921,9 @@ void wm_homefile_read(bContext *C,
|
|||
}
|
||||
}
|
||||
|
||||
/* Reset session-wise ID UUID counter. */
|
||||
BKE_lib_libblock_session_uuid_reset();
|
||||
|
||||
if (!use_factory_settings || (filepath_startup[0] != '\0')) {
|
||||
if (BLI_access(filepath_startup, R_OK) == 0) {
|
||||
success = BKE_blendfile_read(C,
|
||||
|
|
Loading…
Reference in New Issue