Core: IDManagement: Refactor how 'never unused' IDs are defined.

A few ID types are considered as 'never unused' in Blender (UI related
ones, the Libraries and the Scenes). Local IDs of this type are always
considered as used, even if no other ID links to them.

This was previously fairly weekly defined and implemented (mainly in the
writefile code and the 'tag unused' libquery functions).

This commit formalize this characteristic of ID types by adding a new
`IDTYPE_FLAGS_NEVER_UNUSED` flag, and using this in the few places in
the code that handle unused IDs.
This commit is contained in:
Bastien Montagne
2024-05-24 17:39:09 +02:00
parent 1b18e07232
commit 6426de4489
9 changed files with 36 additions and 9 deletions

View File

@@ -48,6 +48,20 @@ enum {
* data-blocks.
*/
IDTYPE_FLAGS_NO_MEMFILE_UNDO = 1 << 5,
/**
* Indicates that the given IDType is considered as unused.
*
* This is used for some 'root' ID types which typically do not have any actual user (WM.
* Scene...). It prevents e.g. their deletion through the 'Purge' operation.
*
* \note This applies to local IDs. Linked data should essentially ignore this flag. In practice,
* currently, only the Scene ID can be linked among the `never unused` types.
*
* \note The implementation of the expected behaviors related to this characteristic is somewhat
* fragile and inconsistent currently. In most case though, code is expected to ensure that such
* IDs have at least an 'extra user' (#LIB_TAG_EXTRAUSER).
*/
IDTYPE_FLAGS_NEVER_UNUSED = 1 << 6,
};
struct IDCacheKey {

View File

@@ -753,7 +753,8 @@ static bool lib_query_unused_ids_tag_recurse(ID *id, UnusedIDsData &data)
return false;
}
if (ELEM(GS(id->name), ID_WM, ID_WS, ID_SCE, ID_SCR, ID_LI)) {
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(id);
if (id_type->flags & IDTYPE_FLAGS_NEVER_UNUSED) {
/* Some 'root' ID types are never unused (even though they may not have actual users), unless
* their actual user-count is set to 0. */
id_relations->tags |= MAINIDRELATIONS_ENTRY_TAGS_PROCESSED;

View File

@@ -86,7 +86,8 @@ IDTypeInfo IDType_ID_LI = {
/*name*/ "Library",
/*name_plural*/ N_("libraries"),
/*translation_context*/ BLT_I18NCONTEXT_ID_LIBRARY,
/*flags*/ IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA,
/*flags*/ IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA |
IDTYPE_FLAGS_NEVER_UNUSED,
/*asset_type_info*/ nullptr,
/*init_data*/ nullptr,

View File

@@ -1594,7 +1594,7 @@ constexpr IDTypeInfo get_type_info()
info.name = "Scene";
info.name_plural = "scenes";
info.translation_context = BLT_I18NCONTEXT_ID_SCENE;
info.flags = 0;
info.flags = IDTYPE_FLAGS_NEVER_UNUSED;
info.asset_type_info = nullptr;
info.init_data = scene_init_data;

View File

@@ -168,7 +168,7 @@ IDTypeInfo IDType_ID_SCR = {
/*name_plural*/ N_("screens"),
/*translation_context*/ BLT_I18NCONTEXT_ID_SCREEN,
/*flags*/ IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_ONLY_APPEND | IDTYPE_FLAGS_NO_ANIMDATA |
IDTYPE_FLAGS_NO_MEMFILE_UNDO,
IDTYPE_FLAGS_NO_MEMFILE_UNDO | IDTYPE_FLAGS_NEVER_UNUSED,
/*asset_type_info*/ nullptr,
/*init_data*/ nullptr,

View File

@@ -180,7 +180,7 @@ IDTypeInfo IDType_ID_WS = {
/*name_plural*/ N_("workspaces"),
/*translation_context*/ BLT_I18NCONTEXT_ID_WORKSPACE,
/*flags*/ IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_ONLY_APPEND | IDTYPE_FLAGS_NO_ANIMDATA |
IDTYPE_FLAGS_NO_MEMFILE_UNDO,
IDTYPE_FLAGS_NO_MEMFILE_UNDO | IDTYPE_FLAGS_NEVER_UNUSED,
/*asset_type_info*/ nullptr,
/*init_data*/ workspace_init_data,

View File

@@ -1311,10 +1311,11 @@ static bool write_file_handle(Main *mainvar,
/* We only write unused IDs in undo case. */
if (!wd->use_memfile) {
/* NOTE: All Scenes, WindowManagers and WorkSpaces should always be written to disk, so
* their user-count should never be zero currently. */
/* NOTE: All 'never unused' local IDs (Scenes, WindowManagers, ...) should always be
* written to disk, so their user-count should never be zero currently. Note that
* libraries have already been skipped above, as they need a specific handling. */
if (id->us == 0) {
BLI_assert(!ELEM(GS(id->name), ID_SCE, ID_WM, ID_WS));
BLI_assert((id_type->flags & IDTYPE_FLAGS_NEVER_UNUSED) == 0);
continue;
}

View File

@@ -13,6 +13,7 @@
#include "BLI_listbase_wrapper.hh"
#include "BLI_utildefines.h"
#include "BKE_idtype.hh"
#include "BKE_main.hh"
#include "../outliner_intern.hh"
@@ -75,6 +76,15 @@ ListBase TreeDisplayIDOrphans::build_tree(const TreeSourceData &source_data)
bool TreeDisplayIDOrphans::datablock_has_orphans(ListBase &lb) const
{
if (BLI_listbase_is_empty(&lb)) {
return false;
}
const IDTypeInfo *id_type = BKE_idtype_get_info_from_id(static_cast<ID *>(lb.first));
if (id_type->flags & IDTYPE_FLAGS_NEVER_UNUSED) {
/* These ID types are never unused. */
return false;
}
for (ID *id : List<ID>(lb)) {
if (ID_REFCOUNTING_USERS(id) <= 0) {
return true;

View File

@@ -254,7 +254,7 @@ IDTypeInfo IDType_ID_WM = {
/*name_plural*/ N_("window_managers"),
/*translation_context*/ BLT_I18NCONTEXT_ID_WINDOWMANAGER,
/*flags*/ IDTYPE_FLAGS_NO_COPY | IDTYPE_FLAGS_NO_LIBLINKING | IDTYPE_FLAGS_NO_ANIMDATA |
IDTYPE_FLAGS_NO_MEMFILE_UNDO,
IDTYPE_FLAGS_NO_MEMFILE_UNDO | IDTYPE_FLAGS_NEVER_UNUSED,
/*asset_type_info*/ nullptr,
/*init_data*/ nullptr,