This repository has been archived on 2023-10-09. You can view files and clone it, but cannot push or open issues or pull requests.
Files
blender-archive/source/blender/depsgraph/intern/depsgraph_eval.cc

400 lines
11 KiB
C++

/*
* ***** 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/depsgraph_eval.cc
* \ingroup depsgraph
*
* 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);
if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) {
BLI_pool_set_num_threads(task_pool, 1);
}
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);
}
bool DEG_needs_eval(Depsgraph *graph)
{
return graph->entry_tags.size() != 0;
}