[Bf-blender-cvs] [b93025db590] master: Add concept of 'runtime' ID in Main data-base.

Bastien Montagne noreply at git.blender.org
Tue Dec 20 05:17:20 CET 2022


Commit: b93025db590a9b64a68015ac6d4f8c208e8a2a52
Author: Bastien Montagne
Date:   Fri Dec 16 10:13:40 2022 +0900
Branches: master
https://developer.blender.org/rBb93025db590a9b64a68015ac6d4f8c208e8a2a52

Add concept of 'runtime' ID in Main data-base.

Such IDs are tagged with the new `LIB_TAG_RUNTIME`. They are essentially
like any other regular ID, except that they do not get written in .blend
files. They also do not make their linked data usages directly linked.
They do be written in undo steps however.

This tag should be ignored in any non-Main IDs (e.g. evaluated data,
`NO_MAIN`, etc.).

This commit also adds a new RNA ID property, `is_runtime`. This is not a
direct mapping to the DNA tag, as a non-main ID will also return True,
and the property is only editable for Main IDs.

Some basic testing for expected behavior of that new tag was also added
to `blendfile_io` py unittest.

Required for brush asset project, see T101908.

Reviewed By: brecht

Differential Revision: https://developer.blender.org/D16675

===================================================================

M	source/blender/blenloader/intern/readfile.cc
M	source/blender/blenloader/intern/writefile.cc
M	source/blender/makesdna/DNA_ID.h
M	source/blender/makesrna/intern/rna_ID.c
M	tests/python/bl_blendfile_io.py

===================================================================

diff --git a/source/blender/blenloader/intern/readfile.cc b/source/blender/blenloader/intern/readfile.cc
index f7fffd82db4..87d3255056b 100644
--- a/source/blender/blenloader/intern/readfile.cc
+++ b/source/blender/blenloader/intern/readfile.cc
@@ -2020,8 +2020,15 @@ static void direct_link_id_common(
     /* When actually reading a file, we do want to reset/re-generate session UUIDS.
      * In undo case, we want to re-use existing ones. */
     id->session_uuid = MAIN_ID_SESSION_UUID_UNSET;
+
+    /* Runtime IDs should never be written in .blend files (except memfiles from undo). */
+    BLI_assert((id->tag & LIB_TAG_RUNTIME) == 0);
   }
 
+  /* No-main and other types of special IDs should never be written in .blend files. */
+  BLI_assert((id->tag & (LIB_TAG_NO_MAIN | LIB_TAG_NO_USER_REFCOUNT | LIB_TAG_NOT_ALLOCATED)) ==
+             0);
+
   if ((tag & LIB_TAG_TEMP_MAIN) == 0) {
     BKE_lib_libblock_session_uuid_ensure(id);
   }
diff --git a/source/blender/blenloader/intern/writefile.cc b/source/blender/blenloader/intern/writefile.cc
index a964c9bd983..0e3c1645fb4 100644
--- a/source/blender/blenloader/intern/writefile.cc
+++ b/source/blender/blenloader/intern/writefile.cc
@@ -1097,7 +1097,10 @@ static int write_id_direct_linked_data_process_cb(LibraryIDLinkCallbackData *cb_
   }
   BLI_assert(!ID_IS_LINKED(id_self));
   BLI_assert((cb_flag & IDWALK_CB_INDIRECT_USAGE) == 0);
-  UNUSED_VARS_NDEBUG(id_self);
+
+  if (id_self->tag & LIB_TAG_RUNTIME) {
+    return IDWALK_RET_NOP;
+  }
 
   if (cb_flag & IDWALK_CB_DIRECT_WEAK_LINK) {
     id_lib_indirect_weak_link(id);
@@ -1207,12 +1210,18 @@ static bool write_file_handle(Main *mainvar,
 
         /* We only write unused IDs in undo case.
          * NOTE: All Scenes, WindowManagers and WorkSpaces should always be written to disk, so
-         * their user-count should never be nullptr currently. */
+         * their user-count should never be zero currently. */
         if (id->us == 0 && !wd->use_memfile) {
           BLI_assert(!ELEM(GS(id->name), ID_SCE, ID_WM, ID_WS));
           continue;
         }
 
+        if ((id->tag & LIB_TAG_RUNTIME) != 0 && !wd->use_memfile) {
+          /* Runtime IDs are never written to .blend files, and they should not influence
+           * (in)direct status of linked IDs they may use. */
+          continue;
+        }
+
         const bool do_override = !ELEM(override_storage, nullptr, bmain) &&
                                  ID_IS_OVERRIDE_LIBRARY_REAL(id);
 
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index fb23dd8644a..90d44d95139 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -808,6 +808,16 @@ enum {
    * RESET_NEVER
    */
   LIB_TAG_NO_MAIN = 1 << 15,
+  /**
+   * ID is considered as runtime, and should not be saved when writing .blend file, nor influence
+   * (in)direct status of linked data.
+   *
+   * Only meaningful for IDs belonging to regular Main database, all other cases are implicitely
+   * considered runtime-only.
+   *
+   * RESET_NEVER
+   */
+  LIB_TAG_RUNTIME = 1 << 22,
   /**
    * Datablock does not refcount usages of other IDs.
    *
@@ -855,7 +865,7 @@ enum {
  *
  * However a few of these need to be explicitely preserved accross undo steps.
  */
-#define LIB_TAG_KEEP_ON_UNDO (LIB_TAG_EXTRAUSER | LIB_TAG_MISSING)
+#define LIB_TAG_KEEP_ON_UNDO (LIB_TAG_EXTRAUSER | LIB_TAG_MISSING | LIB_TAG_RUNTIME)
 
 /* Tag given ID for an update in all the dependency graphs. */
 typedef enum IDRecalcFlag {
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index f9dbd0bc824..7a13d840c9a 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -574,6 +574,33 @@ IDProperty **rna_ID_idprops(PointerRNA *ptr)
   return &id->properties;
 }
 
+int rna_ID_is_runtime_editable(PointerRNA *ptr, const char **r_info)
+{
+  ID *id = (ID *)ptr->data;
+  /* TODO: This should be abstracted in a BKE function or define, somewhat related to T88555. */
+  if (id->tag & (LIB_TAG_NO_MAIN | LIB_TAG_TEMP_MAIN | LIB_TAG_LOCALIZED |
+                 LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT | LIB_TAG_COPIED_ON_WRITE)) {
+    *r_info =
+        "Cannot edit 'runtime' status of non-blendfile data-blocks, as they are by definition "
+        "always runtime";
+    return 0;
+  }
+
+  return PROP_EDITABLE;
+}
+
+bool rna_ID_is_runtime_get(PointerRNA *ptr)
+{
+  ID *id = (ID *)ptr->data;
+  /* TODO: This should be abstracted in a BKE function or define, somewhat related to T88555. */
+  if (id->tag & (LIB_TAG_NO_MAIN | LIB_TAG_TEMP_MAIN | LIB_TAG_LOCALIZED |
+                 LIB_TAG_COPIED_ON_WRITE_EVAL_RESULT | LIB_TAG_COPIED_ON_WRITE)) {
+    return true;
+  }
+
+  return (id->tag & LIB_TAG_RUNTIME) != 0;
+}
+
 void rna_ID_fake_user_set(PointerRNA *ptr, bool value)
 {
   ID *id = (ID *)ptr->data;
@@ -2030,6 +2057,17 @@ static void rna_def_ID(BlenderRNA *brna)
       "This data-block is not an independent one, but is actually a sub-data of another ID "
       "(typical example: root node trees or master collections)");
 
+  prop = RNA_def_property(srna, "is_runtime_data", PROP_BOOLEAN, PROP_NONE);
+  RNA_def_property_boolean_sdna(prop, NULL, "tag", LIB_TAG_RUNTIME);
+  RNA_def_property_editable_func(prop, "rna_ID_is_runtime_editable");
+  RNA_def_property_boolean_funcs(prop, "rna_ID_is_runtime_get", NULL);
+  RNA_def_property_override_flag(prop, PROPOVERRIDE_NO_COMPARISON);
+  RNA_def_property_ui_text(prop,
+                           "Runtime Data",
+                           "This data-block is runtime data, i.e. it won't be saved in .blend "
+                           "file. Note that e.g. evaluated IDs are always runtime, so this value "
+                           "is only editable for data-blocks in Main data-base");
+
   prop = RNA_def_property(srna, "tag", PROP_BOOLEAN, PROP_NONE);
   RNA_def_property_boolean_sdna(prop, NULL, "tag", LIB_TAG_DOIT);
   RNA_def_property_flag(prop, PROP_LIB_EXCEPTION);
diff --git a/tests/python/bl_blendfile_io.py b/tests/python/bl_blendfile_io.py
index fa63b789751..ae49afdfadb 100644
--- a/tests/python/bl_blendfile_io.py
+++ b/tests/python/bl_blendfile_io.py
@@ -47,8 +47,90 @@ class TestBlendFileSaveLoadBasic(TestHelper):
         assert orig_data == read_data
 
 
+# NOTE: Technically this should rather be in `bl_id_management.py` test, but that file uses `unittest` module,
+#       which makes mixing it with tests system used here and passing extra parameters complicated.
+#       Since the main effect of 'RUNTIME' ID tag is on file save, it can as well be here for now.
+class TestIdRuntimeTag(TestHelper):
+
+    def __init__(self, args):
+        self.args = args
+
+    def unique_blendfile_name(self, base_name):
+        return base_name + self.__class__.__name__ + ".blend"
+
+    def test_basics(self):
+        output_dir = self.args.output_dir
+        self.ensure_path(output_dir)
+        bpy.ops.wm.read_homefile(use_empty=False, use_factory_startup=True)
+
+        obj = bpy.data.objects['Cube']
+        assert obj.is_runtime_data == False
+        assert bpy.context.view_layer.depsgraph.ids['Cube'].is_runtime_data == True
+
+        output_work_path = os.path.join(output_dir, self.unique_blendfile_name("blendfile"))
+        bpy.ops.wm.save_as_mainfile(filepath=output_work_path, check_existing=False, compress=False)
+
+        bpy.ops.wm.open_mainfile(filepath=output_work_path, load_ui=False)
+        obj = bpy.data.objects['Cube']
+        assert obj.is_runtime_data == False
+
+        obj.is_runtime_data = True
+        assert obj.is_runtime_data == True
+
+        bpy.ops.wm.save_as_mainfile(filepath=output_work_path, check_existing=False, compress=False)
+        bpy.ops.wm.open_mainfile(filepath=output_work_path, load_ui=False)
+
+        assert 'Cube' not in bpy.data.objects
+        mesh = bpy.data.meshes['Cube']
+        assert mesh.is_runtime_data == False
+        assert mesh.users == 0
+
+    def test_linking(self):
+        output_dir = self.args.output_dir
+        self.ensure_path(output_dir)
+        bpy.ops.wm.read_homefile(use_empty=False, use_factory_startup=True)
+
+        material = bpy.data.materials.new("LibMaterial")
+        material.use_fake_user = True
+
+        output_lib_path = os.path.join(output_dir, self.unique_blendfile_name("blendlib_runtimetag_basic"))
+        bpy.ops.wm.save_as_mainfile(filepath=output_lib_path, check_existing=False, compress=False)
+
+        bpy.ops.wm.read_homefile(use_empty=False, use_factory_startup=True)
+
+        obj = bpy.data.objects['Cube']
+        assert obj.is_runtime_data == False
+        obj.is_runtime_data = True
+
+        link_dir = os.path.join(output_lib_path, "Material")
+        bpy.ops.wm.link(directory=link_dir, filename="LibMaterial")
+
+        linked_material = bpy.data.materials['LibMaterial']
+        assert linked_material.is_library_indirect == False
+
+        obj.material_slots[0].link = 'OBJECT'
+        obj.material_slots[0].material = linked_material
+
+        output_work_path = os.path.join(output_dir, self.unique_blendfile_name("blendfile"))
+        bpy.ops.wm.save_as_mainfile(filepath=output_work_path, check_existing=False, compress=False)
+
+        # Only usage of this linked material is a runtime ID (object),
+        # so writing .blend file will have properly reset its tag to indirectly linked data.
+        assert linked_material.is_library_indirect == True
+
+        bpy.ops.wm.open_mainfile(filepath=output_work_path, load_ui=False)
+
+        assert 'Cube' not in bpy.data.objects
+        assert 'LibMaterial' not in bpy.data.materials
+        mesh = bpy.data.meshes['Cube']
+        assert mesh.is_runtime_data == False
+        assert mesh.users == 0
+
+
 TESTS = (
     TestBlendFileSaveLoadBasic,
+
+    TestIdRuntimeTag,
 )



More information about the Bf-blender-cvs mailing list