diff options
Diffstat (limited to 'source/blender/depsgraph/intern/eval')
-rw-r--r-- | source/blender/depsgraph/intern/eval/deg_eval.cc | 257 | ||||
-rw-r--r-- | source/blender/depsgraph/intern/eval/deg_eval_debug.cc | 250 | ||||
-rw-r--r-- | source/blender/depsgraph/intern/eval/deg_eval_debug.h | 79 | ||||
-rw-r--r-- | source/blender/depsgraph/intern/eval/deg_eval_flush.cc | 345 | ||||
-rw-r--r-- | source/blender/depsgraph/intern/eval/deg_eval_stats.cc | 70 | ||||
-rw-r--r-- | source/blender/depsgraph/intern/eval/deg_eval_stats.h | 40 |
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.duration_last = 0.0f; -} - -static void times_add(DepsgraphStatsTimes ×, 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 |