diff options
Diffstat (limited to 'source/blender/depsgraph/intern/depsgraph_eval.cc')
-rw-r--r-- | source/blender/depsgraph/intern/depsgraph_eval.cc | 386 |
1 files changed, 386 insertions, 0 deletions
diff --git a/source/blender/depsgraph/intern/depsgraph_eval.cc b/source/blender/depsgraph/intern/depsgraph_eval.cc new file mode 100644 index 00000000000..11fd02376a3 --- /dev/null +++ b/source/blender/depsgraph/intern/depsgraph_eval.cc @@ -0,0 +1,386 @@ +/* + * ***** 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 ***** + * + * Evaluation engine entrypoints for Depsgraph Engine + */ + +#include "MEM_guardedalloc.h" + +#include "PIL_time.h" + +extern "C" { +#include "BLI_utildefines.h" +#include "BLI_task.h" + +#include "BKE_depsgraph.h" +#include "BKE_scene.h" + +#include "DEG_depsgraph.h" +} /* extern "C" */ + +#include "atomic_ops.h" + +#include "depsgraph.h" +#include "depsnode.h" +#include "depsnode_component.h" +#include "depsnode_operation.h" +#include "depsgraph_debug.h" + +#ifdef WITH_LEGACY_DEPSGRAPH +static bool use_legacy_depsgraph = true; +#endif + +bool DEG_depsgraph_use_legacy(void) +{ +#ifdef DISABLE_NEW_DEPSGRAPH + return true; +#elif defined(WITH_LEGACY_DEPSGRAPH) + return use_legacy_depsgraph; +#else + BLI_assert(!"Should not be used with new depsgraph"); + return false; +#endif +} + +void DEG_depsgraph_switch_to_legacy(void) +{ +#ifdef WITH_LEGACY_DEPSGRAPH + use_legacy_depsgraph = true; +#else + BLI_assert(!"Should not be used with new depsgraph"); +#endif +} + +void DEG_depsgraph_switch_to_new(void) +{ +#ifdef WITH_LEGACY_DEPSGRAPH + use_legacy_depsgraph = false; +#else + BLI_assert(!"Should not be used with new depsgraph"); +#endif +} + +/* ****************** */ +/* Evaluation Context */ + +/* Create new evaluation context. */ +EvaluationContext *DEG_evaluation_context_new(int mode) +{ + EvaluationContext *eval_ctx = + (EvaluationContext *)MEM_callocN(sizeof(EvaluationContext), + "EvaluationContext"); + eval_ctx->mode = mode; + return eval_ctx; +} + +/** + * Initialize evaluation context. + * Used by the areas which currently overrides the context or doesn't have + * access to a proper one. + */ +void DEG_evaluation_context_init(EvaluationContext *eval_ctx, int mode) +{ + eval_ctx->mode = mode; +} + +/* Free evaluation context. */ +void DEG_evaluation_context_free(EvaluationContext *eval_ctx) +{ + MEM_freeN(eval_ctx); +} + +/* ********************** */ +/* Evaluation Entrypoints */ + +/* Forward declarations. */ +static void schedule_children(TaskPool *pool, + Depsgraph *graph, + OperationDepsNode *node, + const int layers); + +struct DepsgraphEvalState { + EvaluationContext *eval_ctx; + Depsgraph *graph; + int layers; +}; + +static void deg_task_run_func(TaskPool *pool, + void *taskdata, + int UNUSED(threadid)) +{ + DepsgraphEvalState *state = (DepsgraphEvalState *)BLI_task_pool_userdata(pool); + OperationDepsNode *node = (OperationDepsNode *)taskdata; + + if (!node->is_noop()) { + /* Get context. */ + // TODO: who initialises this? "Init" operations aren't able to initialise it!!! + /* TODO(sergey): Wedon't use component contexts at this moment. */ + /* ComponentDepsNode *comp = node->owner; */ + BLI_assert(node->owner != NULL); + + /* Take note of current time. */ + double start_time = PIL_check_seconds_timer(); + DepsgraphDebug::task_started(state->graph, node); + + /* Should only be the case for NOOPs, which never get to this point. */ + BLI_assert(node->evaluate); + + /* Perform operation. */ + node->evaluate(state->eval_ctx); + + /* Note how long this took. */ + double end_time = PIL_check_seconds_timer(); + DepsgraphDebug::task_completed(state->graph, + node, + end_time - start_time); + } + + schedule_children(pool, state->graph, node, state->layers); +} + +static void calculate_pending_parents(Depsgraph *graph, int layers) +{ + for (Depsgraph::OperationNodes::const_iterator it_op = graph->operations.begin(); + it_op != graph->operations.end(); + ++it_op) + { + OperationDepsNode *node = *it_op; + IDDepsNode *id_node = node->owner->owner; + + node->num_links_pending = 0; + node->scheduled = false; + + /* count number of inputs that need updates */ + if ((id_node->layers & layers) != 0 && + (node->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) + { + for (OperationDepsNode::Relations::const_iterator it_rel = node->inlinks.begin(); + it_rel != node->inlinks.end(); + ++it_rel) + { + DepsRelation *rel = *it_rel; + if (rel->from->type == DEPSNODE_TYPE_OPERATION && + (rel->flag & DEPSREL_FLAG_CYCLIC) == 0) + { + OperationDepsNode *from = (OperationDepsNode *)rel->from; + IDDepsNode *id_from_node = from->owner->owner; + if ((id_from_node->layers & layers) != 0 && + (from->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) + { + ++node->num_links_pending; + } + } + } + } + } +} + +static void calculate_eval_priority(OperationDepsNode *node) +{ + 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; + + for (OperationDepsNode::Relations::const_iterator it = node->outlinks.begin(); + it != node->outlinks.end(); + ++it) + { + DepsRelation *rel = *it; + OperationDepsNode *to = (OperationDepsNode *)rel->to; + BLI_assert(to->type == DEPSNODE_TYPE_OPERATION); + calculate_eval_priority(to); + node->eval_priority += to->eval_priority; + } + } + else { + node->eval_priority = 0.0f; + } +} + +static void schedule_graph(TaskPool *pool, + Depsgraph *graph, + const int layers) +{ + BLI_spin_lock(&graph->lock); + for (Depsgraph::OperationNodes::const_iterator it = graph->operations.begin(); + it != graph->operations.end(); + ++it) + { + OperationDepsNode *node = *it; + IDDepsNode *id_node = node->owner->owner; + if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) && + node->num_links_pending == 0 && + (id_node->layers & layers) != 0) + { + BLI_task_pool_push(pool, deg_task_run_func, node, false, TASK_PRIORITY_LOW); + node->scheduled = true; + } + } + BLI_spin_unlock(&graph->lock); +} + +static void schedule_children(TaskPool *pool, + Depsgraph *graph, + OperationDepsNode *node, + const int layers) +{ + for (OperationDepsNode::Relations::const_iterator it = node->outlinks.begin(); + it != node->outlinks.end(); + ++it) + { + DepsRelation *rel = *it; + OperationDepsNode *child = (OperationDepsNode *)rel->to; + BLI_assert(child->type == DEPSNODE_TYPE_OPERATION); + + if (child->scheduled) { + /* Happens when having cyclic dependencies. */ + continue; + } + + IDDepsNode *id_child = child->owner->owner; + if ((id_child->layers & layers) != 0 && + (child->flag & DEPSOP_FLAG_NEEDS_UPDATE) != 0) + { + if ((rel->flag & DEPSREL_FLAG_CYCLIC) == 0) { + BLI_assert(child->num_links_pending > 0); + atomic_sub_uint32(&child->num_links_pending, 1); + } + + if (child->num_links_pending == 0) { + BLI_spin_lock(&graph->lock); + bool need_schedule = !child->scheduled; + child->scheduled = true; + BLI_spin_unlock(&graph->lock); + + if (need_schedule) { + BLI_task_pool_push(pool, deg_task_run_func, child, false, TASK_PRIORITY_LOW); + } + } + } + } +} + +/** + * Evaluate all nodes tagged for updating, + * \warning This is usually done as part of main loop, but may also be + * called from frame-change update. + * + * \note Time sources should be all valid! + */ +void DEG_evaluate_on_refresh_ex(EvaluationContext *eval_ctx, + Depsgraph *graph, + const int layers) +{ + /* Generate base evaluation context, upon which all the others are derived. */ + // TODO: this needs both main and scene access... + + /* Nothing to update, early out. */ + if (graph->entry_tags.size() == 0) { + return; + } + + /* 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 */ + 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); + + calculate_pending_parents(graph, layers); + + /* Clear tags. */ + for (Depsgraph::OperationNodes::const_iterator it = graph->operations.begin(); + it != graph->operations.end(); + ++it) + { + OperationDepsNode *node = *it; + node->done = 0; + } + + /* Calculate priority for operation nodes. */ + for (Depsgraph::OperationNodes::const_iterator it = graph->operations.begin(); + it != graph->operations.end(); + ++it) + { + OperationDepsNode *node = *it; + calculate_eval_priority(node); + } + + DepsgraphDebug::eval_begin(eval_ctx); + + 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); + + /* Clear any uncleared tags - just in case. */ + DEG_graph_clear_tags(graph); +} + +/* Evaluate all nodes tagged for updating. */ +void DEG_evaluate_on_refresh(EvaluationContext *eval_ctx, + Depsgraph *graph, + Scene *scene) +{ + /* Update time on primary timesource. */ + TimeSourceDepsNode *tsrc = graph->find_time_source(); + tsrc->cfra = BKE_scene_frame_get(scene);; + + DEG_evaluate_on_refresh_ex(eval_ctx, graph, graph->layers); +} + +/* Frame-change happened for root scene that graph belongs to. */ +void DEG_evaluate_on_framechange(EvaluationContext *eval_ctx, + Main *bmain, + Depsgraph *graph, + float ctime, + const int layers) +{ + /* Update time on primary timesource. */ + TimeSourceDepsNode *tsrc = graph->find_time_source(); + tsrc->cfra = ctime; + + tsrc->tag_update(graph); + + DEG_graph_flush_updates(bmain, graph); + + /* Perform recalculation updates. */ + DEG_evaluate_on_refresh_ex(eval_ctx, graph, layers); +} |