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

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

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

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

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

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

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

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

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

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