This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/blenkernel/intern/main.c
Bastien Montagne b47a234f98 BKE_Main: Add clear separation between 'temp' mains and global main.
Blender is using more and more temporary Main data-base (historically
for reading linked data, but also now when resyncing liboverrides, for
temp data in asset code, etc.).

This commit aims at making this a bit more formal and defined, by:
* Adding a dedicated flag in Main struct to mark a Main as global.
* Adding some API to replace, or temporarily swap the current global
  Main (`G_MAIN`) by another one.

NOTE: Having to temporarily replace `G_MAIN` is a workaround for the
limitation of current RNA, ideally this should be fixed in RNA itself,
but for now at least having an API helps tracking those cases (since
this is potentially risky operation).

This work is also a preparation for more usages of temp mains in the near
future (Asset Brushes and its presets system e.g. will most likely use
temp mains too).

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D15977
2022-10-05 14:26:45 +02:00

714 lines
21 KiB
C

/* 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 <string.h>
#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);
}