Refactor: Core: BKE_main: init/clear code.

This commit allows to initialize and clear a Main struct which
allocation is handled separately.

There is no behavioral change expected from this commit.

This is a requirement for incoming rewrite of the PartialWrite code
(see #122061 and !122118).

Pull Request: https://projects.blender.org/blender/blender/pulls/122118
This commit is contained in:
Bastien Montagne 2024-07-01 14:21:06 +02:00
parent 8f8c825196
commit f1c2a93623
2 changed files with 83 additions and 38 deletions

@ -271,8 +271,38 @@ struct Main {
* \note Always generate a non-global Main, use #BKE_blender_globals_main_replace to put a newly
* created one in `G_MAIN`.
*/
Main *BKE_main_new();
void BKE_main_free(Main *mainvar);
Main *BKE_main_new(void);
/**
* Initialize a Main data-base.
*
* \note Always generate a non-global Main, use #BKE_blender_globals_main_replace to put a newly
* created one in `G_MAIN`.
*/
void BKE_main_init(Main &bmain);
/**
* Make given \a bmain empty again, and free all runtime mappings.
*
* This is similar to a call to #BKE_main_destroy followed by #BKE_main_init, however the internal
* #Main::lock is kept unchanged, and the #Main::is_global_main flag is not reset to `true` either.
*
* \note: Unlike #BKE_main_free, only process the given \a bmain, without handling any potential
* other linked Main.
*/
void BKE_main_clear(Main &bmain);
/**
* Clear and free all data in given \a bmain, but does not free \a bmain itself.
*
* \note: In most cases, #BKE_main_free should be used instead of this function.
*
* \note: Unlike #BKE_main_free, only process the given \a bmain, without handling any potential
* other linked Main.
*/
void BKE_main_destroy(Main &bmain);
/**
* Completely destroy the given \a bmain, and all its linked 'libraries' ones if any (all other
* bmains, following the #Main.next chained list).
*/
void BKE_main_free(Main *bmain);
/** Struct packaging log/report info about a Main merge result. */
struct MainMergeReport {

@ -45,43 +45,36 @@ static CLG_LogRef LOG = {"bke.main"};
Main *BKE_main_new()
{
Main *bmain = static_cast<Main *>(MEM_callocN(sizeof(Main), "new main"));
bmain->lock = static_cast<MainLock *>(MEM_mallocN(sizeof(SpinLock), "main lock"));
BLI_spin_init((SpinLock *)bmain->lock);
bmain->is_global_main = false;
BKE_main_init(*bmain);
return bmain;
}
void BKE_main_init(Main &bmain)
{
bmain.lock = static_cast<MainLock *>(MEM_mallocN(sizeof(SpinLock), "main lock"));
BLI_spin_init(reinterpret_cast<SpinLock *>(bmain.lock));
bmain.is_global_main = false;
/* Just rebuilding the Action Binding to ID* map once is likely cheaper than,
* for every ID, when it's loaded from disk, check whether it's animated or
* not, and then figure out which Main it went into, and then set the flag. */
bmain->is_action_binding_to_id_map_dirty = true;
return bmain;
bmain.is_action_binding_to_id_map_dirty = true;
}
void BKE_main_free(Main *mainvar)
void BKE_main_clear(Main &bmain)
{
/* In case this is called on a 'split-by-libraries' list of mains.
*
* Should not happen in typical usages, but can occur e.g. if a file reading is aborted. */
if (mainvar->next) {
BKE_main_free(mainvar->next);
}
/* Include this check here as the path may be manipulated after creation. */
BLI_assert_msg(!(mainvar->filepath[0] == '/' && mainvar->filepath[1] == '/'),
"'.blend' relative \"//\" must not be used in Main!");
/* also call when reading a file, erase all, etc */
/* 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. */
/* 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);
MEM_SAFE_FREE(bmain.blen_thumb);
a = set_listbasepointers(mainvar, lbarray);
a = set_listbasepointers(&bmain, lbarray);
while (a--) {
ListBase *lb = lbarray[a];
ID *id, *id_next;
@ -89,14 +82,14 @@ void BKE_main_free(Main *mainvar)
for (id = static_cast<ID *>(lb->first); id != nullptr; id = id_next) {
id_next = static_cast<ID *>(id->next);
#if 1
BKE_id_free_ex(mainvar, id, free_flag, false);
BKE_id_free_ex(&bmain, id, free_flag, false);
#else
/* Errors freeing ID's can be hard to track down,
* enable this so VALGRIND or ASAN will give the line number in its error log. */
# define CASE_ID_INDEX(id_index) \
case id_index: \
BKE_id_free_ex(mainvar, id, free_flag, false); \
BKE_id_free_ex(&bmain, id, free_flag, false); \
break
switch ((eID_Index)a) {
@ -153,25 +146,47 @@ void BKE_main_free(Main *mainvar)
BLI_listbase_clear(lb);
}
if (mainvar->relations) {
BKE_main_relations_free(mainvar);
if (bmain.relations) {
BKE_main_relations_free(&bmain);
}
if (mainvar->id_map) {
BKE_main_idmap_destroy(mainvar->id_map);
if (bmain.id_map) {
BKE_main_idmap_destroy(bmain.id_map);
}
/* NOTE: `name_map` in libraries are freed together with the library IDs above. */
if (mainvar->name_map) {
BKE_main_namemap_destroy(&mainvar->name_map);
if (bmain.name_map) {
BKE_main_namemap_destroy(&bmain.name_map);
}
if (mainvar->name_map_global) {
BKE_main_namemap_destroy(&mainvar->name_map_global);
if (bmain.name_map_global) {
BKE_main_namemap_destroy(&bmain.name_map_global);
}
}
void BKE_main_destroy(Main &bmain)
{
BKE_main_clear(bmain);
BLI_spin_end(reinterpret_cast<SpinLock *>(bmain.lock));
MEM_freeN(bmain.lock);
bmain.lock = nullptr;
}
void BKE_main_free(Main *bmain)
{
/* In case this is called on a 'split-by-libraries' list of mains.
*
* Should not happen in typical usages, but can occur e.g. if a file reading is aborted. */
if (bmain->next) {
BKE_main_free(bmain->next);
}
BLI_spin_end((SpinLock *)mainvar->lock);
MEM_freeN(mainvar->lock);
MEM_freeN(mainvar);
/* Include this check here as the path may be manipulated after creation. */
BLI_assert_msg(!(bmain->filepath[0] == '/' && bmain->filepath[1] == '/'),
"'.blend' relative \"//\" must not be used in Main!");
BKE_main_destroy(*bmain);
MEM_freeN(bmain);
}
static bool are_ids_from_different_mains_matching(Main *bmain_1, ID *id_1, Main *bmain_2, ID *id_2)