/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2001-2002 NaN Holding BV. All rights reserved. */ /** \file * \ingroup bke * * Contains management of #Main database itself. */ #include #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" #include "BLI_ghash.h" #include "BLI_mempool.h" #include "BLI_threads.h" #include "DNA_ID.h" #include "BKE_global.h" #include "BKE_idtype.h" #include "BKE_lib_id.h" #include "BKE_lib_query.h" #include "BKE_main.h" #include "BKE_main_idmap.h" #include "BKE_main_namemap.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" Main *BKE_main_new() { Main *bmain = MEM_callocN(sizeof(Main), "new main"); bmain->lock = MEM_mallocN(sizeof(SpinLock), "main lock"); BLI_spin_init((SpinLock *)bmain->lock); bmain->is_global_main = false; return bmain; } void BKE_main_free(Main *mainvar) { /* also call when reading a file, erase all, etc */ ListBase *lbarray[INDEX_ID_MAX]; int a; /* Since we are removing whole main, no need to bother 'properly' * (and slowly) removing each ID from it. */ const int free_flag = (LIB_ID_FREE_NO_MAIN | LIB_ID_FREE_NO_UI_USER | LIB_ID_FREE_NO_USER_REFCOUNT | LIB_ID_FREE_NO_DEG_TAG); MEM_SAFE_FREE(mainvar->blen_thumb); a = set_listbasepointers(mainvar, lbarray); while (a--) { ListBase *lb = lbarray[a]; ID *id, *id_next; for (id = lb->first; id != NULL; id = id_next) { id_next = id->next; #if 1 BKE_id_free_ex(mainvar, id, free_flag, false); #else /* errors freeing ID's can be hard to track down, * enable this so valgrind will give the line number in its error log */ switch (a) { case 0: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 1: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 2: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 3: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 4: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 5: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 6: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 7: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 8: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 9: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 10: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 11: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 12: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 13: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 14: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 15: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 16: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 17: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 18: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 19: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 20: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 21: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 22: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 23: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 24: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 25: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 26: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 27: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 28: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 29: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 30: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 31: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 32: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 33: BKE_id_free_ex(mainvar, id, free_flag, false); break; case 34: BKE_id_free_ex(mainvar, id, free_flag, false); break; default: BLI_assert_unreachable(); break; } #endif } BLI_listbase_clear(lb); } if (mainvar->relations) { BKE_main_relations_free(mainvar); } if (mainvar->id_map) { BKE_main_idmap_destroy(mainvar->id_map); } if (mainvar->name_map) { BKE_main_namemap_destroy(&mainvar->name_map); } BLI_spin_end((SpinLock *)mainvar->lock); MEM_freeN(mainvar->lock); MEM_freeN(mainvar); } bool BKE_main_is_empty(struct Main *bmain) { ID *id_iter; FOREACH_MAIN_ID_BEGIN (bmain, id_iter) { return false; } FOREACH_MAIN_ID_END; return true; } void BKE_main_lock(struct Main *bmain) { BLI_spin_lock((SpinLock *)bmain->lock); } void BKE_main_unlock(struct Main *bmain) { BLI_spin_unlock((SpinLock *)bmain->lock); } static int main_relations_create_idlink_cb(LibraryIDLinkCallbackData *cb_data) { MainIDRelations *bmain_relations = cb_data->user_data; ID *id_self = cb_data->id_self; ID **id_pointer = cb_data->id_pointer; const int cb_flag = cb_data->cb_flag; if (*id_pointer) { MainIDRelationsEntry **entry_p; /* Add `id_pointer` as child of `id_self`. */ { if (!BLI_ghash_ensure_p( bmain_relations->relations_from_pointers, id_self, (void ***)&entry_p)) { *entry_p = MEM_callocN(sizeof(**entry_p), __func__); (*entry_p)->session_uuid = id_self->session_uuid; } else { BLI_assert((*entry_p)->session_uuid == id_self->session_uuid); } MainIDRelationsEntryItem *to_id_entry = BLI_mempool_alloc(bmain_relations->entry_items_pool); to_id_entry->next = (*entry_p)->to_ids; to_id_entry->id_pointer.to = id_pointer; to_id_entry->session_uuid = (*id_pointer != NULL) ? (*id_pointer)->session_uuid : MAIN_ID_SESSION_UUID_UNSET; to_id_entry->usage_flag = cb_flag; (*entry_p)->to_ids = to_id_entry; } /* Add `id_self` as parent of `id_pointer`. */ if (*id_pointer != NULL) { if (!BLI_ghash_ensure_p( bmain_relations->relations_from_pointers, *id_pointer, (void ***)&entry_p)) { *entry_p = MEM_callocN(sizeof(**entry_p), __func__); (*entry_p)->session_uuid = (*id_pointer)->session_uuid; } else { BLI_assert((*entry_p)->session_uuid == (*id_pointer)->session_uuid); } MainIDRelationsEntryItem *from_id_entry = BLI_mempool_alloc( bmain_relations->entry_items_pool); from_id_entry->next = (*entry_p)->from_ids; from_id_entry->id_pointer.from = id_self; from_id_entry->session_uuid = id_self->session_uuid; from_id_entry->usage_flag = cb_flag; (*entry_p)->from_ids = from_id_entry; } } return IDWALK_RET_NOP; } void BKE_main_relations_create(Main *bmain, const short flag) { if (bmain->relations != NULL) { BKE_main_relations_free(bmain); } bmain->relations = MEM_mallocN(sizeof(*bmain->relations), __func__); bmain->relations->relations_from_pointers = BLI_ghash_new( BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); bmain->relations->entry_items_pool = BLI_mempool_create( sizeof(MainIDRelationsEntryItem), 128, 128, BLI_MEMPOOL_NOP); bmain->relations->flag = flag; ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { const int idwalk_flag = IDWALK_READONLY | ((flag & MAINIDRELATIONS_INCLUDE_UI) != 0 ? IDWALK_INCLUDE_UI : 0); /* Ensure all IDs do have an entry, even if they are not connected to any other. */ MainIDRelationsEntry **entry_p; if (!BLI_ghash_ensure_p(bmain->relations->relations_from_pointers, id, (void ***)&entry_p)) { *entry_p = MEM_callocN(sizeof(**entry_p), __func__); (*entry_p)->session_uuid = id->session_uuid; } else { BLI_assert((*entry_p)->session_uuid == id->session_uuid); } BKE_library_foreach_ID_link( NULL, id, main_relations_create_idlink_cb, bmain->relations, idwalk_flag); } FOREACH_MAIN_ID_END; } void BKE_main_relations_free(Main *bmain) { if (bmain->relations != NULL) { if (bmain->relations->relations_from_pointers != NULL) { BLI_ghash_free(bmain->relations->relations_from_pointers, NULL, MEM_freeN); } BLI_mempool_destroy(bmain->relations->entry_items_pool); MEM_freeN(bmain->relations); bmain->relations = NULL; } } void BKE_main_relations_tag_set(struct Main *bmain, const eMainIDRelationsEntryTags tag, const bool value) { if (bmain->relations == NULL) { return; } GHashIterator *gh_iter; for (gh_iter = BLI_ghashIterator_new(bmain->relations->relations_from_pointers); !BLI_ghashIterator_done(gh_iter); BLI_ghashIterator_step(gh_iter)) { MainIDRelationsEntry *entry = BLI_ghashIterator_getValue(gh_iter); if (value) { entry->tags |= tag; } else { entry->tags &= ~tag; } } BLI_ghashIterator_free(gh_iter); } GSet *BKE_main_gset_create(Main *bmain, GSet *gset) { if (gset == NULL) { gset = BLI_gset_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp, __func__); } ID *id; FOREACH_MAIN_ID_BEGIN (bmain, id) { BLI_gset_add(gset, id); } FOREACH_MAIN_ID_END; return gset; } /* Utils for ID's library weak reference API. */ typedef struct LibWeakRefKey { char filepath[FILE_MAX]; char id_name[MAX_ID_NAME]; } LibWeakRefKey; static LibWeakRefKey *lib_weak_key_create(LibWeakRefKey *key, const char *lib_path, const char *id_name) { if (key == NULL) { key = MEM_mallocN(sizeof(*key), __func__); } BLI_strncpy(key->filepath, lib_path, sizeof(key->filepath)); BLI_strncpy(key->id_name, id_name, sizeof(key->id_name)); return key; } static uint lib_weak_key_hash(const void *ptr) { const LibWeakRefKey *string_pair = ptr; uint hash = BLI_ghashutil_strhash_p_murmur(string_pair->filepath); return hash ^ BLI_ghashutil_strhash_p_murmur(string_pair->id_name); } static bool lib_weak_key_cmp(const void *a, const void *b) { const LibWeakRefKey *string_pair_a = a; const LibWeakRefKey *string_pair_b = b; return !(STREQ(string_pair_a->filepath, string_pair_b->filepath) && STREQ(string_pair_a->id_name, string_pair_b->id_name)); } GHash *BKE_main_library_weak_reference_create(Main *bmain) { GHash *library_weak_reference_mapping = BLI_ghash_new( lib_weak_key_hash, lib_weak_key_cmp, __func__); ListBase *lb; FOREACH_MAIN_LISTBASE_BEGIN (bmain, lb) { ID *id_iter = lb->first; if (id_iter == NULL) { continue; } if (!BKE_idtype_idcode_append_is_reusable(GS(id_iter->name))) { continue; } BLI_assert(BKE_idtype_idcode_is_linkable(GS(id_iter->name))); FOREACH_MAIN_LISTBASE_ID_BEGIN (lb, id_iter) { if (id_iter->library_weak_reference == NULL) { continue; } LibWeakRefKey *key = lib_weak_key_create(NULL, id_iter->library_weak_reference->library_filepath, id_iter->library_weak_reference->library_id_name); BLI_ghash_insert(library_weak_reference_mapping, key, id_iter); } FOREACH_MAIN_LISTBASE_ID_END; } FOREACH_MAIN_LISTBASE_END; return library_weak_reference_mapping; } void BKE_main_library_weak_reference_destroy(GHash *library_weak_reference_mapping) { BLI_ghash_free(library_weak_reference_mapping, MEM_freeN, NULL); } ID *BKE_main_library_weak_reference_search_item(GHash *library_weak_reference_mapping, const char *library_filepath, const char *library_id_name) { LibWeakRefKey key; lib_weak_key_create(&key, library_filepath, library_id_name); return (ID *)BLI_ghash_lookup(library_weak_reference_mapping, &key); } void BKE_main_library_weak_reference_add_item(GHash *library_weak_reference_mapping, const char *library_filepath, const char *library_id_name, ID *new_id) { BLI_assert(GS(library_id_name) == GS(new_id->name)); BLI_assert(new_id->library_weak_reference == NULL); BLI_assert(BKE_idtype_idcode_append_is_reusable(GS(new_id->name))); new_id->library_weak_reference = MEM_mallocN(sizeof(*(new_id->library_weak_reference)), __func__); LibWeakRefKey *key = lib_weak_key_create(NULL, library_filepath, library_id_name); void **id_p; const bool already_exist_in_mapping = BLI_ghash_ensure_p( library_weak_reference_mapping, key, &id_p); BLI_assert(!already_exist_in_mapping); UNUSED_VARS_NDEBUG(already_exist_in_mapping); BLI_strncpy(new_id->library_weak_reference->library_filepath, library_filepath, sizeof(new_id->library_weak_reference->library_filepath)); BLI_strncpy(new_id->library_weak_reference->library_id_name, library_id_name, sizeof(new_id->library_weak_reference->library_id_name)); *id_p = new_id; } void BKE_main_library_weak_reference_update_item(GHash *library_weak_reference_mapping, const char *library_filepath, const char *library_id_name, ID *old_id, ID *new_id) { BLI_assert(GS(library_id_name) == GS(old_id->name)); BLI_assert(GS(library_id_name) == GS(new_id->name)); BLI_assert(old_id->library_weak_reference != NULL); BLI_assert(new_id->library_weak_reference == NULL); BLI_assert(STREQ(old_id->library_weak_reference->library_filepath, library_filepath)); BLI_assert(STREQ(old_id->library_weak_reference->library_id_name, library_id_name)); LibWeakRefKey key; lib_weak_key_create(&key, library_filepath, library_id_name); void **id_p = BLI_ghash_lookup_p(library_weak_reference_mapping, &key); BLI_assert(id_p != NULL && *id_p == old_id); new_id->library_weak_reference = old_id->library_weak_reference; old_id->library_weak_reference = NULL; *id_p = new_id; } void BKE_main_library_weak_reference_remove_item(GHash *library_weak_reference_mapping, const char *library_filepath, const char *library_id_name, ID *old_id) { BLI_assert(GS(library_id_name) == GS(old_id->name)); BLI_assert(old_id->library_weak_reference != NULL); LibWeakRefKey key; lib_weak_key_create(&key, library_filepath, library_id_name); BLI_assert(BLI_ghash_lookup(library_weak_reference_mapping, &key) == old_id); BLI_ghash_remove(library_weak_reference_mapping, &key, MEM_freeN, NULL); MEM_SAFE_FREE(old_id->library_weak_reference); } BlendThumbnail *BKE_main_thumbnail_from_imbuf(Main *bmain, ImBuf *img) { BlendThumbnail *data = NULL; if (bmain) { MEM_SAFE_FREE(bmain->blen_thumb); } if (img) { const size_t data_size = BLEN_THUMB_MEMSIZE(img->x, img->y); data = MEM_mallocN(data_size, __func__); IMB_rect_from_float(img); /* Just in case... */ data->width = img->x; data->height = img->y; memcpy(data->rect, img->rect, data_size - sizeof(*data)); } if (bmain) { bmain->blen_thumb = data; } return data; } ImBuf *BKE_main_thumbnail_to_imbuf(Main *bmain, BlendThumbnail *data) { ImBuf *img = NULL; if (!data && bmain) { data = bmain->blen_thumb; } if (data) { img = IMB_allocFromBuffer( (const uint *)data->rect, NULL, (uint)data->width, (uint)data->height, 4); } return img; } void BKE_main_thumbnail_create(struct Main *bmain) { MEM_SAFE_FREE(bmain->blen_thumb); bmain->blen_thumb = MEM_callocN(BLEN_THUMB_MEMSIZE(BLEN_THUMB_SIZE, BLEN_THUMB_SIZE), __func__); bmain->blen_thumb->width = BLEN_THUMB_SIZE; bmain->blen_thumb->height = BLEN_THUMB_SIZE; } const char *BKE_main_blendfile_path(const Main *bmain) { return bmain->filepath; } const char *BKE_main_blendfile_path_from_global(void) { return BKE_main_blendfile_path(G_MAIN); } ListBase *which_libbase(Main *bmain, short type) { switch ((ID_Type)type) { case ID_SCE: return &(bmain->scenes); case ID_LI: return &(bmain->libraries); case ID_OB: return &(bmain->objects); case ID_ME: return &(bmain->meshes); case ID_CU_LEGACY: return &(bmain->curves); case ID_MB: return &(bmain->metaballs); case ID_MA: return &(bmain->materials); case ID_TE: return &(bmain->textures); case ID_IM: return &(bmain->images); case ID_LT: return &(bmain->lattices); case ID_LA: return &(bmain->lights); case ID_CA: return &(bmain->cameras); case ID_IP: return &(bmain->ipo); case ID_KE: return &(bmain->shapekeys); case ID_WO: return &(bmain->worlds); case ID_SCR: return &(bmain->screens); case ID_VF: return &(bmain->fonts); case ID_TXT: return &(bmain->texts); case ID_SPK: return &(bmain->speakers); case ID_LP: return &(bmain->lightprobes); case ID_SO: return &(bmain->sounds); case ID_GR: return &(bmain->collections); case ID_AR: return &(bmain->armatures); case ID_AC: return &(bmain->actions); case ID_NT: return &(bmain->nodetrees); case ID_BR: return &(bmain->brushes); case ID_PA: return &(bmain->particles); case ID_WM: return &(bmain->wm); case ID_GD: return &(bmain->gpencils); case ID_MC: return &(bmain->movieclips); case ID_MSK: return &(bmain->masks); case ID_LS: return &(bmain->linestyles); case ID_PAL: return &(bmain->palettes); case ID_PC: return &(bmain->paintcurves); case ID_CF: return &(bmain->cachefiles); case ID_WS: return &(bmain->workspaces); case ID_CV: return &(bmain->hair_curves); case ID_PT: return &(bmain->pointclouds); case ID_VO: return &(bmain->volumes); case ID_SIM: return &(bmain->simulations); } return NULL; } int set_listbasepointers(Main *bmain, ListBase *lb[/*INDEX_ID_MAX*/]) { /* Libraries may be accessed from pretty much any other ID. */ lb[INDEX_ID_LI] = &(bmain->libraries); lb[INDEX_ID_IP] = &(bmain->ipo); /* Moved here to avoid problems when freeing with animato (aligorith). */ lb[INDEX_ID_AC] = &(bmain->actions); lb[INDEX_ID_KE] = &(bmain->shapekeys); /* Referenced by gpencil, so needs to be before that to avoid crashes. */ lb[INDEX_ID_PAL] = &(bmain->palettes); /* Referenced by nodes, objects, view, scene etc, before to free after. */ lb[INDEX_ID_GD] = &(bmain->gpencils); lb[INDEX_ID_NT] = &(bmain->nodetrees); lb[INDEX_ID_IM] = &(bmain->images); lb[INDEX_ID_TE] = &(bmain->textures); lb[INDEX_ID_MA] = &(bmain->materials); lb[INDEX_ID_VF] = &(bmain->fonts); /* Important!: When adding a new object type, * the specific data should be inserted here. */ lb[INDEX_ID_AR] = &(bmain->armatures); lb[INDEX_ID_CF] = &(bmain->cachefiles); lb[INDEX_ID_ME] = &(bmain->meshes); lb[INDEX_ID_CU_LEGACY] = &(bmain->curves); lb[INDEX_ID_MB] = &(bmain->metaballs); lb[INDEX_ID_CV] = &(bmain->hair_curves); lb[INDEX_ID_PT] = &(bmain->pointclouds); lb[INDEX_ID_VO] = &(bmain->volumes); lb[INDEX_ID_LT] = &(bmain->lattices); lb[INDEX_ID_LA] = &(bmain->lights); lb[INDEX_ID_CA] = &(bmain->cameras); lb[INDEX_ID_TXT] = &(bmain->texts); lb[INDEX_ID_SO] = &(bmain->sounds); lb[INDEX_ID_GR] = &(bmain->collections); lb[INDEX_ID_PAL] = &(bmain->palettes); lb[INDEX_ID_PC] = &(bmain->paintcurves); lb[INDEX_ID_BR] = &(bmain->brushes); lb[INDEX_ID_PA] = &(bmain->particles); lb[INDEX_ID_SPK] = &(bmain->speakers); lb[INDEX_ID_LP] = &(bmain->lightprobes); lb[INDEX_ID_WO] = &(bmain->worlds); lb[INDEX_ID_MC] = &(bmain->movieclips); lb[INDEX_ID_SCR] = &(bmain->screens); lb[INDEX_ID_OB] = &(bmain->objects); lb[INDEX_ID_LS] = &(bmain->linestyles); /* referenced by scenes */ lb[INDEX_ID_SCE] = &(bmain->scenes); lb[INDEX_ID_WS] = &(bmain->workspaces); /* before wm, so it's freed after it! */ lb[INDEX_ID_WM] = &(bmain->wm); lb[INDEX_ID_MSK] = &(bmain->masks); lb[INDEX_ID_SIM] = &(bmain->simulations); lb[INDEX_ID_NULL] = NULL; return (INDEX_ID_MAX - 1); }