diff --git a/intern/cycles/util/util_math_int3.h b/intern/cycles/util/util_math_int3.h index 1bc6ca7f376..6eef8517665 100644 --- a/intern/cycles/util/util_math_int3.h +++ b/intern/cycles/util/util_math_int3.h @@ -76,8 +76,6 @@ ccl_device_inline int3 clamp(const int3& a, int3& mn, int mx) clamp(a.z, mn.z, mx)); #endif } -#endif /* !__KERNEL_OPENCL__ */ - ccl_device_inline bool operator==(const int3 &a, const int3 &b) { @@ -93,6 +91,7 @@ ccl_device_inline bool operator<(const int3 &a, const int3 &b) { return a.x < b.x && a.y < b.y && a.z < b.z; } +#endif /* !__KERNEL_OPENCL__ */ CCL_NAMESPACE_END diff --git a/source/blender/blenkernel/intern/constraint.c b/source/blender/blenkernel/intern/constraint.c index f0e60296119..b070ccdd4eb 100644 --- a/source/blender/blenkernel/intern/constraint.c +++ b/source/blender/blenkernel/intern/constraint.c @@ -1917,28 +1917,29 @@ static void samevolume_evaluate(bConstraint *con, bConstraintOb *cob, ListBase * bSameVolumeConstraint *data = con->data; float volume = data->volume; - float fac = 1.0f; + float fac = 1.0f, total_scale; float obsize[3]; mat4_to_size(obsize, cob->matrix); /* calculate normalizing scale factor for non-essential values */ - if (obsize[data->flag] != 0) - fac = sqrtf(volume / obsize[data->flag]); + total_scale = obsize[0] * obsize[1] * obsize[2]; + if (total_scale != 0) + fac = sqrtf(volume / total_scale); /* apply scaling factor to the channels not being kept */ switch (data->flag) { case SAMEVOL_X: - mul_v3_fl(cob->matrix[1], fac / obsize[1]); - mul_v3_fl(cob->matrix[2], fac / obsize[2]); + mul_v3_fl(cob->matrix[1], fac); + mul_v3_fl(cob->matrix[2], fac); break; case SAMEVOL_Y: - mul_v3_fl(cob->matrix[0], fac / obsize[0]); - mul_v3_fl(cob->matrix[2], fac / obsize[2]); + mul_v3_fl(cob->matrix[0], fac); + mul_v3_fl(cob->matrix[2], fac); break; case SAMEVOL_Z: - mul_v3_fl(cob->matrix[0], fac / obsize[0]); - mul_v3_fl(cob->matrix[1], fac / obsize[1]); + mul_v3_fl(cob->matrix[0], fac); + mul_v3_fl(cob->matrix[1], fac); break; } } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc index c9fa35bd551..026aa309b02 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_cycle.cc @@ -46,6 +46,8 @@ namespace DEG { +namespace { + typedef enum eCyclicCheckVisitedState { /* Not is not visited at all during traversal. */ NODE_NOT_VISITED = 0, @@ -55,6 +57,30 @@ typedef enum eCyclicCheckVisitedState { NODE_IN_STACK = 2, } eCyclicCheckVisitedState; +struct StackEntry { + OperationDepsNode *node; + StackEntry *from; + DepsRelation *via_relation; +}; + +struct CyclesSolverState { + CyclesSolverState(Depsgraph *graph) + : graph(graph), + traversal_stack(BLI_stack_new(sizeof(StackEntry), + "DEG detect cycles stack")), + num_cycles(0) { + } + ~CyclesSolverState() { + BLI_stack_free(traversal_stack); + if (num_cycles != 0) { + printf("Detected %d dependency cycles\n", num_cycles); + } + } + Depsgraph *graph; + BLI_Stack *traversal_stack; + int num_cycles; +}; + BLI_INLINE void set_node_visited_state(DepsNode *node, eCyclicCheckVisitedState state) { @@ -76,19 +102,20 @@ BLI_INLINE int get_node_num_visited_children(DepsNode *node) return node->done >> 2; } -void deg_graph_detect_cycles(Depsgraph *graph) +void schedule_node_to_stack(CyclesSolverState *state, OperationDepsNode *node) { - struct StackEntry { - OperationDepsNode *node; - StackEntry *from; - DepsRelation *via_relation; - }; + StackEntry entry; + entry.node = node; + entry.from = NULL; + entry.via_relation = NULL; + BLI_stack_push(state->traversal_stack, &entry); + set_node_visited_state(node, NODE_IN_STACK); +} - BLI_Stack *traversal_stack = BLI_stack_new(sizeof(StackEntry), - "DEG detect cycles stack"); - - int num_cycles = 0; - foreach (OperationDepsNode *node, graph->operations) { +/* Schedule leaf nodes (node without input links) for traversal. */ +void schedule_leaf_nodes(CyclesSolverState *state) +{ + foreach (OperationDepsNode *node, state->graph->operations) { bool has_inlinks = false; foreach (DepsRelation *rel, node->inlinks) { if (rel->from->type == DEG_NODE_TYPE_OPERATION) { @@ -97,18 +124,32 @@ void deg_graph_detect_cycles(Depsgraph *graph) } node->done = 0; if (has_inlinks == false) { - StackEntry entry; - entry.node = node; - entry.from = NULL; - entry.via_relation = NULL; - BLI_stack_push(traversal_stack, &entry); - set_node_visited_state(node, NODE_IN_STACK); + schedule_node_to_stack(state, node); } else { set_node_visited_state(node, NODE_NOT_VISITED); } } +} +/* Schedule node which was not checked yet for being belong to + * any of dependency cycle. + */ +bool schedule_non_checked_node(CyclesSolverState *state) +{ + foreach (OperationDepsNode *node, state->graph->operations) { + if (get_node_visited_state(node) == NODE_NOT_VISITED) { + schedule_node_to_stack(state, node); + return true; + } + } + return false; +} + +/* Solve cycles with all nodes which are scheduled for traversal. */ +void solve_cycles(CyclesSolverState *state) +{ + BLI_Stack *traversal_stack = state->traversal_stack; while (!BLI_stack_is_empty(traversal_stack)) { StackEntry *entry = (StackEntry *)BLI_stack_peek(traversal_stack); OperationDepsNode *node = entry->node; @@ -137,7 +178,7 @@ void deg_graph_detect_cycles(Depsgraph *graph) } /* TODO(sergey): So called russian roulette cycle solver. */ rel->flag |= DEPSREL_FLAG_CYCLIC; - ++num_cycles; + ++state->num_cycles; } else if (to_state == NODE_NOT_VISITED) { StackEntry new_entry; @@ -157,11 +198,23 @@ void deg_graph_detect_cycles(Depsgraph *graph) BLI_stack_discard(traversal_stack); } } +} - BLI_stack_free(traversal_stack); +} // namespace - if (num_cycles != 0) { - printf("Detected %d dependency cycles\n", num_cycles); +void deg_graph_detect_cycles(Depsgraph *graph) +{ + CyclesSolverState state(graph); + /* First we solve cycles which are reachable from leaf nodes. */ + schedule_leaf_nodes(&state); + solve_cycles(&state); + /* We are not done yet. It is possible to have closed loop cycle, + * for example A -> B -> C -> A. These nodes were not scheduled + * yet (since they all have inlinks), and were not traversed since + * nobody else points to them. + */ + while (schedule_non_checked_node(&state)) { + solve_cycles(&state); } } diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc index d115111b7e8..e7a9b4b5a69 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.cc @@ -285,6 +285,22 @@ OperationDepsNode *DepsgraphNodeBuilder::add_operation_node( name_tag); } +OperationDepsNode *DepsgraphNodeBuilder::ensure_operation_node( + ID *id, + eDepsNode_Type comp_type, + const DepsEvalOperationCb& op, + eDepsOperation_Code opcode, + const char *name, + int name_tag) +{ + OperationDepsNode *operation = + find_operation_node(id, comp_type, opcode, name, name_tag); + if (operation != NULL) { + return operation; + } + return add_operation_node(id, comp_type, op, opcode, name, name_tag); +} + bool DepsgraphNodeBuilder::has_operation_node(ID *id, eDepsNode_Type comp_type, const char *comp_name, @@ -693,37 +709,57 @@ void DepsgraphNodeBuilder::build_animdata(ID *id) * \param id: ID-Block that driver is attached to * \param fcu: Driver-FCurve */ -OperationDepsNode *DepsgraphNodeBuilder::build_driver(ID *id, FCurve *fcu) +void DepsgraphNodeBuilder::build_driver(ID *id, FCurve *fcurve) { ID *id_cow = get_cow_id(id); /* Create data node for this driver */ - /* TODO(sergey): Avoid creating same operation multiple times, - * in the future we need to avoid lookup of the operation as well - * and use some tagging magic instead. - */ - OperationDepsNode *driver_op = find_operation_node( - id, - DEG_NODE_TYPE_PARAMETERS, - DEG_OPCODE_DRIVER, - fcu->rna_path ? fcu->rna_path : "", - fcu->array_index); + /* TODO(sergey): Shall we use COW of fcu itself here? */ + ensure_operation_node(id, + DEG_NODE_TYPE_PARAMETERS, + function_bind(BKE_animsys_eval_driver, _1, id_cow, fcurve), + DEG_OPCODE_DRIVER, + fcurve->rna_path ? fcurve->rna_path : "", + fcurve->array_index); + build_driver_variables(id, fcurve); +} - if (driver_op == NULL) { - /* TODO(sergey): Shall we use COW of fcu itself here? */ - driver_op = add_operation_node(id, - DEG_NODE_TYPE_PARAMETERS, - function_bind(BKE_animsys_eval_driver, - _1, - id_cow, - fcu), - DEG_OPCODE_DRIVER, - fcu->rna_path ? fcu->rna_path : "", - fcu->array_index); +void DepsgraphNodeBuilder::build_driver_variables(ID * id, FCurve *fcurve) +{ + build_driver_id_property(id, fcurve->rna_path); + LISTBASE_FOREACH (DriverVar *, dvar, &fcurve->driver->variables) { + DRIVER_TARGETS_USED_LOOPER(dvar) + { + build_driver_id_property(dtar->id, dtar->rna_path); + } + DRIVER_TARGETS_LOOPER_END } +} - /* return driver node created */ - return driver_op; +void DepsgraphNodeBuilder::build_driver_id_property(ID *id, + const char *rna_path) +{ + if (id == NULL || rna_path == NULL) { + return; + } + PointerRNA id_ptr, ptr; + PropertyRNA *prop; + RNA_id_pointer_create(id, &id_ptr); + if (!RNA_path_resolve_full(&id_ptr, rna_path, &ptr, &prop, NULL)) { + return; + } + if (prop == NULL) { + return; + } + if (!RNA_property_is_idprop(prop)) { + return; + } + const char *prop_identifier = RNA_property_identifier((PropertyRNA *)prop); + ensure_operation_node(id, + DEG_NODE_TYPE_PARAMETERS, + NULL, + DEG_OPCODE_ID_PROPERTY, + prop_identifier); } /* Recursively build graph for world */ diff --git a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h index ce0ad1e2037..49cccb60843 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_nodes.h +++ b/source/blender/depsgraph/intern/builder/deg_builder_nodes.h @@ -140,6 +140,13 @@ struct DepsgraphNodeBuilder { const char *name = "", int name_tag = -1); + OperationDepsNode *ensure_operation_node(ID *id, + eDepsNode_Type comp_type, + const DepsEvalOperationCb& op, + eDepsOperation_Code opcode, + const char *name = "", + int name_tag = -1); + bool has_operation_node(ID *id, eDepsNode_Type comp_type, const char *comp_name, @@ -179,7 +186,9 @@ struct DepsgraphNodeBuilder { void build_particle_settings(ParticleSettings *part); void build_cloth(Object *object); void build_animdata(ID *id); - OperationDepsNode *build_driver(ID *id, FCurve *fcurve); + void build_driver(ID *id, FCurve *fcurve); + void build_driver_variables(ID *id, FCurve *fcurve); + void build_driver_id_property(ID *id, const char *rna_path); void build_ik_pose(Object *object, bPoseChannel *pchan, bConstraint *con); diff --git a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc index f9d0349a5dc..ddb0f809a53 100644 --- a/source/blender/depsgraph/intern/builder/deg_builder_relations.cc +++ b/source/blender/depsgraph/intern/builder/deg_builder_relations.cc @@ -1178,6 +1178,26 @@ void DepsgraphRelationBuilder::build_driver_data(ID *id, FCurve *fcu) } } } + if (RNA_pointer_is_null(&target_key.ptr)) { + /* TODO(sergey): This would only mean that driver is broken. + * so we can't create relation anyway. However, we need to avoid + * adding drivers which are known to be buggy to a dependency + * graph, in order to save computational power. + */ + } + else { + if (target_key.prop != NULL && + RNA_property_is_idprop(target_key.prop)) + { + OperationKey parameters_key(id, + DEG_NODE_TYPE_PARAMETERS, + DEG_OPCODE_PARAMETERS_EVAL); + add_relation(target_key, + parameters_key, + "Driver Target -> Properties"); + } + add_relation(driver_key, target_key, "Driver -> Target"); + } } } diff --git a/source/blender/depsgraph/intern/depsgraph.cc b/source/blender/depsgraph/intern/depsgraph.cc index 987a0654cca..ee4ffee772e 100644 --- a/source/blender/depsgraph/intern/depsgraph.cc +++ b/source/blender/depsgraph/intern/depsgraph.cc @@ -140,7 +140,7 @@ static bool pointer_to_component_node_criteria( *type = DEG_NODE_TYPE_PARAMETERS; *subdata = ""; *operation_code = DEG_OPCODE_PARAMETERS_EVAL; - *operation_name = pchan->name;; + *operation_name = pchan->name; } else { /* Bone - generally, we just want the bone component. */ @@ -230,10 +230,18 @@ static bool pointer_to_component_node_criteria( } if (prop != NULL) { /* All unknown data effectively falls under "parameter evaluation". */ - *type = DEG_NODE_TYPE_PARAMETERS; - *operation_code = DEG_OPCODE_PARAMETERS_EVAL; - *operation_name = ""; - *operation_name_tag = -1; + if (RNA_property_is_idprop(prop)) { + *type = DEG_NODE_TYPE_PARAMETERS; + *operation_code = DEG_OPCODE_ID_PROPERTY; + *operation_name = RNA_property_identifier((PropertyRNA *)prop); + *operation_name_tag = -1; + } + else { + *type = DEG_NODE_TYPE_PARAMETERS; + *operation_code = DEG_OPCODE_PARAMETERS_EVAL; + *operation_name = ""; + *operation_name_tag = -1; + } return true; } return false; diff --git a/source/blender/depsgraph/intern/depsgraph_type_defines.cc b/source/blender/depsgraph/intern/depsgraph_type_defines.cc index 886601225c7..1ae1b52b8d2 100644 --- a/source/blender/depsgraph/intern/depsgraph_type_defines.cc +++ b/source/blender/depsgraph/intern/depsgraph_type_defines.cc @@ -81,6 +81,7 @@ static const char *stringify_opcode(eDepsOperation_Code opcode) #define STRINGIFY_OPCODE(name) case DEG_OPCODE_##name: return #name /* Generic Operations. */ STRINGIFY_OPCODE(OPERATION); + STRINGIFY_OPCODE(ID_PROPERTY); STRINGIFY_OPCODE(PARAMETERS_EVAL); STRINGIFY_OPCODE(PLACEHOLDER); /* Animation, Drivers, etc. */ diff --git a/source/blender/depsgraph/intern/depsgraph_types.h b/source/blender/depsgraph/intern/depsgraph_types.h index 3f4df21b8d6..f8699186866 100644 --- a/source/blender/depsgraph/intern/depsgraph_types.h +++ b/source/blender/depsgraph/intern/depsgraph_types.h @@ -162,6 +162,7 @@ typedef enum eDepsOperation_Code { DEG_OPCODE_OPERATION = 0, /* Generic parameters evaluation. */ + DEG_OPCODE_ID_PROPERTY, DEG_OPCODE_PARAMETERS_EVAL, // XXX: Placeholder while porting depsgraph code diff --git a/tests/python/modules/test_utils.py b/tests/python/modules/test_utils.py index 489f36c913f..6ca498d8cdf 100755 --- a/tests/python/modules/test_utils.py +++ b/tests/python/modules/test_utils.py @@ -58,11 +58,10 @@ def with_tempdir(wrapped): class AbstractBlenderRunnerTest(unittest.TestCase): """Base class for all test suites which needs to run Blender""" - @classmethod - def setUpClass(cls): - global args - cls.blender = args.blender - cls.testdir = pathlib.Path(args.testdir) + # Set in a subclass + blender: pathlib.Path = None + testdir: pathlib.Path = None + def run_blender(self, filepath: str, python_script: str, timeout: int=300) -> str: """Runs Blender by opening a blendfile and executing a script. @@ -73,6 +72,9 @@ class AbstractBlenderRunnerTest(unittest.TestCase): :param timeout: in seconds """ + assert self.blender, "Path to Blender binary is to be set in setUpClass()" + assert self.testdir, "Path to tests binary is to be set in setUpClass()" + blendfile = self.testdir / filepath command = (