Depsgraph: execute all COPY_ON_WRITE nodes first as a separate stage.
authorAlexander Gavrilov <angavrilov@gmail.com>
Tue, 4 Dec 2018 10:58:29 +0000 (13:58 +0300)
committerAlexander Gavrilov <angavrilov@gmail.com>
Tue, 4 Dec 2018 11:08:50 +0000 (14:08 +0300)
COW nodes in the graph are mostly connected via a relation type
that doesn't propagate the update flags. Unfortunately, due to
the scheduling implementation that means the relations don't
actually guarantee execution order for indirect dependencies.
Relations also don't guarantee order in case of cycles.

As mentioned in IRC, the simplest way to fix possible problems
is to execute all COW nodes as a separate execution stage. This
seems to fix crashes with Data Transfer modifier in a cycle.

Staging works by simply delaying actual scheduling of tasks for
non-COW nodes until the second run of schedule_graph.

Reviewers: sergey

Differential Revision: https://developer.blender.org/D4027

source/blender/depsgraph/intern/depsgraph.cc
source/blender/depsgraph/intern/eval/deg_eval.cc

index 664d30fdaf6cc7383a85d5d5961466608aa0bcd1..f3f4d788da2c7b0f356b5728f70e1d1e7b9c4735 100644 (file)
@@ -405,6 +405,9 @@ DepsRelation *Depsgraph::add_new_relation(OperationDepsNode *from,
                rel->flag |= flags;
                return rel;
        }
+       /* COW nodes can only depend on other COW nodes. */
+       BLI_assert(to->owner->type != DEG_NODE_TYPE_COPY_ON_WRITE ||
+                  from->owner->type == DEG_NODE_TYPE_COPY_ON_WRITE);
        /* Create new relation, and add it to the graph. */
        rel = OBJECT_GUARDED_NEW(DepsRelation, from, to, description);
        rel->flag |= flags;
index d3b49d275b9f36925f41a35d7d3d44e9d5a40296..02d286c3bd1725f8f4433efb7a705db95e59d566 100644 (file)
@@ -73,6 +73,7 @@ static void schedule_children(TaskPool *pool,
 struct DepsgraphEvalState {
        Depsgraph *graph;
        bool do_stats;
+       bool is_cow_stage;
 };
 
 static void deg_task_run_func(TaskPool *pool,
@@ -214,6 +215,17 @@ static void schedule_node(TaskPool *pool, Depsgraph *graph,
        if (node->num_links_pending != 0) {
                return;
        }
+       /* During the COW stage only schedule COW nodes. */
+       DepsgraphEvalState *state = (DepsgraphEvalState *)BLI_task_pool_userdata(pool);
+       if (state->is_cow_stage) {
+               if (node->owner->type != DEG_NODE_TYPE_COPY_ON_WRITE) {
+                       return;
+               }
+       }
+       else {
+               BLI_assert(node->scheduled || node->owner->type != DEG_NODE_TYPE_COPY_ON_WRITE);
+       }
+       /* Actually schedule the node. */
        bool is_scheduled = atomic_fetch_and_or_uint8(
                (uint8_t *)&node->scheduled, (uint8_t)true);
        if (!is_scheduled) {
@@ -312,6 +324,12 @@ void deg_evaluate_on_refresh(Depsgraph *graph)
        /* Prepare all nodes for evaluation. */
        initialize_execution(&state, graph);
        /* Do actual evaluation now. */
+       /* First, process all Copy-On-Write nodes. */
+       state.is_cow_stage = true;
+       schedule_graph(task_pool, graph);
+       BLI_task_pool_work_wait_and_reset(task_pool);
+       /* After that, process all other nodes. */
+       state.is_cow_stage = false;
        schedule_graph(task_pool, graph);
        BLI_task_pool_work_and_wait(task_pool);
        BLI_task_pool_free(task_pool);