Python handlers: Pass depsgraph to events where it makes sense
authorSergey Sharybin <sergey.vfx@gmail.com>
Mon, 9 Sep 2019 08:25:04 +0000 (10:25 +0200)
committerSergey Sharybin <sergey.vfx@gmail.com>
Wed, 11 Sep 2019 08:43:27 +0000 (10:43 +0200)
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
source/blender/blenkernel/intern/callbacks.c
source/blender/blenkernel/intern/scene.c
source/blender/editors/undo/ed_undo.c
source/blender/freestyle/intern/blender_interface/FRS_freestyle.cpp
source/blender/python/intern/bpy_app_handlers.c
source/blender/render/intern/source/pipeline.c
source/blender/windowmanager/intern/wm_files.c
source/blender/windowmanager/intern/wm_init_exit.c

index 380ce30668df210c88717ce65380eb5644a2d54c..e15cf7fed18736b8e4ce4c2e765ad589f0913cfc 100644 (file)
 #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);
index cbecba2efe1d44efd1be4391cd5e6fafa39d541b..367fed818af9759de39f3a22a075f3c137775190 100644 (file)
 
 #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];
index aa812dff877a7e8697d5313dc5ccee91bc6372ca..cd10713897a05bc8ebcc8807a60d15220a63e1a0 100644 (file)
@@ -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. */
index df927726e82f29ba48278dd80ac36ebd2f7a0b0c..315a4c73e5f6fb3f2737abb4256a8c33c45c8623 100644 (file)
@@ -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--;
   }
index afa14d9b1f1b03d12b3b94435f1ef540fffbbc09..07839ac6e610eda454993058d919779372583d92 100644 (file)
@@ -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;
 }
index 77d036532f4a1d00f73ca76fca99ec60a762a81e..e6a8febbf29e2c1cf3116e24e0a05c62dea6fa01 100644 (file)
 
 #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);
   }
index 9ba3e272e8cdc540e9d1bcf2e522816dcfc4e326..2c379700c801331638ef8d89a4500650856bc32c 100644 (file)
@@ -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);
index ae5235c7f58dae96a4c529e68a5429603da65e5f..6a5166d3deccf515830cdb00814d10cc7befed31 100644 (file)
@@ -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;
 }
index 517cbbb621267baf191d542d323ec2d36e62dcdd..12f07b8d10ea064d20a4bd05706c671966ef48a9 100644 (file)
@@ -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);