Motion Paths: Experimental optimisations from joeedh for speeding up the calculation...
authorJoshua Leung <aligorith@gmail.com>
Fri, 21 May 2010 12:17:34 +0000 (12:17 +0000)
committerJoshua Leung <aligorith@gmail.com>
Fri, 21 May 2010 12:17:34 +0000 (12:17 +0000)
This works by tricking the depsgraph into giving us a smaller list of objects to evaluate, with all the necessary objects + their dependencies at the start of the list.

On any complicated setup where non-object parameters need to be referred to (i.e. by drivers) to affect an object's transform, these optimisations will fail and the old (slower) method is still the best way (modify the ifdef and comment out the optimise depsgraph call to do so). However, we'll assume that these aren't too common in real productions, so things should be fine with these fixes. If there really is a need for both, then global options to control these things could follow.

source/blender/blenkernel/intern/anim.c
source/blender/editors/transform/transform_conversions.c
source/blender/makesdna/DNA_object_types.h

index e2ee4c27c316c05f1af529da860c582e624d9adc..9ef52c24e8b280a641bc6ac7e80c14209aeca02c 100644 (file)
@@ -252,6 +252,82 @@ void animviz_get_object_motionpaths(Object *ob, ListBase *targets)
 
 /* ........ */
 
+/* Note on evaluation optimisations:
+ * Optimisations currently used here play tricks with the depsgraph in order to try and 
+ * evaluate as few objects as strictly necessary to get nicer performance under standard
+ * production conditions. For those people who really need the accurate version, 
+ */
+
+/* tweak the object ordering to trick depsgraph into making MotionPath calculations run faster */
+static void motionpaths_calc_optimise_depsgraph(Scene *scene, ListBase *targets)
+{
+       Base *base, *baseNext;
+       MPathTarget *mpt;
+       
+       /* make sure our temp-tag isn't already in use */
+       for (base= scene->base.first; base; base= base->next)
+               base->object->flag &= ~BA_TEMP_TAG;
+       
+       /* for each target, dump its object to the start of the list if it wasn't moved already */
+       for (mpt= targets->first; mpt; mpt= mpt->next) {
+               for (base=scene->base.first; base; base=baseNext) {
+                       baseNext = base->next;
+                       
+                       if ((base->object == mpt->ob) && !(mpt->ob->flag & BA_TEMP_TAG)) {
+                               BLI_remlink(&scene->base, base);
+                               BLI_addhead(&scene->base, base);
+                               
+                               mpt->ob->flag |= BA_TEMP_TAG;
+                               break; // we really don't need to continue anymore once this happens, but this line might really 'break'
+                       }
+               }
+       }
+       
+       /* "brew me a list that's sorted a bit faster now depsy" */
+       DAG_scene_sort(scene);
+}
+
+/* update scene for current frame */
+static void motionpaths_calc_update_scene(Scene *scene)
+{
+#if 1 // 'production' optimisations always on
+       Base *base, *last=NULL;
+       
+       /* only stuff that moves or needs display still */
+       DAG_scene_update_flags(scene, scene->lay);
+       
+       /* find the last object with the tag 
+        *      - all those afterwards are assumed to not be relevant for our calculations
+        */
+       // optimise further by moving out...
+       for (base=scene->base.first; base; base=base->next) {
+               if (base->object->flag & BA_TEMP_TAG)
+                       last = base;
+       }
+       
+       /* perform updates for tagged objects */
+       // XXX: this will break if rigs depend on scene or other data that 
+       // is animated but not attached to/updatable from objects
+       for (base=scene->base.first; base; base=base->next) {
+               /* update this object */
+               object_handle_update(scene, base->object);
+               
+               /* if this is the last one we need to update, let's stop to save some time */
+               if (base == last)
+                       break;
+       }
+#else // original, 'always correct' version
+       /* do all updates 
+        *      - if this is too slow, resort to using a more efficient way 
+        *        that doesn't force complete update, but for now, this is the
+        *        most accurate way!
+        */
+       scene_update_for_newframe(scene, scene->lay); // XXX this is the best way we can get anything moving
+#endif
+}
+
+/* ........ */
+
 /* perform baking for the targets on the current frame */
 static void motionpaths_calc_bake_targets(Scene *scene, ListBase *targets)
 {
@@ -313,7 +389,7 @@ void animviz_calc_motionpaths(Scene *scene, ListBase *targets)
        
        // TODO: this method could be improved...
        //      1) max range for standard baking
-       //      2) minimum range for recalc baking (i.e. between keyfames, but how?)
+       //      2) minimum range for recalc baking (i.e. between keyframes, but how?)
        for (mpt= targets->first; mpt; mpt= mpt->next) {
                /* try to increase area to do (only as much as needed) */
                sfra= MIN2(sfra, mpt->mpath->start_frame);
@@ -321,14 +397,14 @@ void animviz_calc_motionpaths(Scene *scene, ListBase *targets)
        }
        if (efra <= sfra) return;
        
+       /* optimise the depsgraph for faster updates */
+       // TODO: whether this is used should depend on some setting for the level of optimisations used
+       motionpaths_calc_optimise_depsgraph(scene, targets);
+       
        /* calculate path over requested range */
        for (CFRA=sfra; CFRA<=efra; CFRA++) {
-               /* do all updates 
-                *      - if this is too slow, resort to using a more efficient way 
-                *        that doesn't force complete update, but for now, this is the
-                *        most accurate way!
-                */
-               scene_update_for_newframe(scene, scene->lay); // XXX this is the best way we can get anything moving
+               /* update relevant data for new frame */
+               motionpaths_calc_update_scene(scene);
                
                /* perform baking for targets */
                motionpaths_calc_bake_targets(scene, targets);
@@ -336,7 +412,7 @@ void animviz_calc_motionpaths(Scene *scene, ListBase *targets)
        
        /* reset original environment */
        CFRA= cfra;
-       scene_update_for_newframe(scene, scene->lay); // XXX this is the best way we can get anything moving
+       motionpaths_calc_update_scene(scene);
        
        /* clear recalc flags from targets */
        for (mpt= targets->first; mpt; mpt= mpt->next) {
index 9e20d3cfec37b7cbe6dd6885c9adcc061c15d8e9..499631ff7559116a07a17a027e078040660d268e 100644 (file)
@@ -4499,7 +4499,7 @@ static void clear_trans_object_base_flags(TransInfo *t)
                if(base->flag & BA_WAS_SEL)
                        base->flag |= SELECT;
 
-               base->flag &= ~(BA_WAS_SEL|BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA|BA_DO_IPO|BA_TRANSFORM_CHILD|BA_TRANSFORM_PARENT);
+               base->flag &= ~(BA_WAS_SEL|BA_HAS_RECALC_OB|BA_HAS_RECALC_DATA|BA_TEMP_TAG|BA_TRANSFORM_CHILD|BA_TRANSFORM_PARENT);
        }
 }
 
index f50909e641b51c1d9d8cb97cca935b8ccd9d9846..9649b8351a6829e81d6b1f89a40d9fff41caffd5 100644 (file)
@@ -412,14 +412,15 @@ extern Object workob;
 #define BA_HAS_RECALC_OB       4
 #define BA_HAS_RECALC_DATA     8
 
-       // XXX DEPRECEATED SETTING...
-#define BA_DO_IPO                      32
+       /* NOTE: this was used as a proper setting in past, so nullify before using */
+#define BA_TEMP_TAG                    32
 
 #define BA_FROMSET                     128
 
 #define BA_TRANSFORM_CHILD     256 /* child of a transformed object */
 #define BA_TRANSFORM_PARENT    8192 /* parent of a transformed object */
 
+
 /* an initial attempt as making selection more specific! */
 #define BA_DESELECT            0
 #define BA_SELECT              1