Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/depsgraph/intern/eval')
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval.cc257
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_debug.cc250
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_debug.h79
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_flush.cc345
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_stats.cc70
-rw-r--r--source/blender/depsgraph/intern/eval/deg_eval_stats.h40
6 files changed, 409 insertions, 632 deletions
diff --git a/source/blender/depsgraph/intern/eval/deg_eval.cc b/source/blender/depsgraph/intern/eval/deg_eval.cc
index 3a042535d26..fc71b5ccb7b 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval.cc
@@ -34,35 +34,30 @@
#include "PIL_time.h"
-extern "C" {
#include "BLI_utildefines.h"
#include "BLI_task.h"
#include "BLI_ghash.h"
+extern "C" {
#include "BKE_depsgraph.h"
#include "BKE_global.h"
+} /* extern "C" */
#include "DEG_depsgraph.h"
-} /* extern "C" */
#include "atomic_ops.h"
-#include "intern/eval/deg_eval_debug.h"
#include "intern/eval/deg_eval_flush.h"
+#include "intern/eval/deg_eval_stats.h"
#include "intern/nodes/deg_node.h"
#include "intern/nodes/deg_node_component.h"
+#include "intern/nodes/deg_node_id.h"
#include "intern/nodes/deg_node_operation.h"
+#include "intern/nodes/deg_node_time.h"
#include "intern/depsgraph.h"
#include "intern/depsgraph_intern.h"
-#include "util/deg_util_foreach.h"
-
-/* Unfinished and unused, and takes quite some pre-processing time. */
-#undef USE_EVAL_PRIORITY
-/* Use integrated debugger to keep track how much each of the nodes was
- * evaluating.
- */
-#undef USE_DEBUGGER
+#include "util/deg_util_foreach.h"
namespace DEG {
@@ -80,120 +75,31 @@ struct DepsgraphEvalState {
EvaluationContext *eval_ctx;
Depsgraph *graph;
unsigned int layers;
+ bool do_stats;
};
static void deg_task_run_func(TaskPool *pool,
void *taskdata,
int thread_id)
{
- DepsgraphEvalState *state =
- reinterpret_cast<DepsgraphEvalState *>(BLI_task_pool_userdata(pool));
- OperationDepsNode *node = reinterpret_cast<OperationDepsNode *>(taskdata);
-
+ void *userdata_v = BLI_task_pool_userdata(pool);
+ DepsgraphEvalState *state = (DepsgraphEvalState *)userdata_v;
+ OperationDepsNode *node = (OperationDepsNode *)taskdata;
+ /* Sanity checks. */
BLI_assert(!node->is_noop() && "NOOP nodes should not actually be scheduled");
-
- /* Should only be the case for NOOPs, which never get to this point. */
- BLI_assert(node->evaluate);
-
- while (true) {
- /* Get context. */
- /* TODO: Who initialises this? "Init" operations aren't able to
- * initialise it!!!
- */
- /* TODO(sergey): We don't use component contexts at this moment. */
- /* ComponentDepsNode *comp = node->owner; */
- BLI_assert(node->owner != NULL);
-
- /* Since we're not leaving the thread for until the graph branches it is
- * possible to have NO-OP on the way. for which evaluate() will be NULL.
- * but that's all fine, we'll just scheduler it's children.
- */
- if (node->evaluate) {
- /* Take note of current time. */
-#ifdef USE_DEBUGGER
- double start_time = PIL_check_seconds_timer();
- DepsgraphDebug::task_started(state->graph, node);
-#endif
-
- /* Perform operation. */
- node->evaluate(state->eval_ctx);
-
- /* Note how long this took. */
-#ifdef USE_DEBUGGER
- double end_time = PIL_check_seconds_timer();
- DepsgraphDebug::task_completed(state->graph,
- node,
- end_time - start_time);
-#endif
- }
-
- /* If there's only one outgoing link we try to immediately switch to
- * that node evaluation, without leaving the thread.
- *
- * It's only doable if the child don't have extra relations or all they
- * are satisfied.
- *
- * TODO(sergey): Checks here can be de-duplicated with the ones from
- * schedule_node(), however, how to do it nicely?
- */
- if (node->outlinks.size() == 1) {
- DepsRelation *rel = node->outlinks[0];
- OperationDepsNode *child = (OperationDepsNode *)rel->to;
- BLI_assert(child->type == DEPSNODE_TYPE_OPERATION);
- if (!child->scheduled) {
- unsigned int id_layers = child->owner->owner->layers;
- if (!((child->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0 &&
- (id_layers & state->layers) != 0))
- {
- /* Node does not need an update, so can;t continue with the
- * chain and need to switch to another one by leaving the
- * thread.
- */
- break;
- }
- if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) {
- BLI_assert(child->num_links_pending > 0);
- atomic_sub_and_fetch_uint32(&child->num_links_pending, 1);
- }
- if (child->num_links_pending == 0) {
- bool is_scheduled = atomic_fetch_and_or_uint8(
- (uint8_t *)&child->scheduled, (uint8_t)true);
- if (!is_scheduled) {
- /* Node was not scheduled, switch to it! */
- node = child;
- }
- else {
- /* Someone else scheduled the node, leaving us
- * unemployed in this thread, we're leaving.
- */
- break;
- }
- }
- else {
- /* There are other dependencies on the child, can't do
- * anything in the current thread.
- */
- break;
- }
- }
- else {
- /* Happens when having cyclic dependencies.
- *
- * Nothing to do here, single child was already scheduled, we
- * can leave the thread now.
- */
- break;
- }
- }
- else {
- /* TODO(sergey): It's possible to use one of the outgoing relations
- * as a chain which we'll try to keep alive, but it's a bit more
- * involved change.
- */
- schedule_children(pool, state->graph, node, state->layers, thread_id);
- break;
- }
+ /* Perform operation. */
+ if (state->do_stats) {
+ const double start_time = PIL_check_seconds_timer();
+ node->evaluate(state->eval_ctx);
+ node->stats.current_time += PIL_check_seconds_timer() - start_time;
+ }
+ else {
+ node->evaluate(state->eval_ctx);
}
+ /* Schedule children. */
+ BLI_task_pool_delayed_push_begin(pool, thread_id);
+ schedule_children(pool, state->graph, node, state->layers, thread_id);
+ BLI_task_pool_delayed_push_end(pool, thread_id);
}
typedef struct CalculatePengindData {
@@ -201,7 +107,10 @@ typedef struct CalculatePengindData {
unsigned int layers;
} CalculatePengindData;
-static void calculate_pending_func(void *data_v, int i)
+static void calculate_pending_func(
+ void *__restrict data_v,
+ const int i,
+ const ParallelRangeTLS *__restrict /*tls*/)
{
CalculatePengindData *data = (CalculatePengindData *)data_v;
Depsgraph *graph = data->graph;
@@ -217,7 +126,7 @@ static void calculate_pending_func(void *data_v, int i)
(node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0)
{
foreach (DepsRelation *rel, node->inlinks) {
- if (rel->from->type == DEPSNODE_TYPE_OPERATION &&
+ if (rel->from->type == DEG_NODE_TYPE_OPERATION &&
(rel->flag & DEPSREL_FLAG_CYCLIC) == 0)
{
OperationDepsNode *from = (OperationDepsNode *)rel->from;
@@ -235,43 +144,31 @@ static void calculate_pending_func(void *data_v, int i)
static void calculate_pending_parents(Depsgraph *graph, unsigned int layers)
{
const int num_operations = graph->operations.size();
- const bool do_threads = num_operations > 256;
CalculatePengindData data;
data.graph = graph;
data.layers = layers;
+ ParallelRangeSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.min_iter_per_thread = 1024;
BLI_task_parallel_range(0,
num_operations,
&data,
calculate_pending_func,
- do_threads);
+ &settings);
}
-#ifdef USE_EVAL_PRIORITY
-static void calculate_eval_priority(OperationDepsNode *node)
+static void initialize_execution(DepsgraphEvalState *state, Depsgraph *graph)
{
- if (node->done) {
- return;
- }
- node->done = 1;
-
- if (node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
- /* XXX standard cost of a node, could be estimated somewhat later on */
- const float cost = 1.0f;
- /* NOOP nodes have no cost */
- node->eval_priority = node->is_noop() ? cost : 0.0f;
-
- foreach (DepsRelation *rel, node->outlinks) {
- OperationDepsNode *to = (OperationDepsNode *)rel->to;
- BLI_assert(to->type == DEPSNODE_TYPE_OPERATION);
- calculate_eval_priority(to);
- node->eval_priority += to->eval_priority;
+ const bool do_stats = state->do_stats;
+ calculate_pending_parents(graph, state->layers);
+ /* Clear tags and other things which needs to be clear. */
+ foreach (OperationDepsNode *node, graph->operations) {
+ node->done = 0;
+ if (do_stats) {
+ node->stats.reset_current();
}
}
- else {
- node->eval_priority = 0.0f;
- }
}
-#endif
/* Schedule a node if it needs evaluation.
* dec_parents: Decrement pending parents count, true when child nodes are
@@ -330,7 +227,7 @@ static void schedule_children(TaskPool *pool,
{
foreach (DepsRelation *rel, node->outlinks) {
OperationDepsNode *child = (OperationDepsNode *)rel->to;
- BLI_assert(child->type == DEPSNODE_TYPE_OPERATION);
+ BLI_assert(child->type == DEG_NODE_TYPE_OPERATION);
if (child->scheduled) {
/* Happens when having cyclic dependencies. */
continue;
@@ -355,61 +252,59 @@ void deg_evaluate_on_refresh(EvaluationContext *eval_ctx,
Depsgraph *graph,
const unsigned int layers)
{
- /* Generate base evaluation context, upon which all the others are derived. */
- // TODO: this needs both main and scene access...
-
+ /* Set time for the current graph evaluation context. */
+ TimeSourceDepsNode *time_src = graph->find_time_source();
+ eval_ctx->ctime = time_src->cfra;
/* Nothing to update, early out. */
- if (BLI_gset_size(graph->entry_tags) == 0) {
+ if (BLI_gset_len(graph->entry_tags) == 0) {
return;
}
-
- DEG_DEBUG_PRINTF("%s: layers:%u, graph->layers:%u\n",
+ DEG_DEBUG_PRINTF(EVAL, "%s: layers:%u, graph->layers:%u\n",
__func__,
layers,
graph->layers);
-
- /* Set time for the current graph evaluation context. */
- TimeSourceDepsNode *time_src = graph->find_time_source();
- eval_ctx->ctime = time_src->cfra;
-
- /* XXX could use a separate pool for each eval context */
+ const bool do_time_debug = ((G.debug & G_DEBUG_DEPSGRAPH_TIME) != 0);
+ const double start_time = do_time_debug ? PIL_check_seconds_timer() : 0;
+ /* Set up evaluation context for depsgraph itself. */
DepsgraphEvalState state;
state.eval_ctx = eval_ctx;
state.graph = graph;
state.layers = layers;
-
- TaskScheduler *task_scheduler = BLI_task_scheduler_get();
- TaskPool *task_pool = BLI_task_pool_create(task_scheduler, &state);
-
+ state.do_stats = do_time_debug;
+ /* Set up task scheduler and pull for threaded evaluation. */
+ TaskScheduler *task_scheduler;
+ bool need_free_scheduler;
if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) {
- BLI_pool_set_num_threads(task_pool, 1);
+ task_scheduler = BLI_task_scheduler_create(1);
+ need_free_scheduler = true;
}
-
- calculate_pending_parents(graph, layers);
-
- /* Clear tags. */
- foreach (OperationDepsNode *node, graph->operations) {
- node->done = 0;
- }
-
- /* Calculate priority for operation nodes. */
-#ifdef USE_EVAL_PRIORITY
- foreach (OperationDepsNode *node, graph->operations) {
- calculate_eval_priority(node);
+ else {
+ task_scheduler = BLI_task_scheduler_get();
+ need_free_scheduler = false;
}
-#endif
-
- DepsgraphDebug::eval_begin(eval_ctx);
-
+ TaskPool *task_pool = BLI_task_pool_create_suspended(task_scheduler, &state);
+ /* Prepare all nodes for evaluation. */
+ initialize_execution(&state, graph);
+ /* Do actual evaluation now. */
schedule_graph(task_pool, graph, layers);
-
BLI_task_pool_work_and_wait(task_pool);
BLI_task_pool_free(task_pool);
-
- DepsgraphDebug::eval_end(eval_ctx);
-
+ /* Finalize statistics gathering. This is because we only gather single
+ * operation timing here, without aggregating anything to avoid any extra
+ * synchronization.
+ */
+ if (state.do_stats) {
+ deg_eval_stats_aggregate(graph);
+ }
/* Clear any uncleared tags - just in case. */
deg_graph_clear_tags(graph);
+ if (need_free_scheduler) {
+ BLI_task_scheduler_free(task_scheduler);
+ }
+ if (do_time_debug) {
+ printf("Depsgraph updated in %f seconds.\n",
+ PIL_check_seconds_timer() - start_time);
+ }
}
} // namespace DEG
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_debug.cc b/source/blender/depsgraph/intern/eval/deg_eval_debug.cc
deleted file mode 100644
index 060544a4407..00000000000
--- a/source/blender/depsgraph/intern/eval/deg_eval_debug.cc
+++ /dev/null
@@ -1,250 +0,0 @@
-/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2014 Blender Foundation.
- * All rights reserved.
- *
- * Original Author: Lukas Toenne
- * Contributor(s): None Yet
- *
- * ***** END GPL LICENSE BLOCK *****
- */
-
-/** \file blender/depsgraph/intern/eval/deg_eval_debug.cc
- * \ingroup depsgraph
- *
- * Implementation of tools for debugging the depsgraph
- */
-
-#include "intern/eval/deg_eval_debug.h"
-
-#include <cstring> /* required for STREQ later on. */
-
-extern "C" {
-#include "BLI_listbase.h"
-#include "BLI_ghash.h"
-
-#include "DEG_depsgraph_debug.h"
-
-#include "WM_api.h"
-#include "WM_types.h"
-} /* extern "C" */
-
-#include "intern/nodes/deg_node.h"
-#include "intern/nodes/deg_node_component.h"
-#include "intern/nodes/deg_node_operation.h"
-#include "intern/depsgraph_intern.h"
-
-namespace DEG {
-
-DepsgraphStats *DepsgraphDebug::stats = NULL;
-
-static string get_component_name(eDepsNode_Type type, const char *name = "")
-{
- DepsNodeFactory *factory = deg_get_node_factory(type);
- if (name[0] != '\0') {
- return string(factory->tname());
- }
- else {
- return string(factory->tname()) + " | " + name;
- }
-}
-
-static void times_clear(DepsgraphStatsTimes &times)
-{
- times.duration_last = 0.0f;
-}
-
-static void times_add(DepsgraphStatsTimes &times, float time)
-{
- times.duration_last += time;
-}
-
-void DepsgraphDebug::eval_begin(const EvaluationContext *UNUSED(eval_ctx))
-{
- /* TODO(sergey): Stats are currently globally disabled. */
- /* verify_stats(); */
- reset_stats();
-}
-
-void DepsgraphDebug::eval_end(const EvaluationContext *UNUSED(eval_ctx))
-{
- WM_main_add_notifier(NC_SPACE | ND_SPACE_INFO_REPORT, NULL);
-}
-
-void DepsgraphDebug::eval_step(const EvaluationContext *UNUSED(eval_ctx),
- const char *message)
-{
-#ifdef DEG_DEBUG_BUILD
- if (deg_debug_eval_cb)
- deg_debug_eval_cb(deg_debug_eval_userdata, message);
-#else
- (void)message; /* Ignored. */
-#endif
-}
-
-void DepsgraphDebug::task_started(Depsgraph *graph,
- const OperationDepsNode *node)
-{
- if (stats) {
- BLI_spin_lock(&graph->lock);
-
- ComponentDepsNode *comp = node->owner;
- ID *id = comp->owner->id;
-
- DepsgraphStatsID *id_stats = get_id_stats(id, true);
- times_clear(id_stats->times);
-
- /* XXX TODO use something like: if (id->flag & ID_DEG_DETAILS) {...} */
- if (0) {
- /* XXX component name usage needs cleanup! currently mixes identifier
- * and description strings!
- */
- DepsgraphStatsComponent *comp_stats =
- get_component_stats(id, get_component_name(comp->type,
- comp->name).c_str(),
- true);
- times_clear(comp_stats->times);
- }
-
- BLI_spin_unlock(&graph->lock);
- }
-}
-
-void DepsgraphDebug::task_completed(Depsgraph *graph,
- const OperationDepsNode *node,
- double time)
-{
- if (stats) {
- BLI_spin_lock(&graph->lock);
-
- ComponentDepsNode *comp = node->owner;
- ID *id = comp->owner->id;
-
- DepsgraphStatsID *id_stats = get_id_stats(id, true);
- times_add(id_stats->times, time);
-
- /* XXX TODO use something like: if (id->flag & ID_DEG_DETAILS) {...} */
- if (0) {
- /* XXX component name usage needs cleanup! currently mixes identifier
- * and description strings!
- */
- DepsgraphStatsComponent *comp_stats =
- get_component_stats(id,
- get_component_name(comp->type,
- comp->name).c_str(),
- true);
- times_add(comp_stats->times, time);
- }
-
- BLI_spin_unlock(&graph->lock);
- }
-}
-
-/* ********** */
-/* Statistics */
-
-
-/* GHash callback */
-static void deg_id_stats_free(void *val)
-{
- DepsgraphStatsID *id_stats = (DepsgraphStatsID *)val;
-
- if (id_stats) {
- BLI_freelistN(&id_stats->components);
- MEM_freeN(id_stats);
- }
-}
-
-void DepsgraphDebug::stats_init()
-{
- if (!stats) {
- stats = (DepsgraphStats *)MEM_callocN(sizeof(DepsgraphStats),
- "Depsgraph Stats");
- stats->id_stats = BLI_ghash_new(BLI_ghashutil_ptrhash,
- BLI_ghashutil_ptrcmp,
- "Depsgraph ID Stats Hash");
- }
-}
-
-void DepsgraphDebug::stats_free()
-{
- if (stats) {
- BLI_ghash_free(stats->id_stats, NULL, deg_id_stats_free);
- MEM_freeN(stats);
- stats = NULL;
- }
-}
-
-void DepsgraphDebug::verify_stats()
-{
- stats_init();
-}
-
-void DepsgraphDebug::reset_stats()
-{
- if (!stats) {
- return;
- }
-
- /* XXX this doesn't work, will immediately clear all info,
- * since most depsgraph updates have none or very few updates to handle.
- *
- * Could consider clearing only zero-user ID blocks here
- */
-// BLI_ghash_clear(stats->id_stats, NULL, deg_id_stats_free);
-}
-
-DepsgraphStatsID *DepsgraphDebug::get_id_stats(ID *id, bool create)
-{
- DepsgraphStatsID *id_stats = (DepsgraphStatsID *)BLI_ghash_lookup(stats->id_stats, id);
-
- if (!id_stats && create) {
- id_stats = (DepsgraphStatsID *)MEM_callocN(sizeof(DepsgraphStatsID),
- "Depsgraph ID Stats");
- id_stats->id = id;
-
- BLI_ghash_insert(stats->id_stats, id, id_stats);
- }
-
- return id_stats;
-}
-
-DepsgraphStatsComponent *DepsgraphDebug::get_component_stats(
- DepsgraphStatsID *id_stats,
- const char *name,
- bool create)
-{
- DepsgraphStatsComponent *comp_stats;
- for (comp_stats = (DepsgraphStatsComponent *)id_stats->components.first;
- comp_stats != NULL;
- comp_stats = comp_stats->next)
- {
- if (STREQ(comp_stats->name, name)) {
- break;
- }
- }
- if (!comp_stats && create) {
- comp_stats = (DepsgraphStatsComponent *)MEM_callocN(sizeof(DepsgraphStatsComponent),
- "Depsgraph Component Stats");
- BLI_strncpy(comp_stats->name, name, sizeof(comp_stats->name));
- BLI_addtail(&id_stats->components, comp_stats);
- }
- return comp_stats;
-}
-
-} // namespace DEG
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_debug.h b/source/blender/depsgraph/intern/eval/deg_eval_debug.h
deleted file mode 100644
index 0bbe88cc9ca..00000000000
--- a/source/blender/depsgraph/intern/eval/deg_eval_debug.h
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * ***** BEGIN GPL LICENSE BLOCK *****
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2
- * of the License, or (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
- *
- * The Original Code is Copyright (C) 2013 Blender Foundation.
- * All rights reserved.
- *
- * Original Author: Joshua Leung
- * Contributor(s): None Yet
- *
- * ***** END GPL LICENSE BLOCK *****
- */
-
-/** \file blender/depsgraph/intern/eval/deg_eval_debug.h
- * \ingroup depsgraph
- */
-
-#pragma once
-
-#include "intern/depsgraph_types.h"
-
-struct ID;
-struct EvaluationContext;
-
-struct DepsgraphStats;
-struct DepsgraphStatsID;
-struct DepsgraphStatsComponent;
-
-namespace DEG {
-
-struct Depsgraph;
-struct DepsgraphSettings;
-struct OperationDepsNode;
-
-struct DepsgraphDebug {
- static DepsgraphStats *stats;
-
- static void stats_init();
- static void stats_free();
-
- static void verify_stats();
- static void reset_stats();
-
- static void eval_begin(const EvaluationContext *eval_ctx);
- static void eval_end(const EvaluationContext *eval_ctx);
- static void eval_step(const EvaluationContext *eval_ctx,
- const char *message);
-
- static void task_started(Depsgraph *graph, const OperationDepsNode *node);
- static void task_completed(Depsgraph *graph,
- const OperationDepsNode *node,
- double time);
-
- static DepsgraphStatsID *get_id_stats(ID *id, bool create);
- static DepsgraphStatsComponent *get_component_stats(DepsgraphStatsID *id_stats,
- const char *name,
- bool create);
- static DepsgraphStatsComponent *get_component_stats(ID *id,
- const char *name,
- bool create)
- {
- return get_component_stats(get_id_stats(id, create), name, create);
- }
-};
-
-} // namespace DEG
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
index 7c6c25bef0d..e7764cf5a27 100644
--- a/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
+++ b/source/blender/depsgraph/intern/eval/deg_eval_flush.cc
@@ -35,18 +35,19 @@
// TODO(sergey): Use some sort of wrapper.
#include <deque>
-extern "C" {
-#include "DNA_object_types.h"
-
#include "BLI_utildefines.h"
#include "BLI_task.h"
#include "BLI_ghash.h"
-#include "DEG_depsgraph.h"
+extern "C" {
+#include "DNA_object_types.h"
} /* extern "C" */
+#include "DEG_depsgraph.h"
+
#include "intern/nodes/deg_node.h"
#include "intern/nodes/deg_node_component.h"
+#include "intern/nodes/deg_node_id.h"
#include "intern/nodes/deg_node_operation.h"
#include "intern/depsgraph_intern.h"
@@ -54,158 +55,253 @@ extern "C" {
namespace DEG {
+enum {
+ ID_STATE_NONE = 0,
+ ID_STATE_MODIFIED = 1,
+};
+
+enum {
+ COMPONENT_STATE_NONE = 0,
+ COMPONENT_STATE_SCHEDULED = 1,
+ COMPONENT_STATE_DONE = 2,
+};
+
+typedef std::deque<OperationDepsNode *> FlushQueue;
+
namespace {
// TODO(sergey): De-duplicate with depsgraph_tag,cc
void lib_id_recalc_tag(Main *bmain, ID *id)
{
- id->tag |= LIB_TAG_ID_RECALC;
+ id->recalc |= ID_RECALC;
DEG_id_type_tag(bmain, GS(id->name));
}
void lib_id_recalc_data_tag(Main *bmain, ID *id)
{
- id->tag |= LIB_TAG_ID_RECALC_DATA;
+ id->recalc |= ID_RECALC_DATA;
DEG_id_type_tag(bmain, GS(id->name));
}
-} /* namespace */
-
-typedef std::deque<OperationDepsNode *> FlushQueue;
-
-static void flush_init_func(void *data_v, int i)
+void flush_init_operation_node_func(
+ void *__restrict data_v,
+ const int i,
+ const ParallelRangeTLS *__restrict /*tls*/)
{
- /* ID node's done flag is used to avoid multiple editors update
- * for the same ID.
- */
Depsgraph *graph = (Depsgraph *)data_v;
OperationDepsNode *node = graph->operations[i];
- ComponentDepsNode *comp_node = node->owner;
- IDDepsNode *id_node = comp_node->owner;
- id_node->done = 0;
- comp_node->done = 0;
node->scheduled = false;
}
-/* Flush updates from tagged nodes outwards until all affected nodes
- * are tagged.
- */
-void deg_graph_flush_updates(Main *bmain, Depsgraph *graph)
+void flush_init_id_node_func(
+ void *__restrict data_v,
+ const int i,
+ const ParallelRangeTLS *__restrict /*tls*/)
{
- /* Sanity check. */
- if (graph == NULL) {
- return;
- }
+ Depsgraph *graph = (Depsgraph *)data_v;
+ IDDepsNode *id_node = graph->id_nodes[i];
+ id_node->done = ID_STATE_NONE;
+ GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, id_node->components)
+ comp_node->done = COMPONENT_STATE_NONE;
+ GHASH_FOREACH_END();
+}
- /* Nothing to update, early out. */
- if (BLI_gset_size(graph->entry_tags) == 0) {
- return;
+BLI_INLINE void flush_prepare(Depsgraph *graph)
+{
+ {
+ const int num_operations = graph->operations.size();
+ ParallelRangeSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.min_iter_per_thread = 1024;
+ BLI_task_parallel_range(0, num_operations,
+ graph,
+ flush_init_operation_node_func,
+ &settings);
}
+ {
+ const int num_id_nodes = graph->id_nodes.size();
+ ParallelRangeSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.min_iter_per_thread = 1024;
+ BLI_task_parallel_range(0, num_id_nodes,
+ graph,
+ flush_init_id_node_func,
+ &settings);
+ }
+}
- /* TODO(sergey): With a bit of flag magic we can get rid of this
- * extra loop.
- */
- const int num_operations = graph->operations.size();
- const bool do_threads = num_operations > 256;
- BLI_task_parallel_range(0,
- num_operations,
- graph,
- flush_init_func,
- do_threads);
-
- FlushQueue queue;
- /* Starting from the tagged "entry" nodes, flush outwards... */
- /* NOTE: Also need to ensure that for each of these, there is a path back to
- * root, or else they won't be done.
- * NOTE: Count how many nodes we need to handle - entry nodes may be
- * component nodes which don't count for this purpose!
- */
- GSET_FOREACH_BEGIN(OperationDepsNode *, node, graph->entry_tags)
+BLI_INLINE void flush_schedule_entrypoints(Depsgraph *graph, FlushQueue *queue)
+{
+ GSET_FOREACH_BEGIN(OperationDepsNode *, op_node, graph->entry_tags)
{
- queue.push_back(node);
- node->scheduled = true;
+ queue->push_back(op_node);
+ op_node->scheduled = true;
}
GSET_FOREACH_END();
+}
- int num_flushed_objects = 0;
- while (!queue.empty()) {
- OperationDepsNode *node = queue.front();
- queue.pop_front();
-
- for (;;) {
- node->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
-
- ComponentDepsNode *comp_node = node->owner;
- IDDepsNode *id_node = comp_node->owner;
-
- ID *id = id_node->id;
- if(id_node->done == 0) {
- deg_editors_id_update(bmain, id);
- lib_id_recalc_tag(bmain, id);
- /* TODO(sergey): For until we've got proper data nodes in the graph. */
- lib_id_recalc_data_tag(bmain, id);
- }
+BLI_INLINE void flush_handle_id_node(IDDepsNode *id_node)
+{
+ id_node->done = ID_STATE_MODIFIED;
+}
- if(comp_node->done == 0) {
- Object *object = NULL;
- if (GS(id->name) == ID_OB) {
- object = (Object *)id;
- if(id_node->done == 0) {
- ++num_flushed_objects;
- }
- }
- foreach (OperationDepsNode *op, comp_node->operations) {
- op->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
- }
- if (object != NULL) {
- /* This code is used to preserve those areas which does
- * direct object update,
- *
- * Plus it ensures visibility changes and relations and
- * layers visibility update has proper flags to work with.
- */
- if (comp_node->type == DEPSNODE_TYPE_ANIMATION) {
- object->recalc |= OB_RECALC_TIME;
- }
- else if (comp_node->type == DEPSNODE_TYPE_TRANSFORM) {
- object->recalc |= OB_RECALC_OB;
- }
- else {
- object->recalc |= OB_RECALC_DATA;
- }
- }
- }
+/* TODO(sergey): We can reduce number of arguments here. */
+BLI_INLINE void flush_handle_component_node(Depsgraph * /*graph*/,
+ IDDepsNode *id_node,
+ ComponentDepsNode *comp_node,
+ FlushQueue *queue)
+{
+ /* We only handle component once. */
+ if (comp_node->done == COMPONENT_STATE_DONE) {
+ return;
+ }
+ comp_node->done = COMPONENT_STATE_DONE;
+ /* Tag all required operations in component for update. */
+ foreach (OperationDepsNode *op, comp_node->operations) {
+ op->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
+ }
+ if (GS(id_node->id->name) == ID_OB) {
+ Object *object = (Object *)id_node->id;
+ /* This code is used to preserve those areas which does
+ * direct object update,
+ *
+ * Plus it ensures visibility changes and relations and
+ * layers visibility update has proper flags to work with.
+ */
+ switch (comp_node->type) {
+ case DEG_NODE_TYPE_UNDEFINED:
+ case DEG_NODE_TYPE_OPERATION:
+ case DEG_NODE_TYPE_TIMESOURCE:
+ case DEG_NODE_TYPE_ID_REF:
+ case DEG_NODE_TYPE_SEQUENCER:
+ case NUM_DEG_NODE_TYPES:
+ /* Ignore, does not translate to object component. */
+ BLI_assert(!"This should never happen!");
+ break;
+ case DEG_NODE_TYPE_ANIMATION:
+ object->recalc |= OB_RECALC_TIME;
+ break;
+ case DEG_NODE_TYPE_TRANSFORM:
+ object->recalc |= OB_RECALC_OB;
+ break;
+ case DEG_NODE_TYPE_GEOMETRY:
+ case DEG_NODE_TYPE_EVAL_POSE:
+ case DEG_NODE_TYPE_BONE:
+ case DEG_NODE_TYPE_EVAL_PARTICLES:
+ case DEG_NODE_TYPE_SHADING:
+ case DEG_NODE_TYPE_CACHE:
+ case DEG_NODE_TYPE_PROXY:
+ object->recalc |= OB_RECALC_DATA;
+ break;
+ case DEG_NODE_TYPE_PARAMETERS:
+ break;
+ }
+ }
+ /* When some target changes bone, we might need to re-run the
+ * whole IK solver, otherwise result might be unpredictable.
+ */
+ if (comp_node->type == DEG_NODE_TYPE_BONE) {
+ ComponentDepsNode *pose_comp =
+ id_node->find_component(DEG_NODE_TYPE_EVAL_POSE);
+ BLI_assert(pose_comp != NULL);
+ if (pose_comp->done == COMPONENT_STATE_NONE) {
+ queue->push_front(pose_comp->get_entry_operation());
+ pose_comp->done = COMPONENT_STATE_SCHEDULED;
+ }
+ }
+}
- id_node->done = 1;
- comp_node->done = 1;
-
- /* Flush to nodes along links... */
- if (node->outlinks.size() == 1) {
- OperationDepsNode *to_node = (OperationDepsNode *)node->outlinks[0]->to;
- if (to_node->scheduled == false) {
- to_node->scheduled = true;
- node = to_node;
- }
- else {
- break;
- }
+/* Schedule children of the given operation node for traversal.
+ *
+ * One of the children will by-pass the queue and will be returned as a function
+ * return value, so it can start being handled right away, without building too
+ * much of a queue.
+ */
+BLI_INLINE OperationDepsNode *flush_schedule_children(
+ OperationDepsNode *op_node,
+ FlushQueue *queue)
+{
+ OperationDepsNode *result = NULL;
+ foreach (DepsRelation *rel, op_node->outlinks) {
+ if (rel->flag & DEPSREL_FLAG_NO_FLUSH) {
+ continue;
+ }
+ OperationDepsNode *to_node = (OperationDepsNode *)rel->to;
+ if (to_node->scheduled == false) {
+ if (result != NULL) {
+ queue->push_front(to_node);
}
else {
- foreach (DepsRelation *rel, node->outlinks) {
- OperationDepsNode *to_node = (OperationDepsNode *)rel->to;
- if (to_node->scheduled == false) {
- queue.push_front(to_node);
- to_node->scheduled = true;
- }
- }
- break;
+ result = to_node;
}
+ to_node->scheduled = true;
}
}
- DEG_DEBUG_PRINTF("Update flushed to %d objects\n", num_flushed_objects);
+ return result;
}
-static void graph_clear_func(void *data_v, int i)
+BLI_INLINE void flush_editors_id_update(Main *bmain,
+ Depsgraph *graph)
+{
+ foreach (IDDepsNode *id_node, graph->id_nodes) {
+ if (id_node->done != ID_STATE_MODIFIED) {
+ continue;
+ }
+ /* TODO(sergey): Do we need to pass original or evaluated ID here? */
+ ID *id = id_node->id;
+ deg_editors_id_update(bmain, id);
+ lib_id_recalc_tag(bmain, id);
+ /* TODO(sergey): For until we've got proper data nodes in the graph. */
+ lib_id_recalc_data_tag(bmain, id);
+ }
+}
+
+} // namespace
+
+/* Flush updates from tagged nodes outwards until all affected nodes
+ * are tagged.
+ */
+void deg_graph_flush_updates(Main *bmain, Depsgraph *graph)
+{
+ /* Sanity checks. */
+ BLI_assert(bmain != NULL);
+ BLI_assert(graph != NULL);
+ /* Nothing to update, early out. */
+ if (BLI_gset_len(graph->entry_tags) == 0) {
+ return;
+ }
+ /* Reset all flags, get ready for the flush. */
+ flush_prepare(graph);
+ /* Starting from the tagged "entry" nodes, flush outwards. */
+ FlushQueue queue;
+ flush_schedule_entrypoints(graph, &queue);
+ /* Do actual flush. */
+ while (!queue.empty()) {
+ OperationDepsNode *op_node = queue.front();
+ queue.pop_front();
+ while (op_node != NULL) {
+ /* Tag operation as required for update. */
+ op_node->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
+ /* Inform corresponding ID and component nodes about the change. */
+ ComponentDepsNode *comp_node = op_node->owner;
+ IDDepsNode *id_node = comp_node->owner;
+ flush_handle_id_node(id_node);
+ flush_handle_component_node(graph,
+ id_node,
+ comp_node,
+ &queue);
+ /* Flush to nodes along links. */
+ op_node = flush_schedule_children(op_node, &queue);
+ }
+ }
+ /* Inform editors about all changes. */
+ flush_editors_id_update(bmain, graph);
+}
+
+static void graph_clear_func(
+ void *__restrict data_v,
+ const int i,
+ const ParallelRangeTLS *__restrict /*tls*/)
{
Depsgraph *graph = (Depsgraph *)data_v;
OperationDepsNode *node = graph->operations[i];
@@ -218,8 +314,13 @@ void deg_graph_clear_tags(Depsgraph *graph)
{
/* Go over all operation nodes, clearing tags. */
const int num_operations = graph->operations.size();
- const bool do_threads = num_operations > 256;
- BLI_task_parallel_range(0, num_operations, graph, graph_clear_func, do_threads);
+ ParallelRangeSettings settings;
+ BLI_parallel_range_settings_defaults(&settings);
+ settings.min_iter_per_thread = 1024;
+ BLI_task_parallel_range(0, num_operations,
+ graph,
+ graph_clear_func,
+ &settings);
/* Clear any entry tags which haven't been flushed. */
BLI_gset_clear(graph->entry_tags, NULL);
}
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_stats.cc b/source/blender/depsgraph/intern/eval/deg_eval_stats.cc
new file mode 100644
index 00000000000..52ce744cc0a
--- /dev/null
+++ b/source/blender/depsgraph/intern/eval/deg_eval_stats.cc
@@ -0,0 +1,70 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017 Blender Foundation.
+ * All rights reserved.
+ *
+ * Original Author: Sergey Sharybin
+ * Contributor(s): None Yet
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/depsgraph/intern/eval/deg_eval_stats.cc
+ * \ingroup depsgraph
+ */
+
+#include "intern/eval/deg_eval_stats.h"
+
+#include "BLI_utildefines.h"
+#include "BLI_ghash.h"
+
+#include "intern/depsgraph.h"
+
+#include "intern/nodes/deg_node.h"
+#include "intern/nodes/deg_node_component.h"
+#include "intern/nodes/deg_node_id.h"
+#include "intern/nodes/deg_node_operation.h"
+
+#include "util/deg_util_foreach.h"
+
+namespace DEG {
+
+void deg_eval_stats_aggregate(Depsgraph *graph)
+{
+ /* Reset current evaluation stats for ID and component nodes.
+ * Those are not filled in by the evaluation engine.
+ */
+ foreach (DepsNode *node, graph->id_nodes) {
+ IDDepsNode *id_node = (IDDepsNode *)node;
+ GHASH_FOREACH_BEGIN(ComponentDepsNode *, comp_node, id_node->components)
+ {
+ comp_node->stats.reset_current();
+ }
+ GHASH_FOREACH_END();
+ id_node->stats.reset_current();
+ }
+ /* Now accumulate operation timings to components and IDs. */
+ foreach (OperationDepsNode *op_node, graph->operations) {
+ ComponentDepsNode *comp_node = op_node->owner;
+ IDDepsNode *id_node = comp_node->owner;
+ id_node->stats.current_time += op_node->stats.current_time;
+ comp_node->stats.current_time += op_node->stats.current_time;
+ }
+}
+
+} // namespace DEG
diff --git a/source/blender/depsgraph/intern/eval/deg_eval_stats.h b/source/blender/depsgraph/intern/eval/deg_eval_stats.h
new file mode 100644
index 00000000000..8a7272ac89c
--- /dev/null
+++ b/source/blender/depsgraph/intern/eval/deg_eval_stats.h
@@ -0,0 +1,40 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2017 Blender Foundation.
+ * All rights reserved.
+ *
+ * Original Author: Sergey Sharybin
+ * Contributor(s): None Yet
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/depsgraph/intern/eval/deg_eval_stats.h
+ * \ingroup depsgraph
+ */
+
+#pragma once
+
+namespace DEG {
+
+struct Depsgraph;
+
+/* Aggregate operation timings to overall component and ID nodes timing. */
+void deg_eval_stats_aggregate(Depsgraph *graph);
+
+} // namespace DEG