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.
This commit is contained in:
parent
c568985118
commit
dd4e0cd3e3
Notes:
blender-bot
2023-02-14 19:29:56 +01:00
Referenced by issue blender/blender-addons#54558, Make Single User on an action or world in Outliner with the view Blender File crash blender
|
@ -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);
|
||||
|
|
|
@ -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 *************** */
|
||||
|
|
|
@ -131,6 +131,10 @@ typedef struct ID {
|
|||
int us;
|
||||
int icon_id;
|
||||
IDProperty *properties;
|
||||
|
||||
void *py_instance;
|
||||
|
||||
void *pad1;
|
||||
} ID;
|
||||
|
||||
/**
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue