T77086 Animation: Passing Dependency Graph to Drivers

Custom driver functions need access to the dependency graph that is
triggering the evaluation of the driver. This patch passes the
dependency graph pointer through all the animation-related calls.

Instead of passing the evaluation time to functions, the code now passes
an `AnimationEvalContext` pointer:

```
typedef struct AnimationEvalContext {
  struct Depsgraph *const depsgraph;
  const float eval_time;
} AnimationEvalContext;
```

These structs are read-only, meaning that the code cannot change the
evaluation time. Note that the `depsgraph` pointer itself is const, but
it points to a non-const depsgraph.

FCurves and Drivers can be evaluated at a different time than the
current scene time, for example when evaluating NLA strips. This means
that, even though the current time is stored in the dependency graph, we
need an explicit evaluation time.

There are two functions that allow creation of `AnimationEvalContext`
objects:

- `BKE_animsys_eval_context_construct(Depsgraph *depsgraph, float
  eval_time)`, which creates a new context object from scratch, and
- `BKE_animsys_eval_context_construct_at(AnimationEvalContext
  *anim_eval_context, float eval_time)`, which can be used to create a
  `AnimationEvalContext` with the same depsgraph, but at a different
  time. This makes it possible to later add fields without changing any
  of the code that just want to change the eval time.

This also provides a fix for T75553, although it does require a change
to the custom driver function. The driver should call
`custom_function(depsgraph)`, and the function should use that depsgraph
instead of information from `bpy.context`.

Reviewed By: brecht, sergey

Differential Revision: https://developer.blender.org/D8047
This commit is contained in:
Sybren A. Stüvel
2020-07-17 17:38:09 +02:00
committed by Sybren A. Stüvel
parent 6fbfa522e6
commit 686ab4c940
36 changed files with 503 additions and 183 deletions

View File

@@ -33,19 +33,22 @@
#include "BLI_math_base.h"
#include "BLI_string.h"
#include "BKE_animsys.h"
#include "BKE_fcurve_driver.h"
#include "BKE_global.h"
#include "RNA_access.h"
#include "RNA_types.h"
#include "bpy_rna_driver.h" /* for pyrna_driver_get_variable_value */
#include "bpy_intern_string.h"
#include "bpy_driver.h"
#include "bpy_rna.h"
#include "BPY_extern.h"
extern void BPY_update_rna_module(void);
#define USE_RNA_AS_PYOBJECT
#define USE_BYTECODE_WHITELIST
@@ -377,6 +380,37 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, PyObject *d
#endif /* USE_BYTECODE_WHITELIST */
static PyObject *bpy_pydriver_depsgraph_as_pyobject(struct Depsgraph *depsgraph)
{
/* This should never happen, but it's probably better to have None in Python
* than a NULL-wrapping Depsgraph py struct. */
BLI_assert(depsgraph != NULL);
if (depsgraph == NULL) {
Py_RETURN_NONE;
}
struct PointerRNA depsgraph_ptr;
RNA_pointer_create(NULL, &RNA_Depsgraph, depsgraph, &depsgraph_ptr);
return pyrna_struct_CreatePyObject(&depsgraph_ptr);
}
/* Adds a variable 'depsgraph' to the driver variables. This can then be used to obtain evaluated
* datablocks, and the current view layer and scene. See T75553. */
static void bpy_pydriver_namespace_add_depsgraph(PyObject *driver_vars,
struct Depsgraph *depsgraph)
{
PyObject *py_depsgraph = bpy_pydriver_depsgraph_as_pyobject(depsgraph);
const char *depsgraph_variable_name = "depsgraph";
if (PyDict_SetItemString(driver_vars, depsgraph_variable_name, py_depsgraph) == -1) {
fprintf(stderr,
"\tBPY_driver_eval() - couldn't add variable '%s' to namespace\n",
depsgraph_variable_name);
PyErr_Print();
PyErr_Clear();
}
}
/* This evals py driver expressions, 'expr' is a Python expression that
* should evaluate to a float number, which is returned.
*
@@ -396,7 +430,7 @@ static bool bpy_driver_secure_bytecode_validate(PyObject *expr_code, PyObject *d
float BPY_driver_exec(struct PathResolvedRNA *anim_rna,
ChannelDriver *driver,
ChannelDriver *driver_orig,
const float evaltime)
const AnimationEvalContext *anim_eval_context)
{
PyObject *driver_vars = NULL;
PyObject *retval = NULL;
@@ -456,7 +490,7 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna,
}
/* update global namespace */
bpy_pydriver_namespace_update_frame(evaltime);
bpy_pydriver_namespace_update_frame(anim_eval_context->eval_time);
if (driver_orig->flag & DRIVER_FLAG_USE_SELF) {
bpy_pydriver_namespace_update_self(anim_rna);
@@ -589,6 +623,8 @@ float BPY_driver_exec(struct PathResolvedRNA *anim_rna,
}
#endif /* USE_BYTECODE_WHITELIST */
bpy_pydriver_namespace_add_depsgraph(driver_vars, anim_eval_context->depsgraph);
#if 0 /* slow, with this can avoid all Py_CompileString above. */
/* execute expression to get a value */
retval = PyRun_String(expr, Py_eval_input, bpy_pydriver_Dict, driver_vars);