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:
Bastien Montagne 2020-03-05 16:17:14 +01:00
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
8 changed files with 136 additions and 23 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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