This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/blenkernel/intern/depsgraph.c
Sergey Sharybin 94a6019a7b Fix T47715: EXCEPTION_ACCESS_VIOLATION crash when removing modifiers or rendering
The issue was caused by missing indirect dependencies from other scenes in the
current scene depsgraph, which was causing some threading conflicts.

Not sure what would be ideal solution here, for now use stupid but rather robust
approach to solve that. Maybe there's something better to do here tho.
2016-04-28 12:32:59 +02:00

3668 lines
98 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) 2004 Blender Foundation.
* All rights reserved.
*
* Contributor(s): none yet.
*
* ***** END GPL LICENSE BLOCK *****
*/
/** \file blender/blenkernel/intern/depsgraph.c
* \ingroup bke
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include "MEM_guardedalloc.h"
#ifdef WIN32
# include "BLI_winstuff.h"
#endif
#include "BLI_utildefines.h"
#include "BLI_listbase.h"
#include "BLI_ghash.h"
#include "BLI_threads.h"
#include "DNA_anim_types.h"
#include "DNA_camera_types.h"
#include "DNA_group_types.h"
#include "DNA_lamp_types.h"
#include "DNA_lattice_types.h"
#include "DNA_key_types.h"
#include "DNA_material_types.h"
#include "DNA_mesh_types.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
#include "DNA_screen_types.h"
#include "DNA_windowmanager_types.h"
#include "DNA_movieclip_types.h"
#include "DNA_mask_types.h"
#include "BKE_anim.h"
#include "BKE_animsys.h"
#include "BKE_action.h"
#include "BKE_DerivedMesh.h"
#include "BKE_effect.h"
#include "BKE_fcurve.h"
#include "BKE_global.h"
#include "BKE_image.h"
#include "BKE_key.h"
#include "BKE_library.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_material.h"
#include "BKE_mball.h"
#include "BKE_modifier.h"
#include "BKE_object.h"
#include "BKE_paint.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
#include "BKE_scene.h"
#include "BKE_screen.h"
#include "BKE_tracking.h"
#include "GPU_buffers.h"
#include "atomic_ops.h"
#include "depsgraph_private.h"
#include "DEG_depsgraph.h"
#include "DEG_depsgraph_build.h"
#include "DEG_depsgraph_debug.h"
#include "DEG_depsgraph_query.h"
#ifdef WITH_LEGACY_DEPSGRAPH
static SpinLock threaded_update_lock;
void DAG_init(void)
{
BLI_spin_init(&threaded_update_lock);
DEG_register_node_types();
}
void DAG_exit(void)
{
BLI_spin_end(&threaded_update_lock);
DEG_free_node_types();
}
/* Queue and stack operations for dag traversal
*
* the queue store a list of freenodes to avoid successive alloc/dealloc
*/
DagNodeQueue *queue_create(int slots)
{
DagNodeQueue *queue;
DagNodeQueueElem *elem;
int i;
queue = MEM_mallocN(sizeof(DagNodeQueue), "DAG queue");
queue->freenodes = MEM_mallocN(sizeof(DagNodeQueue), "DAG queue");
queue->count = 0;
queue->maxlevel = 0;
queue->first = queue->last = NULL;
elem = MEM_mallocN(sizeof(DagNodeQueueElem), "DAG queue elem3");
elem->node = NULL;
elem->next = NULL;
queue->freenodes->first = queue->freenodes->last = elem;
for (i = 1; i < slots; i++) {
elem = MEM_mallocN(sizeof(DagNodeQueueElem), "DAG queue elem4");
elem->node = NULL;
elem->next = NULL;
queue->freenodes->last->next = elem;
queue->freenodes->last = elem;
}
queue->freenodes->count = slots;
return queue;
}
void queue_raz(DagNodeQueue *queue)
{
DagNodeQueueElem *elem;
elem = queue->first;
if (queue->freenodes->last)
queue->freenodes->last->next = elem;
else
queue->freenodes->first = queue->freenodes->last = elem;
elem->node = NULL;
queue->freenodes->count++;
while (elem->next) {
elem = elem->next;
elem->node = NULL;
queue->freenodes->count++;
}
queue->freenodes->last = elem;
queue->count = 0;
}
void queue_delete(DagNodeQueue *queue)
{
DagNodeQueueElem *elem;
DagNodeQueueElem *temp;
elem = queue->first;
while (elem) {
temp = elem;
elem = elem->next;
MEM_freeN(temp);
}
elem = queue->freenodes->first;
while (elem) {
temp = elem;
elem = elem->next;
MEM_freeN(temp);
}
MEM_freeN(queue->freenodes);
MEM_freeN(queue);
}
/* insert in queue, remove in front */
void push_queue(DagNodeQueue *queue, DagNode *node)
{
DagNodeQueueElem *elem;
int i;
if (node == NULL) {
fprintf(stderr, "pushing null node\n");
return;
}
/*fprintf(stderr, "BFS push : %s %d\n", ((ID *) node->ob)->name, queue->count);*/
elem = queue->freenodes->first;
if (elem != NULL) {
queue->freenodes->first = elem->next;
if (queue->freenodes->last == elem) {
queue->freenodes->last = NULL;
queue->freenodes->first = NULL;
}
queue->freenodes->count--;
}
else { /* alllocating more */
elem = MEM_mallocN(sizeof(DagNodeQueueElem), "DAG queue elem1");
elem->node = NULL;
elem->next = NULL;
queue->freenodes->first = queue->freenodes->last = elem;
for (i = 1; i < DAGQUEUEALLOC; i++) {
elem = MEM_mallocN(sizeof(DagNodeQueueElem), "DAG queue elem2");
elem->node = NULL;
elem->next = NULL;
queue->freenodes->last->next = elem;
queue->freenodes->last = elem;
}
queue->freenodes->count = DAGQUEUEALLOC;
elem = queue->freenodes->first;
queue->freenodes->first = elem->next;
}
elem->next = NULL;
elem->node = node;
if (queue->last != NULL)
queue->last->next = elem;
queue->last = elem;
if (queue->first == NULL) {
queue->first = elem;
}
queue->count++;
}
/* insert in front, remove in front */
void push_stack(DagNodeQueue *queue, DagNode *node)
{
DagNodeQueueElem *elem;
int i;
elem = queue->freenodes->first;
if (elem != NULL) {
queue->freenodes->first = elem->next;
if (queue->freenodes->last == elem) {
queue->freenodes->last = NULL;
queue->freenodes->first = NULL;
}
queue->freenodes->count--;
}
else { /* alllocating more */
elem = MEM_mallocN(sizeof(DagNodeQueueElem), "DAG queue elem1");
elem->node = NULL;
elem->next = NULL;
queue->freenodes->first = queue->freenodes->last = elem;
for (i = 1; i < DAGQUEUEALLOC; i++) {
elem = MEM_mallocN(sizeof(DagNodeQueueElem), "DAG queue elem2");
elem->node = NULL;
elem->next = NULL;
queue->freenodes->last->next = elem;
queue->freenodes->last = elem;
}
queue->freenodes->count = DAGQUEUEALLOC;
elem = queue->freenodes->first;
queue->freenodes->first = elem->next;
}
elem->next = queue->first;
elem->node = node;
queue->first = elem;
if (queue->last == NULL)
queue->last = elem;
queue->count++;
}
DagNode *pop_queue(DagNodeQueue *queue)
{
DagNodeQueueElem *elem;
DagNode *node;
elem = queue->first;
if (elem) {
queue->first = elem->next;
if (queue->last == elem) {
queue->last = NULL;
queue->first = NULL;
}
queue->count--;
if (queue->freenodes->last)
queue->freenodes->last->next = elem;
queue->freenodes->last = elem;
if (queue->freenodes->first == NULL)
queue->freenodes->first = elem;
node = elem->node;
elem->node = NULL;
elem->next = NULL;
queue->freenodes->count++;
return node;
}
else {
fprintf(stderr, "return null\n");
return NULL;
}
}
DagNode *get_top_node_queue(DagNodeQueue *queue)
{
return queue->first->node;
}
DagForest *dag_init(void)
{
DagForest *forest;
/* use callocN to init all zero */
forest = MEM_callocN(sizeof(DagForest), "DAG root");
forest->ugly_hack_sorry = true;
return forest;
}
/* isdata = object data... */
/* XXX this needs to be extended to be more flexible (so that not only objects are evaluated via depsgraph)... */
static void dag_add_driver_relation(AnimData *adt, DagForest *dag, DagNode *node, int isdata)
{
FCurve *fcu;
DagNode *node1;
for (fcu = adt->drivers.first; fcu; fcu = fcu->next) {
ChannelDriver *driver = fcu->driver;
DriverVar *dvar;
int isdata_fcu = (isdata) || (fcu->rna_path && strstr(fcu->rna_path, "modifiers["));
/* loop over variables to get the target relationships */
for (dvar = driver->variables.first; dvar; dvar = dvar->next) {
/* only used targets */
DRIVER_TARGETS_USED_LOOPER(dvar)
{
if (dtar->id) {
/* FIXME: other data types need to be added here so that they can work! */
if (GS(dtar->id->name) == ID_OB) {
Object *ob = (Object *)dtar->id;
/* normal channel-drives-channel */
node1 = dag_get_node(dag, dtar->id);
/* check if bone... */
if ((ob->type == OB_ARMATURE) &&
( ((dtar->rna_path) && strstr(dtar->rna_path, "pose.bones[")) ||
((dtar->flag & DTAR_FLAG_STRUCT_REF) && (dtar->pchan_name[0])) ))
{
dag_add_relation(dag, node1, node, isdata_fcu ? DAG_RL_DATA_DATA : DAG_RL_DATA_OB, "Driver");
}
/* check if ob data */
else if (dtar->rna_path && strstr(dtar->rna_path, "data."))
dag_add_relation(dag, node1, node, isdata_fcu ? DAG_RL_DATA_DATA : DAG_RL_DATA_OB, "Driver");
/* normal */
else
dag_add_relation(dag, node1, node, isdata_fcu ? DAG_RL_OB_DATA : DAG_RL_OB_OB, "Driver");
}
}
}
DRIVER_TARGETS_LOOPER_END
}
}
}
/* XXX: forward def for material driver handling... */
static void dag_add_material_driver_relations(DagForest *dag, DagNode *node, Material *ma);
/* recursive handling for shader nodetree drivers */
static void dag_add_shader_nodetree_driver_relations(DagForest *dag, DagNode *node, bNodeTree *ntree)
{
bNode *n;
/* nodetree itself */
if (ntree->adt) {
dag_add_driver_relation(ntree->adt, dag, node, 1);
}
/* nodetree's nodes... */
for (n = ntree->nodes.first; n; n = n->next) {
if (n->id) {
if (GS(n->id->name) == ID_MA) {
dag_add_material_driver_relations(dag, node, (Material *)n->id);
}
else if (n->type == NODE_GROUP) {
dag_add_shader_nodetree_driver_relations(dag, node, (bNodeTree *)n->id);
}
}
}
}
/* recursive handling for material drivers */
static void dag_add_material_driver_relations(DagForest *dag, DagNode *node, Material *ma)
{
/* Prevent infinite recursion by checking (and tagging the material) as having been visited
* already (see build_dag()). This assumes ma->id.tag & LIB_TAG_DOIT isn't set by anything else
* in the meantime... [#32017]
*/
if (ma->id.tag & LIB_TAG_DOIT)
return;
ma->id.tag |= LIB_TAG_DOIT;
/* material itself */
if (ma->adt)
dag_add_driver_relation(ma->adt, dag, node, 1);
/* textures */
// TODO...
//dag_add_texture_driver_relations(DagForest *dag, DagNode *node, ID *id);
/* material's nodetree */
if (ma->nodetree)
dag_add_shader_nodetree_driver_relations(dag, node, ma->nodetree);
ma->id.tag &= ~LIB_TAG_DOIT;
}
/* recursive handling for lamp drivers */
static void dag_add_lamp_driver_relations(DagForest *dag, DagNode *node, Lamp *la)
{
/* Prevent infinite recursion by checking (and tagging the lamp) as having been visited
* already (see build_dag()). This assumes la->id.tag & LIB_TAG_DOIT isn't set by anything else
* in the meantime... [#32017]
*/
if (la->id.tag & LIB_TAG_DOIT)
return;
la->id.tag |= LIB_TAG_DOIT;
/* lamp itself */
if (la->adt)
dag_add_driver_relation(la->adt, dag, node, 1);
/* textures */
// TODO...
//dag_add_texture_driver_relations(DagForest *dag, DagNode *node, ID *id);
/* lamp's nodetree */
if (la->nodetree)
dag_add_shader_nodetree_driver_relations(dag, node, la->nodetree);
la->id.tag &= ~LIB_TAG_DOIT;
}
static void check_and_create_collision_relation(DagForest *dag, Object *ob, DagNode *node, Object *ob1, int skip_forcefield, bool no_collision)
{
DagNode *node2;
if (ob1->pd && (ob1->pd->deflect || ob1->pd->forcefield) && (ob1 != ob)) {
if ((skip_forcefield && ob1->pd->forcefield == skip_forcefield) || (no_collision && ob1->pd->forcefield == 0))
return;
node2 = dag_get_node(dag, ob1);
dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Field Collision");
}
}
static void dag_add_collision_field_relation(DagForest *dag, Scene *scene, Object *ob, DagNode *node, int skip_forcefield, bool no_collision)
{
Base *base;
ParticleSystem *particle_system;
for (particle_system = ob->particlesystem.first;
particle_system;
particle_system = particle_system->next)
{
EffectorWeights *effector_weights = particle_system->part->effector_weights;
if (effector_weights->group) {
GroupObject *group_object;
for (group_object = effector_weights->group->gobject.first;
group_object;
group_object = group_object->next)
{
if ((group_object->ob->lay & ob->lay)) {
check_and_create_collision_relation(dag, ob, node, group_object->ob, skip_forcefield, no_collision);
}
}
}
}
/* would be nice to have a list of colliders here
* so for now walk all objects in scene check 'same layer rule' */
for (base = scene->base.first; base; base = base->next) {
if ((base->lay & ob->lay)) {
Object *ob1 = base->object;
check_and_create_collision_relation(dag, ob, node, ob1, skip_forcefield, no_collision);
}
}
}
static void build_dag_object(DagForest *dag, DagNode *scenenode, Main *bmain, Scene *scene, Object *ob, int mask)
{
bConstraint *con;
DagNode *node;
DagNode *node2;
DagNode *node3;
Key *key;
ParticleSystem *psys;
int addtoroot = 1;
node = dag_get_node(dag, ob);
if ((ob->data) && (mask & DAG_RL_DATA)) {
node2 = dag_get_node(dag, ob->data);
dag_add_relation(dag, node, node2, DAG_RL_DATA, "Object-Data Relation");
node2->first_ancestor = ob;
node2->ancestor_count += 1;
}
/* also build a custom data mask for dependencies that need certain layers */
if (ob->type == OB_ARMATURE) {
if (ob->pose) {
bPoseChannel *pchan;
for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
for (con = pchan->constraints.first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
for (ct = targets.first; ct; ct = ct->next) {
if (ct->tar && ct->tar != ob) {
// fprintf(stderr, "armature %s target :%s\n", ob->id.name, target->id.name);
node3 = dag_get_node(dag, ct->tar);
if (ct->subtarget[0]) {
dag_add_relation(dag, node3, node, DAG_RL_OB_DATA | DAG_RL_DATA_DATA, cti->name);
if (ct->tar->type == OB_MESH)
node3->customdata_mask |= CD_MASK_MDEFORMVERT;
}
else if (ELEM(con->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO, CONSTRAINT_TYPE_SPLINEIK))
dag_add_relation(dag, node3, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, cti->name);
else
dag_add_relation(dag, node3, node, DAG_RL_OB_DATA, cti->name);
}
}
if (cti->flush_constraint_targets)
cti->flush_constraint_targets(con, &targets, 1);
}
}
}
}
}
/* driver dependencies, nla modifiers */
#if 0 // XXX old animation system
if (ob->nlastrips.first) {
bActionStrip *strip;
bActionChannel *chan;
for (strip = ob->nlastrips.first; strip; strip = strip->next) {
if (strip->modifiers.first) {
bActionModifier *amod;
for (amod = strip->modifiers.first; amod; amod = amod->next) {
if (amod->ob) {
node2 = dag_get_node(dag, amod->ob);
dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "NLA Strip Modifier");
}
}
}
}
}
#endif // XXX old animation system
if (ob->adt)
dag_add_driver_relation(ob->adt, dag, node, (ob->type == OB_ARMATURE)); // XXX isdata arg here doesn't give an accurate picture of situation
key = BKE_key_from_object(ob);
if (key && key->adt)
dag_add_driver_relation(key->adt, dag, node, 1);
if (ob->modifiers.first) {
ModifierData *md;
for (md = ob->modifiers.first; md; md = md->next) {
const ModifierTypeInfo *mti = modifierType_getInfo(md->type);
if (mti->updateDepgraph) mti->updateDepgraph(md, dag, bmain, scene, ob, node);
}
}
if (ob->parent) {
node2 = dag_get_node(dag, ob->parent);
switch (ob->partype) {
case PARSKEL:
dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_OB, "Parent");
break;
case PARVERT1: case PARVERT3:
dag_add_relation(dag, node2, node, DAG_RL_DATA_OB | DAG_RL_OB_OB, "Vertex Parent");
node2->customdata_mask |= CD_MASK_ORIGINDEX;
break;
case PARBONE:
dag_add_relation(dag, node2, node, DAG_RL_DATA_OB | DAG_RL_OB_OB, "Bone Parent");
break;
default:
if (ob->parent->type == OB_LATTICE)
dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_OB, "Lattice Parent");
else if (ob->parent->type == OB_CURVE) {
Curve *cu = ob->parent->data;
if (cu->flag & CU_PATH)
dag_add_relation(dag, node2, node, DAG_RL_DATA_OB | DAG_RL_OB_OB, "Curve Parent");
else
dag_add_relation(dag, node2, node, DAG_RL_OB_OB, "Curve Parent");
}
else
dag_add_relation(dag, node2, node, DAG_RL_OB_OB, "Parent");
break;
}
/* exception case: parent is duplivert */
if (ob->type == OB_MBALL && (ob->parent->transflag & OB_DUPLIVERTS)) {
dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_OB, "Duplivert");
}
addtoroot = 0;
}
if (ob->proxy) {
node2 = dag_get_node(dag, ob->proxy);
dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA | DAG_RL_OB_OB, "Proxy");
/* inverted relation, so addtoroot shouldn't be set to zero */
}
if (ob->transflag & OB_DUPLI) {
if ((ob->transflag & OB_DUPLIGROUP) && ob->dup_group) {
GroupObject *go;
for (go = ob->dup_group->gobject.first; go; go = go->next) {
if (go->ob) {
node2 = dag_get_node(dag, go->ob);
/* node2 changes node1, this keeps animations updated in groups?? not logical? */
dag_add_relation(dag, node2, node, DAG_RL_OB_OB, "Dupligroup");
}
}
}
}
/* softbody collision */
if ((ob->type == OB_MESH) || (ob->type == OB_CURVE) || (ob->type == OB_LATTICE)) {
if (ob->particlesystem.first ||
modifiers_isModifierEnabled(ob, eModifierType_Softbody) ||
modifiers_isModifierEnabled(ob, eModifierType_Cloth) ||
modifiers_isModifierEnabled(ob, eModifierType_DynamicPaint))
{
dag_add_collision_field_relation(dag, scene, ob, node, 0, false); /* TODO: use effectorweight->group */
}
else if (modifiers_isModifierEnabled(ob, eModifierType_Smoke)) {
dag_add_collision_field_relation(dag, scene, ob, node, PFIELD_SMOKEFLOW, false);
}
else if (ob->rigidbody_object) {
dag_add_collision_field_relation(dag, scene, ob, node, 0, true);
}
}
/* object data drivers */
if (ob->data) {
AnimData *adt = BKE_animdata_from_id((ID *)ob->data);
if (adt)
dag_add_driver_relation(adt, dag, node, 1);
}
/* object type/data relationships */
switch (ob->type) {
case OB_CAMERA:
{
Camera *cam = (Camera *)ob->data;
if (cam->dof_ob) {
node2 = dag_get_node(dag, cam->dof_ob);
dag_add_relation(dag, node2, node, DAG_RL_OB_OB, "Camera DoF");
}
break;
}
case OB_MBALL:
{
Object *mom = BKE_mball_basis_find(scene, ob);
if (mom != ob) {
node2 = dag_get_node(dag, mom);
dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Metaball"); /* mom depends on children! */
}
break;
}
case OB_CURVE:
case OB_FONT:
{
Curve *cu = ob->data;
if (cu->bevobj) {
node2 = dag_get_node(dag, cu->bevobj);
dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Curve Bevel");
}
if (cu->taperobj) {
node2 = dag_get_node(dag, cu->taperobj);
dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Curve Taper");
}
if (ob->type == OB_FONT) {
/* Really rather dirty hack. needs to support font family to work
* reliably on render export.
*
* This totally mimics behavior of regular verts duplication with
* parenting. The only tricky thing here is to get list of objects
* used for the custom "font".
*
* This shouldn't harm so much because this code only runs on DAG
* rebuild and this feature is not that commonly used.
*
* - sergey -
*/
if (cu->family[0] != '\n') {
ListBase *duplilist;
DupliObject *dob;
duplilist = object_duplilist(G.main->eval_ctx, scene, ob);
for (dob = duplilist->first; dob; dob = dob->next) {
node2 = dag_get_node(dag, dob->ob);
dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Object Font");
}
free_object_duplilist(duplilist);
}
if (cu->textoncurve) {
node2 = dag_get_node(dag, cu->textoncurve);
/* Text on curve requires path to be evaluated for the target curve. */
node2->eval_flags |= DAG_EVAL_NEED_CURVE_PATH;
dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Texture On Curve");
}
}
break;
}
}
/* material drivers */
if (ob->totcol) {
int a;
for (a = 1; a <= ob->totcol; a++) {
Material *ma = give_current_material(ob, a);
if (ma) {
/* recursively figure out if there are drivers, and hook these up to this object */
dag_add_material_driver_relations(dag, node, ma);
}
}
}
else if (ob->type == OB_LAMP) {
dag_add_lamp_driver_relations(dag, node, ob->data);
}
/* particles */
psys = ob->particlesystem.first;
if (psys) {
GroupObject *go;
for (; psys; psys = psys->next) {
BoidRule *rule = NULL;
BoidState *state = NULL;
ParticleSettings *part = psys->part;
ListBase *effectors = NULL;
EffectorCache *eff;
if (part->adt) {
dag_add_driver_relation(part->adt, dag, node, 1);
}
dag_add_relation(dag, node, node, DAG_RL_OB_DATA, "Particle-Object Relation");
if (!psys_check_enabled(ob, psys))
continue;
if (ELEM(part->phystype, PART_PHYS_KEYED, PART_PHYS_BOIDS)) {
ParticleTarget *pt = psys->targets.first;
for (; pt; pt = pt->next) {
if (pt->ob && BLI_findlink(&pt->ob->particlesystem, pt->psys - 1)) {
node2 = dag_get_node(dag, pt->ob);
dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Particle Targets");
}
}
}
if (part->ren_as == PART_DRAW_OB && part->dup_ob) {
node2 = dag_get_node(dag, part->dup_ob);
/* note that this relation actually runs in the wrong direction, the problem
* is that dupli system all have this (due to parenting), and the render
* engine instancing assumes particular ordering of objects in list */
dag_add_relation(dag, node, node2, DAG_RL_OB_OB, "Particle Object Visualization");
if (part->dup_ob->type == OB_MBALL)
dag_add_relation(dag, node, node2, DAG_RL_DATA_DATA, "Particle Object Visualization");
}
if (part->ren_as == PART_DRAW_GR && part->dup_group) {
for (go = part->dup_group->gobject.first; go; go = go->next) {
node2 = dag_get_node(dag, go->ob);
dag_add_relation(dag, node2, node, DAG_RL_OB_OB, "Particle Group Visualization");
}
}
effectors = pdInitEffectors(scene, ob, psys, part->effector_weights, false);
if (effectors) {
for (eff = effectors->first; eff; eff = eff->next) {
if (eff->psys) {
node2 = dag_get_node(dag, eff->ob);
dag_add_relation(dag, node2, node, DAG_RL_DATA_DATA | DAG_RL_OB_DATA, "Particle Field");
}
}
}
pdEndEffectors(&effectors);
if (part->boids) {
for (state = part->boids->states.first; state; state = state->next) {
for (rule = state->rules.first; rule; rule = rule->next) {
Object *ruleob = NULL;
if (rule->type == eBoidRuleType_Avoid)
ruleob = ((BoidRuleGoalAvoid *)rule)->ob;
else if (rule->type == eBoidRuleType_FollowLeader)
ruleob = ((BoidRuleFollowLeader *)rule)->ob;
if (ruleob) {
node2 = dag_get_node(dag, ruleob);
dag_add_relation(dag, node2, node, DAG_RL_OB_DATA, "Boid Rule");
}
}
}
}
}
}
/* object constraints */
for (con = ob->constraints.first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
if (!cti)
continue;
/* special case for camera tracking -- it doesn't use targets to define relations */
if (ELEM(cti->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_CAMERASOLVER, CONSTRAINT_TYPE_OBJECTSOLVER)) {
int depends_on_camera = 0;
if (cti->type == CONSTRAINT_TYPE_FOLLOWTRACK) {
bFollowTrackConstraint *data = (bFollowTrackConstraint *)con->data;
if ((data->clip || data->flag & FOLLOWTRACK_ACTIVECLIP) && data->track[0])
depends_on_camera = 1;
if (data->depth_ob) {
node2 = dag_get_node(dag, data->depth_ob);
dag_add_relation(dag, node2, node, DAG_RL_DATA_OB | DAG_RL_OB_OB, cti->name);
}
}
else if (cti->type == CONSTRAINT_TYPE_OBJECTSOLVER)
depends_on_camera = 1;
if (depends_on_camera && scene->camera) {
node2 = dag_get_node(dag, scene->camera);
dag_add_relation(dag, node2, node, DAG_RL_DATA_OB | DAG_RL_OB_OB, cti->name);
}
dag_add_relation(dag, scenenode, node, DAG_RL_SCENE, "Scene Relation");
addtoroot = 0;
}
else if (cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
for (ct = targets.first; ct; ct = ct->next) {
Object *obt;
if (ct->tar)
obt = ct->tar;
else
continue;
node2 = dag_get_node(dag, obt);
if (ELEM(con->type, CONSTRAINT_TYPE_FOLLOWPATH, CONSTRAINT_TYPE_CLAMPTO))
dag_add_relation(dag, node2, node, DAG_RL_DATA_OB | DAG_RL_OB_OB, cti->name);
else {
if (ELEM(obt->type, OB_ARMATURE, OB_MESH, OB_LATTICE) && (ct->subtarget[0])) {
dag_add_relation(dag, node2, node, DAG_RL_DATA_OB | DAG_RL_OB_OB, cti->name);
if (obt->type == OB_MESH)
node2->customdata_mask |= CD_MASK_MDEFORMVERT;
}
else
dag_add_relation(dag, node2, node, DAG_RL_OB_OB, cti->name);
}
addtoroot = 0;
}
if (cti->flush_constraint_targets)
cti->flush_constraint_targets(con, &targets, 1);
}
}
if (addtoroot == 1)
dag_add_relation(dag, scenenode, node, DAG_RL_SCENE, "Scene Relation");
}
static void build_dag_group(DagForest *dag, DagNode *scenenode, Main *bmain, Scene *scene, Group *group, short mask)
{
GroupObject *go;
if (group->id.tag & LIB_TAG_DOIT)
return;
group->id.tag |= LIB_TAG_DOIT;
for (go = group->gobject.first; go; go = go->next) {
build_dag_object(dag, scenenode, bmain, scene, go->ob, mask);
if (go->ob->dup_group)
build_dag_group(dag, scenenode, bmain, scene, go->ob->dup_group, mask);
}
}
DagForest *build_dag(Main *bmain, Scene *sce, short mask)
{
Base *base;
Object *ob;
DagNode *node;
DagNode *scenenode;
DagForest *dag;
DagAdjList *itA;
dag = sce->theDag;
if (dag)
free_forest(dag);
else {
dag = dag_init();
sce->theDag = dag;
}
dag->need_update = false;
BKE_main_id_tag_idcode(bmain, ID_OB, LIB_TAG_DOIT, false);
/* clear "LIB_TAG_DOIT" flag from all materials, to prevent infinite recursion problems later [#32017] */
BKE_main_id_tag_idcode(bmain, ID_MA, LIB_TAG_DOIT, false);
BKE_main_id_tag_idcode(bmain, ID_LA, LIB_TAG_DOIT, false);
BKE_main_id_tag_idcode(bmain, ID_GR, LIB_TAG_DOIT, false);
/* add base node for scene. scene is always the first node in DAG */
scenenode = dag_add_node(dag, sce);
/* add current scene objects */
for (base = sce->base.first; base; base = base->next) {
ob = base->object;
ob->id.tag |= LIB_TAG_DOIT;
build_dag_object(dag, scenenode, bmain, sce, ob, mask);
if (ob->proxy)
build_dag_object(dag, scenenode, bmain, sce, ob->proxy, mask);
if (ob->dup_group)
build_dag_group(dag, scenenode, bmain, sce, ob->dup_group, mask);
}
/* There might be situations when object from current scene depends on
* objects form other scene AND objects from other scene has own
* dependencies on objects from other scene.
*
* This is really important to include such indirect dependencies in order
* to keep threaded update safe but since we don't really know if object is
* coming from current scene or another scene we do rather stupid tag-based
* check here: all the objects for which build_dag_object() was called are
* getting tagged with LIB_TAG_DOIT. This way if some node has untagged
* object we know it's an object from other scene.
*
* It should be enough to to it once, because if there's longer chain of
* indirect dependencies, all the new nodes will be added to the end of the
* list, meaning we'll keep covering them in this for loop.
*/
for (node = sce->theDag->DagNode.first; node != NULL; node = node->next) {
if (node->type == ID_OB) {
ob = node->ob;
if ((ob->id.tag & LIB_TAG_DOIT) == 0) {
ob->id.tag |= LIB_TAG_DOIT;
build_dag_object(dag, scenenode, bmain, sce, ob, mask);
if (ob->proxy)
build_dag_object(dag, scenenode, bmain, sce, ob->proxy, mask);
if (ob->dup_group)
build_dag_group(dag, scenenode, bmain, sce, ob->dup_group, mask);
}
}
}
BKE_main_id_tag_idcode(bmain, ID_GR, LIB_TAG_DOIT, false);
/* Now all relations were built, but we need to solve 1 exceptional case;
* When objects have multiple "parents" (for example parent + constraint working on same object)
* the relation type has to be synced. One of the parents can change, and should give same event to child */
/* nodes were callocced, so we can use node->color for temporal storage */
for (node = sce->theDag->DagNode.first; node; node = node->next) {
if (node->type == ID_OB) {
for (itA = node->child; itA; itA = itA->next) {
if (itA->node->type == ID_OB) {
itA->node->color |= itA->type;
}
}
/* also flush custom data mask */
((Object *)node->ob)->customdata_mask = node->customdata_mask;
if (node->parent == NULL) {
dag_add_relation(dag, scenenode, node, DAG_RL_SCENE, "Scene Relation");
}
}
}
/* now set relations equal, so that when only one parent changes, the correct recalcs are found */
for (node = sce->theDag->DagNode.first; node; node = node->next) {
if (node->type == ID_OB) {
for (itA = node->child; itA; itA = itA->next) {
if (itA->node->type == ID_OB) {
itA->type |= itA->node->color;
}
}
}
}
/* cycle detection and solving */
// solve_cycles(dag);
return dag;
}
void free_forest(DagForest *Dag)
{ /* remove all nodes and deps */
DagNode *tempN;
DagAdjList *tempA;
DagAdjList *itA;
DagNode *itN = Dag->DagNode.first;
while (itN) {
itA = itN->child;
while (itA) {
tempA = itA;
itA = itA->next;
MEM_freeN(tempA);
}
itA = itN->parent;
while (itA) {
tempA = itA;
itA = itA->next;
MEM_freeN(tempA);
}
tempN = itN;
itN = itN->next;
MEM_freeN(tempN);
}
BLI_ghash_free(Dag->nodeHash, NULL, NULL);
Dag->nodeHash = NULL;
Dag->DagNode.first = NULL;
Dag->DagNode.last = NULL;
Dag->numNodes = 0;
}
DagNode *dag_find_node(DagForest *forest, void *fob)
{
if (forest->nodeHash)
return BLI_ghash_lookup(forest->nodeHash, fob);
return NULL;
}
static int dag_print_dependencies = 0; /* debugging */
/* no checking of existence, use dag_find_node first or dag_get_node */
DagNode *dag_add_node(DagForest *forest, void *fob)
{
DagNode *node;
node = MEM_callocN(sizeof(DagNode), "DAG node");
if (node) {
node->ob = fob;
node->color = DAG_WHITE;
if (forest->ugly_hack_sorry) node->type = GS(((ID *) fob)->name); /* sorry, done for pose sorting */
if (forest->numNodes) {
((DagNode *) forest->DagNode.last)->next = node;
forest->DagNode.last = node;
forest->numNodes++;
}
else {
forest->DagNode.last = node;
forest->DagNode.first = node;
forest->numNodes = 1;
}
if (!forest->nodeHash)
forest->nodeHash = BLI_ghash_ptr_new("dag_add_node gh");
BLI_ghash_insert(forest->nodeHash, fob, node);
}
return node;
}
DagNode *dag_get_node(DagForest *forest, void *fob)
{
DagNode *node;
node = dag_find_node(forest, fob);
if (!node)
node = dag_add_node(forest, fob);
return node;
}
DagNode *dag_get_sub_node(DagForest *forest, void *fob)
{
DagNode *node;
DagAdjList *mainchild, *prev = NULL;
mainchild = ((DagNode *) forest->DagNode.first)->child;
/* remove from first node (scene) adj list if present */
while (mainchild) {
if (mainchild->node == fob) {
if (prev) {
prev->next = mainchild->next;
MEM_freeN(mainchild);
break;
}
else {
((DagNode *) forest->DagNode.first)->child = mainchild->next;
MEM_freeN(mainchild);
break;
}
}
prev = mainchild;
mainchild = mainchild->next;
}
node = dag_find_node(forest, fob);
if (!node)
node = dag_add_node(forest, fob);
return node;
}
static void dag_add_parent_relation(DagForest *UNUSED(forest), DagNode *fob1, DagNode *fob2, short rel, const char *name)
{
DagAdjList *itA = fob2->parent;
while (itA) { /* search if relation exist already */
if (itA->node == fob1) {
itA->type |= rel;
itA->count += 1;
return;
}
itA = itA->next;
}
/* create new relation and insert at head. MALLOC alert! */
itA = MEM_mallocN(sizeof(DagAdjList), "DAG adj list");
itA->node = fob1;
itA->type = rel;
itA->count = 1;
itA->next = fob2->parent;
itA->name = name;
fob2->parent = itA;
}
void dag_add_relation(DagForest *forest, DagNode *fob1, DagNode *fob2, short rel, const char *name)
{
DagAdjList *itA = fob1->child;
/* parent relation is for cycle checking */
dag_add_parent_relation(forest, fob1, fob2, rel, name);
/* TODO(sergey): Find a better place for this. */
#ifdef WITH_OPENSUBDIV
if ((rel & (DAG_RL_DATA_DATA | DAG_RL_DATA_OB)) != 0) {
if (fob1->type == ID_OB) {
if ((fob1->eval_flags & DAG_EVAL_NEED_CPU) == 0) {
Object *ob2 = fob2->ob;
if (ob2->recalc & OB_RECALC_ALL) {
/* Make sure object has all the data on CPU. */
Object *ob1 = fob1->ob;
ob1->recalc |= OB_RECALC_DATA;
}
fob1->eval_flags |= DAG_EVAL_NEED_CPU;
}
}
}
#endif
while (itA) { /* search if relation exist already */
if (itA->node == fob2) {
itA->type |= rel;
itA->count += 1;
return;
}
itA = itA->next;
}
/* create new relation and insert at head. MALLOC alert! */
itA = MEM_mallocN(sizeof(DagAdjList), "DAG adj list");
itA->node = fob2;
itA->type = rel;
itA->count = 1;
itA->next = fob1->child;
itA->name = name;
fob1->child = itA;
}
static const char *dag_node_name(DagForest *dag, DagNode *node)
{
if (node->ob == NULL)
return "null";
else if (dag->ugly_hack_sorry)
return ((ID *)(node->ob))->name + 2;
else
return ((bPoseChannel *)(node->ob))->name;
}
static void dag_node_print_dependencies(DagForest *dag, DagNode *node)
{
DagAdjList *itA;
printf("%s depends on:\n", dag_node_name(dag, node));
for (itA = node->parent; itA; itA = itA->next)
printf(" %s through %s\n", dag_node_name(dag, itA->node), itA->name);
printf("\n");
}
static int dag_node_print_dependency_recurs(DagForest *dag, DagNode *node, DagNode *endnode)
{
DagAdjList *itA;
if (node->color == DAG_BLACK)
return 0;
node->color = DAG_BLACK;
if (node == endnode)
return 1;
for (itA = node->parent; itA; itA = itA->next) {
if (dag_node_print_dependency_recurs(dag, itA->node, endnode)) {
printf(" %s depends on %s through %s.\n", dag_node_name(dag, node), dag_node_name(dag, itA->node), itA->name);
return 1;
}
}
return 0;
}
static void dag_node_print_dependency_cycle(DagForest *dag, DagNode *startnode, DagNode *endnode, const char *name)
{
DagNode *node;
for (node = dag->DagNode.first; node; node = node->next)
node->color = DAG_WHITE;
printf(" %s depends on %s through %s.\n", dag_node_name(dag, endnode), dag_node_name(dag, startnode), name);
dag_node_print_dependency_recurs(dag, startnode, endnode);
printf("\n");
}
static int dag_node_recurs_level(DagNode *node, int level)
{
DagAdjList *itA;
int newlevel;
node->color = DAG_BLACK; /* done */
newlevel = ++level;
for (itA = node->parent; itA; itA = itA->next) {
if (itA->node->color == DAG_WHITE) {
itA->node->ancestor_count = dag_node_recurs_level(itA->node, level);
newlevel = MAX2(newlevel, level + itA->node->ancestor_count);
}
else
newlevel = MAX2(newlevel, level + itA->node->ancestor_count);
}
return newlevel;
}
static void dag_check_cycle(DagForest *dag)
{
DagNode *node;
DagAdjList *itA;
dag->is_acyclic = true;
/* debugging print */
if (dag_print_dependencies)
for (node = dag->DagNode.first; node; node = node->next)
dag_node_print_dependencies(dag, node);
/* tag nodes unchecked */
for (node = dag->DagNode.first; node; node = node->next)
node->color = DAG_WHITE;
for (node = dag->DagNode.first; node; node = node->next) {
if (node->color == DAG_WHITE) {
node->ancestor_count = dag_node_recurs_level(node, 0);
}
}
/* check relations, and print errors */
for (node = dag->DagNode.first; node; node = node->next) {
for (itA = node->parent; itA; itA = itA->next) {
if (itA->node->ancestor_count > node->ancestor_count) {
if (node->ob && itA->node->ob) {
dag->is_acyclic = false;
printf("Dependency cycle detected:\n");
dag_node_print_dependency_cycle(dag, itA->node, node, itA->name);
}
}
}
}
/* parent relations are only needed for cycle checking, so free now */
for (node = dag->DagNode.first; node; node = node->next) {
while (node->parent) {
itA = node->parent->next;
MEM_freeN(node->parent);
node->parent = itA;
}
}
}
/* debug test functions */
void graph_print_queue(DagNodeQueue *nqueue)
{
DagNodeQueueElem *queueElem;
queueElem = nqueue->first;
while (queueElem) {
fprintf(stderr, "** %s %i %i-%i ", ((ID *) queueElem->node->ob)->name, queueElem->node->color, queueElem->node->DFS_dvtm, queueElem->node->DFS_fntm);
queueElem = queueElem->next;
}
fprintf(stderr, "\n");
}
void graph_print_queue_dist(DagNodeQueue *nqueue)
{
DagNodeQueueElem *queueElem;
int count;
queueElem = nqueue->first;
count = 0;
while (queueElem) {
fprintf(stderr, "** %25s %2.2i-%2.2i ", ((ID *) queueElem->node->ob)->name, queueElem->node->DFS_dvtm, queueElem->node->DFS_fntm);
while (count < queueElem->node->DFS_dvtm - 1) { fputc(' ', stderr); count++; }
fputc('|', stderr);
while (count < queueElem->node->DFS_fntm - 2) { fputc('-', stderr); count++; }
fputc('|', stderr);
fputc('\n', stderr);
count = 0;
queueElem = queueElem->next;
}
fprintf(stderr, "\n");
}
void graph_print_adj_list(DagForest *dag)
{
DagNode *node;
DagAdjList *itA;
node = dag->DagNode.first;
while (node) {
fprintf(stderr, "node : %s col: %i", ((ID *) node->ob)->name, node->color);
itA = node->child;
while (itA) {
fprintf(stderr, "-- %s ", ((ID *) itA->node->ob)->name);
itA = itA->next;
}
fprintf(stderr, "\n");
node = node->next;
}
}
/* ************************ API *********************** */
/* mechanism to allow editors to be informed of depsgraph updates,
* to do their own updates based on changes... */
static void (*EditorsUpdateIDCb)(Main *bmain, ID *id) = NULL;
static void (*EditorsUpdateSceneCb)(Main *bmain, Scene *scene, int updated) = NULL;
static void (*EditorsUpdateScenePreCb)(Main *bmain, Scene *scene, bool time) = NULL;
void DAG_editors_update_cb(void (*id_func)(Main *bmain, ID *id),
void (*scene_func)(Main *bmain, Scene *scene, int updated),
void (*scene_pre_func)(Main *bmain, Scene *scene, bool time))
{
if (DEG_depsgraph_use_legacy()) {
EditorsUpdateIDCb = id_func;
EditorsUpdateSceneCb = scene_func;
EditorsUpdateScenePreCb = scene_pre_func;
}
else {
/* New dependency graph. */
DEG_editors_set_update_cb(id_func, scene_func, scene_pre_func);
}
}
void DAG_editors_update_pre(Main *bmain, Scene *scene, bool time)
{
if (DEG_depsgraph_use_legacy()) {
if (EditorsUpdateScenePreCb != NULL) {
EditorsUpdateScenePreCb(bmain, scene, time);
}
}
else {
DEG_editors_update_pre(bmain, scene, time);
}
}
static void dag_editors_id_update(Main *bmain, ID *id)
{
if (EditorsUpdateIDCb)
EditorsUpdateIDCb(bmain, id);
}
static void dag_editors_scene_update(Main *bmain, Scene *scene, int updated)
{
if (EditorsUpdateSceneCb)
EditorsUpdateSceneCb(bmain, scene, updated);
}
/* groups with objects in this scene need to be put in the right order as well */
static void scene_sort_groups(Main *bmain, Scene *sce)
{
Base *base;
Group *group;
GroupObject *go;
Object *ob;
/* test; are group objects all in this scene? */
for (ob = bmain->object.first; ob; ob = ob->id.next) {
ob->id.tag &= ~LIB_TAG_DOIT;
ob->id.newid = NULL; /* newid abuse for GroupObject */
}
for (base = sce->base.first; base; base = base->next)
base->object->id.tag |= LIB_TAG_DOIT;
for (group = bmain->group.first; group; group = group->id.next) {
for (go = group->gobject.first; go; go = go->next) {
if ((go->ob->id.tag & LIB_TAG_DOIT) == 0)
break;
}
/* this group is entirely in this scene */
if (go == NULL) {
ListBase listb = {NULL, NULL};
for (go = group->gobject.first; go; go = go->next)
go->ob->id.newid = (ID *)go;
/* in order of sorted bases we reinsert group objects */
for (base = sce->base.first; base; base = base->next) {
if (base->object->id.newid) {
go = (GroupObject *)base->object->id.newid;
base->object->id.newid = NULL;
BLI_remlink(&group->gobject, go);
BLI_addtail(&listb, go);
}
}
/* copy the newly sorted listbase */
group->gobject = listb;
}
}
}
static void dag_scene_tag_rebuild(Scene *sce)
{
if (sce->theDag) {
sce->theDag->need_update = true;
}
}
/* free the depency graph */
static void dag_scene_free(Scene *sce)
{
if (sce->theDag) {
free_forest(sce->theDag);
MEM_freeN(sce->theDag);
sce->theDag = NULL;
}
}
/* Check whether object data needs to be evaluated before it
* might be used by others.
*
* Means that mesh object needs to have proper derivedFinal,
* curves-typed objects are to have proper curve cache.
*
* Other objects or objects which are tagged for data update are
* not considered to be in need of evaluation.
*/
static bool check_object_needs_evaluation(Object *object)
{
if (object->recalc & OB_RECALC_ALL) {
/* Object is tagged for update anyway, no need to re-tag it. */
return false;
}
if (object->type == OB_MESH) {
return object->derivedFinal == NULL;
}
else if (ELEM(object->type, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_LATTICE)) {
return object->curve_cache == NULL;
}
return false;
}
/* Check whether object data is tagged for update. */
static bool check_object_tagged_for_update(Object *object)
{
if (object->recalc & OB_RECALC_ALL) {
return true;
}
if (ELEM(object->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_LATTICE)) {
ID *data_id = object->data;
return (data_id->tag & (LIB_TAG_ID_RECALC_DATA | LIB_TAG_ID_RECALC)) != 0;
}
return false;
}
/* Flush changes from tagged objects in the scene to their
* dependencies which are not evaluated yet.
*
* This is needed to ensure all the dependencies are met
* before objects gets handled by object_handle_update(),
*
* This is needed when visible layers are changed or changing
* scene graph layout which involved usage of objects which
* aren't in the scene or weren't visible yet.
*/
static void dag_invisible_dependencies_flush(Scene *scene)
{
DagNode *root_node = scene->theDag->DagNode.first, *node;
DagNodeQueue *queue;
for (node = root_node; node != NULL; node = node->next) {
node->color = DAG_WHITE;
}
queue = queue_create(DAGQUEUEALLOC);
for (node = root_node; node != NULL; node = node->next) {
if (node->color == DAG_WHITE) {
push_stack(queue, node);
node->color = DAG_GRAY;
while (queue->count) {
DagNode *current_node = get_top_node_queue(queue);
DagAdjList *itA;
bool skip = false;
for (itA = current_node->child; itA; itA = itA->next) {
if (itA->node->color == DAG_WHITE) {
itA->node->color = DAG_GRAY;
push_stack(queue, itA->node);
skip = true;
break;
}
}
if (!skip) {
current_node = pop_queue(queue);
if (current_node->type == ID_OB) {
Object *current_object = current_node->ob;
if (check_object_needs_evaluation(current_object)) {
for (itA = current_node->child; itA; itA = itA->next) {
if (itA->node->type == ID_OB) {
Object *object = itA->node->ob;
if (check_object_tagged_for_update(object)) {
current_object->recalc |= OB_RECALC_OB | OB_RECALC_DATA;
}
}
}
}
}
node->color = DAG_BLACK;
}
}
}
}
queue_delete(queue);
}
static void dag_invisible_dependencies_check_flush(Main *bmain, Scene *scene)
{
if (DAG_id_type_tagged(bmain, ID_OB) ||
DAG_id_type_tagged(bmain, ID_ME) || /* Mesh */
DAG_id_type_tagged(bmain, ID_CU) || /* Curve */
DAG_id_type_tagged(bmain, ID_MB) || /* MetaBall */
DAG_id_type_tagged(bmain, ID_LT)) /* Lattice */
{
dag_invisible_dependencies_flush(scene);
}
}
/* sort the base list on dependency order */
static void dag_scene_build(Main *bmain, Scene *sce)
{
DagNode *node, *rootnode;
DagNodeQueue *nqueue;
DagAdjList *itA;
int time;
int skip = 0;
ListBase tempbase;
Base *base;
BLI_listbase_clear(&tempbase);
build_dag(bmain, sce, DAG_RL_ALL_BUT_DATA);
dag_check_cycle(sce->theDag);
nqueue = queue_create(DAGQUEUEALLOC);
for (node = sce->theDag->DagNode.first; node; node = node->next) {
node->color = DAG_WHITE;
}
time = 1;
rootnode = sce->theDag->DagNode.first;
rootnode->color = DAG_GRAY;
time++;
push_stack(nqueue, rootnode);
while (nqueue->count) {
skip = 0;
node = get_top_node_queue(nqueue);
itA = node->child;
while (itA != NULL) {
if (itA->node->color == DAG_WHITE) {
itA->node->DFS_dvtm = time;
itA->node->color = DAG_GRAY;
time++;
push_stack(nqueue, itA->node);
skip = 1;
break;
}
itA = itA->next;
}
if (!skip) {
if (node) {
node = pop_queue(nqueue);
if (node->ob == sce) /* we are done */
break;
node->color = DAG_BLACK;
time++;
base = sce->base.first;
while (base && base->object != node->ob)
base = base->next;
if (base) {
BLI_remlink(&sce->base, base);
BLI_addhead(&tempbase, base);
}
}
}
}
/* temporal correction for circular dependencies */
base = sce->base.first;
while (base) {
BLI_remlink(&sce->base, base);
BLI_addhead(&tempbase, base);
//if (G.debug & G_DEBUG)
printf("cyclic %s\n", base->object->id.name);
base = sce->base.first;
}
sce->base = tempbase;
queue_delete(nqueue);
/* all groups with objects in this scene gets resorted too */
scene_sort_groups(bmain, sce);
if (G.debug & G_DEBUG) {
printf("\nordered\n");
for (base = sce->base.first; base; base = base->next) {
printf(" %s\n", base->object->id.name);
}
}
/* Make sure that new dependencies which came from invisible layers
* are tagged for update (if they're needed for objects which were
* tagged for update).
*/
dag_invisible_dependencies_check_flush(bmain, sce);
}
/* clear all dependency graphs */
void DAG_relations_tag_update(Main *bmain)
{
if (DEG_depsgraph_use_legacy()) {
Scene *sce;
for (sce = bmain->scene.first; sce; sce = sce->id.next) {
dag_scene_tag_rebuild(sce);
}
}
else {
/* New dependency graph. */
DEG_relations_tag_update(bmain);
}
}
/* rebuild dependency graph only for a given scene */
void DAG_scene_relations_rebuild(Main *bmain, Scene *sce)
{
if (DEG_depsgraph_use_legacy()) {
dag_scene_free(sce);
DAG_scene_relations_update(bmain, sce);
}
else {
/* New dependency graph. */
DEG_scene_relations_rebuild(bmain, sce);
}
}
/* create dependency graph if it was cleared or didn't exist yet */
void DAG_scene_relations_update(Main *bmain, Scene *sce)
{
if (DEG_depsgraph_use_legacy()) {
if (!sce->theDag || sce->theDag->need_update)
dag_scene_build(bmain, sce);
}
else {
/* New dependency graph. */
DEG_scene_relations_update(bmain, sce);
}
}
void DAG_scene_relations_validate(Main *bmain, Scene *sce)
{
if (!DEG_depsgraph_use_legacy()) {
DEG_debug_scene_relations_validate(bmain, sce);
}
}
void DAG_scene_free(Scene *sce)
{
if (DEG_depsgraph_use_legacy()) {
if (sce->theDag) {
free_forest(sce->theDag);
MEM_freeN(sce->theDag);
sce->theDag = NULL;
}
}
else {
if (sce->depsgraph) {
DEG_graph_free(sce->depsgraph);
sce->depsgraph = NULL;
}
}
}
static void lib_id_recalc_tag(Main *bmain, ID *id)
{
id->tag |= LIB_TAG_ID_RECALC;
DAG_id_type_tag(bmain, GS(id->name));
}
static void lib_id_recalc_data_tag(Main *bmain, ID *id)
{
id->tag |= LIB_TAG_ID_RECALC_DATA;
DAG_id_type_tag(bmain, GS(id->name));
}
/* node was checked to have lasttime != curtime and is if type ID_OB */
static void flush_update_node(Main *bmain, DagNode *node, unsigned int layer, int curtime)
{
DagAdjList *itA;
Object *ob, *obc;
int oldflag;
bool changed = false;
unsigned int all_layer;
node->lasttime = curtime;
ob = node->ob;
if (ob && (ob->recalc & OB_RECALC_ALL)) {
all_layer = node->scelay;
/* got an object node that changes, now check relations */
for (itA = node->child; itA; itA = itA->next) {
all_layer |= itA->lay;
/* the relationship is visible */
if ((itA->lay & layer)) { // XXX || (itA->node->ob == obedit)
if (itA->node->type == ID_OB) {
obc = itA->node->ob;
oldflag = obc->recalc;
/* got a ob->obc relation, now check if flag needs flush */
if (ob->recalc & OB_RECALC_OB) {
if (itA->type & DAG_RL_OB_OB) {
//printf("ob %s changes ob %s\n", ob->id.name, obc->id.name);
obc->recalc |= OB_RECALC_OB;
lib_id_recalc_tag(bmain, &obc->id);
}
if (itA->type & DAG_RL_OB_DATA) {
//printf("ob %s changes obdata %s\n", ob->id.name, obc->id.name);
obc->recalc |= OB_RECALC_DATA;
lib_id_recalc_data_tag(bmain, &obc->id);
}
}
if (ob->recalc & OB_RECALC_DATA) {
if (itA->type & DAG_RL_DATA_OB) {
//printf("obdata %s changes ob %s\n", ob->id.name, obc->id.name);
obc->recalc |= OB_RECALC_OB;
lib_id_recalc_tag(bmain, &obc->id);
}
if (itA->type & DAG_RL_DATA_DATA) {
//printf("obdata %s changes obdata %s\n", ob->id.name, obc->id.name);
obc->recalc |= OB_RECALC_DATA;
lib_id_recalc_data_tag(bmain, &obc->id);
}
}
if (oldflag != obc->recalc) changed = 1;
}
}
}
/* even nicer, we can clear recalc flags... */
if ((all_layer & layer) == 0) { // XXX && (ob != obedit)) {
/* but existing displaylists or derivedmesh should be freed */
if (ob->recalc & OB_RECALC_DATA)
BKE_object_free_derived_caches(ob);
ob->recalc &= ~OB_RECALC_ALL;
}
}
/* check case where child changes and parent forcing obdata to change */
/* should be done regardless if this ob has recalc set */
/* could merge this in with loop above...? (ton) */
for (itA = node->child; itA; itA = itA->next) {
/* the relationship is visible */
if ((itA->lay & layer)) { // XXX || (itA->node->ob == obedit)
if (itA->node->type == ID_OB) {
obc = itA->node->ob;
/* child moves */
if ((obc->recalc & OB_RECALC_ALL) == OB_RECALC_OB) {
/* parent has deforming info */
if (itA->type & (DAG_RL_OB_DATA | DAG_RL_DATA_DATA)) {
// printf("parent %s changes ob %s\n", ob->id.name, obc->id.name);
obc->recalc |= OB_RECALC_DATA;
lib_id_recalc_data_tag(bmain, &obc->id);
}
}
}
}
}
/* we only go deeper if node not checked or something changed */
for (itA = node->child; itA; itA = itA->next) {
if (changed || itA->node->lasttime != curtime)
flush_update_node(bmain, itA->node, layer, curtime);
}
}
/* node was checked to have lasttime != curtime, and is of type ID_OB */
static unsigned int flush_layer_node(Scene *sce, DagNode *node, int curtime)
{
DagAdjList *itA;
node->lasttime = curtime;
node->lay = node->scelay;
for (itA = node->child; itA; itA = itA->next) {
if (itA->node->type == ID_OB) {
if (itA->node->lasttime != curtime) {
itA->lay = flush_layer_node(sce, itA->node, curtime); /* lay is only set once for each relation */
}
else {
itA->lay = itA->node->lay;
}
node->lay |= itA->lay;
}
}
return node->lay;
}
/* node was checked to have lasttime != curtime, and is of type ID_OB */
static void flush_pointcache_reset(Main *bmain, Scene *scene, DagNode *node,
int curtime, unsigned int lay, bool reset)
{
DagAdjList *itA;
Object *ob;
node->lasttime = curtime;
for (itA = node->child; itA; itA = itA->next) {
if (itA->node->type == ID_OB) {
if (itA->node->lasttime != curtime) {
ob = (Object *)(itA->node->ob);
if (reset || (ob->recalc & OB_RECALC_ALL)) {
if (BKE_ptcache_object_reset(scene, ob, PTCACHE_RESET_DEPSGRAPH)) {
/* Don't tag nodes which are on invisible layer. */
if (itA->node->lay & lay) {
ob->recalc |= OB_RECALC_DATA;
lib_id_recalc_data_tag(bmain, &ob->id);
}
}
flush_pointcache_reset(bmain, scene, itA->node, curtime, lay, true);
}
else
flush_pointcache_reset(bmain, scene, itA->node, curtime, lay, false);
}
}
}
}
/* flush layer flags to dependencies */
static void dag_scene_flush_layers(Scene *sce, int lay)
{
DagNode *node, *firstnode;
DagAdjList *itA;
Base *base;
int lasttime;
firstnode = sce->theDag->DagNode.first; /* always scene node */
for (itA = firstnode->child; itA; itA = itA->next)
itA->lay = 0;
sce->theDag->time++; /* so we know which nodes were accessed */
lasttime = sce->theDag->time;
/* update layer flags in nodes */
for (base = sce->base.first; base; base = base->next) {
node = dag_get_node(sce->theDag, base->object);
node->scelay = base->object->lay;
}
/* ensure cameras are set as if they are on a visible layer, because
* they ared still used for rendering or setting the camera view
*
* XXX, this wont work for local view / unlocked camera's */
if (sce->camera) {
node = dag_get_node(sce->theDag, sce->camera);
node->scelay |= lay;
}
#ifdef DURIAN_CAMERA_SWITCH
{
TimeMarker *m;
for (m = sce->markers.first; m; m = m->next) {
if (m->camera) {
node = dag_get_node(sce->theDag, m->camera);
node->scelay |= lay;
}
}
}
#endif
/* flush layer nodes to dependencies */
for (itA = firstnode->child; itA; itA = itA->next)
if (itA->node->lasttime != lasttime && itA->node->type == ID_OB)
flush_layer_node(sce, itA->node, lasttime);
}
static void dag_tag_renderlayers(Scene *sce, unsigned int lay)
{
if (sce->nodetree) {
bNode *node;
Base *base;
unsigned int lay_changed = 0;
for (base = sce->base.first; base; base = base->next)
if (base->lay & lay)
if (base->object->recalc)
lay_changed |= base->lay;
for (node = sce->nodetree->nodes.first; node; node = node->next) {
if (node->id == (ID *)sce) {
SceneRenderLayer *srl = BLI_findlink(&sce->r.layers, node->custom1);
if (srl && (srl->lay & lay_changed))
nodeUpdate(sce->nodetree, node);
}
}
}
}
/* flushes all recalc flags in objects down the dependency tree */
void DAG_scene_flush_update(Main *bmain, Scene *sce, unsigned int lay, const short time)
{
DagNode *firstnode;
DagAdjList *itA;
Object *ob;
int lasttime;
if (!DEG_depsgraph_use_legacy()) {
return;
}
if (sce->theDag == NULL || sce->theDag->need_update) {
printf("DAG zero... not allowed to happen!\n");
DAG_scene_relations_update(bmain, sce);
}
firstnode = sce->theDag->DagNode.first; /* always scene node */
/* first we flush the layer flags */
dag_scene_flush_layers(sce, lay);
/* then we use the relationships + layer info to flush update events */
sce->theDag->time++; /* so we know which nodes were accessed */
lasttime = sce->theDag->time;
for (itA = firstnode->child; itA; itA = itA->next)
if (itA->node->lasttime != lasttime && itA->node->type == ID_OB)
flush_update_node(bmain, itA->node, lay, lasttime);
/* if update is not due to time change, do pointcache clears */
if (!time) {
sce->theDag->time++; /* so we know which nodes were accessed */
lasttime = sce->theDag->time;
for (itA = firstnode->child; itA; itA = itA->next) {
if (itA->node->lasttime != lasttime && itA->node->type == ID_OB) {
ob = (Object *)(itA->node->ob);
if (ob->recalc & OB_RECALC_ALL) {
if (BKE_ptcache_object_reset(sce, ob, PTCACHE_RESET_DEPSGRAPH)) {
ob->recalc |= OB_RECALC_DATA;
lib_id_recalc_data_tag(bmain, &ob->id);
}
flush_pointcache_reset(bmain, sce, itA->node, lasttime,
lay, true);
}
else
flush_pointcache_reset(bmain, sce, itA->node, lasttime,
lay, false);
}
}
}
dag_tag_renderlayers(sce, lay);
}
static bool modifier_nlastrips_use_time(ListBase *strips)
{
NlaStrip *strip;
if (strips) {
for (strip = strips->first; strip; strip = strip->next) {
if (modifier_nlastrips_use_time(&strip->strips)) {
return true;
}
else if (strip->act) {
FCurve *fcu;
for (fcu = strip->act->curves.first; fcu; fcu = fcu->next) {
if (fcu->rna_path && strstr(fcu->rna_path, "modifiers["))
return true;
}
}
}
}
return false;
}
static bool object_modifiers_use_time(Object *ob)
{
ModifierData *md;
/* check if a modifier in modifier stack needs time input */
for (md = ob->modifiers.first; md; md = md->next) {
if (modifier_dependsOnTime(md))
return true;
}
/* check whether any modifiers are animated */
if (ob->adt) {
AnimData *adt = ob->adt;
NlaTrack *nlt;
FCurve *fcu;
/* action - check for F-Curves with paths containing 'modifiers[' */
if (adt->action) {
for (fcu = adt->action->curves.first; fcu; fcu = fcu->next) {
if (fcu->rna_path && strstr(fcu->rna_path, "modifiers["))
return true;
}
}
/* This here allows modifier properties to get driven and still update properly
*
* Workaround to get [#26764] (e.g. subsurf levels not updating when animated/driven)
* working, without the updating problems ([#28525] [#28690] [#28774] [#28777]) caused
* by the RNA updates cache introduced in r.38649
*/
for (fcu = adt->drivers.first; fcu; fcu = fcu->next) {
if (fcu->rna_path && strstr(fcu->rna_path, "modifiers["))
return true;
}
/* Also check NLA Strips... [#T45938] */
for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
if (modifier_nlastrips_use_time(&nlt->strips))
return true;
}
}
return false;
}
static short animdata_use_time(AnimData *adt)
{
NlaTrack *nlt;
if (adt == NULL) return 0;
/* check action - only if assigned, and it has anim curves */
if (adt->action && adt->action->curves.first)
return 1;
/* check NLA tracks + strips */
for (nlt = adt->nla_tracks.first; nlt; nlt = nlt->next) {
if (nlt->strips.first)
return 1;
}
/* If we have drivers, more likely than not, on a frame change
* they'll need updating because their owner changed
*
* This is kindof a hack to get around a whole host of problems
* involving drivers using non-object datablock data (which the
* depsgraph currently has no way of representing let alone correctly
* dependency sort+tagging). By doing this, at least we ensure that
* some commonly attempted drivers (such as scene -> current frame;
* see "Driver updates fail" thread on Bf-committers dated July 2)
* will work correctly, and that other non-object datablocks will have
* their drivers update at least on frame change.
*
* -- Aligorith, July 4 2011
*/
if (adt->drivers.first)
return 1;
return 0;
}
static void dag_object_time_update_flags(Main *bmain, Scene *scene, Object *ob)
{
if (ob->constraints.first) {
bConstraint *con;
for (con = ob->constraints.first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
if (cti) {
/* special case for camera tracking -- it doesn't use targets to define relations */
if (ELEM(cti->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_CAMERASOLVER, CONSTRAINT_TYPE_OBJECTSOLVER)) {
ob->recalc |= OB_RECALC_OB;
}
else if (cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
for (ct = targets.first; ct; ct = ct->next) {
if (ct->tar) {
ob->recalc |= OB_RECALC_OB;
break;
}
}
if (cti->flush_constraint_targets)
cti->flush_constraint_targets(con, &targets, 1);
}
}
}
}
if (ob->parent) {
/* motion path or bone child */
if (ob->parent->type == OB_CURVE || ob->parent->type == OB_ARMATURE) ob->recalc |= OB_RECALC_OB;
}
#if 0 // XXX old animation system
if (ob->nlastrips.first) {
if (ob->dup_group) {
bActionStrip *strip;
/* this case is for groups with nla, whilst nla target has no action or nla */
for (strip = ob->nlastrips.first; strip; strip = strip->next) {
if (strip->object)
strip->object->recalc |= OB_RECALC_ALL;
}
}
}
#endif // XXX old animation system
if (animdata_use_time(ob->adt)) {
ob->recalc |= OB_RECALC_OB;
ob->adt->recalc |= ADT_RECALC_ANIM;
}
if ((ob->adt) && (ob->type == OB_ARMATURE)) ob->recalc |= OB_RECALC_DATA;
if (object_modifiers_use_time(ob)) ob->recalc |= OB_RECALC_DATA;
if ((ob->pose) && (ob->pose->flag & POSE_CONSTRAINTS_TIMEDEPEND)) ob->recalc |= OB_RECALC_DATA;
// XXX: scene here may not be the scene that contains the rigidbody world affecting this!
if (ob->rigidbody_object && BKE_scene_check_rigidbody_active(scene))
ob->recalc |= OB_RECALC_OB;
{
AnimData *adt = BKE_animdata_from_id((ID *)ob->data);
Mesh *me;
Curve *cu;
Lattice *lt;
switch (ob->type) {
case OB_MESH:
me = ob->data;
if (me->key) {
if (!(ob->shapeflag & OB_SHAPE_LOCK)) {
ob->recalc |= OB_RECALC_DATA;
}
}
if (ob->particlesystem.first)
ob->recalc |= OB_RECALC_DATA;
break;
case OB_CURVE:
case OB_SURF:
cu = ob->data;
if (cu->key) {
if (!(ob->shapeflag & OB_SHAPE_LOCK)) {
ob->recalc |= OB_RECALC_DATA;
}
}
break;
case OB_FONT:
cu = ob->data;
if (BLI_listbase_is_empty(&cu->nurb) && cu->str && cu->vfont)
ob->recalc |= OB_RECALC_DATA;
break;
case OB_LATTICE:
lt = ob->data;
if (lt->key) {
if (!(ob->shapeflag & OB_SHAPE_LOCK)) {
ob->recalc |= OB_RECALC_DATA;
}
}
break;
case OB_MBALL:
if (ob->transflag & OB_DUPLI) ob->recalc |= OB_RECALC_DATA;
break;
case OB_EMPTY:
/* update animated images */
if (ob->empty_drawtype == OB_EMPTY_IMAGE && ob->data)
if (BKE_image_is_animated(ob->data))
ob->recalc |= OB_RECALC_DATA;
break;
}
if (animdata_use_time(adt)) {
ob->recalc |= OB_RECALC_DATA;
adt->recalc |= ADT_RECALC_ANIM;
}
if (ob->particlesystem.first) {
ParticleSystem *psys = ob->particlesystem.first;
for (; psys; psys = psys->next) {
if (psys_check_enabled(ob, psys)) {
ob->recalc |= OB_RECALC_DATA;
break;
}
}
}
}
if (ob->recalc & OB_RECALC_OB)
lib_id_recalc_tag(bmain, &ob->id);
if (ob->recalc & OB_RECALC_DATA)
lib_id_recalc_data_tag(bmain, &ob->id);
}
/* recursively update objects in groups, each group is done at most once */
static void dag_group_update_flags(Main *bmain, Scene *scene, Group *group, const bool do_time)
{
GroupObject *go;
if (group->id.tag & LIB_TAG_DOIT)
return;
group->id.tag |= LIB_TAG_DOIT;
for (go = group->gobject.first; go; go = go->next) {
if (do_time)
dag_object_time_update_flags(bmain, scene, go->ob);
if (go->ob->dup_group)
dag_group_update_flags(bmain, scene, go->ob->dup_group, do_time);
}
}
/* flag all objects that need recalc, for changes in time for example */
/* do_time: make this optional because undo resets objects to their animated locations without this */
void DAG_scene_update_flags(Main *bmain, Scene *scene, unsigned int lay, const bool do_time, const bool do_invisible_flush)
{
Base *base;
Object *ob;
Group *group;
GroupObject *go;
Scene *sce_iter;
BKE_main_id_tag_idcode(bmain, ID_GR, LIB_TAG_DOIT, false);
/* set ob flags where animated systems are */
for (SETLOOPER(scene, sce_iter, base)) {
ob = base->object;
if (do_time) {
/* now if DagNode were part of base, the node->lay could be checked... */
/* we do all now, since the scene_flush checks layers and clears recalc flags even */
/* NOTE: "sce_iter" not "scene" so that rigidbodies in background scenes work
* (i.e. muting + rbw availability can be checked and tagged properly) [#33970]
*/
dag_object_time_update_flags(bmain, sce_iter, ob);
}
/* recursively tag groups with LIB_TAG_DOIT, and update flags for objects */
if (ob->dup_group)
dag_group_update_flags(bmain, scene, ob->dup_group, do_time);
}
for (sce_iter = scene; sce_iter; sce_iter = sce_iter->set)
DAG_scene_flush_update(bmain, sce_iter, lay, 1);
if (do_time) {
/* test: set time flag, to disable baked systems to update */
for (SETLOOPER(scene, sce_iter, base)) {
ob = base->object;
if (ob->recalc & OB_RECALC_ALL)
ob->recalc |= OB_RECALC_TIME;
}
/* hrmf... an exception to look at once, for invisible camera object we do it over */
if (scene->camera)
dag_object_time_update_flags(bmain, scene, scene->camera);
}
/* and store the info in groupobject */
for (group = bmain->group.first; group; group = group->id.next) {
if (group->id.tag & LIB_TAG_DOIT) {
for (go = group->gobject.first; go; go = go->next) {
go->recalc = go->ob->recalc;
// printf("ob %s recalc %d\n", go->ob->id.name, go->recalc);
}
group->id.tag &= ~LIB_TAG_DOIT;
}
}
if (do_invisible_flush) {
dag_invisible_dependencies_check_flush(bmain, scene);
}
}
/* struct returned by DagSceneLayer */
typedef struct DagSceneLayer {
struct DagSceneLayer *next, *prev;
Scene *scene;
unsigned int layer;
} DagSceneLayer;
/* returns visible scenes with valid DAG */
static void dag_current_scene_layers(Main *bmain, ListBase *lb)
{
wmWindowManager *wm;
wmWindow *win;
BLI_listbase_clear(lb);
/* if we have a windowmanager, look into windows */
if ((wm = bmain->wm.first)) {
BKE_main_id_flag_listbase(&bmain->scene, LIB_TAG_DOIT, 1);
for (win = wm->windows.first; win; win = win->next) {
if (win->screen && win->screen->scene->theDag) {
Scene *scene = win->screen->scene;
DagSceneLayer *dsl;
if (scene->id.tag & LIB_TAG_DOIT) {
dsl = MEM_mallocN(sizeof(DagSceneLayer), "dag scene layer");
BLI_addtail(lb, dsl);
dsl->scene = scene;
dsl->layer = BKE_screen_visible_layers(win->screen, scene);
scene->id.tag &= ~LIB_TAG_DOIT;
}
else {
/* It is possible that multiple windows shares the same scene
* and have different layers visible.
*
* Here we deal with such cases by squashing layers bits from
* multiple windoew to the DagSceneLayer.
*
* TODO(sergey): Such a lookup could be optimized perhaps,
* however should be fine for now since we usually have only
* few open windows.
*/
for (dsl = lb->first; dsl; dsl = dsl->next) {
if (dsl->scene == scene) {
dsl->layer |= BKE_screen_visible_layers(win->screen, scene);
break;
}
}
}
}
}
}
else {
/* if not, use the first sce */
DagSceneLayer *dsl = MEM_mallocN(sizeof(DagSceneLayer), "dag scene layer");
BLI_addtail(lb, dsl);
dsl->scene = bmain->scene.first;
dsl->layer = dsl->scene->lay;
/* XXX for background mode, we should get the scene
* from somewhere, for the -S option, but it's in
* the context, how to get it here? */
}
}
static void dag_group_on_visible_update(Scene *scene, Group *group)
{
GroupObject *go;
if (group->id.tag & LIB_TAG_DOIT)
return;
group->id.tag |= LIB_TAG_DOIT;
for (go = group->gobject.first; go; go = go->next) {
if (ELEM(go->ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_LATTICE)) {
go->ob->recalc |= OB_RECALC_DATA;
go->ob->id.tag |= LIB_TAG_DOIT;
lib_id_recalc_tag(G.main, &go->ob->id);
}
if (go->ob->proxy_from) {
go->ob->recalc |= OB_RECALC_OB;
go->ob->id.tag |= LIB_TAG_DOIT;
lib_id_recalc_tag(G.main, &go->ob->id);
}
if (go->ob->dup_group)
dag_group_on_visible_update(scene, go->ob->dup_group);
}
}
void DAG_on_visible_update(Main *bmain, const bool do_time)
{
ListBase listbase;
DagSceneLayer *dsl;
if (!DEG_depsgraph_use_legacy()) {
/* Inform new dependnecy graphs about visibility changes. */
DEG_on_visible_update(bmain, do_time);
return;
}
/* get list of visible scenes and layers */
dag_current_scene_layers(bmain, &listbase);
for (dsl = listbase.first; dsl; dsl = dsl->next) {
Scene *scene = dsl->scene;
Scene *sce_iter;
Base *base;
Object *ob;
DagNode *node;
unsigned int lay = dsl->layer, oblay;
/* derivedmeshes and displists are not saved to file so need to be
* remade, tag them so they get remade in the scene update loop,
* note armature poses or object matrices are preserved and do not
* require updates, so we skip those */
for (sce_iter = scene; sce_iter; sce_iter = sce_iter->set)
dag_scene_flush_layers(sce_iter, lay);
BKE_main_id_tag_idcode(bmain, ID_GR, LIB_TAG_DOIT, false);
for (SETLOOPER(scene, sce_iter, base)) {
ob = base->object;
node = (sce_iter->theDag) ? dag_get_node(sce_iter->theDag, ob) : NULL;
oblay = (node) ? node->lay : ob->lay;
if ((oblay & lay) & ~scene->lay_updated) {
/* TODO(sergey): Why do we need armature here now but didn't need before? */
if (ELEM(ob->type, OB_MESH, OB_CURVE, OB_SURF, OB_FONT, OB_MBALL, OB_LATTICE, OB_ARMATURE)) {
ob->recalc |= OB_RECALC_DATA;
lib_id_recalc_tag(bmain, &ob->id);
}
/* This should not be needed here, but in some cases, like after a redo, we can end up with
* a wrong final matrix (see T42472).
* Quoting Sergey, this comes from BKE_object_handle_update_ex, which is calling
* BKE_object_where_is_calc_ex when it shouldn't, but that issue is not easily fixable.
*/
else {
ob->recalc |= OB_RECALC_OB;
lib_id_recalc_tag(bmain, &ob->id);
}
if (ob->proxy && (ob->proxy_group == NULL)) {
ob->proxy->recalc |= OB_RECALC_DATA;
lib_id_recalc_tag(bmain, &ob->id);
}
if (ob->dup_group)
dag_group_on_visible_update(scene, ob->dup_group);
}
}
BKE_main_id_tag_idcode(bmain, ID_GR, LIB_TAG_DOIT, false);
/* now tag update flags, to ensure deformers get calculated on redraw */
DAG_scene_update_flags(bmain, scene, lay, do_time, true);
scene->lay_updated |= lay;
}
BLI_freelistN(&listbase);
/* hack to get objects updating on layer changes */
DAG_id_type_tag(bmain, ID_OB);
/* so masks update on load */
if (bmain->mask.first) {
Mask *mask;
for (mask = bmain->mask.first; mask; mask = mask->id.next) {
DAG_id_tag_update(&mask->id, 0);
}
}
}
static void dag_id_flush_update__isDependentTexture(
void *userData, Object *UNUSED(ob), ID **idpoin, int UNUSED(cd_flag))
{
struct { ID *id; bool is_dependent; } *data = userData;
if (*idpoin && GS((*idpoin)->name) == ID_TE) {
if (data->id == (*idpoin))
data->is_dependent = 1;
}
}
static void dag_id_flush_update(Main *bmain, Scene *sce, ID *id)
{
Object *obt, *ob = NULL;
short idtype;
/* here we flush a few things before actual scene wide flush, mostly
* due to only objects and not other datablocks being in the depsgraph */
/* set flags & pointcache for object */
if (GS(id->name) == ID_OB) {
ob = (Object *)id;
BKE_ptcache_object_reset(sce, ob, PTCACHE_RESET_DEPSGRAPH);
/* So if someone tagged object recalc directly,
* id_tag_update bit-field stays relevant
*/
if (ob->recalc & OB_RECALC_ALL) {
DAG_id_type_tag(bmain, GS(id->name));
}
if (ob->recalc & OB_RECALC_DATA) {
/* all users of this ob->data should be checked */
id = ob->data;
/* no point in trying in this cases */
if (id && id->us <= 1) {
dag_editors_id_update(bmain, id);
id = NULL;
}
}
}
/* set flags & pointcache for object data */
if (id) {
idtype = GS(id->name);
if (OB_DATA_SUPPORT_ID(idtype)) {
for (obt = bmain->object.first; obt; obt = obt->id.next) {
if (!(ob && obt == ob) && obt->data == id) {
obt->recalc |= OB_RECALC_DATA;
lib_id_recalc_data_tag(bmain, &obt->id);
BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH);
}
}
}
else if (idtype == ID_VF) {
for (obt = bmain->object.first; obt; obt = obt->id.next) {
if (obt->type == OB_FONT) {
Curve *cu = obt->data;
if (ELEM((struct VFont *)id, CURVE_VFONT_ANY(cu))) {
obt->recalc |= OB_RECALC_DATA;
lib_id_recalc_data_tag(bmain, &obt->id);
}
}
}
}
/* set flags based on textures - can influence depgraph via modifiers */
if (idtype == ID_TE) {
for (obt = bmain->object.first; obt; obt = obt->id.next) {
struct { ID *id; bool is_dependent; } data;
data.id = id;
data.is_dependent = 0;
modifiers_foreachIDLink(obt, dag_id_flush_update__isDependentTexture, &data);
if (data.is_dependent) {
obt->recalc |= OB_RECALC_DATA;
lib_id_recalc_data_tag(bmain, &obt->id);
}
/* particle settings can use the texture as well */
if (obt->particlesystem.first) {
ParticleSystem *psys = obt->particlesystem.first;
MTex **mtexp, *mtex;
int a;
for (; psys; psys = psys->next) {
mtexp = psys->part->mtex;
for (a = 0; a < MAX_MTEX; a++, mtexp++) {
mtex = *mtexp;
if (mtex && mtex->tex == (Tex *)id) {
obt->recalc |= OB_RECALC_DATA;
lib_id_recalc_data_tag(bmain, &obt->id);
if (mtex->mapto & PAMAP_INIT)
psys->recalc |= PSYS_RECALC_RESET;
if (mtex->mapto & PAMAP_CHILD)
psys->recalc |= PSYS_RECALC_CHILD;
BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH);
}
}
}
}
}
}
/* set flags based on ShapeKey */
if (idtype == ID_KE) {
for (obt = bmain->object.first; obt; obt = obt->id.next) {
Key *key = BKE_key_from_object(obt);
if (!(ob && obt == ob) && ((ID *)key == id)) {
obt->flag |= (OB_RECALC_OB | OB_RECALC_DATA);
lib_id_recalc_tag(bmain, &obt->id);
lib_id_recalc_data_tag(bmain, &obt->id);
BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH);
}
}
}
/* set flags based on particle settings */
if (idtype == ID_PA) {
ParticleSystem *psys;
for (obt = bmain->object.first; obt; obt = obt->id.next)
for (psys = obt->particlesystem.first; psys; psys = psys->next)
if (&psys->part->id == id)
BKE_ptcache_object_reset(sce, obt, PTCACHE_RESET_DEPSGRAPH);
}
if (ELEM(idtype, ID_MA, ID_TE)) {
obt = sce->basact ? sce->basact->object : NULL;
if (obt && obt->mode & OB_MODE_TEXTURE_PAINT) {
BKE_texpaint_slots_refresh_object(sce, obt);
BKE_paint_proj_mesh_data_check(sce, obt, NULL, NULL, NULL, NULL);
GPU_drawobject_free(obt->derivedFinal);
}
}
if (idtype == ID_MC) {
MovieClip *clip = (MovieClip *) id;
BKE_tracking_dopesheet_tag_update(&clip->tracking);
for (obt = bmain->object.first; obt; obt = obt->id.next) {
bConstraint *con;
for (con = obt->constraints.first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
if (ELEM(cti->type, CONSTRAINT_TYPE_FOLLOWTRACK, CONSTRAINT_TYPE_CAMERASOLVER,
CONSTRAINT_TYPE_OBJECTSOLVER))
{
obt->recalc |= OB_RECALC_OB;
lib_id_recalc_tag(bmain, &obt->id);
break;
}
}
}
if (sce->nodetree) {
bNode *node;
for (node = sce->nodetree->nodes.first; node; node = node->next) {
if (node->id == id) {
nodeUpdate(sce->nodetree, node);
}
}
}
}
/* Not pretty to iterate all the nodes here, but it's as good as it
* could be with the current depsgraph design/
*/
if (idtype == ID_IM) {
FOREACH_NODETREE(bmain, ntree, parent_id) {
if (ntree->type == NTREE_SHADER) {
bNode *node;
for (node = ntree->nodes.first; node; node = node->next) {
if (node->id == id) {
lib_id_recalc_tag(bmain, &ntree->id);
break;
}
}
}
} FOREACH_NODETREE_END
}
if (idtype == ID_MSK) {
if (sce->nodetree) {
bNode *node;
for (node = sce->nodetree->nodes.first; node; node = node->next) {
if (node->id == id) {
nodeUpdate(sce->nodetree, node);
}
}
}
}
/* camera's matrix is used to orient reconstructed stuff,
* so it should happen tracking-related constraints recalculation
* when camera is changing (sergey) */
if (sce->camera && &sce->camera->id == id) {
MovieClip *clip = BKE_object_movieclip_get(sce, sce->camera, true);
if (clip)
dag_id_flush_update(bmain, sce, &clip->id);
}
/* update editors */
dag_editors_id_update(bmain, id);
}
}
void DAG_ids_flush_tagged(Main *bmain)
{
ListBase listbase;
DagSceneLayer *dsl;
ListBase *lbarray[MAX_LIBARRAY];
int a;
bool do_flush = false;
if (!DEG_depsgraph_use_legacy()) {
DEG_ids_flush_tagged(bmain);
return;
}
/* get list of visible scenes and layers */
dag_current_scene_layers(bmain, &listbase);
if (BLI_listbase_is_empty(&listbase))
return;
/* loop over all ID types */
a = set_listbasepointers(bmain, lbarray);
while (a--) {
ListBase *lb = lbarray[a];
ID *id = lb->first;
/* we tag based on first ID type character to avoid
* looping over all ID's in case there are no tags */
if (id && bmain->id_tag_update[id->name[0]]) {
for (; id; id = id->next) {
if (id->tag & (LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA)) {
for (dsl = listbase.first; dsl; dsl = dsl->next)
dag_id_flush_update(bmain, dsl->scene, id);
do_flush = true;
}
}
}
}
/* flush changes to other objects */
if (do_flush) {
for (dsl = listbase.first; dsl; dsl = dsl->next)
DAG_scene_flush_update(bmain, dsl->scene, dsl->layer, 0);
}
BLI_freelistN(&listbase);
}
void DAG_ids_check_recalc(Main *bmain, Scene *scene, bool time)
{
ListBase *lbarray[MAX_LIBARRAY];
int a;
bool updated = false;
if (!DEG_depsgraph_use_legacy()) {
DEG_ids_check_recalc(bmain, scene, time);
return;
}
/* loop over all ID types */
a = set_listbasepointers(bmain, lbarray);
while (a--) {
ListBase *lb = lbarray[a];
ID *id = lb->first;
/* we tag based on first ID type character to avoid
* looping over all ID's in case there are no tags */
if (id && bmain->id_tag_update[id->name[0]]) {
updated = true;
break;
}
}
dag_editors_scene_update(bmain, scene, (updated || time));
}
/* It is possible that scene_update_post and frame_update_post handlers
* will modify objects. The issue is that DAG_ids_clear_recalc is called
* just after callbacks, which leaves objects with recalc flags but no
* corresponding bit in ID recalc bitfield. This leads to some kind of
* regression when using ID type tag fields to check whether there objects
* to be updated internally comparing threaded DAG with legacy one.
*
* For now let's have a workaround which will preserve tag for ID_OB
* if there're objects with OB_RECALC_ALL bits. This keeps behavior
* unchanged comparing with 2.69 release.
*
* TODO(sergey): Need to get rid of such a workaround.
*
* - sergey -
*/
#define POST_UPDATE_HANDLER_WORKAROUND
void DAG_ids_clear_recalc(Main *bmain)
{
ListBase *lbarray[MAX_LIBARRAY];
bNodeTree *ntree;
int a;
#ifdef POST_UPDATE_HANDLER_WORKAROUND
bool have_updated_objects = false;
if (DAG_id_type_tagged(bmain, ID_OB)) {
ListBase listbase;
DagSceneLayer *dsl;
/* We need to check all visible scenes, otherwise resetting
* OB_ID changed flag will only work fine for first scene of
* multiple visible and all the rest will skip update.
*
* This could also lead to wrong behavior scene update handlers
* because of missing ID datablock changed flags.
*
* This is a bit of a bummer to allocate list here, but likely
* it wouldn't become too much bad because it only happens when
* objects were actually changed.
*/
dag_current_scene_layers(bmain, &listbase);
for (dsl = listbase.first; dsl; dsl = dsl->next) {
Scene *scene = dsl->scene;
DagNode *node;
for (node = scene->theDag->DagNode.first;
node != NULL && have_updated_objects == false;
node = node->next)
{
if (node->type == ID_OB) {
Object *object = (Object *) node->ob;
if (object->recalc & OB_RECALC_ALL) {
have_updated_objects = true;
break;
}
}
}
}
BLI_freelistN(&listbase);
}
#endif
/* loop over all ID types */
a = set_listbasepointers(bmain, lbarray);
while (a--) {
ListBase *lb = lbarray[a];
ID *id = lb->first;
/* we tag based on first ID type character to avoid
* looping over all ID's in case there are no tags */
if (id && bmain->id_tag_update[id->name[0]]) {
for (; id; id = id->next) {
if (id->tag & (LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA))
id->tag &= ~(LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA);
/* some ID's contain semi-datablock nodetree */
ntree = ntreeFromID(id);
if (ntree && (ntree->id.tag & (LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA)))
ntree->id.tag &= ~(LIB_TAG_ID_RECALC | LIB_TAG_ID_RECALC_DATA);
}
}
}
memset(bmain->id_tag_update, 0, sizeof(bmain->id_tag_update));
#ifdef POST_UPDATE_HANDLER_WORKAROUND
if (have_updated_objects) {
DAG_id_type_tag(bmain, ID_OB);
}
#endif
}
void DAG_id_tag_update_ex(Main *bmain, ID *id, short flag)
{
if (!DEG_depsgraph_use_legacy()) {
DEG_id_tag_update_ex(bmain, id, flag);
return;
}
if (id == NULL) return;
if (G.debug & G_DEBUG_DEPSGRAPH) {
printf("%s: id=%s flag=%d\n", __func__, id->name, flag);
}
/* tag ID for update */
if (flag) {
if (flag & OB_RECALC_OB)
lib_id_recalc_tag(bmain, id);
if (flag & (OB_RECALC_DATA | PSYS_RECALC))
lib_id_recalc_data_tag(bmain, id);
}
else
lib_id_recalc_tag(bmain, id);
/* flag is for objects and particle systems */
if (flag) {
Object *ob;
short idtype = GS(id->name);
if (idtype == ID_OB) {
/* only quick tag */
ob = (Object *)id;
ob->recalc |= (flag & OB_RECALC_ALL);
}
else if (idtype == ID_PA) {
ParticleSystem *psys;
/* this is weak still, should be done delayed as well */
for (ob = bmain->object.first; ob; ob = ob->id.next) {
for (psys = ob->particlesystem.first; psys; psys = psys->next) {
if (&psys->part->id == id) {
ob->recalc |= (flag & OB_RECALC_ALL);
psys->recalc |= (flag & PSYS_RECALC);
lib_id_recalc_tag(bmain, &ob->id);
lib_id_recalc_data_tag(bmain, &ob->id);
}
}
}
}
else {
/* disable because this is called on various ID types automatically.
* where printing warning is not useful. for now just ignore */
/* BLI_assert(!"invalid flag for this 'idtype'"); */
}
}
}
void DAG_id_tag_update(ID *id, short flag)
{
DAG_id_tag_update_ex(G.main, id, flag);
}
void DAG_id_type_tag(Main *bmain, short idtype)
{
if (idtype == ID_NT) {
/* stupid workaround so parent datablocks of nested nodetree get looped
* over when we loop over tagged datablock types */
DAG_id_type_tag(bmain, ID_MA);
DAG_id_type_tag(bmain, ID_TE);
DAG_id_type_tag(bmain, ID_LA);
DAG_id_type_tag(bmain, ID_WO);
DAG_id_type_tag(bmain, ID_SCE);
}
bmain->id_tag_update[((char *)&idtype)[0]] = 1;
}
int DAG_id_type_tagged(Main *bmain, short idtype)
{
return bmain->id_tag_update[((char *)&idtype)[0]];
}
#if 0 // UNUSED
/* recursively descends tree, each node only checked once */
/* node is checked to be of type object */
static int parent_check_node(DagNode *node, int curtime)
{
DagAdjList *itA;
node->lasttime = curtime;
if (node->color == DAG_GRAY)
return DAG_GRAY;
for (itA = node->child; itA; itA = itA->next) {
if (itA->node->type == ID_OB) {
if (itA->node->color == DAG_GRAY)
return DAG_GRAY;
/* descend if not done */
if (itA->node->lasttime != curtime) {
itA->node->color = parent_check_node(itA->node, curtime);
if (itA->node->color == DAG_GRAY)
return DAG_GRAY;
}
}
}
return DAG_WHITE;
}
#endif
/* ******************* DAG FOR ARMATURE POSE ***************** */
/* we assume its an armature with pose */
void DAG_pose_sort(Object *ob)
{
bPose *pose = ob->pose;
bPoseChannel *pchan;
bConstraint *con;
DagNode *node;
DagNode *node2, *node3;
DagNode *rootnode;
DagForest *dag;
DagNodeQueue *nqueue;
DagAdjList *itA;
ListBase tempbase;
int skip = 0;
dag = dag_init();
dag->ugly_hack_sorry = false; /* no ID structs */
rootnode = dag_add_node(dag, NULL); /* node->ob becomes NULL */
/* we add the hierarchy and the constraints */
for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
int addtoroot = 1;
node = dag_get_node(dag, pchan);
if (pchan->parent) {
node2 = dag_get_node(dag, pchan->parent);
dag_add_relation(dag, node2, node, 0, "Parent Relation");
addtoroot = 0;
}
for (con = pchan->constraints.first; con; con = con->next) {
const bConstraintTypeInfo *cti = BKE_constraint_typeinfo_get(con);
ListBase targets = {NULL, NULL};
bConstraintTarget *ct;
if (cti && cti->get_constraint_targets) {
cti->get_constraint_targets(con, &targets);
for (ct = targets.first; ct; ct = ct->next) {
if (ct->tar == ob && ct->subtarget[0]) {
bPoseChannel *target = BKE_pose_channel_find_name(ob->pose, ct->subtarget);
if (target) {
node2 = dag_get_node(dag, target);
dag_add_relation(dag, node2, node, 0, "Pose Constraint");
if (con->type == CONSTRAINT_TYPE_KINEMATIC) {
bKinematicConstraint *data = (bKinematicConstraint *)con->data;
bPoseChannel *parchan;
int segcount = 0;
/* exclude tip from chain? */
if (!(data->flag & CONSTRAINT_IK_TIP))
parchan = pchan->parent;
else
parchan = pchan;
/* Walk to the chain's root */
while (parchan) {
node3 = dag_get_node(dag, parchan);
dag_add_relation(dag, node2, node3, 0, "IK Constraint");
segcount++;
if (segcount == data->rootbone || segcount > 255) break; /* 255 is weak */
parchan = parchan->parent;
}
}
}
}
}
if (cti->flush_constraint_targets)
cti->flush_constraint_targets(con, &targets, 1);
}
}
if (addtoroot == 1) {
dag_add_relation(dag, rootnode, node, 0, "Root Bone Relation");
}
}
dag_check_cycle(dag);
/* now we try to sort... */
BLI_listbase_clear(&tempbase);
nqueue = queue_create(DAGQUEUEALLOC);
/* tag nodes unchecked */
for (node = dag->DagNode.first; node; node = node->next)
node->color = DAG_WHITE;
rootnode->color = DAG_GRAY;
push_stack(nqueue, rootnode);
while (nqueue->count) {
skip = 0;
node = get_top_node_queue(nqueue);
itA = node->child;
while (itA != NULL) {
if (itA->node->color == DAG_WHITE) {
itA->node->color = DAG_GRAY;
push_stack(nqueue, itA->node);
skip = 1;
break;
}
itA = itA->next;
}
if (!skip) {
if (node) {
node = pop_queue(nqueue);
if (node->ob == NULL) /* we are done */
break;
node->color = DAG_BLACK;
/* put node in new list */
BLI_remlink(&pose->chanbase, node->ob);
BLI_addhead(&tempbase, node->ob);
}
}
}
/* temporal correction for circular dependencies */
while (pose->chanbase.first) {
pchan = pose->chanbase.first;
BLI_remlink(&pose->chanbase, pchan);
BLI_addhead(&tempbase, pchan);
printf("cyclic %s\n", pchan->name);
}
pose->chanbase = tempbase;
queue_delete(nqueue);
// printf("\nordered\n");
// for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
// printf(" %s\n", pchan->name);
// }
free_forest(dag);
MEM_freeN(dag);
}
/* ************************ DAG FOR THREADED UPDATE ********************* */
/* Initialize run-time data in the graph needed for traversing it
* from multiple threads and start threaded tree traversal by adding
* the root node to the queue.
*
* This will mark DAG nodes as object/non-object and will calculate
* num_pending_parents of nodes (which is how many non-updated parents node
* have, which helps a lot checking whether node could be scheduled
* already or not).
*/
void DAG_threaded_update_begin(Scene *scene,
void (*func)(void *node, void *user_data),
void *user_data)
{
DagNode *node;
/* We reset num_pending_parents to zero first and tag node as not scheduled yet... */
for (node = scene->theDag->DagNode.first; node; node = node->next) {
node->num_pending_parents = 0;
node->scheduled = false;
}
/* ... and then iterate over all the nodes and
* increase num_pending_parents for node childs.
*/
for (node = scene->theDag->DagNode.first; node; node = node->next) {
DagAdjList *itA;
for (itA = node->child; itA; itA = itA->next) {
if (itA->node != node) {
itA->node->num_pending_parents++;
}
}
}
/* Add root nodes to the queue. */
BLI_spin_lock(&threaded_update_lock);
for (node = scene->theDag->DagNode.first; node; node = node->next) {
if (node->num_pending_parents == 0) {
node->scheduled = true;
func(node, user_data);
}
}
BLI_spin_unlock(&threaded_update_lock);
}
/* This function is called when handling node is done.
*
* This function updates num_pending_parents for all childs and
* schedules them if they're ready.
*/
void DAG_threaded_update_handle_node_updated(void *node_v,
void (*func)(void *node, void *user_data),
void *user_data)
{
DagNode *node = node_v;
DagAdjList *itA;
for (itA = node->child; itA; itA = itA->next) {
DagNode *child_node = itA->node;
if (child_node != node) {
atomic_sub_uint32(&child_node->num_pending_parents, 1);
if (child_node->num_pending_parents == 0) {
bool need_schedule;
BLI_spin_lock(&threaded_update_lock);
need_schedule = child_node->scheduled == false;
child_node->scheduled = true;
BLI_spin_unlock(&threaded_update_lock);
if (need_schedule) {
func(child_node, user_data);
}
}
}
}
}
/* ************************ DAG DEBUGGING ********************* */
void DAG_print_dependencies(Main *bmain, Scene *scene, Object *ob)
{
/* utility for debugging dependencies */
dag_print_dependencies = 1;
if (ob && (ob->mode & OB_MODE_POSE)) {
printf("\nDEPENDENCY RELATIONS for %s\n\n", ob->id.name + 2);
DAG_pose_sort(ob);
}
else {
printf("\nDEPENDENCY RELATIONS for %s\n\n", scene->id.name + 2);
DAG_scene_relations_rebuild(bmain, scene);
}
dag_print_dependencies = 0;
}
/* ************************ DAG querying ********************* */
/* Will return Object ID if node represents Object,
* and will return NULL otherwise.
*/
Object *DAG_get_node_object(void *node_v)
{
DagNode *node = node_v;
if (node->type == ID_OB) {
return node->ob;
}
return NULL;
}
/* Returns node name, used for debug output only, atm. */
const char *DAG_get_node_name(Scene *scene, void *node_v)
{
DagNode *node = node_v;
return dag_node_name(scene->theDag, node);
}
short DAG_get_eval_flags_for_object(Scene *scene, void *object)
{
DagNode *node;
if (!DEG_depsgraph_use_legacy()) {
return DEG_get_eval_flags_for_id(scene->depsgraph, (ID *)object);
}
if (scene->theDag == NULL) {
/* Happens when converting objects to mesh from a python script
* after modifying scene graph.
*
* Currently harmless because it's only called for temporary
* objects which are out of the DAG anyway.
*/
return 0;
}
node = dag_find_node(scene->theDag, object);
if (node) {
return node->eval_flags;
}
else {
/* Happens when external render engine exports temporary objects
* which are not in the DAG.
*/
/* TODO(sergey): Doublecheck objects with Curve Deform exports all fine. */
/* TODO(sergey): Weak but currently we can't really access proper DAG from
* the modifiers stack. This is because in most cases modifier is to use
* the foreground scene, but to access evaluation flags we need to know
* active background scene, which we don't know.
*/
if (scene->set) {
return DAG_get_eval_flags_for_object(scene->set, object);
}
return 0;
}
}
bool DAG_is_acyclic(Scene *scene)
{
return scene->theDag->is_acyclic;
}
#else
/* *********************************************************************
* Stubs to avoid linking issues and make sure legacy crap is not used *
* *********************************************************************
*/
DagNodeQueue *queue_create(int UNUSED(slots))
{
BLI_assert(!"Should not be used with new dependnecy graph");
return NULL;
}
void queue_raz(DagNodeQueue *UNUSED(queue))
{
BLI_assert(!"Should not be used with new dependnecy graph");
}
void queue_delete(DagNodeQueue *UNUSED(queue))
{
BLI_assert(!"Should not be used with new dependnecy graph");
}
void push_queue(DagNodeQueue *UNUSED(queue), DagNode *UNUSED(node))
{
BLI_assert(!"Should not be used with new dependnecy graph");
}
void push_stack(DagNodeQueue *UNUSED(queue), DagNode *UNUSED(node))
{
BLI_assert(!"Should not be used with new dependnecy graph");
}
DagNode *pop_queue(DagNodeQueue *UNUSED(queue))
{
BLI_assert(!"Should not be used with new dependnecy graph");
return NULL;
}
DagNode *get_top_node_queue(DagNodeQueue *UNUSED(queue))
{
BLI_assert(!"Should not be used with new dependnecy graph");
return NULL;
}
DagForest *dag_init(void)
{
BLI_assert(!"Should not be used with new dependnecy graph");
return NULL;
}
DagForest *build_dag(Main *UNUSED(bmain),
Scene *UNUSED(sce),
short UNUSED(mask))
{
BLI_assert(!"Should not be used with new dependnecy graph");
return NULL;
}
void free_forest(DagForest *UNUSED(Dag))
{
BLI_assert(!"Should not be used with new dependnecy graph");
}
DagNode *dag_find_node(DagForest *UNUSED(forest), void *UNUSED(fob))
{
BLI_assert(!"Should not be used with new dependnecy graph");
return NULL;
}
DagNode *dag_add_node(DagForest *UNUSED(forest), void *UNUSED(fob))
{
BLI_assert(!"Should not be used with new dependnecy graph");
return NULL;
}
DagNode *dag_get_node(DagForest *UNUSED(forest), void *UNUSED(fob))
{
BLI_assert(!"Should not be used with new dependnecy graph");
return NULL;
}
DagNode *dag_get_sub_node(DagForest *UNUSED(forest), void *UNUSED(fob))
{
BLI_assert(!"Should not be used with new dependnecy graph");
return NULL;
}
void dag_add_relation(DagForest *UNUSED(forest),
DagNode *UNUSED(fob1),
DagNode *UNUSED(fob2),
short UNUSED(rel),
const char *UNUSED(name))
{
BLI_assert(!"Should not be used with new dependnecy graph");
}
/* debug test functions */
void graph_print_queue(DagNodeQueue *UNUSED(nqueue))
{
BLI_assert(!"Should not be used with new dependnecy graph");
}
void graph_print_queue_dist(DagNodeQueue *UNUSED(nqueue))
{
BLI_assert(!"Should not be used with new dependnecy graph");
}
void graph_print_adj_list(DagForest *UNUSED(dag))
{
BLI_assert(!"Should not be used with new dependnecy graph");
}
void DAG_scene_flush_update(Main *UNUSED(bmain),
Scene *UNUSED(sce),
unsigned int UNUSED(lay),
const short UNUSED(time))
{
BLI_assert(!"Should not be used with new dependnecy graph");
}
void DAG_scene_update_flags(Main *UNUSED(bmain),
Scene *UNUSED(scene),
unsigned int UNUSED(lay),
const bool UNUSED(do_time),
const bool UNUSED(do_invisible_flush))
{
BLI_assert(!"Should not be used with new dependnecy graph");
}
/* ******************* DAG FOR ARMATURE POSE ***************** */
void DAG_pose_sort(Object *UNUSED(ob))
{
BLI_assert(!"Should not be used with new dependnecy graph");
}
/* ************************ DAG FOR THREADED UPDATE ********************* */
void DAG_threaded_update_begin(Scene *UNUSED(scene),
void (*func)(void *node, void *user_data),
void *UNUSED(user_data))
{
BLI_assert(!"Should not be used with new dependnecy graph");
(void)func;
}
void DAG_threaded_update_handle_node_updated(void *UNUSED(node_v),
void (*func)(void *node, void *user_data),
void *UNUSED(user_data))
{
BLI_assert(!"Should not be used with new dependnecy graph");
(void)func;
}
/* ************************ DAG querying ********************* */
Object *DAG_get_node_object(void *UNUSED(node_v))
{
BLI_assert(!"Should not be used with new dependnecy graph");
return NULL;
}
const char *DAG_get_node_name(Scene *UNUSED(scene), void *UNUSED(node_v))
{
BLI_assert(!"Should not be used with new dependnecy graph");
return "INVALID";
}
bool DAG_is_acyclic(Scene *UNUSED(scene))
{
BLI_assert(!"Should not be used with new dependnecy graph");
return false;
}
/* ************************************
* This functions are to be supported *
* ************************************
*/
void DAG_init(void)
{
DEG_register_node_types();
}
void DAG_exit(void)
{
DEG_free_node_types();
}
/* ************************ API *********************** */
void DAG_editors_update_cb(DEG_EditorUpdateIDCb id_func,
DEG_EditorUpdateSceneCb scene_func,
DEG_EditorUpdateScenePreCb scene_func_pre)
{
DEG_editors_set_update_cb(id_func, scene_func, scene_func_pre);
}
void DAG_editors_update_pre(Main *bmain, Scene *scene, bool time)
{
DEG_editors_update_pre(bmain, scene, time);
}
/* Tag all relations for update. */
void DAG_relations_tag_update(Main *bmain)
{
DEG_relations_tag_update(bmain);
}
/* Rebuild dependency graph only for a given scene. */
void DAG_scene_relations_rebuild(Main *bmain, Scene *scene)
{
DEG_scene_relations_rebuild(bmain, scene);
}
/* Create dependency graph if it was cleared or didn't exist yet. */
void DAG_scene_relations_update(Main *bmain, Scene *scene)
{
DEG_scene_relations_update(bmain, scene);
}
void DAG_scene_relations_validate(Main *bmain, Scene *scene)
{
DEG_debug_scene_relations_validate(bmain, scene);
}
void DAG_scene_free(Scene *scene)
{
DEG_scene_graph_free(scene);
}
void DAG_on_visible_update(Main *bmain, const bool do_time)
{
DEG_on_visible_update(bmain, do_time);
}
void DAG_ids_check_recalc(Main *bmain, Scene *scene, bool time)
{
DEG_ids_check_recalc(bmain, scene, time);
}
void DAG_id_tag_update(ID *id, short flag)
{
DEG_id_tag_update_ex(G.main, id, flag);
}
void DAG_id_tag_update_ex(Main *bmain, ID *id, short flag)
{
DEG_id_tag_update_ex(bmain, id, flag);
}
void DAG_id_type_tag(Main *bmain, short idtype)
{
DEG_id_type_tag(bmain, idtype);
}
int DAG_id_type_tagged(Main *bmain, short idtype)
{
return DEG_id_type_tagged(bmain, idtype);
}
void DAG_ids_clear_recalc(Main *bmain)
{
DEG_ids_clear_recalc(bmain);
}
short DAG_get_eval_flags_for_object(Scene *scene, void *object)
{
return DEG_get_eval_flags_for_id(scene->depsgraph, (ID *)object);
}
void DAG_ids_flush_tagged(Main *bmain)
{
DEG_ids_flush_tagged(bmain);
}
/* ************************ DAG DEBUGGING ********************* */
void DAG_print_dependencies(Main *UNUSED(bmain),
Scene *scene,
Object *UNUSED(ob))
{
DEG_debug_graphviz(scene->depsgraph, stdout, "Depsgraph", false);
}
#endif