[Bf-blender-cvs] [dd4e0cd3e30] master: DNA/RNA: add 'py_instance' for ID types

Campbell Barton noreply at git.blender.org
Mon Dec 11 08:52:37 CET 2017


Commit: dd4e0cd3e30a88657981064c3f8fcd81612d9394
Author: Campbell Barton
Date:   Mon Dec 11 18:37:54 2017 +1100
Branches: master
https://developer.blender.org/rBdd4e0cd3e30a88657981064c3f8fcd81612d9394

DNA/RNA: add 'py_instance' for ID types

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.

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

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

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

diff --git a/source/blender/blenkernel/intern/library_remap.c b/source/blender/blenkernel/intern/library_remap.c
index 62cc5108b4e..49effd033e3 100644
--- a/source/blender/blenkernel/intern/library_remap.c
+++ b/source/blender/blenkernel/intern/library_remap.c
@@ -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);
diff --git a/source/blender/blenloader/intern/readfile.c b/source/blender/blenloader/intern/readfile.c
index caf58ba3f86..5e12fc969ab 100644
--- a/source/blender/blenloader/intern/readfile.c
+++ b/source/blender/blenloader/intern/readfile.c
@@ -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 *************** */
diff --git a/source/blender/makesdna/DNA_ID.h b/source/blender/makesdna/DNA_ID.h
index 923df70dd4d..564aa994df1 100644
--- a/source/blender/makesdna/DNA_ID.h
+++ b/source/blender/makesdna/DNA_ID.h
@@ -131,6 +131,10 @@ typedef struct ID {
 	int us;
 	int icon_id;
 	IDProperty *properties;
+
+	void *py_instance;
+
+	void *pad1;
 } ID;
 
 /**
diff --git a/source/blender/makesrna/intern/rna_ID.c b/source/blender/makesrna/intern/rna_ID.c
index 9d42ba55ecc..9205e15671b 100644
--- a/source/blender/makesrna/intern/rna_ID.c
+++ b/source/blender/makesrna/intern/rna_ID.c
@@ -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)
diff --git a/source/blender/makesrna/intern/rna_internal.h b/source/blender/makesrna/intern/rna_internal.h
index 01c3c8033cf..2d513bd60b4 100644
--- a/source/blender/makesrna/intern/rna_internal.h
+++ b/source/blender/makesrna/intern/rna_internal.h
@@ -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,
diff --git a/source/blender/python/intern/bpy_rna.c b/source/blender/python/intern/bpy_rna.c
index 1a9fd920af5..deb045c514b 100644
--- a/source/blender/python/intern/bpy_rna.c
+++ b/source/blender/python/intern/bpy_rna.c
@@ -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);
-			}
 		}
 	}



More information about the Bf-blender-cvs mailing list