From a650258158dd7ad8fa9b6cb1b7da749e30ae15c1 Mon Sep 17 00:00:00 2001 From: Sergey Sharybin Date: Mon, 9 Sep 2019 10:25:04 +0200 Subject: Python handlers: Pass depsgraph to events where it makes sense The goal is to make it possible to access evaluated datablocks at a corresponding context. For example, be able to check evaluated state if an object used for rendering. Allows to write scripts in a safe manner for T63548 and T60094. Reviewers: brecht Differential Revision: https://developer.blender.org/D5726 --- source/blender/blenkernel/BKE_callbacks.h | 15 ++++++- source/blender/blenkernel/intern/callbacks.c | 41 +++++++++++++++++- source/blender/blenkernel/intern/scene.c | 9 ++-- source/blender/editors/undo/ed_undo.c | 4 +- .../intern/blender_interface/FRS_freestyle.cpp | 5 ++- source/blender/python/intern/bpy_app_handlers.c | 50 ++++++++++++++++++---- source/blender/render/intern/source/pipeline.c | 32 +++++++------- source/blender/windowmanager/intern/wm_files.c | 20 ++++----- source/blender/windowmanager/intern/wm_init_exit.c | 6 +-- 9 files changed, 133 insertions(+), 49 deletions(-) diff --git a/source/blender/blenkernel/BKE_callbacks.h b/source/blender/blenkernel/BKE_callbacks.h index 380ce30668d..e15cf7fed18 100644 --- a/source/blender/blenkernel/BKE_callbacks.h +++ b/source/blender/blenkernel/BKE_callbacks.h @@ -21,8 +21,10 @@ #ifndef __BKE_CALLBACKS_H__ #define __BKE_CALLBACKS_H__ +struct Depsgraph; struct ID; struct Main; +struct PointerRNA; /** * Common suffix uses: @@ -59,12 +61,21 @@ typedef enum { typedef struct bCallbackFuncStore { struct bCallbackFuncStore *next, *prev; - void (*func)(struct Main *, struct ID *, void *arg); + void (*func)(struct Main *, struct PointerRNA **, const int num_pointers, void *arg); void *arg; short alloc; } bCallbackFuncStore; -void BKE_callback_exec(struct Main *bmain, struct ID *self, eCbEvent evt); +void BKE_callback_exec(struct Main *bmain, + struct PointerRNA **pointers, + const int num_pointers, + eCbEvent evt); +void BKE_callback_exec_null(struct Main *bmain, eCbEvent evt); +void BKE_callback_exec_id(struct Main *bmain, struct ID *id, eCbEvent evt); +void BKE_callback_exec_id_depsgraph(struct Main *bmain, + struct ID *id, + struct Depsgraph *depsgraph, + eCbEvent evt); void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt); void BKE_callback_global_init(void); diff --git a/source/blender/blenkernel/intern/callbacks.c b/source/blender/blenkernel/intern/callbacks.c index cbecba2efe1..367fed818af 100644 --- a/source/blender/blenkernel/intern/callbacks.c +++ b/source/blender/blenkernel/intern/callbacks.c @@ -25,18 +25,55 @@ #include "MEM_guardedalloc.h" +#include "RNA_types.h" +#include "RNA_access.h" + static ListBase callback_slots[BKE_CB_EVT_TOT] = {{NULL}}; -void BKE_callback_exec(struct Main *bmain, struct ID *self, eCbEvent evt) +void BKE_callback_exec(struct Main *bmain, + struct PointerRNA **pointers, + const int num_pointers, + eCbEvent evt) { ListBase *lb = &callback_slots[evt]; bCallbackFuncStore *funcstore; for (funcstore = lb->first; funcstore; funcstore = funcstore->next) { - funcstore->func(bmain, self, funcstore->arg); + funcstore->func(bmain, pointers, num_pointers, funcstore->arg); } } +void BKE_callback_exec_null(struct Main *bmain, eCbEvent evt) +{ + BKE_callback_exec(bmain, NULL, 0, evt); +} + +void BKE_callback_exec_id(struct Main *bmain, struct ID *id, eCbEvent evt) +{ + PointerRNA id_ptr; + RNA_id_pointer_create(id, &id_ptr); + + PointerRNA *pointers[1] = {&id_ptr}; + + BKE_callback_exec(bmain, pointers, 1, evt); +} + +void BKE_callback_exec_id_depsgraph(struct Main *bmain, + struct ID *id, + struct Depsgraph *depsgraph, + eCbEvent evt) +{ + PointerRNA id_ptr; + RNA_id_pointer_create(id, &id_ptr); + + PointerRNA depsgraph_ptr; + RNA_pointer_create(NULL, &RNA_Depsgraph, depsgraph, &depsgraph_ptr); + + PointerRNA *pointers[2] = {&id_ptr, &depsgraph_ptr}; + + BKE_callback_exec(bmain, pointers, 2, evt); +} + void BKE_callback_add(bCallbackFuncStore *funcstore, eCbEvent evt) { ListBase *lb = &callback_slots[evt]; diff --git a/source/blender/blenkernel/intern/scene.c b/source/blender/blenkernel/intern/scene.c index aa812dff877..cd10713897a 100644 --- a/source/blender/blenkernel/intern/scene.c +++ b/source/blender/blenkernel/intern/scene.c @@ -1309,7 +1309,7 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on bool run_callbacks = DEG_id_type_any_updated(depsgraph); if (run_callbacks) { - BKE_callback_exec(bmain, &scene->id, BKE_CB_EVT_DEPSGRAPH_UPDATE_PRE); + BKE_callback_exec_id(bmain, &scene->id, BKE_CB_EVT_DEPSGRAPH_UPDATE_PRE); } for (int pass = 0; pass < 2; pass++) { @@ -1327,7 +1327,8 @@ static void scene_graph_update_tagged(Depsgraph *depsgraph, Main *bmain, bool on BKE_scene_update_sound(depsgraph, bmain); /* Notify python about depsgraph update. */ if (run_callbacks) { - BKE_callback_exec(bmain, &scene->id, BKE_CB_EVT_DEPSGRAPH_UPDATE_POST); + BKE_callback_exec_id_depsgraph( + bmain, &scene->id, depsgraph, BKE_CB_EVT_DEPSGRAPH_UPDATE_POST); } /* Inform editors about possible changes. */ DEG_ids_check_recalc(bmain, depsgraph, scene, view_layer, false); @@ -1362,7 +1363,7 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph, Main *bmain) ViewLayer *view_layer = DEG_get_input_view_layer(depsgraph); /* Keep this first. */ - BKE_callback_exec(bmain, &scene->id, BKE_CB_EVT_FRAME_CHANGE_PRE); + BKE_callback_exec_id(bmain, &scene->id, BKE_CB_EVT_FRAME_CHANGE_PRE); for (int pass = 0; pass < 2; pass++) { /* Update animated image textures for particles, modifiers, gpu, etc, @@ -1384,7 +1385,7 @@ void BKE_scene_graph_update_for_newframe(Depsgraph *depsgraph, Main *bmain) /* Notify editors and python about recalc. */ if (pass == 0) { - BKE_callback_exec(bmain, &scene->id, BKE_CB_EVT_FRAME_CHANGE_POST); + BKE_callback_exec_id_depsgraph(bmain, &scene->id, depsgraph, BKE_CB_EVT_FRAME_CHANGE_POST); } /* Inform editors about possible changes. */ diff --git a/source/blender/editors/undo/ed_undo.c b/source/blender/editors/undo/ed_undo.c index df927726e82..315a4c73e5f 100644 --- a/source/blender/editors/undo/ed_undo.c +++ b/source/blender/editors/undo/ed_undo.c @@ -172,7 +172,7 @@ static int ed_undo_step_impl( /* Note: ignore grease pencil for now. */ Main *bmain = CTX_data_main(C); wm->op_undo_depth++; - BKE_callback_exec( + BKE_callback_exec_id( bmain, &scene->id, (step_for_callback > 0) ? BKE_CB_EVT_UNDO_PRE : BKE_CB_EVT_REDO_PRE); wm->op_undo_depth--; } @@ -220,7 +220,7 @@ static int ed_undo_step_impl( Main *bmain = CTX_data_main(C); scene = CTX_data_scene(C); wm->op_undo_depth++; - BKE_callback_exec( + BKE_callback_exec_id( bmain, &scene->id, step_for_callback > 0 ? BKE_CB_EVT_UNDO_POST : BKE_CB_EVT_REDO_POST); wm->op_undo_depth--; } diff --git a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp index afa14d9b1f1..07839ac6e61 100644 --- a/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp +++ b/source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp @@ -80,7 +80,10 @@ static AppView *view = NULL; static FreestyleLineSet lineset_buffer; static bool lineset_copied = false; -static void load_post_callback(struct Main * /*main*/, struct ID * /*id*/, void * /*arg*/) +static void load_post_callback(struct Main * /*main*/, + struct PointerRNA ** /*pointers*/, + const int /*num_pointers*/, + void * /*arg*/) { lineset_copied = false; } diff --git a/source/blender/python/intern/bpy_app_handlers.c b/source/blender/python/intern/bpy_app_handlers.c index 77d036532f4..e6a8febbf29 100644 --- a/source/blender/python/intern/bpy_app_handlers.c +++ b/source/blender/python/intern/bpy_app_handlers.c @@ -36,7 +36,10 @@ #include "BPY_extern.h" -void bpy_app_generic_callback(struct Main *main, struct ID *id, void *arg); +void bpy_app_generic_callback(struct Main *main, + struct PointerRNA **pointers, + const int num_pointers, + void *arg); static PyTypeObject BlenderAppCbType; @@ -290,32 +293,58 @@ void BPY_app_handlers_reset(const short do_all) PyGILState_Release(gilstate); } +static PyObject *choose_arguments(PyObject *func, PyObject *args_all, PyObject *args_single) +{ + if (!PyFunction_Check(func)) { + return args_all; + } + PyCodeObject *code = (PyCodeObject *)PyFunction_GetCode(func); + if (code->co_argcount == 1) { + return args_single; + } + return args_all; +} + /* the actual callback - not necessarily called from py */ -void bpy_app_generic_callback(struct Main *UNUSED(main), struct ID *id, void *arg) +void bpy_app_generic_callback(struct Main *UNUSED(main), + struct PointerRNA **pointers, + const int num_pointers, + void *arg) { PyObject *cb_list = py_cb_array[POINTER_AS_INT(arg)]; if (PyList_GET_SIZE(cb_list) > 0) { PyGILState_STATE gilstate = PyGILState_Ensure(); - PyObject *args = PyTuple_New(1); /* save python creating each call */ + const int num_arguments = 2; + PyObject *args_all = PyTuple_New(num_arguments); /* save python creating each call */ + PyObject *args_single = PyTuple_New(1); PyObject *func; PyObject *ret; Py_ssize_t pos; /* setup arguments */ - if (id) { - PointerRNA id_ptr; - RNA_id_pointer_create(id, &id_ptr); - PyTuple_SET_ITEM(args, 0, pyrna_struct_CreatePyObject(&id_ptr)); + for (int i = 0; i < num_pointers; ++i) { + PyTuple_SET_ITEM(args_all, i, pyrna_struct_CreatePyObject(pointers[i])); + } + for (int i = num_pointers; i < num_arguments; ++i) { + PyTuple_SET_ITEM(args_all, i, Py_INCREF_RET(Py_None)); + } + + if (num_pointers == 0) { + PyTuple_SET_ITEM(args_single, 0, Py_INCREF_RET(Py_None)); + } + else if (num_pointers == 1) { + args_single = args_all; } else { - PyTuple_SET_ITEM(args, 0, Py_INCREF_RET(Py_None)); + PyTuple_SET_ITEM(args_single, 0, pyrna_struct_CreatePyObject(pointers[0])); } /* Iterate the list and run the callbacks * note: don't store the list size since the scripts may remove themselves */ for (pos = 0; pos < PyList_GET_SIZE(cb_list); pos++) { func = PyList_GET_ITEM(cb_list, pos); + PyObject *args = choose_arguments(func, args_all, args_single); ret = PyObject_Call(func, args, NULL); if (ret == NULL) { /* Don't set last system variables because they might cause some @@ -332,7 +361,10 @@ void bpy_app_generic_callback(struct Main *UNUSED(main), struct ID *id, void *ar } } - Py_DECREF(args); + Py_DECREF(args_all); + if (args_single != args_all) { + Py_DECREF(args_single); + } PyGILState_Release(gilstate); } diff --git a/source/blender/render/intern/source/pipeline.c b/source/blender/render/intern/source/pipeline.c index 9ba3e272e8c..2c379700c80 100644 --- a/source/blender/render/intern/source/pipeline.c +++ b/source/blender/render/intern/source/pipeline.c @@ -226,7 +226,7 @@ static void stats_background(void *UNUSED(arg), RenderStats *rs) /* NOTE: using G_MAIN seems valid here??? * Not sure it's actually even used anyway, we could as well pass NULL? */ - BKE_callback_exec(G_MAIN, NULL, BKE_CB_EVT_RENDER_STATS); + BKE_callback_exec_null(G_MAIN, BKE_CB_EVT_RENDER_STATS); fputc('\n', stdout); fflush(stdout); @@ -2090,7 +2090,7 @@ void RE_RenderFrame(Render *re, int frame, const bool write_still) { - BKE_callback_exec(re->main, (ID *)scene, BKE_CB_EVT_RENDER_INIT); + BKE_callback_exec_id(re->main, &scene->id, BKE_CB_EVT_RENDER_INIT); /* Ugly global still... * is to prevent preview events and signal subsurfs etc to make full resol. */ @@ -2103,9 +2103,9 @@ void RE_RenderFrame(Render *re, const RenderData rd = scene->r; MEM_reset_peak_memory(); - render_init_depsgraph(re); + BKE_callback_exec_id(re->main, &scene->id, BKE_CB_EVT_RENDER_PRE); - BKE_callback_exec(re->main, (ID *)scene, BKE_CB_EVT_RENDER_PRE); + render_init_depsgraph(re); do_render_all_options(re); @@ -2131,14 +2131,14 @@ void RE_RenderFrame(Render *re, } /* keep after file save */ - BKE_callback_exec(re->main, (ID *)scene, BKE_CB_EVT_RENDER_POST); + BKE_callback_exec_id(re->main, &scene->id, BKE_CB_EVT_RENDER_POST); if (write_still) { - BKE_callback_exec(re->main, (ID *)scene, BKE_CB_EVT_RENDER_WRITE); + BKE_callback_exec_id(re->main, &scene->id, BKE_CB_EVT_RENDER_WRITE); } } - BKE_callback_exec( - re->main, (ID *)scene, G.is_break ? BKE_CB_EVT_RENDER_CANCEL : BKE_CB_EVT_RENDER_COMPLETE); + BKE_callback_exec_id( + re->main, &scene->id, G.is_break ? BKE_CB_EVT_RENDER_CANCEL : BKE_CB_EVT_RENDER_COMPLETE); RE_CleanAfterRender(re); @@ -2429,7 +2429,7 @@ static int do_write_image_or_movie(Render *re, /* NOTE: using G_MAIN seems valid here??? * Not sure it's actually even used anyway, we could as well pass NULL? */ - BKE_callback_exec(G_MAIN, NULL, BKE_CB_EVT_RENDER_STATS); + BKE_callback_exec_null(G_MAIN, BKE_CB_EVT_RENDER_STATS); BLI_timecode_string_from_time_simple(name, sizeof(name), re->i.lastframetime - render_time); printf(" (Saving: %s)\n", name); @@ -2495,7 +2495,7 @@ void RE_RenderAnim(Render *re, const bool is_multiview_name = ((rd.scemode & R_MULTIVIEW) != 0 && (rd.im_format.views_format == R_IMF_VIEWS_INDIVIDUAL)); - BKE_callback_exec(re->main, (ID *)scene, BKE_CB_EVT_RENDER_INIT); + BKE_callback_exec_id(re->main, &scene->id, BKE_CB_EVT_RENDER_INIT); /* do not fully call for each frame, it initializes & pops output window */ if (!render_initialize_from_main(re, &rd, bmain, scene, single_layer, camera_override, 0, 1)) { @@ -2660,8 +2660,8 @@ void RE_RenderAnim(Render *re, re->r.cfra = scene->r.cfra; /* weak.... */ - /* run callbacs before rendering, before the scene is updated */ - BKE_callback_exec(re->main, (ID *)scene, BKE_CB_EVT_RENDER_PRE); + /* run callbacks before rendering, before the scene is updated */ + BKE_callback_exec_id(re->main, &scene->id, BKE_CB_EVT_RENDER_PRE); do_render_all_options(re); totrendered++; @@ -2712,8 +2712,8 @@ void RE_RenderAnim(Render *re, if (G.is_break == false) { /* keep after file save */ - BKE_callback_exec(re->main, (ID *)scene, BKE_CB_EVT_RENDER_POST); - BKE_callback_exec(re->main, (ID *)scene, BKE_CB_EVT_RENDER_WRITE); + BKE_callback_exec_id(re->main, &scene->id, BKE_CB_EVT_RENDER_POST); + BKE_callback_exec_id(re->main, &scene->id, BKE_CB_EVT_RENDER_WRITE); } } } @@ -2731,8 +2731,8 @@ void RE_RenderAnim(Render *re, re->flag &= ~R_ANIMATION; - BKE_callback_exec( - re->main, (ID *)scene, G.is_break ? BKE_CB_EVT_RENDER_CANCEL : BKE_CB_EVT_RENDER_COMPLETE); + BKE_callback_exec_id( + re->main, &scene->id, G.is_break ? BKE_CB_EVT_RENDER_CANCEL : BKE_CB_EVT_RENDER_COMPLETE); BKE_sound_reset_scene_specs(re->pipeline_scene_eval); RE_CleanAfterRender(re); diff --git a/source/blender/windowmanager/intern/wm_files.c b/source/blender/windowmanager/intern/wm_files.c index ae5235c7f58..6a5166d3dec 100644 --- a/source/blender/windowmanager/intern/wm_files.c +++ b/source/blender/windowmanager/intern/wm_files.c @@ -537,16 +537,16 @@ static void wm_file_read_post(bContext *C, if (use_userdef) { if (is_factory_startup) { - BKE_callback_exec(bmain, NULL, BKE_CB_EVT_LOAD_FACTORY_USERDEF_POST); + BKE_callback_exec_null(bmain, BKE_CB_EVT_LOAD_FACTORY_USERDEF_POST); } } if (use_data) { /* important to do before NULL'ing the context */ - BKE_callback_exec(bmain, NULL, BKE_CB_EVT_VERSION_UPDATE); - BKE_callback_exec(bmain, NULL, BKE_CB_EVT_LOAD_POST); + BKE_callback_exec_null(bmain, BKE_CB_EVT_VERSION_UPDATE); + BKE_callback_exec_null(bmain, BKE_CB_EVT_LOAD_POST); if (is_factory_startup) { - BKE_callback_exec(bmain, NULL, BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST); + BKE_callback_exec_null(bmain, BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST); } } @@ -609,7 +609,7 @@ bool WM_file_read(bContext *C, const char *filepath, ReportList *reports) WM_cursor_wait(1); - BKE_callback_exec(CTX_data_main(C), NULL, BKE_CB_EVT_LOAD_PRE); + BKE_callback_exec_null(CTX_data_main(C), BKE_CB_EVT_LOAD_PRE); BLI_timer_on_file_load(); UI_view2d_zoom_cache_reset(); @@ -807,7 +807,7 @@ void wm_homefile_read(bContext *C, } if (use_data) { - BKE_callback_exec(CTX_data_main(C), NULL, BKE_CB_EVT_LOAD_PRE); + BKE_callback_exec_null(CTX_data_main(C), BKE_CB_EVT_LOAD_PRE); BLI_timer_on_file_load(); G.relbase_valid = 0; @@ -1368,7 +1368,7 @@ static bool wm_file_write(bContext *C, const char *filepath, int fileflags, Repo /* Call pre-save callbacks before writing preview, * that way you can generate custom file thumbnail. */ - BKE_callback_exec(bmain, NULL, BKE_CB_EVT_SAVE_PRE); + BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_PRE); /* Enforce full override check/generation on file save. */ BKE_main_override_library_operations_create(bmain, true); @@ -1421,7 +1421,7 @@ static bool wm_file_write(bContext *C, const char *filepath, int fileflags, Repo wm_history_file_update(); } - BKE_callback_exec(bmain, NULL, BKE_CB_EVT_SAVE_POST); + BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_POST); /* run this function after because the file cant be written before the blend is */ if (ibuf_thumb) { @@ -1646,7 +1646,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - BKE_callback_exec(bmain, NULL, BKE_CB_EVT_SAVE_PRE); + BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_PRE); /* check current window and close it if temp */ if (win && WM_window_is_temp_screen(win)) { @@ -1674,7 +1674,7 @@ static int wm_homefile_write_exec(bContext *C, wmOperator *op) G.save_over = 0; - BKE_callback_exec(bmain, NULL, BKE_CB_EVT_SAVE_POST); + BKE_callback_exec_null(bmain, BKE_CB_EVT_SAVE_POST); return OPERATOR_FINISHED; } diff --git a/source/blender/windowmanager/intern/wm_init_exit.c b/source/blender/windowmanager/intern/wm_init_exit.c index 517cbbb6212..12f07b8d10e 100644 --- a/source/blender/windowmanager/intern/wm_init_exit.c +++ b/source/blender/windowmanager/intern/wm_init_exit.c @@ -378,10 +378,10 @@ void WM_init(bContext *C, int argc, const char **argv) * note that recovering the last session does its own callbacks. */ CTX_wm_window_set(C, CTX_wm_manager(C)->windows.first); - BKE_callback_exec(bmain, NULL, BKE_CB_EVT_VERSION_UPDATE); - BKE_callback_exec(bmain, NULL, BKE_CB_EVT_LOAD_POST); + BKE_callback_exec_null(bmain, BKE_CB_EVT_VERSION_UPDATE); + BKE_callback_exec_null(bmain, BKE_CB_EVT_LOAD_POST); if (is_factory_startup) { - BKE_callback_exec(bmain, NULL, BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST); + BKE_callback_exec_null(bmain, BKE_CB_EVT_LOAD_FACTORY_STARTUP_POST); } wm_file_read_report(C, bmain); -- cgit v1.2.3