Speedup for constraints update from python script

General idea is to avoid actual calculation from property update()
callback and tag things for update later instead.

That said, pose constraint flags are now tagged for update and
handled as a part of object update. In the new depsgraph it'll
be a nice dedicated operation node.

Also avoid updating disabled flags for all the modifiers. This
part of the path is not totally optimal since it'll still need
to iterate over bones in order to get pchan, but to optimize it
further would be nice to find a way to avoid pchan requirement
all together.

Reviewers: campbellbarton

Reviewed By: campbellbarton

Differential Revision: https://developer.blender.org/D1191
This commit is contained in:
Sergey Sharybin 2015-03-19 18:28:49 +05:00
parent c69b5e0276
commit 15b37a4a4d
7 changed files with 309 additions and 229 deletions

View File

@ -161,6 +161,9 @@ void extract_pose_from_pose(struct bPose *pose, const struct bPose *src);
/* sets constraint flags */
void BKE_pose_update_constraint_flags(struct bPose *pose);
/* tag constraint flags for update */
void BKE_pose_tag_update_constraint_flags(struct bPose *pose);
/* return the name of structure pointed by pose->ikparam */
const char *BKE_pose_ikparam_get_name(struct bPose *pose);

View File

@ -929,6 +929,12 @@ void BKE_pose_update_constraint_flags(bPose *pose)
pchan->constflag |= PCHAN_HAS_CONST;
}
}
pose->flag &= ~POSE_CONSTRAINTS_NEED_UPDATE_FLAGS;
}
void BKE_pose_tag_update_constraint_flags(bPose *pose)
{
pose->flag |= POSE_CONSTRAINTS_NEED_UPDATE_FLAGS;
}
/* Clears all BONE_UNKEYED flags for every pose channel in every pose

View File

@ -3012,8 +3012,12 @@ void BKE_object_handle_update_ex(EvaluationContext *eval_ctx,
{
if (ob->recalc & OB_RECALC_ALL) {
/* speed optimization for animation lookups */
if (ob->pose)
if (ob->pose) {
BKE_pose_channels_hash_make(ob->pose);
if (ob->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(ob->pose);
}
}
if (ob->recalc & OB_RECALC_DATA) {
if (ob->type == OB_ARMATURE) {

View File

@ -175,6 +175,9 @@ void ED_object_constraint_set_active(struct Object *ob, struct bConstraint *con)
void ED_object_constraint_update(struct Object *ob);
void ED_object_constraint_dependency_update(struct Main *bmain, struct Object *ob);
void ED_object_constraint_tag_update(struct Object *ob, struct bConstraint *con);
void ED_object_constraint_dependency_tag_update(struct Main *bmain, struct Object *ob, struct bConstraint *con);
/* object_lattice.c */
bool mouse_lattice(struct bContext *C, const int mval[2], bool extend, bool deselect, bool toggle);
void undo_push_lattice(struct bContext *C, const char *name);

View File

@ -260,17 +260,221 @@ static void set_constraint_nth_target(bConstraint *con, Object *target, const ch
/* ------------- Constraint Sanity Testing ------------------- */
/* checks validity of object pointers, and NULLs,
* if Bone doesnt exist it sets the CONSTRAINT_DISABLE flag.
*/
static void test_constraints(Object *owner, bPoseChannel *pchan)
static void test_constraint(Object *owner, bPoseChannel *pchan, bConstraint *con, int type)
{
bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
bool check_targets = true;
/* clear disabled-flag first */
con->flag &= ~CONSTRAINT_DISABLE;
if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
bKinematicConstraint *data = con->data;
/* bad: we need a separate set of checks here as poletarget is
* optional... otherwise poletarget must exist too or else
* the constraint is deemed invalid
*/
/* default IK check ... */
if (BKE_object_exists_check(data->tar) == 0) {
data->tar = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
else if (data->tar == owner) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) {
con->flag |= CONSTRAINT_DISABLE;
}
}
if (data->poletar) {
if (BKE_object_exists_check(data->poletar) == 0) {
data->poletar = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
else if (data->poletar == owner) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->polesubtarget)) {
con->flag |= CONSTRAINT_DISABLE;
}
}
}
/* ... can be overwritten here */
BIK_test_constraint(owner, con);
/* targets have already been checked for this */
check_targets = false;
}
else if (con->type == CONSTRAINT_TYPE_PIVOT) {
bPivotConstraint *data = con->data;
/* target doesn't have to exist, but if it is non-null, it must exist! */
if (data->tar && BKE_object_exists_check(data->tar) == 0) {
data->tar = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
else if (data->tar == owner) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) {
con->flag |= CONSTRAINT_DISABLE;
}
}
/* targets have already been checked for this */
check_targets = false;
}
else if (con->type == CONSTRAINT_TYPE_ACTION) {
bActionConstraint *data = con->data;
/* validate action */
if (data->act == NULL) {
/* must have action */
con->flag |= CONSTRAINT_DISABLE;
}
else if (data->act->idroot != ID_OB) {
/* only object-rooted actions can be used */
data->act = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
}
else if (con->type == CONSTRAINT_TYPE_FOLLOWPATH) {
bFollowPathConstraint *data = con->data;
/* don't allow track/up axes to be the same */
if (data->upflag == data->trackflag)
con->flag |= CONSTRAINT_DISABLE;
if (data->upflag + 3 == data->trackflag)
con->flag |= CONSTRAINT_DISABLE;
}
else if (con->type == CONSTRAINT_TYPE_TRACKTO) {
bTrackToConstraint *data = con->data;
/* don't allow track/up axes to be the same */
if (data->reserved2 == data->reserved1)
con->flag |= CONSTRAINT_DISABLE;
if (data->reserved2 + 3 == data->reserved1)
con->flag |= CONSTRAINT_DISABLE;
}
else if (con->type == CONSTRAINT_TYPE_LOCKTRACK) {
bLockTrackConstraint *data = con->data;
if (data->lockflag == data->trackflag)
con->flag |= CONSTRAINT_DISABLE;
if (data->lockflag + 3 == data->trackflag)
con->flag |= CONSTRAINT_DISABLE;
}
else if (con->type == CONSTRAINT_TYPE_SPLINEIK) {
bSplineIKConstraint *data = con->data;
/* if the number of points does not match the amount required by the chain length,
* free the points array and request a rebind...
*/
if ((data->points == NULL) || (data->numpoints != data->chainlen + 1)) {
/* free the points array */
if (data->points) {
MEM_freeN(data->points);
data->points = NULL;
}
/* clear the bound flag, forcing a rebind next time this is evaluated */
data->flag &= ~CONSTRAINT_SPLINEIK_BOUND;
}
}
else if (con->type == CONSTRAINT_TYPE_FOLLOWTRACK) {
bFollowTrackConstraint *data = con->data;
if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0) {
if (data->clip != NULL && data->track[0]) {
MovieTracking *tracking = &data->clip->tracking;
MovieTrackingObject *tracking_object;
if (data->object[0])
tracking_object = BKE_tracking_object_get_named(tracking, data->object);
else
tracking_object = BKE_tracking_object_get_camera(tracking);
if (!tracking_object) {
con->flag |= CONSTRAINT_DISABLE;
}
else {
if (!BKE_tracking_track_get_named(tracking, tracking_object, data->track))
con->flag |= CONSTRAINT_DISABLE;
}
}
else {
con->flag |= CONSTRAINT_DISABLE;
}
}
}
else if (con->type == CONSTRAINT_TYPE_CAMERASOLVER) {
bCameraSolverConstraint *data = con->data;
if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL))
con->flag |= CONSTRAINT_DISABLE;
}
else if (con->type == CONSTRAINT_TYPE_OBJECTSOLVER) {
bObjectSolverConstraint *data = con->data;
if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL))
con->flag |= CONSTRAINT_DISABLE;
}
/* Check targets for constraints */
if (check_targets && cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
/* disable and clear constraints targets that are incorrect */
for (ct = targets.first; ct; ct = ct->next) {
/* general validity checks (for those constraints that need this) */
if (BKE_object_exists_check(ct->tar) == 0) {
/* object doesn't exist, but constraint requires target */
ct->tar = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
else if (ct->tar == owner) {
if (type == CONSTRAINT_OBTYPE_BONE) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), ct->subtarget)) {
/* bone must exist in armature... */
/* TODO: clear subtarget? */
con->flag |= CONSTRAINT_DISABLE;
}
else if (STREQ(pchan->name, ct->subtarget)) {
/* cannot target self */
ct->subtarget[0] = '\0';
con->flag |= CONSTRAINT_DISABLE;
}
}
else {
/* cannot use self as target */
ct->tar = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
}
/* target checks for specific constraints */
if (ELEM(con->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_SPLINEIK)) {
if (ct->tar) {
if (ct->tar->type != OB_CURVE) {
ct->tar = NULL;
con->flag |= CONSTRAINT_DISABLE;
}
else {
Curve *cu = ct->tar->data;
/* auto-set 'Path' setting on curve so this works */
cu->flag |= CU_PATH;
}
}
}
}
/* free any temporary targets */
if (cti->flush_constraint_targets)
cti->flush_constraint_targets(con, &targets, 0);
}
}
static int constraint_type_get(Object *owner, bPoseChannel *pchan)
{
bConstraint *curcon;
ListBase *conlist = NULL;
int type;
if (owner == NULL) return;
/* Check parents */
if (pchan) {
switch (owner->type) {
@ -284,7 +488,22 @@ static void test_constraints(Object *owner, bPoseChannel *pchan)
}
else
type = CONSTRAINT_OBTYPE_OBJECT;
return type;
}
/* checks validity of object pointers, and NULLs,
* if Bone doesnt exist it sets the CONSTRAINT_DISABLE flag.
*/
static void test_constraints(Object *owner, bPoseChannel *pchan)
{
bConstraint *curcon;
ListBase *conlist = NULL;
int type;
if (owner == NULL) return;
type = constraint_type_get(owner, pchan);
/* Get the constraint list for this object */
switch (type) {
case CONSTRAINT_OBTYPE_OBJECT:
@ -298,213 +517,7 @@ static void test_constraints(Object *owner, bPoseChannel *pchan)
/* Check all constraints - is constraint valid? */
if (conlist) {
for (curcon = conlist->first; curcon; curcon = curcon->next) {
bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(curcon);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
/* clear disabled-flag first */
curcon->flag &= ~CONSTRAINT_DISABLE;
if (curcon->type == CONSTRAINT_TYPE_KINEMATIC) {
bKinematicConstraint *data = curcon->data;
/* bad: we need a separate set of checks here as poletarget is
* optional... otherwise poletarget must exist too or else
* the constraint is deemed invalid
*/
/* default IK check ... */
if (BKE_object_exists_check(data->tar) == 0) {
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (data->tar == owner) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) {
curcon->flag |= CONSTRAINT_DISABLE;
}
}
if (data->poletar) {
if (BKE_object_exists_check(data->poletar) == 0) {
data->poletar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (data->poletar == owner) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->polesubtarget)) {
curcon->flag |= CONSTRAINT_DISABLE;
}
}
}
/* ... can be overwritten here */
BIK_test_constraint(owner, curcon);
/* targets have already been checked for this */
continue;
}
else if (curcon->type == CONSTRAINT_TYPE_PIVOT) {
bPivotConstraint *data = curcon->data;
/* target doesn't have to exist, but if it is non-null, it must exist! */
if (data->tar && BKE_object_exists_check(data->tar) == 0) {
data->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (data->tar == owner) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), data->subtarget)) {
curcon->flag |= CONSTRAINT_DISABLE;
}
}
/* targets have already been checked for this */
continue;
}
else if (curcon->type == CONSTRAINT_TYPE_ACTION) {
bActionConstraint *data = curcon->data;
/* validate action */
if (data->act == NULL) {
/* must have action */
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (data->act->idroot != ID_OB) {
/* only object-rooted actions can be used */
data->act = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
}
}
else if (curcon->type == CONSTRAINT_TYPE_FOLLOWPATH) {
bFollowPathConstraint *data = curcon->data;
/* don't allow track/up axes to be the same */
if (data->upflag == data->trackflag)
curcon->flag |= CONSTRAINT_DISABLE;
if (data->upflag + 3 == data->trackflag)
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (curcon->type == CONSTRAINT_TYPE_TRACKTO) {
bTrackToConstraint *data = curcon->data;
/* don't allow track/up axes to be the same */
if (data->reserved2 == data->reserved1)
curcon->flag |= CONSTRAINT_DISABLE;
if (data->reserved2 + 3 == data->reserved1)
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (curcon->type == CONSTRAINT_TYPE_LOCKTRACK) {
bLockTrackConstraint *data = curcon->data;
if (data->lockflag == data->trackflag)
curcon->flag |= CONSTRAINT_DISABLE;
if (data->lockflag + 3 == data->trackflag)
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (curcon->type == CONSTRAINT_TYPE_SPLINEIK) {
bSplineIKConstraint *data = curcon->data;
/* if the number of points does not match the amount required by the chain length,
* free the points array and request a rebind...
*/
if ((data->points == NULL) || (data->numpoints != data->chainlen + 1)) {
/* free the points array */
if (data->points) {
MEM_freeN(data->points);
data->points = NULL;
}
/* clear the bound flag, forcing a rebind next time this is evaluated */
data->flag &= ~CONSTRAINT_SPLINEIK_BOUND;
}
}
else if (curcon->type == CONSTRAINT_TYPE_FOLLOWTRACK) {
bFollowTrackConstraint *data = curcon->data;
if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0) {
if (data->clip != NULL && data->track[0]) {
MovieTracking *tracking = &data->clip->tracking;
MovieTrackingObject *tracking_object;
if (data->object[0])
tracking_object = BKE_tracking_object_get_named(tracking, data->object);
else
tracking_object = BKE_tracking_object_get_camera(tracking);
if (!tracking_object) {
curcon->flag |= CONSTRAINT_DISABLE;
}
else {
if (!BKE_tracking_track_get_named(tracking, tracking_object, data->track))
curcon->flag |= CONSTRAINT_DISABLE;
}
}
else {
curcon->flag |= CONSTRAINT_DISABLE;
}
}
}
else if (curcon->type == CONSTRAINT_TYPE_CAMERASOLVER) {
bCameraSolverConstraint *data = curcon->data;
if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL))
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (curcon->type == CONSTRAINT_TYPE_OBJECTSOLVER) {
bObjectSolverConstraint *data = curcon->data;
if ((data->flag & CAMERASOLVER_ACTIVECLIP) == 0 && (data->clip == NULL))
curcon->flag |= CONSTRAINT_DISABLE;
}
/* Check targets for constraints */
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(curcon, &targets);
/* disable and clear constraints targets that are incorrect */
for (ct = targets.first; ct; ct = ct->next) {
/* general validity checks (for those constraints that need this) */
if (BKE_object_exists_check(ct->tar) == 0) {
/* object doesn't exist, but constraint requires target */
ct->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (ct->tar == owner) {
if (type == CONSTRAINT_OBTYPE_BONE) {
if (!BKE_armature_find_bone_name(BKE_armature_from_object(owner), ct->subtarget)) {
/* bone must exist in armature... */
/* TODO: clear subtarget? */
curcon->flag |= CONSTRAINT_DISABLE;
}
else if (STREQ(pchan->name, ct->subtarget)) {
/* cannot target self */
ct->subtarget[0] = '\0';
curcon->flag |= CONSTRAINT_DISABLE;
}
}
else {
/* cannot use self as target */
ct->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
}
}
/* target checks for specific constraints */
if (ELEM(curcon->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_SPLINEIK)) {
if (ct->tar) {
if (ct->tar->type != OB_CURVE) {
ct->tar = NULL;
curcon->flag |= CONSTRAINT_DISABLE;
}
else {
Curve *cu = ct->tar->data;
/* auto-set 'Path' setting on curve so this works */
cu->flag |= CU_PATH;
}
}
}
}
/* free any temporary targets */
if (cti->flush_constraint_targets)
cti->flush_constraint_targets(curcon, &targets, 0);
}
test_constraint(owner, pchan, curcon, type);
}
}
}
@ -524,6 +537,26 @@ void object_test_constraints(Object *owner)
}
}
static void object_test_constraint(Object *owner, bConstraint *con)
{
if (owner->type == OB_ARMATURE && owner->pose) {
if (BLI_findindex(&owner->constraints, con) != -1) {
test_constraint(owner, NULL, con, CONSTRAINT_OBTYPE_OBJECT);
}
else {
bPoseChannel *pchan;
for (pchan = owner->pose->chanbase.first; pchan; pchan = pchan->next) {
if (BLI_findindex(&pchan->constraints, con) != -1) {
test_constraint(owner, pchan, con, CONSTRAINT_OBTYPE_BONE);
break;
}
}
}
}
else {
test_constraint(owner, NULL, con, CONSTRAINT_OBTYPE_OBJECT);
}
}
/************************ generic functions for operators using constraint names and data context *********************/
@ -1161,20 +1194,49 @@ void ED_object_constraint_update(Object *ob)
DAG_id_tag_update(&ob->id, OB_RECALC_OB);
}
static void object_pose_tag_update(Object *ob)
{
ob->pose->flag |= POSE_RECALC; /* Checks & sort pose channels. */
if (ob->proxy && ob->adt) {
/* We need to make use of ugly POSE_ANIMATION_WORKAROUND here too, else anim data are not reloaded
* after calling `BKE_pose_rebuild()`, which causes T43872.
* Note that this is a bit wide here, since we cannot be sure whether there are some locked proxy bones
* or not...
* XXX Temp hack until new depsgraph hopefully solves this. */
ob->adt->recalc |= ADT_RECALC_ANIM;
}
}
void ED_object_constraint_dependency_update(Main *bmain, Object *ob)
{
ED_object_constraint_update(ob);
if (ob->pose) {
ob->pose->flag |= POSE_RECALC; /* Checks & sort pose channels. */
if (ob->proxy && ob->adt) {
/* We need to make use of ugly POSE_ANIMATION_WORKAROUND here too, else anim data are not reloaded
* after calling `BKE_pose_rebuild()`, which causes T43872.
* Note that this is a bit wide here, since we cannot be sure whether there are some locked proxy bones
* or not...
* XXX Temp hack until new depsgraph hopefully solves this. */
ob->adt->recalc |= ADT_RECALC_ANIM;
}
object_pose_tag_update(ob);
}
DAG_relations_tag_update(bmain);
}
void ED_object_constraint_tag_update(Object *ob, bConstraint *con)
{
if (ob->pose) {
BKE_pose_tag_update_constraint_flags(ob->pose);
}
object_test_constraint(ob, con);
if (ob->type == OB_ARMATURE)
DAG_id_tag_update(&ob->id, OB_RECALC_DATA | OB_RECALC_OB);
else
DAG_id_tag_update(&ob->id, OB_RECALC_OB);
}
void ED_object_constraint_dependency_tag_update(Main *bmain, Object *ob, bConstraint *con)
{
ED_object_constraint_tag_update(ob, con);
if (ob->pose) {
object_pose_tag_update(ob);
}
DAG_relations_tag_update(bmain);
}

View File

@ -375,7 +375,9 @@ typedef enum ePose_Flags {
/* set by BKE_pose_rebuild to give a chance to the IK solver to rebuild IK tree */
POSE_WAS_REBUILT = (1 << 5),
/* set by game_copy_pose to indicate that this pose is used in the game engine */
POSE_GAME_ENGINE = (1 << 6)
POSE_GAME_ENGINE = (1 << 6),
/* pose constraint flags needs to be updated */
POSE_CONSTRAINTS_NEED_UPDATE_FLAGS = (1 << 7),
} ePose_Flags;
/* IK Solvers ------------------------------------ */

View File

@ -271,12 +271,12 @@ static char *rna_Constraint_path(PointerRNA *ptr)
static void rna_Constraint_update(Main *UNUSED(bmain), Scene *UNUSED(scene), PointerRNA *ptr)
{
ED_object_constraint_update(ptr->id.data);
ED_object_constraint_tag_update(ptr->id.data, ptr->data);
}
static void rna_Constraint_dependency_update(Main *bmain, Scene *UNUSED(scene), PointerRNA *ptr)
{
ED_object_constraint_dependency_update(bmain, ptr->id.data);
ED_object_constraint_dependency_tag_update(bmain, ptr->id.data, ptr->data);
}
static void rna_Constraint_influence_update(Main *bmain, Scene *scene, PointerRNA *ptr)