Merge branch 'master' into blender2.8

This commit is contained in:
Sergey Sharybin 2018-03-02 16:42:27 +01:00
commit c327cf489c
10 changed files with 197 additions and 67 deletions

View File

@ -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

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -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 */

View File

@ -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);

View File

@ -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");
}
}
}

View File

@ -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;

View File

@ -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. */

View File

@ -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

View File

@ -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 = (