DNA/RNA: add 'py_instance' for ID types
authorCampbell Barton <ideasman42@gmail.com>
Mon, 11 Dec 2017 07:37:54 +0000 (18:37 +1100)
committerCampbell Barton <ideasman42@gmail.com>
Mon, 11 Dec 2017 07:37:54 +0000 (18:37 +1100)
Avoid creating new Python instances
every time a scene, object, mesh .. etc are accessed.

Also resolves crashes T28724, T53530
although it's only valid for ID types, not modifiers vertices etc.

Back-ported from blender2.8 branch.

source/blender/blenkernel/intern/library_remap.c
source/blender/blenloader/intern/readfile.c
source/blender/makesdna/DNA_ID.h
source/blender/makesrna/intern/rna_ID.c
source/blender/makesrna/intern/rna_internal.h
source/blender/python/intern/bpy_rna.c

index 62cc5108b4ed2ed0a57a986ec828d62ad96d456c..49effd033e30fbd2f48d86cb87d70967adef46e8 100644 (file)
@@ -959,8 +959,13 @@ void BKE_libblock_free_ex(Main *bmain, void *idv, const bool do_id_user, const b
        DAG_id_type_tag(bmain, type);
 
 #ifdef WITH_PYTHON
+#ifdef WITH_PYTHON_SAFETY
        BPY_id_release(id);
 #endif
+       if (id->py_instance) {
+               BPY_DECREF_RNA_INVALIDATE(id->py_instance);
+       }
+#endif
 
        if (do_id_user) {
                BKE_libblock_relink_ex(bmain, id, NULL, NULL, true);
index caf58ba3f8624d2c72bf4f41e85d2688098011a6..5e12fc969ab1c43fff0d5eb89eb8b5123a93bdeb 100644 (file)
@@ -2210,6 +2210,7 @@ static void direct_link_id(FileData *fd, ID *id)
                /* this case means the data was written incorrectly, it should not happen */
                IDP_DirectLinkGroup_OrFree(&id->properties, (fd->flags & FD_FLAGS_SWITCH_ENDIAN), fd);
        }
+       id->py_instance = NULL;
 }
 
 /* ************ READ CurveMapping *************** */
index 923df70dd4d1238a820fc6e00d49beaafce5ec24..564aa994df12838ebd150a5ce581ce7384433d68 100644 (file)
@@ -131,6 +131,10 @@ typedef struct ID {
        int us;
        int icon_id;
        IDProperty *properties;
+
+       void *py_instance;
+
+       void *pad1;
 } ID;
 
 /**
index 9d42ba55ecc9bd93c66e6928471d7d68182f5294..9205e15671b43da22b25b5977aee7aa81f83934d 100644 (file)
@@ -387,6 +387,14 @@ static void rna_ID_animation_data_free(ID *id, Main *bmain)
        DAG_relations_tag_update(bmain);
 }
 
+#ifdef WITH_PYTHON
+void **rna_ID_instance(PointerRNA *ptr)
+{
+       ID *id = (ID *)ptr->data;
+       return &id->py_instance;
+}
+#endif
+
 static void rna_IDPArray_begin(CollectionPropertyIterator *iter, PointerRNA *ptr)
 {
        IDProperty *prop = (IDProperty *)ptr->data;
@@ -1064,6 +1072,10 @@ static void rna_def_ID(BlenderRNA *brna)
                                        "Tag the ID to update its display data, "
                                        "e.g. when calling :class:`bpy.types.Scene.update`");
        RNA_def_enum_flag(func, "refresh", update_flag_items, 0, "", "Type of updates to perform");
+
+#ifdef WITH_PYTHON
+       RNA_def_struct_register_funcs(srna, NULL, NULL, "rna_ID_instance");
+#endif
 }
 
 static void rna_def_library(BlenderRNA *brna)
index 01c3c8033cf802ca9676e0ebea156f0f073ee892..2d513bd60b4ce781b35e3bf33b7eaf42b019ab1e 100644 (file)
@@ -217,6 +217,7 @@ void rna_ID_name_set(struct PointerRNA *ptr, const char *value);
 struct StructRNA *rna_ID_refine(struct PointerRNA *ptr);
 struct IDProperty *rna_ID_idprops(struct PointerRNA *ptr, bool create);
 void rna_ID_fake_user_set(struct PointerRNA *ptr, int value);
+void **rna_ID_instance(PointerRNA *ptr);
 struct IDProperty *rna_PropertyGroup_idprops(struct PointerRNA *ptr, bool create);
 void rna_PropertyGroup_unregister(struct Main *bmain, struct StructRNA *type);
 struct StructRNA *rna_PropertyGroup_register(struct Main *bmain, struct ReportList *reports, void *data,
index 1a9fd920af5697f68bc90d050e19ea4da6e283bd..deb045c514b2bf357b4be3b7acd45c9adb899537 100644 (file)
@@ -6762,7 +6762,30 @@ PyObject *pyrna_struct_CreatePyObject(PointerRNA *ptr)
        if (ptr->data == NULL && ptr->type == NULL) { /* Operator RNA has NULL data */
                Py_RETURN_NONE;
        }
-       else {
+
+       /* New in 2.8x, since not many types support instancing
+        * we may want to use a flag to avoid looping over all classes. - campbell */
+       void **instance = ptr->data ? RNA_struct_instance(ptr) : NULL;
+       if (instance && *instance) {
+               pyrna = *instance;
+
+               /* Refine may have changed types after the first instance was created. */
+               if (ptr->type == pyrna->ptr.type) {
+                       Py_INCREF(pyrna);
+                       return (PyObject *)pyrna;
+               }
+               else {
+                       /* Existing users will need to use 'type_recast' method. */
+                       Py_DECREF(pyrna);
+                       *instance = NULL;
+                       /* Continue as if no instance was made */
+#if 0          /* no need to assign, will be written to next... */
+                       pyrna = NULL;
+#endif
+               }
+       }
+
+       {
                PyTypeObject *tp = (PyTypeObject *)pyrna_struct_Subtype(ptr);
 
                if (tp) {
@@ -6783,6 +6806,12 @@ PyObject *pyrna_struct_CreatePyObject(PointerRNA *ptr)
                return NULL;
        }
 
+       /* Blender's instance owns a reference (to avoid Python freeing it). */
+       if (instance) {
+               *instance = pyrna;
+               Py_INCREF(pyrna);
+       }
+
        pyrna->ptr = *ptr;
 #ifdef PYRNA_FREE_SUPPORT
        pyrna->freeptr = false;
@@ -7547,7 +7576,6 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
        PyObject *args;
        PyObject *ret = NULL, *py_srna = NULL, *py_class_instance = NULL, *parmitem;
        PyTypeObject *py_class;
-       void **py_class_instance_store = NULL;
        PropertyRNA *parm;
        ParameterIterator iter;
        PointerRNA funcptr;
@@ -7562,7 +7590,7 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
        PyGILState_STATE gilstate;
 
 #ifdef USE_PEDANTIC_WRITE
-       const bool is_operator = RNA_struct_is_a(ptr->type, &RNA_Operator);
+       const bool is_readonly_init = !RNA_struct_is_a(ptr->type, &RNA_Operator);
        // const char *func_id = RNA_function_identifier(func);  /* UNUSED */
        /* testing, for correctness, not operator and not draw function */
        const bool is_readonly = !(RNA_function_flag(func) & FUNC_ALLOW_WRITE);
@@ -7597,10 +7625,6 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
                                        py_class_instance = *instance;
                                        Py_INCREF(py_class_instance);
                                }
-                               else {
-                                       /* store the instance here once its created */
-                                       py_class_instance_store = instance;
-                               }
                        }
                }
                /* end exception */
@@ -7627,7 +7651,7 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
                        if (py_class->tp_init) {
 #ifdef USE_PEDANTIC_WRITE
                                const int prev_write = rna_disallow_writes;
-                               rna_disallow_writes = is_operator ? false : true;  /* only operators can write on __init__ */
+                               rna_disallow_writes = is_readonly_init ? false : true;  /* only operators can write on __init__ */
 #endif
 
                                /* true in most cases even when the class its self doesn't define an __init__ function. */
@@ -7671,10 +7695,6 @@ static int bpy_class_call(bContext *C, PointerRNA *ptr, FunctionRNA *func, Param
                        if (py_class_instance == NULL) {
                                err = -1; /* so the error is not overridden below */
                        }
-                       else if (py_class_instance_store) {
-                               *py_class_instance_store = py_class_instance;
-                               Py_INCREF(py_class_instance);
-                       }
                }
        }