Merging r58112 through r58124 from trunk into soc-2013-depsgraph_mt
[blender.git] / source / blender / blenkernel / intern / scene.c
index 744a8247233ce316d4d94f8101da4c744d787886..0735ff63606c5c5a21db2207caf9b7b26dcc3dce 100644 (file)
@@ -57,6 +57,7 @@
 #include "BLI_callbacks.h"
 #include "BLI_string.h"
 #include "BLI_threads.h"
+#include "BLI_task.h"
 
 #include "BLF_translation.h"
 
@@ -1162,32 +1163,235 @@ static void scene_do_rb_simulation_recursive(Scene *scene, float ctime)
                BKE_rigidbody_do_simulation(scene, ctime);
 }
 
-static void scene_update_tagged_recursive(Main *bmain, Scene *scene, Scene *scene_parent)
+typedef struct ThreadedObjectUpdateState {
+       Scene *scene;
+       Scene *scene_parent;
+       SpinLock lock;
+} ThreadedObjectUpdateState;
+
+static void scene_update_object_add_task(void *node, void *user_data);
+
+static void scene_update_all_bases(Scene *scene, Scene *scene_parent)
 {
        Base *base;
-       
-       scene->customdata_mask = scene_parent->customdata_mask;
 
-       /* sets first, we allow per definition current scene to have
-        * dependencies on sets, but not the other way around. */
-       if (scene->set)
-               scene_update_tagged_recursive(bmain, scene->set, scene_parent);
-       
-       /* scene objects */
        for (base = scene->base.first; base; base = base->next) {
-               Object *ob = base->object;
-               
-               BKE_object_handle_update_ex(scene_parent, ob, scene->rigidbody_world);
-               
-               if (ob->dup_group && (ob->transflag & OB_DUPLIGROUP))
-                       BKE_group_handle_recalc_and_update(scene_parent, ob, ob->dup_group);
-                       
+               Object *object = base->object;
+
+               BKE_object_handle_update_ex(scene_parent, object, scene->rigidbody_world);
+
+               if (object->dup_group && (object->transflag & OB_DUPLIGROUP))
+                       BKE_group_handle_recalc_and_update(scene_parent, object, object->dup_group);
+
                /* always update layer, so that animating layers works (joshua july 2010) */
                /* XXX commented out, this has depsgraph issues anyway - and this breaks setting scenes
                 * (on scene-set, the base-lay is copied to ob-lay (ton nov 2012) */
                // base->lay = ob->lay;
        }
-       
+}
+
+static void scene_update_object_func(TaskPool *pool, void *taskdata, int threadid)
+{
+#define PRINT if (G.debug & G_DEBUG) printf
+
+       ThreadedObjectUpdateState *state = (ThreadedObjectUpdateState *) BLI_task_pool_userdata(pool);
+       void *node = taskdata;
+       Object *object = DAG_threaded_update_get_node_object(node);
+       Scene *scene = state->scene;
+       Scene *scene_parent = state->scene_parent;
+
+       if (object) {
+               PRINT("Thread %d: update object %s\n", threadid, object->id.name);
+
+               /* We only update object itself here, dupli-group will be updated
+                * separately from main thread because of we've got no idea about
+                * dependnecies inside the group.
+                */
+               BKE_object_handle_update_ex(scene_parent, object, scene->rigidbody_world);
+       }
+       else {
+               PRINT("Threda %d: update node %s\n", threadid,
+                       DAG_threaded_update_get_node_name(node));
+       }
+
+       BLI_spin_lock(&state->lock);
+       /* Update will decrease child's valency and schedule child with zero valency. */
+       DAG_threaded_update_handle_node_updated(node,scene_update_object_add_task, pool);
+       BLI_spin_unlock(&state->lock);
+
+#undef PRINT
+}
+
+static void scene_update_object_add_task(void *node, void *user_data)
+{
+       TaskPool *task_pool = user_data;
+
+       BLI_task_pool_push(task_pool, scene_update_object_func, node, false, TASK_PRIORITY_LOW);
+}
+
+static void scene_update_objects_threaded(Scene *scene, Scene *scene_parent)
+{
+       TaskScheduler *task_scheduler;
+       TaskPool *task_pool;
+       ThreadedObjectUpdateState state;
+       int tot_thread = BLI_system_thread_count();
+
+       if (tot_thread == 1) {
+               /* If only one thread is possible we don't bother self with
+                * task pool, which would be an overhead in cas e of single
+                * CPU core.
+                */
+               scene_update_all_bases(scene, scene_parent);
+               return;
+       }
+
+       /* Ensure malloc will go go fine from threads,
+        * this is needed because we could be in main thread here
+        * and malloc could be non-threda safe at this point because
+        * no other jobs are running.
+        */
+       BLI_begin_threaded_malloc();
+
+       /* XXX: Releasing DrawObject is not thread safe, but adding lock
+        *      around it is gonna to harm even more. So for now let's
+        *      free all caches from main thread.
+        *
+        * TODO(sergey): Making DrawObject thread-safe is a nice task on
+        *               it's own and it'll also make it possible to remove
+        *               this hack.
+        */
+       {
+               Base *base;
+               for (base = scene->base.first; base; base = base->next) {
+                       Object *ob = base->object;
+
+                       if (ob->recalc & OB_RECALC_ALL) {
+                               BKE_object_free_derived_caches(ob);
+
+                               if (ob->dup_group && (ob->transflag & OB_DUPLIGROUP)) {
+                                       GroupObject *go;
+                                       for (go = ob->dup_group->gobject.first; go; go = go->next) {
+                                               if (go->ob && go->ob->recalc) {
+                                                       BKE_object_free_derived_caches(go->ob);
+                                               }
+                                       }
+                               }
+                       }
+               }
+       }
+
+       state.scene = scene;
+       state.scene_parent = scene_parent;
+       BLI_spin_init(&state.lock);
+
+       task_scheduler = BLI_task_scheduler_create(tot_thread);
+       task_pool = BLI_task_pool_create(task_scheduler, &state);
+
+       /* Initialize run-time data in the graph needed for traversing it
+        * from multiple threads.
+        *
+        * This will mark DAG nodes as object/non-object and will calculate
+        * "valency" of nodes (which is how many non-updated parents node
+        * have, which helps a lot checking whether node could be scheduled
+        * already or not).
+        */
+       DAG_threaded_update_begin(scene);
+
+       /* Put all nodes which are already ready for schedule to the task pool.
+        * usually its just a Scene node.
+        *
+        * We do lock here so no tthreads will start updating nodes valency
+        * while we're still fillign the queue in. Otherwise it's possible
+        * to run into situations when the same task is adding twice to the
+        * queue due to non-safe nature of function below.
+        */
+       BLI_spin_lock(&state.lock);
+       DAG_threaded_update_foreach_ready_node(scene, scene_update_object_add_task, task_pool);
+       BLI_spin_unlock(&state.lock);
+
+       /* work and wait until tasks are done */
+       BLI_task_pool_work_and_wait(task_pool);
+
+       /* free */
+       BLI_task_pool_free(task_pool);
+       BLI_task_scheduler_free(task_scheduler);
+
+       BLI_end_threaded_malloc();
+
+       BLI_spin_end(&state.lock);
+
+       /* XXX: Weak, very weak!
+        *
+        * We update dupligroups in single thread! :S
+        *
+        * This is because we've got absolutely no idea about dependencies
+        * inside the group and we only know order of objects in which we
+        * need to perform objects update.
+        *
+        * We even can not update different groups in different threads,
+        * because groups could share the objects and detecting whether
+        * object is updating in multiple threads is not so much easy.
+        *
+        * This is solvable with local group dependency graph or expanding
+        * current dependency graph to be aware of dependencies inside
+        * groups.
+        *
+        * P.S. Objects from the dup_group are very much likely in scene's
+        *      dependency graph and were alreayd updated in threaded tasks
+        *      scheduler already.
+        *
+        *      So objects from the dupli_groups are likely don't have
+        *      OB_RECALC_ALL flag here, but it seems they still do have
+        *      non-zero recalc flags, and here we make sure things are
+        *      100% by calling BKE_group_handle_recalc_and_update.
+        */
+       {
+               Base *base;
+               for (base = scene->base.first; base; base = base->next) {
+                       Object *object = base->object;
+
+                       if (object->dup_group && (object->transflag & OB_DUPLIGROUP))
+                               BKE_group_handle_recalc_and_update(scene_parent, object, object->dup_group);
+               }
+       }
+}
+
+static void scene_update_objects(Scene *scene, Scene *scene_parent)
+{
+       Base *base;
+       int update_count = 0;
+
+       /* Optimization thing: don't do threads if no modifier
+        * stack need to be evaluated.
+        */
+       for (base = scene->base.first; base; base = base->next) {
+               Object *ob = base->object;
+
+               if (ob->recalc & OB_RECALC_ALL) {
+                       update_count++;
+               }
+       }
+
+       if (update_count > 1) {
+               scene_update_objects_threaded(scene, scene_parent);
+       }
+       else {
+               scene_update_all_bases(scene, scene_parent);
+       }
+}
+
+static void scene_update_tagged_recursive(Main *bmain, Scene *scene, Scene *scene_parent)
+{
+       scene->customdata_mask = scene_parent->customdata_mask;
+
+       /* sets first, we allow per definition current scene to have
+        * dependencies on sets, but not the other way around. */
+       if (scene->set)
+               scene_update_tagged_recursive(bmain, scene->set, scene_parent);
+
+       /* scene objects */
+       scene_update_objects(scene, scene_parent);
+
        /* scene drivers... */
        scene_update_drivers(bmain, scene);