diff options
author | Sergey Sharybin <sergey.vfx@gmail.com> | 2018-11-20 17:42:37 +0300 |
---|---|---|
committer | Sergey Sharybin <sergey.vfx@gmail.com> | 2018-11-20 17:53:59 +0300 |
commit | 4dc639ac9965f3d0e8b2304363c62d888cdd1dc9 (patch) | |
tree | f0272c9c70e5095dbabf5bce59d07cd8492f1b74 /source/blender/blenkernel/intern/armature.c | |
parent | 163be42a9661741757325039909ba81f0701b248 (diff) |
Speedup rigs with multiple objects deformed by bbones
Previously each of the objects which has armature modifier will
request deformation matricies from bbones. Thing is, all those
deformations are the same and do not depend on object which is
being modified. What's even worse is that this calculation is
not cheap.
This change makes it so bbones deformation is calculated once
and stored in the armature object. After this armature modifiers
simply use it.
With a rigs we've got here dependency graph evaluation time
goes down from 0.02 sec to 0.012 sec.
Possible further optimization is to make bbone deformation
calculated at the time when bone is calculated. This will avoid
an extra threaded loop over all bones.
Diffstat (limited to 'source/blender/blenkernel/intern/armature.c')
-rw-r--r-- | source/blender/blenkernel/intern/armature.c | 139 |
1 files changed, 108 insertions, 31 deletions
diff --git a/source/blender/blenkernel/intern/armature.c b/source/blender/blenkernel/intern/armature.c index ae28655bcfe..bd68caf1037 100644 --- a/source/blender/blenkernel/intern/armature.c +++ b/source/blender/blenkernel/intern/armature.c @@ -75,6 +75,8 @@ #include "BIK_api.h" +#include "atomic_ops.h" + /* **************** Generic Functions, data level *************** */ bArmature *BKE_armature_add(Main *bmain, const char *name) @@ -813,6 +815,13 @@ typedef struct bPoseChanDeform { DualQuat *b_bone_dual_quats; } bPoseChanDeform; +/* Definition of cached object bbone deformations. */ +typedef struct ObjectBBoneDeform { + DualQuat *dualquats; + bPoseChanDeform *pdef_info_array; + int num_pchan; +} ObjectBBoneDeform; + static void pchan_b_bone_defmats(bPoseChannel *pchan, bPoseChanDeform *pdef_info, const bool use_quaternion) { Bone *bone = pchan->bone; @@ -1065,14 +1074,12 @@ void armature_deform_verts( float (*defMats)[3][3], int numVerts, int deformflag, float (*prevCos)[3], const char *defgrp_name, bGPDstroke *gps) { - bPoseChanDeform *pdef_info_array; - bPoseChanDeform *pdef_info = NULL; + const bPoseChanDeform *pdef_info = NULL; bArmature *arm = armOb->data; bPoseChannel *pchan, **defnrToPC = NULL; int *defnrToPCIndex = NULL; MDeformVert *dverts = NULL; bDeformGroup *dg; - DualQuat *dualquats = NULL; float obinv[4][4], premat[4][4], postmat[4][4]; const bool use_envelope = (deformflag & ARM_DEF_ENVELOPE) != 0; const bool use_quaternion = (deformflag & ARM_DEF_QUATERNION) != 0; @@ -1081,7 +1088,6 @@ void armature_deform_verts( int i, target_totvert = 0; /* safety for vertexgroup overflow */ bool use_dverts = false; int armature_def_nr; - int totchan; /* in editmode, or not an armature */ if (arm->edbo || (armOb->pose == NULL)) { @@ -1098,21 +1104,20 @@ void armature_deform_verts( mul_m4_m4m4(postmat, obinv, armOb->obmat); invert_m4_m4(premat, postmat); - /* bone defmats are already in the channels, chan_mat */ - - /* initialize B_bone matrices and dual quaternions */ - totchan = BLI_listbase_count(&armOb->pose->chanbase); - - if (use_quaternion) { - dualquats = MEM_callocN(sizeof(DualQuat) * totchan, "dualquats"); + /* Use pre-calculated bbone deformation. + * + * TODO(sergey): Make this code robust somehow when there are dependency + * cycles involved. */ + ObjectBBoneDeform * bbone_deform = + armature_cached_bbone_deformation_get(armOb); + if (bbone_deform == NULL || bbone_deform->pdef_info_array == NULL) { + fprintf(stderr, + "Armature does not have bbone cache %s, " + "usually happens due to a dependency cycle.", + armOb->id.name + 2); + return; } - - pdef_info_array = MEM_callocN(sizeof(bPoseChanDeform) * totchan, "bPoseChanDeform"); - - ArmatureBBoneDefmatsData data = { - .pdef_info_array = pdef_info_array, .dualquats = dualquats, .use_quaternion = use_quaternion - }; - BLI_task_parallel_listbase(&armOb->pose->chanbase, &data, armature_bbone_defmats_cb, totchan > 512); + const bPoseChanDeform *pdef_info_array = bbone_deform->pdef_info_array; /* get the def_nr for the overall armature vertex group if present */ armature_def_nr = defgroup_name_index(target, defgrp_name); @@ -1344,23 +1349,10 @@ void armature_deform_verts( } } - if (dualquats) - MEM_freeN(dualquats); if (defnrToPC) MEM_freeN(defnrToPC); if (defnrToPCIndex) MEM_freeN(defnrToPCIndex); - - /* free B_bone matrices */ - pdef_info = pdef_info_array; - for (pchan = armOb->pose->chanbase.first; pchan; pchan = pchan->next, pdef_info++) { - if (pdef_info->b_bone_mats) - MEM_freeN(pdef_info->b_bone_mats); - if (pdef_info->b_bone_dual_quats) - MEM_freeN(pdef_info->b_bone_dual_quats); - } - - MEM_freeN(pdef_info_array); } /* ************ END Armature Deform ******************* */ @@ -2491,3 +2483,88 @@ bPoseChannel *BKE_armature_splineik_solver_find_root( } return rootchan; } + +/* ****************************** BBone cache ****************************** */ + +ObjectBBoneDeform * armature_cached_bbone_deformation_get(Object *object) +{ + return object->runtime.cached_bbone_deformation; +} + +void armature_cached_bbone_deformation_free_data(Object *object) +{ + ObjectBBoneDeform *bbone_deform = + armature_cached_bbone_deformation_get(object); + if (bbone_deform == NULL) { + return; + } + /* free B_bone matrices */ + for (int i = 0; i < bbone_deform->num_pchan; i++) { + bPoseChanDeform *pdef_info = &bbone_deform->pdef_info_array[i]; + if (pdef_info->b_bone_mats != NULL) { + MEM_freeN(pdef_info->b_bone_mats); + } + if (pdef_info->b_bone_dual_quats != NULL) { + MEM_freeN(pdef_info->b_bone_dual_quats); + } + } + /* Free arrays. */ + MEM_SAFE_FREE(bbone_deform->pdef_info_array); + MEM_SAFE_FREE(bbone_deform->dualquats); + /* Tag that we've got no data, so we are safe for sequential calls to + * data free. */ + bbone_deform->num_pchan = 0; +} + +void armature_cached_bbone_deformation_free(Object *object) +{ + ObjectBBoneDeform *bbone_deform = + armature_cached_bbone_deformation_get(object); + if (bbone_deform == NULL) { + return; + } + armature_cached_bbone_deformation_free_data(object); + MEM_freeN(bbone_deform); + object->runtime.cached_bbone_deformation = NULL; +} + +void armature_cached_bbone_deformation_update(Object *object) +{ + BLI_assert(object->type == OB_ARMATURE); + BLI_assert(object->pose != NULL); + bPose *pose = object->pose; + const int totchan = BLI_listbase_count(&pose->chanbase); + const bool use_quaternion = true; + /* Make sure cache exists. */ + ObjectBBoneDeform *bbone_deform = + armature_cached_bbone_deformation_get(object); + if (bbone_deform == NULL) { + bbone_deform = MEM_callocN(sizeof(*bbone_deform), "bbone deform cache"); + object->runtime.cached_bbone_deformation = bbone_deform; + } + /* Make sure arrays are allocateds at the proper size. */ + armature_cached_bbone_deformation_free_data(object); + DualQuat *dualquats = NULL; + if (use_quaternion) { + dualquats = MEM_calloc_arrayN( + sizeof(DualQuat), totchan, "dualquats"); + } + bPoseChanDeform *pdef_info_array = MEM_calloc_arrayN( + sizeof(bPoseChanDeform), totchan, "bPoseChanDeform"); + /* Calculate deofrmation matricies. */ + ArmatureBBoneDefmatsData data = { + .pdef_info_array = pdef_info_array, + .dualquats = dualquats, + .use_quaternion = use_quaternion + }; + BLI_task_parallel_listbase(&pose->chanbase, + &data, + armature_bbone_defmats_cb, + totchan > 512); + /* Store pointers. */ + bbone_deform->dualquats = dualquats; + atomic_cas_ptr((void **)&bbone_deform->pdef_info_array, + bbone_deform->pdef_info_array, + pdef_info_array); + bbone_deform->num_pchan = totchan; +} |