Merging r57816 through r57896 from trunk into soc-2013-depsgraph_mt
authorSergey Sharybin <sergey.vfx@gmail.com>
Mon, 1 Jul 2013 09:07:21 +0000 (09:07 +0000)
committerSergey Sharybin <sergey.vfx@gmail.com>
Mon, 1 Jul 2013 09:07:21 +0000 (09:07 +0000)
21 files changed:
release/datafiles/startup.blend
release/scripts/startup/bl_ui/properties_render.py
source/blender/blenkernel/BKE_curve.h
source/blender/blenkernel/BKE_depsgraph.h
source/blender/blenkernel/BKE_mball.h
source/blender/blenkernel/intern/DerivedMesh.c
source/blender/blenkernel/intern/curve.c
source/blender/blenkernel/intern/depsgraph.c
source/blender/blenkernel/intern/displist.c
source/blender/blenkernel/intern/mball.c
source/blender/blenkernel/intern/scene.c
source/blender/blenloader/intern/readfile.c
source/blender/editors/render/render_internal.c
source/blender/editors/space_image/image_ops.c
source/blender/makesdna/DNA_scene_types.h
source/blender/makesdna/DNA_windowmanager_types.h
source/blender/makesrna/intern/rna_scene.c
source/blender/windowmanager/WM_api.h
source/blender/windowmanager/WM_types.h
source/blender/windowmanager/intern/wm_event_system.c
source/blender/windowmanager/intern/wm_operators.c

index c604d16829ce6a5106e895002102e33bd2a83707..8b58493fe3a4a1b8e404ee214d1610104aabbab8 100644 (file)
Binary files a/release/datafiles/startup.blend and b/release/datafiles/startup.blend differ
index 799c109ae0cd0606ce2260c4dc6b786277538132..bf4975f2e43ab4f3b2f54e6cc41cc3a507a76e21 100644 (file)
@@ -70,6 +70,7 @@ class RENDER_PT_render(RenderButtonsPanel, Panel):
         row.operator("render.play_rendered_anim", text="Play", icon='PLAY')
 
         layout.prop(rd, "display_mode", text="Display")
+        layout.prop(rd, "use_lock_interface")
 
 
 class RENDER_PT_dimensions(RenderButtonsPanel, Panel):
index 3be77086336b44c34a97a315229763076f4e3373..6c5bbbd487ea67557d412b68cb73962059e10ab2 100644 (file)
@@ -86,7 +86,7 @@ struct ListBase *BKE_curve_editNurbs_get(struct Curve *cu);
 float *BKE_curve_make_orco(struct Scene *scene, struct Object *ob, int *r_numVerts);
 float *BKE_curve_surf_make_orco(struct Object *ob);
 
-void BKE_curve_bevelList_make(struct Object *ob);
+void BKE_curve_bevelList_make(struct Object *ob, bool for_render);
 void BKE_curve_bevel_make(struct Scene *scene, struct Object *ob,  struct ListBase *disp, int forRender, int renderResolution);
 
 void BKE_curve_forward_diff_bezier(float q0, float q1, float q2, float q3, float *p, int it, int stride);
index 6baf20aeb2c89e4423cbbe35cbd57c9d8c23cef4..09170c2898c1ae5665f609159d48a37d036fb2b2 100644 (file)
@@ -48,6 +48,7 @@ struct ID;
 struct Main;
 struct Object;
 struct Scene;
+struct ListBase;
 
 /* Build and Update
  *
@@ -115,10 +116,58 @@ void DAG_pose_sort(struct Object *ob);
 void DAG_editors_update_cb(void (*id_func)(struct Main *bmain, struct ID *id),
                            void (*scene_func)(struct Main *bmain, struct Scene *scene, int updated));
 
+/* Threaded update: get groups of independent bases
+ *
+ * DAG_get_independent_groups goes over dependency graph and collects
+ * groups of bases in a way that there's no dependencies between this
+ * groups at all.
+ *
+ * Result is stored in a list called groups. This is a sliced list,
+ * which means every element of list groups is a LinkData which data
+ * represents list of bases in that group.
+ *
+ * List of bases uses LinkData as well, this is so because bases are
+ * used from actual scene.
+ *
+ * Here's an example of groups storage. There're two groups, one of
+ * them consists of two bases: base1 and base2, and base2 depends on
+ * base1. Second group contains base3 which doesn't depend on base1
+ * and base2.
+ *
+ *   groups
+ *    |
+ *    +- LinkData
+ *    |      |
+ *    |      +- BasesList
+ *    |              |
+ *    |              +- LinkData
+ *    |              |      |
+ *    |              |      + - base1
+ *    |              |
+ *    |              +- LinkData
+ *    |                     |
+ *    |                     + - base2
+ *    |
+ *    +- LinkData
+ *           |
+ *           +- BasesList
+ *                   |
+ *                   +- LinkData
+ *                          |
+ *                          + - base3
+ *
+ * Bases in every group are sorted in a dependency order, meaning
+ * first base in group doesn't depend on any other objects, and
+ * further bases depends on bases above.
+ */
+void DAG_get_independent_groups(struct Scene *scene, struct ListBase *groups);
+
 /* Debugging: print dependency graph for scene or armature object to console */
 
 void DAG_print_dependencies(struct Main *bmain, struct Scene *scene, struct Object *ob);
 
+void DAG_print_dependency_groups(struct Scene *scene);
+
 #ifdef __cplusplus
 }
 #endif
index 44459300c5640b8d7f6574e373b3b0e4ed97636e..7665e1b54dc6df497daf6cb9ff84889f9f329a38 100644 (file)
@@ -47,7 +47,7 @@ void BKE_mball_make_local(struct MetaBall *mb);
 
 void BKE_mball_cubeTable_free(void);
 
-void BKE_mball_polygonize(struct Scene *scene, struct Object *ob, struct ListBase *dispbase);
+void BKE_mball_polygonize(struct Scene *scene, struct Object *ob, struct ListBase *dispbase, bool for_render);
 bool BKE_mball_is_basis_for(struct Object *ob1, struct Object *ob2);
 bool BKE_mball_is_basis(struct Object *ob);
 struct Object *BKE_mball_basis_find(struct Scene *scene, struct Object *ob);
index 6f86b0c3f887c66e11fc5c599a329d7b4be701bd..b2ba1755303b4d216e2b867934fa5f4b86aba2cd 100644 (file)
@@ -49,6 +49,7 @@
 #include "BLI_array.h"
 #include "BLI_utildefines.h"
 #include "BLI_linklist.h"
+#include "BLI_threads.h"
 
 #include "BKE_pbvh.h"
 #include "BKE_cdderivedmesh.h"
index 3b9fbbb1c4d98948dfbb57ee9ff707250fbc7089..3b902a2b6768b6552d6eac77ec21696f60b99123 100644 (file)
@@ -2269,7 +2269,7 @@ static void make_bevel_list_2D(BevList *bl)
        }
 }
 
-void BKE_curve_bevelList_make(Object *ob)
+void BKE_curve_bevelList_make(Object *ob, bool for_render)
 {
        /*
         * - convert all curves to polys, with indication of resol and flags for double-vertices
@@ -2325,7 +2325,7 @@ void BKE_curve_bevelList_make(Object *ob)
                        bl->nr = 0;
                }
                else {
-                       if (G.is_rendering && cu->resolu_ren != 0)
+                       if (for_render && cu->resolu_ren != 0)
                                resolu = cu->resolu_ren;
                        else
                                resolu = nu->resolu;
index 2206770dfbd7b064bf54aac0d46f211c9f62e398..d10d6b23ba4a0eb45bef01b3b8650f7258cc5e80 100644 (file)
@@ -2652,6 +2652,172 @@ void DAG_pose_sort(Object *ob)
        ugly_hack_sorry = 1;
 }
 
+/* ******************* DAG FOR THREADED UPDATR ***************** */
+
+void DAG_get_independent_groups(Scene *scene, ListBase *groups)
+{
+#if defined __GNUC__ || defined __sun
+#  define PRINT(format, args ...) { if (dag_print_dependencies) printf(format, ##args); } (void)0
+#else
+#  define PRINT(format, ...) { if (dag_print_dependencies) printf(__VA_ARGS__); } (void)0
+#endif
+
+       DagNode *node, *rootnode;
+       DagNodeQueue *node_queue;
+       DagAdjList *itA;
+       bool skip = false;
+       Base *base;
+       ListBase *current_group = NULL;
+       bool has_group = false;
+       int root_count = 0;
+
+       PRINT("Independend groups of objects:\n");
+       PRINT("\nACHTUNG!!! Order of objects in groups is reversed in outout!\n");
+       PRINT("Don't ask why, just be aware of this.\n\n");
+
+       /* ** STEP 0: Some pre-initialization. ** */
+
+       rootnode = scene->theDag->DagNode.first;
+
+       node_queue = queue_create(DAGQUEUEALLOC);
+
+       /* Mark all objects as unhandled.
+        * This flag is checked later to detect cycle dependencies.
+        */
+       for (base = scene->base.first; base; base = base->next) {
+               base->object->id.flag |= LIB_DOIT;
+       }
+
+       /* ** STEP 1: Find all nodes which doesn't depend on other nodes ** */
+
+       /* Mark all nodes as not visited. */
+       for (node = scene->theDag->DagNode.first; node; node = node->next) {
+               node->color = DAG_WHITE;
+       }
+
+       /* Detect all the nodes which doesn't have parent. */
+       for (node = scene->theDag->DagNode.first; node; node = node->next) {
+               DagAdjList *itA;
+
+               if (node == rootnode) {
+                       continue;
+               }
+
+               for (itA = node->child; itA; itA = itA->next) {
+                       if (itA->node != node) {
+                               itA->node->color = DAG_BLACK;
+                       }
+               }
+       }
+
+       /* Always put root to a traverse queue. */
+       rootnode->color = DAG_GRAY;
+       push_stack(node_queue, rootnode);
+
+       /* Put all nodes which that depend on other nodes to a traverse queue.
+        * Also mark this nodes as gray (since they're visited but not finished)
+        * and mark all other nodes as white (they're not viisted).
+        */
+       for (node = scene->theDag->DagNode.first; node; node = node->next) {
+               if (node == rootnode) {
+                       continue;
+               }
+
+               if (node->color == DAG_WHITE) {
+                       PRINT("Root node: %s\n", dag_node_name(node));
+
+                       node->color = DAG_GRAY;
+                       push_stack(node_queue, node);
+               }
+               else {
+                       node->color = DAG_WHITE;
+               }
+       }
+
+       root_count = node_queue->count;
+
+       /* ** STEP 2: Traverse the graph and collect all the groups. ** */
+
+       while (node_queue->count) {
+               skip = false;
+               node = get_top_node_queue(node_queue);
+
+               itA = node->child;
+               while (itA != NULL) {
+                       if (itA->node->color == DAG_WHITE) {
+                               itA->node->color = DAG_GRAY;
+                               push_stack(node_queue, itA->node);
+                               skip = true;
+                               break;
+                       }
+                       itA = itA->next;
+               }
+
+               if (!skip) {
+                       if (node) {
+                               node = pop_queue(node_queue);
+                               if (node->ob == scene) {
+                                       /* Whatever Thom, we are done! */
+                                       break;
+                               }
+
+                               node->color = DAG_BLACK;
+
+                               base = scene->base.first;
+                               while (base && base->object != node->ob) {
+                                       base = base->next;
+                               }
+
+                               if (base) {
+                                       base->object->id.flag &= ~LIB_DOIT;
+
+                                       if (has_group == false) {
+                                               PRINT("- Next group\n");
+
+                                               if (groups) {
+                                                       current_group = MEM_callocN(sizeof(ListBase), "DAG independent group");
+                                                       BLI_addhead(groups, BLI_genericNodeN(current_group));
+                                               }
+
+                                               has_group = true;
+                                       }
+
+                                       PRINT("  %s\n", dag_node_name(node));
+
+                                       if (groups) {
+                                               BLI_addhead(current_group, BLI_genericNodeN(base));
+                                       }
+                               }
+
+                               if (node_queue->count < root_count) {
+                                       root_count--;
+                                       has_group = false;
+                               }
+                       }
+               }
+       }
+
+       /* Here we put all cyclic objects to separate groups */
+       for (base = scene->base.first; base; base = base->next) {
+               if (base->object->id.flag & LIB_DOIT) {
+                       base->object->id.flag &= ~LIB_DOIT;
+
+                       PRINT("- Next cyclic group\n");
+                       PRINT("  %s\n", base->object->id.name + 2);
+
+                       if (groups) {
+                               current_group = MEM_callocN(sizeof(ListBase), "DAG independent group");
+                               BLI_addtail(groups, BLI_genericNodeN(current_group));
+                               BLI_addtail(current_group, BLI_genericNodeN(base));
+                       }
+               }
+       }
+
+       queue_delete(node_queue);
+
+#undef PRINT
+}
+
 /* ************************ DAG DEBUGGING ********************* */
 
 void DAG_print_dependencies(Main *bmain, Scene *scene, Object *ob)
@@ -2671,3 +2837,11 @@ void DAG_print_dependencies(Main *bmain, Scene *scene, Object *ob)
        dag_print_dependencies = 0;
 }
 
+void DAG_print_dependency_groups(Scene *scene)
+{
+       dag_print_dependencies = 1;
+
+       DAG_get_independent_groups(scene, NULL);
+
+       dag_print_dependencies = 0;
+}
index 38a0b8483397a4e9c588b7e0a60e1f08146a307e..edfcefe14c7f90f26a2a6adf241cccef62932626 100644 (file)
@@ -712,17 +712,11 @@ void BKE_displist_make_mball(Scene *scene, Object *ob)
        if (!ob || ob->type != OB_MBALL)
                return;
 
-       /* XXX: mball stuff uses plenty of global variables
-        *      while this is unchanged updating during render is unsafe
-        */
-       if (G.is_rendering)
-               return;
-
        BKE_displist_free(&(ob->disp));
 
        if (ob->type == OB_MBALL) {
                if (ob == BKE_mball_basis_find(scene, ob)) {
-                       BKE_mball_polygonize(scene, ob, &ob->disp);
+                       BKE_mball_polygonize(scene, ob, &ob->disp, false);
                        BKE_mball_texspace_calc(ob);
 
                        object_deform_mball(ob, &ob->disp);
@@ -734,7 +728,7 @@ void BKE_displist_make_mball(Scene *scene, Object *ob)
 
 void BKE_displist_make_mball_forRender(Scene *scene, Object *ob, ListBase *dispbase)
 {
-       BKE_mball_polygonize(scene, ob, dispbase);
+       BKE_mball_polygonize(scene, ob, dispbase, true);
        BKE_mball_texspace_calc(ob);
 
        object_deform_mball(ob, dispbase);
@@ -1407,7 +1401,7 @@ static void do_makeDispListCurveTypes(Scene *scene, Object *ob, ListBase *dispba
                if (!forOrco)
                        curve_calc_modifiers_pre(scene, ob, forRender, renderResolution, &originalVerts, &deformedVerts, &numVerts);
 
-               BKE_curve_bevelList_make(ob);
+               BKE_curve_bevelList_make(ob, forRender != FALSE);
 
                /* If curve has no bevel will return nothing */
                BKE_curve_bevel_make(scene, ob, &dlbev, forRender, renderResolution);
index 31212c3a6b79b7fb71c0724444fdcd87db918da7..f84a6088ee53e875f8a4568931fbc3547dcdf7b7 100644 (file)
@@ -2269,7 +2269,7 @@ static void mball_count(PROCESS *process, Scene *scene, Object *basis)
        }
 }
 
-void BKE_mball_polygonize(Scene *scene, Object *ob, ListBase *dispbase)
+void BKE_mball_polygonize(Scene *scene, Object *ob, ListBase *dispbase, bool for_render)
 {
        MetaBall *mb;
        DispList *dl;
@@ -2282,7 +2282,7 @@ void BKE_mball_polygonize(Scene *scene, Object *ob, ListBase *dispbase)
        mball_count(&process, scene, ob);
 
        if (process.totelem == 0) return;
-       if ((G.is_rendering == FALSE) && (mb->flag == MB_UPDATE_NEVER)) return;
+       if ((for_render == FALSE) && (mb->flag == MB_UPDATE_NEVER)) return;
        if ((G.moving & (G_TRANSFORM_OBJ | G_TRANSFORM_EDIT)) && mb->flag == MB_UPDATE_FAST) return;
 
        process.thresh = mb->thresh;
@@ -2320,7 +2320,7 @@ void BKE_mball_polygonize(Scene *scene, Object *ob, ListBase *dispbase)
        }
 
        /* width is size per polygonize cube */
-       if (G.is_rendering) {
+       if (for_render) {
                width = mb->rendersize;
        }
        else {
index 26563afa65b4441019014076409f94fa6d99266c..5636b5a8b87c79fcb0f0afd142efa5197b5cd150 100644 (file)
@@ -1163,32 +1163,298 @@ 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)
+#undef USE_THREADED_UPDATE
+
+/* Debugging only!
+ *
+ * Will enable some additional checks about whether threaded
+ * update went all fine or there's some mistake in code somewhere
+ * (like, missing object update or object being updated from two threads).
+ */
+#undef UPDATE_SANITY_CHECK
+
+typedef struct SceneUpdateThreadHandle {
+#ifdef UPDATE_SANITY_CHECK
+       int thread;
+#endif
+
+       /* Current scene and it's parent */
+       Scene *scene;
+       Scene *scene_parent;
+
+       /* Every thread handles several independent groups.
+        * This is a current group.
+        */
+       LinkData *current_group_link;
+
+       /* This is a first group which shouldn't be handled
+        * (aka it's handled by another thread as as soon
+        * as current thread reaches it thread shall stop).
+        */
+       LinkData *barrier_group_link;
+
+       /* Current link to a base which wasn't updated yet. */
+       LinkData *current_base_link;
+} SceneUpdateThreadHandle;
+
+static Base *scene_update_thread_next_base(SceneUpdateThreadHandle *handle)
+{
+       Base *base = NULL;
+
+       /* If current base link became NULL, it means traversing current group
+        * is finished and we need to go to a next group.
+        */
+       if (handle->current_base_link == NULL) {
+               ListBase *current_bases;
+
+               handle->current_group_link = handle->current_group_link->next;
+
+               /* If we've reached barrier group, we need to stop current thread. */
+               if (handle->current_group_link == handle->barrier_group_link) {
+                       return NULL;
+               }
+
+               current_bases = handle->current_group_link->data;
+               handle->current_base_link = current_bases->first;
+       }
+
+       if (handle->current_base_link) {
+               base = handle->current_base_link->data;
+               handle->current_base_link = handle->current_base_link->next;
+       }
+
+       return base;
+}
+
+static void scene_update_single_base(Scene *scene_parent, Scene *scene, Base *base)
+{
+       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;
+
+#ifdef UPDATE_SANITY_CHECK
+       base->object->id.flag &= ~LIB_DOIT;
+#endif
+}
+
+static void scene_update_all_bases(Scene *scene_parent, Scene *scene)
 {
        Base *base;
-       
+
+       for (base = scene->base.first; base; base = base->next) {
+               scene_update_single_base(scene_parent, scene, base);
+       }
+}
+
+static void *scene_update_tagged_thread(void *handle_v)
+{
+       SceneUpdateThreadHandle *handle = handle_v;
+       Base *base;
+
+       while ((base = scene_update_thread_next_base(handle))) {
+#ifdef UPDATE_SANITY_CHECK
+               BLI_lock_thread(LOCK_CUSTOM1);
+               printf("Thread %d: updating %s\n", handle->thread, base->object->id.name + 2);
+
+               BLI_assert(base->object->id.flag & LIB_DOIT);
+#endif
+
+               scene_update_single_base(handle->scene_parent, handle->scene, base);
+
+#ifdef UPDATE_SANITY_CHECK
+               BLI_unlock_thread(LOCK_CUSTOM1);
+#endif
+       }
+
+       return NULL;
+}
+
+static void scene_update_objects_threaded(Scene *scene, Scene *scene_parent)
+{
+       ListBase threads;
+       SceneUpdateThreadHandle *handles;
+       ListBase groups = {NULL, NULL};
+       LinkData *current_group_link;
+       int i, tot_thread = BLI_system_thread_count();
+       int tot_group, groups_per_thread, additional_group_threads;
+
+       /* 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);
+                       }
+               }
+       }
+
+       /* Get independent groups of bases. */
+       DAG_get_independent_groups(scene, &groups);
+
+       /* TODO(sergey): It could make sense to make DAG_get_independent_groups
+        *               to return number of groups. */
+       tot_group = BLI_countlist(&groups);
+
+       /* We don't use more threads than we've got groups. */
+       tot_thread = min_ii(tot_group, tot_thread);
+       if (tot_thread > 1) {
+               BLI_init_threads(&threads, scene_update_tagged_thread, tot_thread);
+       }
+
+       handles = MEM_callocN(sizeof(SceneUpdateThreadHandle) * tot_thread,
+                             "scene update object handles");
+
+#ifdef UPDATE_SANITY_CHECK
+       {
+               Base *base;
+               for (base = scene->base.first; base; base = base->next) {
+                       base->object->id.flag |= LIB_DOIT;
+               }
+       }
+#endif
+
+       /* Every thread handles groups_per_thread groups of bases. */
+       current_group_link = groups.first;
+       groups_per_thread = tot_group / tot_thread;
+
+       /* Some threads will handle more groups,
+        * This happens if devision of groups didn't give integer value
+        * and in this case 'additional_group_threads' of threads will
+        * handle one more extra group.
+        */
+       additional_group_threads = tot_group - groups_per_thread * tot_thread;
+
+       /* Fill in thread handles. */
+       for (i = 0; i < tot_thread; i++) {
+               SceneUpdateThreadHandle *handle = &handles[i];
+               ListBase *current_bases = current_group_link->data;
+               int j, current_groups_per_thread = groups_per_thread;
+
+               if (i < additional_group_threads) {
+                       current_groups_per_thread++;
+               }
+
+#ifdef  UPDATE_SANITY_CHECK
+               handle->thread = i;
+#endif
+
+               handle->scene = scene;
+               handle->scene_parent = scene_parent;
+               handle->current_group_link = current_group_link;
+               handle->current_base_link = current_bases->first;
+
+               /* Find the barried link, which will also be a start group link
+                * for the next thread.
+                *
+                * If this is the last thread, we could skip iteration cycle here.
+                */
+               if (i != tot_thread - 1) {
+                       for (j = 0; j < current_groups_per_thread && current_group_link; j++) {
+                               current_group_link = current_group_link->next;
+                       }
+               }
+               else {
+                       current_group_link = NULL;
+               }
+
+               handle->barrier_group_link = current_group_link;
+
+               if (tot_thread > 1) {
+                       BLI_insert_thread(&threads, handle);
+               }
+       }
+
+       if (tot_thread > 1) {
+               BLI_end_threads(&threads);
+       }
+       else {
+               scene_update_tagged_thread(handles);
+       }
+
+       /* Free memory used by thread handles. */
+       MEM_freeN(handles);
+
+       /* Traverse groups and fee all the memory used by them. */
+       for (current_group_link = groups.first;
+            current_group_link;
+            current_group_link = current_group_link->next)
+       {
+               ListBase *current_bases = current_group_link->data;
+
+               BLI_freelistN(current_bases);
+               MEM_freeN(current_bases);
+       }
+       BLI_freelistN(&groups);
+
+#ifdef UPDATE_SANITY_CHECK
+       {
+               Base *base;
+               for (base = scene->base.first; base; base = base->next) {
+                       BLI_assert((base->object->id.flag & LIB_DOIT) == 0);
+               }
+       }
+#endif
+}
+
+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++;
+               }
+       }
+
+#ifndef USE_THREADED_UPDATE
+       if (true) {
+               scene_update_all_bases(scene_parent, scene);
+       }
+       else
+#endif
+       if (update_count > 1) {
+               scene_update_objects_threaded(scene, scene_parent);
+       }
+       else {
+               scene_update_all_bases(scene_parent, scene);
+       }
+}
+
+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 */
-       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);
-                       
-               /* 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;
-       }
-       
+       scene_update_objects(scene, scene_parent);
+
        /* scene drivers... */
        scene_update_drivers(bmain, scene);
 
index 5d30bf6ff738cfa315e7cee25ef897d6e465df15..9fa352a581768cea38de2209901c430afc5fd9f8 100644 (file)
@@ -5519,6 +5519,7 @@ static void direct_link_windowmanager(FileData *fd, wmWindowManager *wm)
        wm->winactive = NULL;
        wm->initialized = 0;
        wm->op_undo_depth = 0;
+       wm->is_interface_locked = 0;
 }
 
 static void lib_link_windowmanager(FileData *fd, Main *main)
index 09138a5523aeeaf0a767d02bbfb2f88dca80b231..563f416ead0bc84799d2e4988caa7cc5dda529c5 100644 (file)
@@ -273,6 +273,7 @@ typedef struct RenderJob {
        short *do_update;
        float *progress;
        ReportList *reports;
+       bool interface_locked;
 } RenderJob;
 
 static void render_freejob(void *rjv)
@@ -498,6 +499,15 @@ static void render_endjob(void *rjv)
 
                BKE_image_release_ibuf(ima, ibuf, lock);
        }
+
+       /* Finally unlock the user interface (if it was locked). */
+       if (rj->interface_locked) {
+               /* Interface was locked, so window manager couldn't have been changed
+                * and using one from Global will unlock exactly the same manager as
+                * was locked before running the job.
+                */
+               WM_set_locked_interface(G.main->wm.first, false);
+       }
 }
 
 /* called by render, check job 'stop' value or the global */
@@ -523,10 +533,14 @@ static int render_break(void *UNUSED(rjv))
 
 /* runs in thread, no cursor setting here works. careful with notifiers too (malloc conflicts) */
 /* maybe need a way to get job send notifer? */
-static void render_drawlock(void *UNUSED(rjv), int lock)
+static void render_drawlock(void *rjv, int lock)
 {
-       BKE_spacedata_draw_locks(lock);
-       
+       RenderJob *rj = rjv;
+
+       /* If interface is locked, renderer callback shall do nothing. */
+       if (!rj->interface_locked) {
+               BKE_spacedata_draw_locks(lock);
+       }
 }
 
 /* catch esc */
@@ -649,6 +663,23 @@ static int screen_render_invoke(bContext *C, wmOperator *op, const wmEvent *even
                        rj->lay |= v3d->localvd->lay;
        }
 
+       /* Lock the user interface depending on render settings. */
+       if (scene->r.use_lock_interface) {
+               WM_set_locked_interface(CTX_wm_manager(C), true);
+
+               /* Set flag interface need to be unlocked.
+                *
+                * This is so because we don't have copy of render settings
+                * accessible from render job and copy is needed in case
+                * of non-locked rendering, so we wouldn't try to unlock
+                * anything if option was initially unset but then was
+                * enabled during rendering.
+                */
+               rj->interface_locked = true;
+
+               /* TODO(sergey): clean memory used by viewport? */
+       }
+
        /* setup job */
        if (RE_seq_render_active(scene, &scene->r)) name = "Sequence Render";
        else name = "Render";
index 20bbfbbf343b8b01d8de23a51121cb6dce71c91c..a845f8e525c33fa57ff9b992563786506c7427f7 100644 (file)
@@ -360,7 +360,7 @@ void IMAGE_OT_view_pan(wmOperatorType *ot)
        ot->poll = space_image_main_area_poll;
 
        /* flags */
-       ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER;
+       ot->flag = OPTYPE_BLOCKING | OPTYPE_GRAB_POINTER | OPTYPE_ALLOW_LOCKED;
        
        /* properties */
        RNA_def_float_vector(ot->srna, "offset", 2, NULL, -FLT_MAX, FLT_MAX,
@@ -575,7 +575,7 @@ void IMAGE_OT_view_zoom(wmOperatorType *ot)
        ot->poll = space_image_main_area_poll;
 
        /* flags */
-       ot->flag = OPTYPE_BLOCKING;
+       ot->flag = OPTYPE_BLOCKING | OPTYPE_ALLOW_LOCKED;
        
        /* properties */
        RNA_def_float(ot->srna, "factor", 0.0f, -FLT_MAX, FLT_MAX,
@@ -638,6 +638,9 @@ void IMAGE_OT_view_ndof(wmOperatorType *ot)
        
        /* api callbacks */
        ot->invoke = image_view_ndof_invoke;
+
+       /* flags */
+       ot->flag = OPTYPE_ALLOW_LOCKED;
 }
 
 /********************** view all operator *********************/
@@ -693,6 +696,9 @@ void IMAGE_OT_view_all(wmOperatorType *ot)
        /* api callbacks */
        ot->exec = image_view_all_exec;
        ot->poll = space_image_main_area_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_GRAB_POINTER;
 }
 
 /********************** view selected operator *********************/
@@ -755,6 +761,9 @@ void IMAGE_OT_view_selected(wmOperatorType *ot)
        /* api callbacks */
        ot->exec = image_view_selected_exec;
        ot->poll = image_view_selected_poll;
+
+       /* flags */
+       ot->flag = OPTYPE_GRAB_POINTER;
 }
 
 /********************** view zoom in/out operator *********************/
@@ -797,6 +806,9 @@ void IMAGE_OT_view_zoom_in(wmOperatorType *ot)
        ot->exec = image_view_zoom_in_exec;
        ot->poll = space_image_main_area_poll;
 
+       /* flags */
+       ot->flag = OPTYPE_ALLOW_LOCKED;
+
        /* properties */
        RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location", "Cursor location in screen coordinates", -10.0f, 10.0f);
 }
@@ -839,6 +851,9 @@ void IMAGE_OT_view_zoom_out(wmOperatorType *ot)
        ot->exec = image_view_zoom_out_exec;
        ot->poll = space_image_main_area_poll;
 
+       /* flags */
+       ot->flag = OPTYPE_ALLOW_LOCKED;
+
        /* properties */
        RNA_def_float_vector(ot->srna, "location", 2, NULL, -FLT_MAX, FLT_MAX, "Location", "Cursor location in screen coordinates", -10.0f, 10.0f);
 }
@@ -880,7 +895,10 @@ void IMAGE_OT_view_zoom_ratio(wmOperatorType *ot)
        /* api callbacks */
        ot->exec = image_view_zoom_ratio_exec;
        ot->poll = space_image_main_area_poll;
-       
+
+       /* flags */
+       ot->flag = OPTYPE_ALLOW_LOCKED;
+
        /* properties */
        RNA_def_float(ot->srna, "ratio", 0.0f, -FLT_MAX, FLT_MAX,
                      "Ratio", "Zoom ratio, 1.0 is 1:1, higher is zoomed in, lower is zoomed out", -FLT_MAX, FLT_MAX);
index 5e877ed697b35a43cb0dee974d7c1847a61b867c..ac38578b981b8e1b9907d9c661860352405385e2 100644 (file)
@@ -428,7 +428,8 @@ typedef struct RenderData {
         * Render to image editor, fullscreen or to new window.
         */
        short displaymode;
-       short pad7;
+       char use_lock_interface;
+       char pad7;
 
        /**
         * Flags for render settings. Use bit-masking to access the settings.
index 27aef3b8ec63715969d37b88dfa05bdc4dd999d8..29fac36ddd078030b854496a8c44a605704b2474 100644 (file)
@@ -151,6 +151,9 @@ typedef struct wmWindowManager {
 
        ListBase timers;                                        /* active timers */
        struct wmTimer *autosavetimer;          /* timer for auto save */
+
+       char is_interface_locked;               /* indicates whether interface is locked for user interaction */
+       char par[7];
 } wmWindowManager;
 
 /* wmWindowManager.initialized */
index d46b1a2843945ae1769a0881904d98f0c705796e..cb6ba0f85d0400d24b3ed7a10f06256ed221ea89 100644 (file)
@@ -4520,7 +4520,12 @@ static void rna_def_scene_render_data(BlenderRNA *brna)
        RNA_def_property_enum_items(prop, display_mode_items);
        RNA_def_property_ui_text(prop, "Display", "Select where rendered images will be displayed");
        RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
-       
+
+       prop = RNA_def_property(srna, "use_lock_interface", PROP_BOOLEAN, PROP_NONE);
+       RNA_def_property_boolean_sdna(prop, NULL, "use_lock_interface", 1);
+       RNA_def_property_ui_text(prop, "Lock Interface", "Lock interface during rendering in favor of giving more memory to the renderer");
+       RNA_def_property_update(prop, NC_SCENE | ND_RENDER_OPTIONS, NULL);
+
        prop = RNA_def_property(srna, "filepath", PROP_STRING, PROP_FILEPATH);
        RNA_def_property_string_sdna(prop, NULL, "pic");
        RNA_def_property_ui_text(prop, "Output Path",
index 72b54e2f1f7359fea668a6806b75f583e8de34e4..fb9b8b8f10b37bca5477137bc8ffa38d3fd15310 100644 (file)
@@ -412,6 +412,9 @@ void        WM_main_playanim(int argc, const char **argv);
 /* debugging only, convenience function to write on crash */
 int write_crash_blend(void);
 
+                       /* Lock the interface for any communication */
+void        WM_set_locked_interface(struct wmWindowManager *wm, bool lock);
+
 #ifdef __cplusplus
 }
 #endif
index f1932c8aa97f2bbb6a205482150c2403765d581d..2364c4b13bb71ad78f53d54732224d1093275400 100644 (file)
@@ -133,6 +133,7 @@ struct ImBuf;
                                                                 * and don't make sense to be accessed from the
                                                                 * search menu, even if poll() returns TRUE.
                                                                 * currently only used for the search toolbox */
+#define OPTYPE_ALLOW_LOCKED    128     /* Allow operator to run when interface is locked */
 
 /* context to call operator in for WM_operator_name_call */
 /* rna_ui.c contains EnumPropertyItem's of these, keep in sync */
index 5b469a6c50fdf9656e0c1c0991db5d1e9818a1b1..b9455601ede4e76db1fbbbde716f3ca8e342970e 100644 (file)
@@ -1441,6 +1441,22 @@ static void wm_event_modalkeymap(const bContext *C, wmOperator *op, wmEvent *eve
        }
 }
 
+/* Check whether operator is allowed to run in case interface is locked,
+ * If interface is unlocked, will always return truth.
+ */
+static bool wm_operator_check_locked_interface(bContext *C, wmOperatorType *ot)
+{
+       wmWindowManager *wm = CTX_wm_manager(C);
+
+       if (wm->is_interface_locked) {
+               if ((ot->flag & OPTYPE_ALLOW_LOCKED) == 0) {
+                       return false;
+               }
+       }
+
+       return true;
+}
+
 /* bad hacking event system... better restore event type for checking of KM_CLICK for example */
 /* XXX modal maps could use different method (ton) */
 static void wm_event_modalmap_end(wmEvent *event)
@@ -1467,7 +1483,12 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
                wmOperator *op = handler->op;
                wmOperatorType *ot = op->type;
 
-               if (ot->modal) {
+               if (!wm_operator_check_locked_interface(C, ot)) {
+                       /* Interface is locked and pperator is not allowed to run,
+                        * nothing to do in this case.
+                        */
+               }
+               else if (ot->modal) {
                        /* we set context to where modal handler came from */
                        wmWindowManager *wm = CTX_wm_manager(C);
                        ScrArea *area = CTX_wm_area(C);
@@ -1537,7 +1558,9 @@ static int wm_handler_operator_call(bContext *C, ListBase *handlers, wmEventHand
                wmOperatorType *ot = WM_operatortype_find(event->keymap_idname, 0);
 
                if (ot) {
-                       retval = wm_operator_invoke(C, ot, event, properties, NULL, FALSE);
+                       if (wm_operator_check_locked_interface(C, ot)) {
+                               retval = wm_operator_invoke(C, ot, event, properties, NULL, FALSE);
+                       }
                }
        }
        /* Finished and pass through flag as handled */
@@ -1743,7 +1766,15 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
                /* comment this out to flood the console! (if you really want to test) */
                !ELEM(event->type, MOUSEMOVE, INBETWEEN_MOUSEMOVE)
                ;
+#  if defined __GNUC__ || defined __sun
+#    define PRINT(format, args ...) { if (do_debug_handler) printf(format, ##args); } (void)0
+#  else
+#    define PRINT(format, ...) { if (do_debug_handler) printf(__VA_ARGS__); } (void)0
+#  endif
+#else
+#  define PRINT(format, ...)
 #endif
+
        wmWindowManager *wm = CTX_wm_manager(C);
        wmEventHandler *handler, *nexthandler;
        int action = WM_HANDLER_CONTINUE;
@@ -1779,28 +1810,16 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
                                wmKeyMap *keymap = WM_keymap_active(wm, handler->keymap);
                                wmKeyMapItem *kmi;
 
-#ifndef NDEBUG
-                               if (do_debug_handler) {
-                                       printf("%s:   checking '%s' ...", __func__, keymap->idname);
-                               }
-#endif
+                               PRINT("%s:   checking '%s' ...", __func__, keymap->idname);
 
                                if (!keymap->poll || keymap->poll(C)) {
 
-#ifndef NDEBUG
-                                       if (do_debug_handler) {
-                                               printf("pass\n");
-                                       }
-#endif
+                                       PRINT("pass\n");
 
                                        for (kmi = keymap->items.first; kmi; kmi = kmi->next) {
                                                if (wm_eventmatch(event, kmi)) {
 
-#ifndef NDEBUG
-                                                       if (do_debug_handler) {
-                                                               printf("%s:     item matched '%s'\n", __func__, kmi->idname);
-                                                       }
-#endif
+                                                       PRINT("%s:     item matched '%s'\n", __func__, kmi->idname);
 
                                                        /* weak, but allows interactive callback to not use rawkey */
                                                        event->keymap_idname = kmi->idname;
@@ -1819,32 +1838,28 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
                                                                        if (G.debug & (G_DEBUG_EVENTS | G_DEBUG_HANDLERS))
                                                                                printf("%s:       handled - and pass on! '%s'\n", __func__, kmi->idname);
                                                                
-#ifndef NDEBUG
-                                                               if (do_debug_handler) {
-                                                                       printf("%s:       un-handled '%s'...", __func__, kmi->idname);
-                                                               }
-#endif
+                                                                       PRINT("%s:       un-handled '%s'...", __func__, kmi->idname);
                                                        }
                                                }
                                        }
                                }
                                else {
-#ifndef NDEBUG
-                                       if (do_debug_handler) {
-                                               printf("fail\n");
-                                       }
-#endif
+                                       PRINT("fail\n");
                                }
                        }
                        else if (handler->ui_handle) {
-                               action |= wm_handler_ui_call(C, handler, event, always_pass);
+                               if (!wm->is_interface_locked) {
+                                       action |= wm_handler_ui_call(C, handler, event, always_pass);
+                               }
                        }
                        else if (handler->type == WM_HANDLER_FILESELECT) {
-                               /* screen context changes here */
-                               action |= wm_handler_fileselect_call(C, handlers, handler, event);
+                               if (!wm->is_interface_locked) {
+                                       /* screen context changes here */
+                                       action |= wm_handler_fileselect_call(C, handlers, handler, event);
+                               }
                        }
                        else if (handler->dropboxes) {
-                               if (event->type == EVT_DROP) {
+                               if (!wm->is_interface_locked && event->type == EVT_DROP) {
                                        wmDropBox *drop = handler->dropboxes->first;
                                        for (; drop; drop = drop->next) {
                                                /* other drop custom types allowed */
@@ -1910,6 +1925,8 @@ static int wm_handlers_do_intern(bContext *C, wmEvent *event, ListBase *handlers
        if (action == (WM_HANDLER_BREAK | WM_HANDLER_MODAL))
                wm_cursor_arrow_move(CTX_wm_window(C), event);
 
+#undef PRINT
+
        return action;
 }
 
@@ -3219,3 +3236,24 @@ void wm_event_add_ghostevent(wmWindowManager *wm, wmWindow *win, int type, int U
        WM_event_print(&event);
 #endif
 }
+
+void WM_set_locked_interface(wmWindowManager *wm, bool lock)
+{
+       /* This will prevent events from being handled while interface is locked
+        *
+        * Use a "local" flag for now, because currently no other areas could
+        * benefit of locked interface anyway (aka using G.is_interface_locked
+        * wouldn't be useful anywhere outside of window manager, so let's not
+        * pollute global context with such an information for now).
+        */
+       wm->is_interface_locked = lock ? 1 : 0;
+
+       /* This will prevent drawing regions which uses non-threadsafe data.
+        * Currently it'll be just a 3D viewport.
+        *
+        * TODO(sergey): Make it different locked states, so different jobs
+        *               could lock different areas of blender and allow
+        *               interation with others?
+        */
+       BKE_spacedata_draw_locks(lock);
+}
index 9e0cb6a1d383de3b8bee5abdad13e8f574e9417a..42e72f521371545fa47c79f9b6c8a21f6317ccb5 100644 (file)
@@ -4075,6 +4075,24 @@ static void WM_OT_dependency_relations(wmOperatorType *ot)
        ot->exec = dependency_relations_exec;
 }
 
+static int dependency_groups_exec(bContext *C, wmOperator *UNUSED(op))
+{
+       Scene *scene = CTX_data_scene(C);
+
+       DAG_print_dependency_groups(scene);
+
+       return OPERATOR_FINISHED;
+}
+
+static void WM_OT_dependency_groups(wmOperatorType *ot)
+{
+       ot->name = "Dependency Groups";
+       ot->idname = "WM_OT_dependency_groups";
+       ot->description = "Print dependency graph groups to the console";
+
+       ot->exec = dependency_groups_exec;
+}
+
 /* ******************************************************* */
 
 static int wm_ndof_sensitivity_exec(bContext *UNUSED(C), wmOperator *op)
@@ -4167,6 +4185,7 @@ void wm_operatortype_init(void)
        WM_operatortype_append(WM_OT_redraw_timer);
        WM_operatortype_append(WM_OT_memory_statistics);
        WM_operatortype_append(WM_OT_dependency_relations);
+       WM_operatortype_append(WM_OT_dependency_groups);
        WM_operatortype_append(WM_OT_debug_menu);
        WM_operatortype_append(WM_OT_operator_defaults);
        WM_operatortype_append(WM_OT_splash);