Fix T38054: High CPU usage with many objects
authorSergey Sharybin <sergey.vfx@gmail.com>
Thu, 9 Jan 2014 13:13:26 +0000 (19:13 +0600)
committerSergey Sharybin <sergey.vfx@gmail.com>
Mon, 13 Jan 2014 09:57:51 +0000 (15:57 +0600)
This is a regression since threaded dependency graph landed to master.
Root of the issue goes to the loads of graph preparation being done
even if there's nothing to be updated.

The idea of this change is to use ID type recalc bits to determine
whether there're objects to be updated. Generally speaking, we now
check object and object data datablocks with DAG_id_type_tagged()
and if there's no such IDs tagged we skip the whole task pool creation
and so,

The only difficult aspect was that in some circumstances it was possible
that there are tagged objects but nothing in ID recalc bit fields.

There were several different circumstances when it was possible:

* When one assigns object->recalc flag directly DAG flush didn't
  set corresponding bits to ID recalc bits. Partially it is fixed
  by making it so flush will set bitfield, but also for object
  types there's no reason to assign recalc flag directly. Using
  generic DAG_id_type_tag works almost the same fast as direct
  assignment, ensures all the bitflags are set properly and for the
  long run it seems it's what we would actually want to.

* DAG_on_visible_update() didn't set recalc bits at all.

* Some areas were checking for object->recalc != 0, however it is was
  possible that object recalc flag contains PSYS_RECALC_CHILD which
  was never cleaned from there.

  No idea why would we need to assign such a flag when enabling
  scene simplification, this is to be investigated separately.

* It is possible that scene_update_post and frame_update_post handlers
  will modify objects. The issue is that DAG_ids_clear_recalc is called
  just after callbacks, which leaves objects with recalc flags but no
  corresponding bit in ID recalc bitfield. This leads to some kind of
  regression when using ID type tag fields to check whether there objects
  to be updated internally comparing threaded DAG with legacy one.

  For now let's have a workaround which will preserve tag for ID_OB
  if there're objects with OB_RECALC_ALL bits. This keeps behavior
  unchanged comparing with 2.69 release.

source/blender/blenkernel/intern/depsgraph.c
source/blender/blenkernel/intern/library.c
source/blender/blenkernel/intern/scene.c
source/blender/editors/mesh/editmesh_utils.c
source/blender/editors/transform/transform_conversions.c
source/blender/freestyle/intern/blender_interface/BlenderStrokeRenderer.cpp
source/blender/makesrna/intern/rna_scene.c
source/blender/render/intern/source/convertblender.c

index 13f0c2977c278df26ab9e89334baf9db1062ebac..01327452831aeffcae18163b527a76783fd3036c 100644 (file)
@@ -2031,7 +2031,7 @@ void DAG_scene_update_flags(Main *bmain, Scene *scene, unsigned int lay, const s
                /* test: set time flag, to disable baked systems to update */
                for (SETLOOPER(scene, sce_iter, base)) {
                        ob = base->object;
-                       if (ob->recalc)
+                       if (ob->recalc & OB_RECALC_ALL)
                                ob->recalc |= OB_RECALC_TIME;
                }
 
@@ -2115,10 +2115,14 @@ static void dag_group_on_visible_update(Group *group)
        group->id.flag |= LIB_DOIT;
 
        for (go = group->gobject.first; go; go = go->next) {
-               if (ELEM6(go->ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_LATTICE))
+               if (ELEM6(go->ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_LATTICE)) {
                        go->ob->recalc |= OB_RECALC_DATA;
-               if (go->ob->proxy_from)
+                       lib_id_recalc_tag(G.main, &go->ob->id);
+               }
+               if (go->ob->proxy_from) {
                        go->ob->recalc |= OB_RECALC_OB;
+                       lib_id_recalc_tag(G.main, &go->ob->id);
+               }
 
                if (go->ob->dup_group)
                        dag_group_on_visible_update(go->ob->dup_group);
@@ -2156,10 +2160,14 @@ void DAG_on_visible_update(Main *bmain, const short do_time)
                        oblay = (node) ? node->lay : ob->lay;
 
                        if ((oblay & lay) & ~scene->lay_updated) {
-                               if (ELEM6(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_LATTICE))
+                               if (ELEM6(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_LATTICE)) {
                                        ob->recalc |= OB_RECALC_DATA;
-                               if (ob->proxy && (ob->proxy_group == NULL))
+                                       lib_id_recalc_tag(bmain, &ob->id);
+                               }
+                               if (ob->proxy && (ob->proxy_group == NULL)) {
                                        ob->proxy->recalc |= OB_RECALC_DATA;
+                                       lib_id_recalc_tag(bmain, &ob->id);
+                               }
                                if (ob->dup_group) 
                                        dag_group_on_visible_update(ob->dup_group);
                        }
@@ -2210,6 +2218,13 @@ static void dag_id_flush_update(Main *bmain, Scene *sce, ID *id)
                ob = (Object *)id;
                BKE_ptcache_object_reset(sce, ob, PTCACHE_RESET_DEPSGRAPH);
 
+               /* So if someone tagged object recalc directly,
+                * id_tag_update biffield stays relevant
+                */
+               if (ob->recalc & OB_RECALC_ALL) {
+                       DAG_id_type_tag(bmain, GS(id->name));
+               }
+
                if (ob->recalc & OB_RECALC_DATA) {
                        /* all users of this ob->data should be checked */
                        id = ob->data;
@@ -2311,6 +2326,7 @@ static void dag_id_flush_update(Main *bmain, Scene *sce, ID *id)
                                                  CONSTRAINT_TYPE_OBJECTSOLVER))
                                        {
                                                obt->recalc |= OB_RECALC_OB;
+                                               lib_id_recalc_tag(bmain, &obt->id);
                                                break;
                                        }
                                }
@@ -2427,12 +2443,34 @@ void DAG_ids_check_recalc(Main *bmain, Scene *scene, int time)
        dag_editors_scene_update(bmain, scene, (updated || time));
 }
 
+/* It is possible that scene_update_post and frame_update_post handlers
+ * will modify objects. The issue is that DAG_ids_clear_recalc is called
+ * just after callbacks, which leaves objects with recalc flags but no
+ * corresponding bit in ID recalc bitfield. This leads to some kind of
+ * regression when using ID type tag fields to check whether there objects
+ * to be updated internally comparing threaded DAG with legacy one.
+ *
+ * For now let's have a workaround which will preserve tag for ID_OB
+ * if there're objects with OB_RECALC_ALL bits. This keeps behavior
+ * unchanged comparing with 2.69 release.
+ *
+ * TODO(sergey): Need to get rid of such a workaround.
+ *
+ *                                                 - sergey -
+ */
+
+#define POST_UPDATE_HANDLER_WORKAROUND
+
 void DAG_ids_clear_recalc(Main *bmain)
 {
        ListBase *lbarray[MAX_LIBARRAY];
        bNodeTree *ntree;
        int a;
 
+#ifdef POST_UPDATE_HANDLER_WORKAROUND
+       bool have_updated_objects = false;
+#endif
+
        /* loop over all ID types */
        a  = set_listbasepointers(bmain, lbarray);
 
@@ -2447,6 +2485,15 @@ void DAG_ids_clear_recalc(Main *bmain)
                                if (id->flag & (LIB_ID_RECALC | LIB_ID_RECALC_DATA))
                                        id->flag &= ~(LIB_ID_RECALC | LIB_ID_RECALC_DATA);
 
+#ifdef POST_UPDATE_HANDLER_WORKAROUND
+                               if (GS(id->name) == ID_OB) {
+                                       Object *object = (Object *) id;
+                                       if (object->recalc & OB_RECALC_ALL) {
+                                               have_updated_objects = true;
+                                       }
+                               }
+#endif
+
                                /* some ID's contain semi-datablock nodetree */
                                ntree = ntreeFromID(id);
                                if (ntree && (ntree->id.flag & (LIB_ID_RECALC | LIB_ID_RECALC_DATA)))
@@ -2456,6 +2503,12 @@ void DAG_ids_clear_recalc(Main *bmain)
        }
 
        memset(bmain->id_tag_update, 0, sizeof(bmain->id_tag_update));
+
+#ifdef POST_UPDATE_HANDLER_WORKAROUND
+       if (have_updated_objects) {
+               DAG_id_type_tag(bmain, ID_OB);
+       }
+#endif
 }
 
 void DAG_id_tag_update_ex(Main *bmain, ID *id, short flag)
index 821df59c67ddd449b4509b429eb6a2ffeb291b7a..f2264190a5c99a004db5c38e289120f780a2f718 100644 (file)
@@ -553,6 +553,8 @@ void BKE_main_lib_objects_recalc_all(Main *bmain)
        for (ob = bmain->object.first; ob; ob = ob->id.next)
                if (ob->id.lib)
                        ob->recalc |= OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME;
+
+       DAG_id_type_tag(bmain, ID_OB);
 }
 
 /**
index e489759e31207052e29af5db9421b6754a29d39b..4d8075e9cba6a5f3d6c5f1ed3d31c49aa27cdc1b 100644 (file)
@@ -1365,18 +1365,80 @@ static void print_threads_statistics(ThreadedObjectUpdateState *state)
 #endif
 }
 
-static void scene_update_objects(EvaluationContext *eval_ctx, Scene *scene, Scene *scene_parent)
+static bool scene_need_update_objects(Main *bmain)
+{
+       return
+               /* Object datablocks themselves (for OB_RECALC_OB) */
+               DAG_id_type_tagged(bmain, ID_OB) ||
+
+               /* Objects data datablocks (for OB_RECALC_DATA) */
+               DAG_id_type_tagged(bmain, ID_ME)  ||  /* Mesh */
+               DAG_id_type_tagged(bmain, ID_CU)  ||  /* Curve */
+               DAG_id_type_tagged(bmain, ID_MB)  ||  /* MetaBall */
+               DAG_id_type_tagged(bmain, ID_LA)  ||  /* Lamp */
+               DAG_id_type_tagged(bmain, ID_LT)  ||  /* Lattice */
+               DAG_id_type_tagged(bmain, ID_CA)  ||  /* Camera */
+               DAG_id_type_tagged(bmain, ID_KE)  ||  /* KE */
+               DAG_id_type_tagged(bmain, ID_SPK) ||  /* Speaker */
+               DAG_id_type_tagged(bmain, ID_AR);     /* Armature */
+}
+
+static void scene_update_objects(EvaluationContext *eval_ctx, Main *bmain, Scene *scene, Scene *scene_parent)
 {
        TaskScheduler *task_scheduler = BLI_task_scheduler_get();
        TaskPool *task_pool;
        ThreadedObjectUpdateState state;
 
+       /* Early check for whether we need to invoke all the task-based
+        * tihngs (spawn new ppol, traverse dependency graph and so on).
+        *
+        * Basically if there's no ID datablocks tagged for update which
+        * corresponds to object->recalc flags (which are checked in
+        * BKE_object_handle_update() then we do nothing here.
+        */
+       if (!scene_need_update_objects(bmain)) {
+               /* For debug builds we check whether early return didn't give
+                * us any regressions in terms of missing updates.
+                *
+                * TODO(sergey): Remove once we're sure the check above is correct.
+                */
+#ifndef NDEBUG
+               Base *base;
+
+               for (base = scene->base.first; base; base = base->next) {
+                       Object *object = base->object;
+
+                       BLI_assert((object->recalc & OB_RECALC_ALL) == 0);
+
+                       if (object->proxy) {
+                               BLI_assert((object->proxy->recalc & OB_RECALC_ALL) == 0);
+                       }
+
+                       if (object->dup_group && (object->transflag & OB_DUPLIGROUP)) {
+                               GroupObject *go;
+                               for (go = object->dup_group->gobject.first; go; go = go->next) {
+                                       if (go->ob) {
+                                               BLI_assert((go->ob->recalc & OB_RECALC_ALL) == 0);
+                                       }
+                               }
+                       }
+               }
+#endif
+
+               return;
+       }
+
        state.eval_ctx = eval_ctx;
        state.scene = scene;
        state.scene_parent = scene_parent;
-       memset(state.statistics, 0, sizeof(state.statistics));
-       state.has_updated_objects = false;
-       state.base_time = PIL_check_seconds_timer();
+
+       /* Those are only needed when blender is run with --debug argument. */
+       if (G.debug & G_DEBUG) {
+               memset(state.statistics, 0, sizeof(state.statistics));
+               state.has_updated_objects = false;
+               state.base_time = PIL_check_seconds_timer();
+       }
+
 #ifdef MBALL_SINGLETHREAD_HACK
        state.has_mballs = false;
 #endif
@@ -1408,7 +1470,7 @@ static void scene_update_tagged_recursive(EvaluationContext *eval_ctx, Main *bma
                scene_update_tagged_recursive(eval_ctx, bmain, scene->set, scene_parent);
 
        /* scene objects */
-       scene_update_objects(eval_ctx, scene, scene_parent);
+       scene_update_objects(eval_ctx, bmain, scene, scene_parent);
 
        /* scene drivers... */
        scene_update_drivers(bmain, scene);
index 8475512cdaa1c85c852815cae78a29683f478a25..9f70135b7876f95997078d7f3864333182c1c9c4 100644 (file)
@@ -113,7 +113,10 @@ void EDBM_mesh_ensure_valid_dm_hack(Scene *scene, BMEditMesh *em)
        if ((((ID *)em->ob->data)->flag & LIB_ID_RECALC) ||
            (em->ob->recalc & OB_RECALC_DATA))
        {
-               em->ob->recalc |= OB_RECALC_DATA;  /* since we may not have done selection flushing */
+               /* since we may not have done selection flushing */
+               if ((em->ob->recalc & OB_RECALC_DATA) == 0) {
+                       DAG_id_tag_update(&em->ob->id, OB_RECALC_DATA);
+               }
                BKE_object_handle_update(G.main->eval_ctx, scene, em->ob);
        }
 }
index 2c001ca652793748eb32db5d2fc143be103cca76..55b114b60ef218b255cbf4eaddc062c5ae8ad156 100644 (file)
@@ -4943,8 +4943,7 @@ static void set_trans_object_base_flags(TransInfo *t)
                                        base->flag |= BA_WAS_SEL;
                                }
                        }
-                       /* used for flush, depgraph will change recalcs if needed :) */
-                       ob->recalc |= OB_RECALC_OB;
+                       DAG_id_tag_update(&ob->id, OB_RECALC_OB);
                }
        }
 
@@ -5018,8 +5017,7 @@ static int count_proportional_objects(TransInfo *t)
                    (BASE_EDITABLE_BGMODE(v3d, scene, base)))
                {
 
-                       /* used for flush, depgraph will change recalcs if needed :) */
-                       ob->recalc |= OB_RECALC_OB;
+                       DAG_id_tag_update(&ob->id, OB_RECALC_OB);
 
                        total += 1;
                }
@@ -5833,7 +5831,7 @@ void special_aftertrans_update(bContext *C, TransInfo *t)
 
                        /* pointcache refresh */
                        if (BKE_ptcache_object_reset(t->scene, ob, PTCACHE_RESET_OUTDATED))
-                               ob->recalc |= OB_RECALC_DATA;
+                               DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
 
                        /* Needed for proper updating of "quick cached" dynamics. */
                        /* Creates troubles for moving animated objects without */
index d665a0630acb69b79a10eec890cca7edefd056bc..8d8b39a6358b657f790911afa7258eee9967f046 100644 (file)
@@ -42,6 +42,7 @@ extern "C" {
 #include "DNA_screen_types.h"
 
 #include "BKE_customdata.h"
+#include "BKE_depsgraph.h"
 #include "BKE_global.h"
 #include "BKE_library.h" /* free_libblock */
 #include "BKE_main.h" /* struct Main */
@@ -471,7 +472,7 @@ Object *BlenderStrokeRenderer::NewMesh() const
 #else
        (void)base;
 #endif
-       ob->recalc = OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME;
+       DAG_id_tag_update(&ob->id, OB_RECALC_OB | OB_RECALC_DATA | OB_RECALC_TIME);
 
        return ob;
 }
index 46edb48fde19d87352ff5b042fb73aadd4b1c824..e7edf413f3a7dec189b5a54a9e2469c0dee8b04d 100644 (file)
@@ -1270,6 +1270,7 @@ static void object_simplify_update(Object *ob)
 
        for (md = ob->modifiers.first; md; md = md->next) {
                if (ELEM3(md->type, eModifierType_Subsurf, eModifierType_Multires, eModifierType_ParticleSystem)) {
+                       /* TODO(sergey): Figure out what da heck we're using PSYS flag on object.  */
                        ob->recalc |= PSYS_RECALC_CHILD;
                        DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
                }
index 382229cca88958eea1f348f954cd97ca198dae11..57d7a87199c52dc46cc0abe3a5ed76cdd23378a1 100644 (file)
@@ -5146,7 +5146,9 @@ void RE_Database_FromScene(Render *re, Main *bmain, Scene *scene, unsigned int l
                normalize_m4_m4(mat, camera->obmat);
                invert_m4(mat);
                RE_SetView(re, mat);
-               camera->recalc= OB_RECALC_OB; /* force correct matrix for scaled cameras */
+
+               /* force correct matrix for scaled cameras */
+               DAG_id_tag_update(&camera->id, OB_RECALC_OB);
        }
        
        /* store for incremental render, viewmat rotates dbase */