Add Chromium-only Blender WebEngine parity work

This commit is contained in:
mes123456
2026-08-12 04:47:48 -04:00
commit 9fd26010f6
18225 changed files with 11622124 additions and 0 deletions

View File

@@ -0,0 +1,250 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/builder/deg_builder.h"
#include <cstring>
#include "DNA_ID.h"
#include "DNA_armature_types.h"
#include "DNA_layer_types.h"
#include "DNA_modifier_types.h"
#include "DNA_object_types.h"
#include "BLI_string.h"
#include "BKE_action.hh"
#include "BKE_collection.hh"
#include "BKE_lib_id.hh"
#include "RNA_prototypes.hh"
#include "intern/builder/deg_builder_cache.h"
#include "intern/builder/deg_builder_remove_noop.h"
#include "intern/depsgraph.hh"
#include "intern/depsgraph_tag.hh"
#include "intern/depsgraph_type.hh"
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/eval/deg_eval_visibility.h"
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_component.hh"
#include "intern/node/deg_node_id.hh"
#include "DEG_depsgraph.hh"
namespace blender::deg {
bool deg_check_id_in_depsgraph(const Depsgraph *graph, ID *id_orig)
{
IDNode *id_node = graph->find_id_node(id_orig);
return id_node != nullptr;
}
bool deg_check_base_in_depsgraph(const Depsgraph *graph, Base *base)
{
Object *object_orig = base->base_orig->object;
IDNode *id_node = graph->find_id_node(&object_orig->id);
if (id_node == nullptr) {
return false;
}
return id_node->has_base;
}
/* -------------------------------------------------------------------- */
/** \name Base Class for Builders
* \{ */
DepsgraphBuilder::DepsgraphBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache)
: bmain_(bmain), graph_(graph), cache_(cache)
{
}
bool DepsgraphBuilder::need_pull_base_into_graph(const Base *base)
{
/* Simple check: enabled bases are always part of dependency graph. */
const int base_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? BASE_ENABLED_VIEWPORT :
BASE_ENABLED_RENDER;
if (!graph_->use_visibility_optimization || (base->flag & base_flag)) {
return true;
}
/* More involved check: since we don't support dynamic changes in dependency graph topology and
* all visible objects are to be part of dependency graph, we pull all objects which has animated
* visibility. */
return is_object_visibility_animated(base->object);
}
bool DepsgraphBuilder::is_object_visibility_animated(const Object *object)
{
AnimatedPropertyID property_id;
if (graph_->mode == DAG_EVAL_VIEWPORT) {
property_id = AnimatedPropertyID(&object->id, RNA_Object, "hide_viewport");
}
else if (graph_->mode == DAG_EVAL_RENDER) {
property_id = AnimatedPropertyID(&object->id, RNA_Object, "hide_render");
}
else {
BLI_assert_msg(0, "Unknown evaluation mode.");
return false;
}
return cache_->isPropertyAnimated(&object->id, property_id);
}
bool DepsgraphBuilder::is_modifier_visibility_animated(const Object *object,
const ModifierData *modifier)
{
AnimatedPropertyID property_id;
if (graph_->mode == DAG_EVAL_VIEWPORT) {
property_id = AnimatedPropertyID(&object->id, RNA_Modifier, (void *)modifier, "show_viewport");
}
else if (graph_->mode == DAG_EVAL_RENDER) {
property_id = AnimatedPropertyID(&object->id, RNA_Modifier, (void *)modifier, "show_render");
}
else {
BLI_assert_msg(0, "Unknown evaluation mode.");
return false;
}
return cache_->isPropertyAnimated(&object->id, property_id);
}
bool DepsgraphBuilder::check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan)
{
BLI_assert(object->type == OB_ARMATURE);
bArmature *armature = id_cast<bArmature *>(object->data);
if (pchan == nullptr) {
return false;
}
const Bone *bone = pchan->bone_get(*armature);
if (bone == nullptr) {
return false;
}
/* We don't really care whether segments are higher than 1 due to static user input (as in,
* rigger entered value like 3 manually), or due to animation. In either way we need to create
* special evaluation. */
if (bone->segments > 1) {
return true;
}
AnimatedPropertyID property_id(
&armature->id, RNA_Bone, const_cast<Bone *>(bone), "bbone_segments");
/* Check both Object and Armature animation data, because drivers modifying Armature
* state could easily be created in the Object AnimData. */
return cache_->isPropertyAnimated(&object->id, property_id) ||
cache_->isPropertyAnimated(&armature->id, property_id);
}
bool DepsgraphBuilder::check_pchan_has_bbone_segments(const Object *object,
const bPoseChannel *pchan)
{
return check_pchan_has_bbone(object, pchan);
}
bool DepsgraphBuilder::check_pchan_has_bbone_segments(const Object *object, const char *bone_name)
{
const bPoseChannel *pchan = BKE_pose_channel_find_name(object->pose, bone_name);
return check_pchan_has_bbone_segments(object, pchan);
}
const char *DepsgraphBuilder::get_rna_path_relative_to_scene_camera(const Scene *scene,
const PointerRNA &target_prop,
const char *rna_path)
{
if (rna_path == nullptr || target_prop.data != scene || target_prop.type != RNA_Scene ||
!BLI_str_startswith(rna_path, "camera"))
{
return nullptr;
}
/* Return the part of the path relative to the camera. */
switch (rna_path[6]) {
case '.':
return rna_path + 7;
case '[':
return rna_path + 6;
default:
return nullptr;
}
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Builder Finalizer.
* \{ */
void deg_graph_build_finalize(Main *bmain, Depsgraph *graph)
{
deg_graph_flush_visibility_flags(graph);
deg_graph_remove_unused_noops(graph);
/* Re-tag IDs for update if it was tagged before the relations
* update tag. */
for (IDNode *id_node : graph->id_nodes) {
const ID_Type id_type = id_node->id_type;
ID *id_orig = id_node->id_orig;
id_node->finalize_build(graph);
int flag = 0;
/* Tag rebuild if special evaluation flags changed. */
if (id_node->eval_flags != id_node->previous_eval_flags) {
flag |= ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY;
}
/* Tag rebuild if the custom data mask changed. */
if (id_node->customdata_masks != id_node->previous_customdata_masks) {
flag |= ID_RECALC_GEOMETRY;
}
const bool is_expanded = deg_eval_copy_is_expanded(id_node->id_cow);
if (!is_expanded) {
flag |= ID_RECALC_SYNC_TO_EVAL;
/* This means ID is being added to the dependency graph first
* time, which is similar to "ob-visible-change" */
if (id_type == ID_OB) {
flag |= ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY;
}
if (id_type == ID_NT) {
flag |= ID_RECALC_NTREE_OUTPUT;
}
}
else {
if (id_type == ID_GR) {
/* Collection content might have changed (children collection might have been added or
* removed from the graph based on their inclusion and visibility flags). */
BKE_collection_object_cache_free(
nullptr, reinterpret_cast<Collection *>(id_node->id_cow), LIB_ID_CREATE_NO_DEG_TAG);
}
else if (id_type == ID_SCE) {
/* During undo the sequence strips might obtain a new session ID, which will disallow the
* audio handles to be re-used. Tag for the audio and sequence update to ensure the audio
* handles are open.
* NOTE: This is not something that should be required, and perhaps indicates a weakness in
* design somewhere else. For the cause of the problem check #117760. */
flag |= ID_RECALC_AUDIO | ID_RECALC_SEQUENCER_STRIPS;
}
}
/* Restore recalc flags from original ID, which could possibly contain recalc flags set by
* an operator and then were carried on by the undo system.
*
* Only do it for active dependency graph, because otherwise modifications to the original
* objects might keep affecting the render pipeline. For example, when a Python script is
* executed in headless mode it will tag original objects for recalculation, and the flag
* will never be reset to 0 because there is no active dependency graph (since the
* DEG_ids_clear_recalc() only clears original ID recalc flags for the active depsgraph).
*
* A bit of a safety is to also consider the accumulated recalc flags from the original
* data-block for the first evaluation of the data-block within an inactive graph. */
if (graph->is_active || !is_expanded) {
flag |= id_orig->recalc;
}
if (flag != 0) {
graph_id_tag_update(bmain, graph, id_node->id_orig, flag, DEG_UPDATE_SOURCE_RELATIONS);
}
}
}
/** \} */
} // namespace blender::deg

View File

@@ -0,0 +1,63 @@
/* SPDX-FileCopyrightText: 2016 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
namespace blender {
struct Base;
struct ID;
struct Main;
struct ModifierData;
struct Object;
struct PointerRNA;
struct Scene;
struct bPoseChannel;
namespace deg {
struct Depsgraph;
class DepsgraphBuilderCache;
class DepsgraphBuilder {
public:
virtual ~DepsgraphBuilder() = default;
virtual bool need_pull_base_into_graph(const Base *base);
virtual bool is_object_visibility_animated(const Object *object);
virtual bool is_modifier_visibility_animated(const Object *object, const ModifierData *modifier);
virtual bool check_pchan_has_bbone(const Object *object, const bPoseChannel *pchan);
virtual bool check_pchan_has_bbone_segments(const Object *object, const bPoseChannel *pchan);
virtual bool check_pchan_has_bbone_segments(const Object *object, const char *bone_name);
/**
* If `target_prop` + `rna_path` uses indirection via the `scene.camera` pointer, returns
* the sub-string of `rna_path` relative to the camera; otherwise returns nullptr.
*/
static const char *get_rna_path_relative_to_scene_camera(const Scene *scene,
const PointerRNA &target_prop,
const char *rna_path);
protected:
/* NOTE: The builder does NOT take ownership over any of those resources. */
DepsgraphBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache);
/* State which never changes, same for the whole builder time. */
Main *bmain_;
Depsgraph *graph_;
DepsgraphBuilderCache *cache_;
};
bool deg_check_id_in_depsgraph(const Depsgraph *graph, ID *id_orig);
bool deg_check_base_in_depsgraph(const Depsgraph *graph, Base *base);
void deg_graph_build_finalize(Main *bmain, Depsgraph *graph);
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,148 @@
/* SPDX-FileCopyrightText: 2018 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/builder/deg_builder_cache.h"
#include "DNA_anim_types.h"
#include "BKE_anim_data.hh"
#include "RNA_access.hh"
#include "RNA_path.hh"
namespace blender::deg {
/* Animated property storage. */
AnimatedPropertyID::AnimatedPropertyID() : data(nullptr), property_rna(nullptr) {}
AnimatedPropertyID::AnimatedPropertyID(const PointerRNA *pointer_rna,
const PropertyRNA *property_rna)
: AnimatedPropertyID(*pointer_rna, property_rna)
{
}
AnimatedPropertyID::AnimatedPropertyID(const PointerRNA &pointer_rna,
const PropertyRNA *property_rna)
: data(pointer_rna.data), property_rna(property_rna)
{
}
AnimatedPropertyID::AnimatedPropertyID(const ID *id, StructRNA *type, const char *property_name)
: data(id)
{
property_rna = RNA_struct_type_find_property(type, property_name);
}
AnimatedPropertyID::AnimatedPropertyID(const ID * /*id*/,
StructRNA *type,
void *data,
const char *property_name)
: data(data)
{
property_rna = RNA_struct_type_find_property(type, property_name);
}
bool operator==(const AnimatedPropertyID &a, const AnimatedPropertyID &b)
{
return a.data == b.data && a.property_rna == b.property_rna;
}
uint64_t AnimatedPropertyID::hash() const
{
uintptr_t ptr1 = uintptr_t(data);
uintptr_t ptr2 = uintptr_t(property_rna);
return uint64_t(((ptr1 >> 4) * 33) ^ (ptr2 >> 4));
}
AnimatedPropertyStorage::AnimatedPropertyStorage() : is_fully_initialized(false) {}
void AnimatedPropertyStorage::initializeFromID(DepsgraphBuilderCache *builder_cache, const ID *id)
{
PointerRNA own_pointer_rna = RNA_id_pointer_create(const_cast<ID *>(id));
BKE_fcurves_id_cb(const_cast<ID *>(id), [&](ID * /*id*/, FCurve *fcurve) {
if (fcurve->rna_path == nullptr || fcurve->rna_path[0] == '\0') {
return;
}
/* Resolve property. */
PointerRNA pointer_rna;
PropertyRNA *property_rna = nullptr;
if (!RNA_path_resolve_property(
&own_pointer_rna, fcurve->rna_path, &pointer_rna, &property_rna))
{
return;
}
/* Get storage for the ID.
* This is needed to deal with cases when nested datablock is animated by its parent. */
AnimatedPropertyStorage *animated_property_storage = this;
if (pointer_rna.owner_id != own_pointer_rna.owner_id) {
animated_property_storage = builder_cache->ensureAnimatedPropertyStorage(
pointer_rna.owner_id);
}
/* Set the property as animated. */
animated_property_storage->tagPropertyAsAnimated(&pointer_rna, property_rna);
});
}
void AnimatedPropertyStorage::tagPropertyAsAnimated(const AnimatedPropertyID &property_id)
{
animated_objects_set.add(property_id.data);
animated_properties_set.add(property_id);
}
void AnimatedPropertyStorage::tagPropertyAsAnimated(const PointerRNA *pointer_rna,
const PropertyRNA *property_rna)
{
tagPropertyAsAnimated(AnimatedPropertyID(pointer_rna, property_rna));
}
bool AnimatedPropertyStorage::isPropertyAnimated(const AnimatedPropertyID &property_id)
{
return animated_properties_set.contains(property_id);
}
bool AnimatedPropertyStorage::isPropertyAnimated(const PointerRNA *pointer_rna,
const PropertyRNA *property_rna)
{
return isPropertyAnimated(AnimatedPropertyID(pointer_rna, property_rna));
}
bool AnimatedPropertyStorage::isAnyPropertyAnimated(const PointerRNA *pointer_rna)
{
return animated_objects_set.contains(pointer_rna->data);
}
/* Builder cache itself. */
DepsgraphBuilderCache::~DepsgraphBuilderCache()
{
for (AnimatedPropertyStorage *animated_property_storage :
animated_property_storage_map_.values())
{
delete animated_property_storage;
}
}
AnimatedPropertyStorage *DepsgraphBuilderCache::ensureAnimatedPropertyStorage(const ID *id)
{
return animated_property_storage_map_.lookup_or_add_cb(
id, []() { return new AnimatedPropertyStorage(); });
}
AnimatedPropertyStorage *DepsgraphBuilderCache::ensureInitializedAnimatedPropertyStorage(
const ID *id)
{
AnimatedPropertyStorage *animated_property_storage = ensureAnimatedPropertyStorage(id);
if (!animated_property_storage->is_fully_initialized) {
animated_property_storage->initializeFromID(this, id);
animated_property_storage->is_fully_initialized = true;
}
return animated_property_storage;
}
} // namespace blender::deg

View File

@@ -0,0 +1,109 @@
/* SPDX-FileCopyrightText: 2018 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "MEM_guardedalloc.h"
#include "RNA_types.hh"
#include "BLI_map.hh"
#include "BLI_set.hh"
namespace blender {
struct ID;
struct PointerRNA;
struct PropertyRNA;
struct StructRNA;
namespace deg {
class DepsgraphBuilderCache;
/* Identifier for animated property. */
class AnimatedPropertyID {
public:
AnimatedPropertyID();
AnimatedPropertyID(const PointerRNA *pointer_rna, const PropertyRNA *property_rna);
AnimatedPropertyID(const PointerRNA &pointer_rna, const PropertyRNA *property_rna);
AnimatedPropertyID(const ID *id, StructRNA *type, const char *property_name);
AnimatedPropertyID(const ID *id, StructRNA *type, void *data, const char *property_name);
uint64_t hash() const;
friend bool operator==(const AnimatedPropertyID &a, const AnimatedPropertyID &b);
/* Corresponds to PointerRNA.data. */
const void *data;
const PropertyRNA *property_rna;
MEM_CXX_CLASS_ALLOC_FUNCS("AnimatedPropertyID");
};
class AnimatedPropertyStorage {
public:
AnimatedPropertyStorage();
void initializeFromID(DepsgraphBuilderCache *builder_cache, const ID *id);
void tagPropertyAsAnimated(const AnimatedPropertyID &property_id);
void tagPropertyAsAnimated(const PointerRNA *pointer_rna, const PropertyRNA *property_rna);
bool isPropertyAnimated(const AnimatedPropertyID &property_id);
bool isPropertyAnimated(const PointerRNA *pointer_rna, const PropertyRNA *property_rna);
bool isAnyPropertyAnimated(const PointerRNA *pointer_rna);
/* The storage is fully initialized from all F-Curves from corresponding ID. */
bool is_fully_initialized;
/* indexed by PointerRNA.data. */
Set<const void *> animated_objects_set;
Set<AnimatedPropertyID> animated_properties_set;
MEM_CXX_CLASS_ALLOC_FUNCS("AnimatedPropertyStorage");
};
/* Cached data which can be re-used by multiple builders. */
class DepsgraphBuilderCache {
public:
~DepsgraphBuilderCache();
/* Makes sure storage for animated properties exists and initialized for the given ID. */
AnimatedPropertyStorage *ensureAnimatedPropertyStorage(const ID *id);
AnimatedPropertyStorage *ensureInitializedAnimatedPropertyStorage(const ID *id);
/* Shortcuts to go through ensureInitializedAnimatedPropertyStorage and its
* isPropertyAnimated.
*
* NOTE: Avoid using for multiple subsequent lookups, query for the storage once, and then query
* the storage.
*
* TODO(sergey): Technically, this makes this class something else than just a cache, but what is
* the better name? */
template<typename... Args> bool isPropertyAnimated(const ID *id, Args... args)
{
AnimatedPropertyStorage *animated_property_storage = ensureInitializedAnimatedPropertyStorage(
id);
return animated_property_storage->isPropertyAnimated(args...);
}
bool isAnyPropertyAnimated(const PointerRNA *ptr)
{
AnimatedPropertyStorage *animated_property_storage = ensureInitializedAnimatedPropertyStorage(
ptr->owner_id);
return animated_property_storage->isAnyPropertyAnimated(ptr);
}
Map<const ID *, AnimatedPropertyStorage *> animated_property_storage_map_;
MEM_CXX_CLASS_ALLOC_FUNCS("DepsgraphBuilderCache");
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,221 @@
/* SPDX-FileCopyrightText: 2015 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/builder/deg_builder_cycle.h"
// TOO(sergey): Use some wrappers over those?
#include <cstdio>
#include <cstdlib>
#include "BLI_stack.hh"
#include "CLG_log.h"
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_component.hh"
#include "intern/node/deg_node_operation.hh"
#include "intern/depsgraph.hh"
#include "intern/depsgraph_relation.hh"
namespace blender {
static CLG_LogRef LOG = {"depsgraph"};
namespace deg {
namespace {
enum eCyclicCheckVisitedState {
/* Not is not visited at all during traversal. */
NODE_NOT_VISITED = 0,
/* Node has been visited during traversal and not in current stack. */
NODE_VISITED = 1,
/* Node has been visited during traversal and is in current stack. */
NODE_IN_STACK = 2,
};
struct StackEntry {
OperationNode *node;
StackEntry *from;
Relation *via_relation;
};
struct CyclesSolverState {
CyclesSolverState(Depsgraph *graph) : graph(graph) {}
~CyclesSolverState()
{
if (num_cycles != 0) {
CLOG_WARN(&LOG, "Detected %d dependency cycles", num_cycles);
}
}
Depsgraph *graph;
Stack<StackEntry> traversal_stack;
int num_cycles = 0;
};
inline void set_node_visited_state(Node *node, eCyclicCheckVisitedState state)
{
node->custom_flags = (node->custom_flags & ~0x3) | int(state);
}
inline eCyclicCheckVisitedState get_node_visited_state(Node *node)
{
return eCyclicCheckVisitedState(node->custom_flags & 0x3);
}
inline void set_node_num_visited_children(Node *node, int num_children)
{
node->custom_flags = (node->custom_flags & 0x3) | (num_children << 2);
}
inline int get_node_num_visited_children(Node *node)
{
return node->custom_flags >> 2;
}
void schedule_node_to_stack(CyclesSolverState *state, OperationNode *node)
{
StackEntry entry;
entry.node = node;
entry.from = nullptr;
entry.via_relation = nullptr;
state->traversal_stack.push(entry);
set_node_visited_state(node, NODE_IN_STACK);
}
/* Schedule leaf nodes (node without input links) for traversal. */
void schedule_leaf_nodes(CyclesSolverState *state)
{
for (OperationNode *node : state->graph->operations) {
bool has_inlinks = false;
for (Relation *rel : node->inlinks) {
if (rel->from->type == NodeType::OPERATION) {
has_inlinks = true;
}
}
node->custom_flags = 0;
if (has_inlinks == false) {
schedule_node_to_stack(state, node);
}
else {
set_node_visited_state(node, NODE_NOT_VISITED);
}
}
}
/* Schedule node which was not checked yet for being belong to
* any of dependency cycle.
*/
bool schedule_non_checked_node(CyclesSolverState *state)
{
for (OperationNode *node : state->graph->operations) {
if (get_node_visited_state(node) == NODE_NOT_VISITED) {
schedule_node_to_stack(state, node);
return true;
}
}
return false;
}
bool check_relation_can_murder(Relation *relation)
{
if (relation->flag & RELATION_FLAG_GODMODE) {
return false;
}
return true;
}
Relation *select_relation_to_murder(Relation *relation, StackEntry *cycle_start_entry)
{
/* More or less Russian roulette solver, which will make sure only
* specially marked relations are kept alive.
*
* TODO(sergey): There might be better strategies here. */
if (check_relation_can_murder(relation)) {
return relation;
}
StackEntry *current = cycle_start_entry;
OperationNode *to_node = static_cast<OperationNode *>(relation->to);
while (current->node != to_node) {
if (check_relation_can_murder(current->via_relation)) {
return current->via_relation;
}
current = current->from;
}
return relation;
}
/* Solve cycles with all nodes which are scheduled for traversal. */
void solve_cycles(CyclesSolverState *state)
{
Stack<StackEntry> &traversal_stack = state->traversal_stack;
while (!traversal_stack.is_empty()) {
StackEntry *entry = &traversal_stack.peek();
OperationNode *node = entry->node;
bool all_child_traversed = true;
const int num_visited = get_node_num_visited_children(node);
for (int i = num_visited; i < node->outlinks.size(); i++) {
Relation *rel = node->outlinks[i];
if (rel->to->type == NodeType::OPERATION) {
OperationNode *to = static_cast<OperationNode *>(rel->to);
eCyclicCheckVisitedState to_state = get_node_visited_state(to);
if (to_state == NODE_IN_STACK) {
std::string cycle_str = " " + to->full_identifier() + " depends on\n " +
node->full_identifier() + " via '" + rel->name + "'\n";
StackEntry *current = entry;
while (current->node != to) {
BLI_assert(current != nullptr);
cycle_str += " " + current->from->node->full_identifier() + " via '" +
current->via_relation->name + "'\n";
current = current->from;
}
CLOG_WARN(&LOG, "Dependency cycle detected:\n%s", cycle_str.c_str());
Relation *sacrificial_relation = select_relation_to_murder(rel, entry);
sacrificial_relation->flag |= RELATION_FLAG_CYCLIC;
++state->num_cycles;
}
else if (to_state == NODE_NOT_VISITED) {
StackEntry new_entry;
new_entry.node = to;
new_entry.from = entry;
new_entry.via_relation = rel;
traversal_stack.push(new_entry);
set_node_visited_state(node, NODE_IN_STACK);
all_child_traversed = false;
set_node_num_visited_children(node, i);
break;
}
}
}
if (all_child_traversed) {
set_node_visited_state(node, NODE_VISITED);
traversal_stack.pop();
}
}
}
} // namespace
void deg_graph_detect_cycles(Depsgraph *graph)
{
CyclesSolverState state(graph);
/* First we solve cycles which are reachable from leaf nodes. */
schedule_leaf_nodes(&state);
solve_cycles(&state);
/* We are not done yet. It is possible to have closed loop cycle,
* for example A -> B -> C -> A. These nodes were not scheduled
* yet (since they all have inlinks), and were not traversed since
* nobody else points to them. */
while (schedule_non_checked_node(&state)) {
solve_cycles(&state);
}
}
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,18 @@
/* SPDX-FileCopyrightText: 2015 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
namespace blender::deg {
struct Depsgraph;
/* Detect and solve dependency cycles. */
void deg_graph_detect_cycles(Depsgraph *graph);
} // namespace blender::deg

View File

@@ -0,0 +1,110 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*
* Methods for constructing depsgraph
*/
#include "intern/builder/deg_builder_key.h"
#include "RNA_access.hh"
#include "RNA_path.hh"
namespace blender::deg {
/* -------------------------------------------------------------------- */
/** \name Time source
* \{ */
std::string TimeSourceKey::identifier() const
{
return std::string("TimeSourceKey");
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Component
* \{ */
std::string ComponentKey::identifier() const
{
const char *idname = (id) ? id->name : "<None>";
std::string result = std::string("ComponentKey(");
result += idname;
result += ", " + std::string(nodeTypeAsString(type));
if (name[0] != '\0') {
result += ", '" + std::string(name) + "'";
}
result += ')';
return result;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Operation
* \{ */
std::string OperationKey::identifier() const
{
std::string result = std::string("OperationKey(");
result += "type: " + std::string(nodeTypeAsString(component_type));
result += ", component name: '" + std::string(component_name) + "'";
result += ", operation code: " + std::string(operationCodeAsString(opcode));
if (name[0] != '\0') {
result += ", '" + std::string(name) + "'";
}
result += ")";
return result;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name RNA path
* \{ */
RNAPathKey::RNAPathKey(ID *id, const char *path, RNAPointerSource source) : id(id), source(source)
{
/* Create ID pointer for root of path lookup. */
PointerRNA id_ptr = RNA_id_pointer_create(id);
/* Try to resolve path. */
int index;
if (!RNA_path_resolve_full(&id_ptr, path, &ptr, &prop, &index)) {
ptr = PointerRNA_NULL;
prop = nullptr;
}
}
RNAPathKey::RNAPathKey(ID *id, const PointerRNA &ptr, PropertyRNA *prop, RNAPointerSource source)
: id(id), ptr(ptr), prop(prop), source(source)
{
}
RNAPathKey::RNAPathKey(const PointerRNA &target_prop,
const char *rna_path_from_target_prop,
const RNAPointerSource source)
: id(target_prop.owner_id), source(source)
{
/* Try to resolve path. */
int index;
if (!RNA_path_resolve_full(&target_prop, rna_path_from_target_prop, &ptr, &prop, &index)) {
ptr = PointerRNA_NULL;
prop = nullptr;
}
}
std::string RNAPathKey::identifier() const
{
const char *id_name = (id) ? id->name : "<No ID>";
const char *prop_name = (prop) ? RNA_property_identifier(prop) : "<No Prop>";
return std::string("RnaPathKey(") + "id: " + id_name + ", prop: '" + prop_name + "')";
}
/** \} */
} // namespace blender::deg

View File

@@ -0,0 +1,183 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "intern/builder/deg_builder_rna.h"
#include "intern/node/deg_node_component.hh"
#include "intern/node/deg_node_id.hh"
#include "intern/node/deg_node_operation.hh"
#include "DNA_ID.h"
#include "RNA_types.hh"
namespace blender {
struct ID;
struct PropertyRNA;
namespace deg {
struct TimeSourceKey {
TimeSourceKey() = default;
std::string identifier() const;
};
struct ComponentKey {
ComponentKey() = default;
ComponentKey(const ID *id, NodeType type, const char *name = "") : id(id), type(type), name(name)
{
}
std::string identifier() const;
const ID *id = nullptr;
NodeType type = NodeType::UNDEFINED;
const char *name = "";
};
struct OperationKey {
OperationKey() = default;
OperationKey(const ID *id, NodeType component_type, const char *name, int name_tag = -1)
: id(id), component_type(component_type), name(name), name_tag(name_tag)
{
}
OperationKey(const ID *id,
NodeType component_type,
const char *component_name,
const char *name,
int name_tag)
: id(id),
component_type(component_type),
component_name(component_name),
name(name),
name_tag(name_tag)
{
}
OperationKey(const ID *id, NodeType component_type, OperationCode opcode)
: id(id), component_type(component_type), opcode(opcode)
{
}
OperationKey(const ID *id,
NodeType component_type,
const char *component_name,
OperationCode opcode)
: id(id), component_type(component_type), component_name(component_name), opcode(opcode)
{
}
OperationKey(const ID *id,
NodeType component_type,
OperationCode opcode,
const char *name,
int name_tag = -1)
: id(id), component_type(component_type), opcode(opcode), name(name), name_tag(name_tag)
{
}
OperationKey(const ID *id,
NodeType component_type,
const char *component_name,
OperationCode opcode,
const char *name,
int name_tag = -1)
: id(id),
component_type(component_type),
component_name(component_name),
opcode(opcode),
name(name),
name_tag(name_tag)
{
}
OperationKey(OperationKey &&other) noexcept = default;
OperationKey &operator=(OperationKey &&other) = default;
OperationKey(const OperationKey &other) = default;
OperationKey &operator=(const OperationKey &other) = default;
std::string identifier() const;
const ID *id = nullptr;
NodeType component_type = NodeType::UNDEFINED;
const char *component_name = "";
OperationCode opcode = OperationCode::OPERATION;
const char *name = "";
int name_tag = -1;
};
/* Similar to the #OperationKey but does not contain external references, which makes it
* suitable to identify operations even after the original database or graph was destroyed.
* The downside of this key over the #OperationKey is that it performs string allocation upon
* the key construction. */
struct PersistentOperationKey : public OperationKey {
/* Create the key which identifies the given operation node. */
PersistentOperationKey(const OperationNode *operation_node)
{
const ComponentNode *component_node = operation_node->owner;
const IDNode *id_node = component_node->owner;
/* Copy names over to our object, so that the key stays valid even after the `operation_node`
* is destroyed. */
component_name_storage_ = component_node->name;
name_storage_ = operation_node->name;
/* Assign fields used by the #OperationKey API. */
id = id_node->id_orig;
component_type = component_node->type;
component_name = component_name_storage_.c_str();
opcode = operation_node->opcode;
name = name_storage_.c_str();
name_tag = operation_node->name_tag;
}
PersistentOperationKey(PersistentOperationKey &&other) noexcept : OperationKey(other)
{
component_name_storage_ = std::move(other.component_name_storage_);
name_storage_ = std::move(other.name_storage_);
/* Re-assign pointers to the strings.
* This is needed because string content can actually change address if the string uses the
* small string optimization. */
component_name = component_name_storage_.c_str();
name = name_storage_.c_str();
}
PersistentOperationKey &operator=(PersistentOperationKey &&other) = delete;
PersistentOperationKey(const PersistentOperationKey &other) = delete;
PersistentOperationKey &operator=(const PersistentOperationKey &other) = delete;
private:
std::string component_name_storage_;
std::string name_storage_;
};
struct RNAPathKey {
RNAPathKey(ID *id, const char *path, RNAPointerSource source);
RNAPathKey(const PointerRNA &target_prop,
const char *rna_path_from_target_prop,
RNAPointerSource source);
RNAPathKey(ID *id, const PointerRNA &ptr, PropertyRNA *prop, RNAPointerSource source);
std::string identifier() const;
ID *id;
PointerRNA ptr;
PropertyRNA *prop;
RNAPointerSource source;
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,47 @@
/* SPDX-FileCopyrightText: 2018 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/builder/deg_builder_map.h"
#include "DNA_ID.h"
namespace blender::deg {
bool BuilderMap::check_is_built(ID *id, int tag) const
{
return (this->get_ID_tag(id) & tag) == tag;
}
void BuilderMap::tag_built(ID *id, int tag)
{
id_tags_.lookup_or_add(id, 0) |= tag;
}
bool BuilderMap::check_is_built_and_tag(ID *id, int tag)
{
int &id_tag = id_tags_.lookup_or_add(id, 0);
const bool result = (id_tag & tag) == tag;
id_tag |= tag;
return result;
}
int BuilderMap::get_ID_tag(ID *id) const
{
return id_tags_.lookup_default(id, 0);
}
Set<const ID *> BuilderMap::get_ids() const
{
Set<const ID *> result;
for (const ID *id : id_tags_.keys()) {
result.add(id);
}
return result;
}
} // namespace blender::deg

View File

@@ -0,0 +1,79 @@
/* SPDX-FileCopyrightText: 2018 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "BLI_map.hh"
#include "BLI_set.hh"
namespace blender {
struct ID;
namespace deg {
class BuilderMap {
public:
enum {
TAG_ANIMATION = (1 << 0),
TAG_PARAMETERS = (1 << 1),
TAG_TRANSFORM = (1 << 2),
TAG_GEOMETRY = (1 << 3),
TAG_SCENE_COMPOSITOR = (1 << 4),
TAG_SCENE_SEQUENCER = (1 << 5),
TAG_SCENE_AUDIO = (1 << 6),
/**
* Specific tag for whether the collection -> children object relations have been built.
* Purposefully not included in TAG_COMPLETE so it doesn't influence other decisions about
* whether the collection is considered complete.
*/
TAG_COLLECTION_CHILDREN_HIERARCHY = (1 << 7),
TAG_COLLECTION_PROPERTIES = (1 << 8),
/* All ID components has been built. */
TAG_COMPLETE = (TAG_ANIMATION | TAG_PARAMETERS | TAG_TRANSFORM | TAG_GEOMETRY |
TAG_SCENE_COMPOSITOR | TAG_SCENE_SEQUENCER | TAG_SCENE_AUDIO |
TAG_COLLECTION_PROPERTIES),
};
/* Check whether given ID is already handled by builder (or if it's being handled). */
bool check_is_built(ID *id, int tag = TAG_COMPLETE) const;
/* Tag given ID as handled/built. */
void tag_built(ID *id, int tag = TAG_COMPLETE);
/* Combination of previous two functions, returns truth if ID was already handled, or tags is
* handled otherwise and return false. */
bool check_is_built_and_tag(ID *id, int tag = TAG_COMPLETE);
template<typename T> bool check_is_built(T *datablock, int tag = TAG_COMPLETE) const
{
return this->check_is_built(&datablock->id, tag);
}
template<typename T> void tag_built(T *datablock, int tag = TAG_COMPLETE)
{
this->tag_built(&datablock->id, tag);
}
template<typename T> bool check_is_built_and_tag(T *datablock, int tag = TAG_COMPLETE)
{
return this->check_is_built_and_tag(&datablock->id, tag);
}
Set<const ID *> get_ids() const;
protected:
int get_ID_tag(ID *id) const;
Map<ID *, int> id_tags_;
};
} // namespace deg
} // namespace blender

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,360 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "BKE_lib_query.hh" /* For LibraryForeachIDCallbackFlag enum. */
#include "BLI_set.hh"
#include "DNA_armature_types.h"
#include "DNA_listBase.h"
#include "intern/builder/deg_builder.h"
#include "intern/builder/deg_builder_key.h"
#include "intern/builder/deg_builder_map.h"
#include "intern/depsgraph_type.hh"
#include "intern/node/deg_node_id.hh"
#include "intern/node/deg_node_operation.hh"
#include "DEG_depsgraph.hh"
namespace blender {
struct BoneCollection;
struct CacheFile;
struct Camera;
struct Collection;
struct FCurve;
struct FreestyleLineSet;
struct FreestyleLineStyle;
struct ID;
struct IDProperty;
struct Image;
struct Key;
struct LayerCollection;
struct Light;
struct LightProbe;
struct Main;
struct Mask;
struct Material;
struct MovieClip;
struct NlaStrip;
struct Object;
struct ParticleSettings;
struct Scene;
struct Speaker;
struct Tex;
struct VFont;
struct World;
struct bAction;
struct bArmature;
struct bConstraint;
struct bNodeSocket;
struct bNodeTree;
struct bPoseChannel;
struct bSound;
struct PointerRNA;
namespace deg {
struct ComponentNode;
struct Depsgraph;
class DepsgraphBuilderCache;
struct IDNode;
struct OperationKey;
struct OperationNode;
struct TimeSourceNode;
class DepsgraphNodeBuilder : public DepsgraphBuilder {
public:
DepsgraphNodeBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache);
~DepsgraphNodeBuilder() override;
/* For given original ID get ID which is created by copy-on-evaluation system. */
ID *get_cow_id(const ID *id_orig) const;
/* Similar to above, but for the cases when there is no ID node we create
* one. */
ID *ensure_cow_id(ID *id_orig);
/* Helper wrapper function which wraps get_cow_id with a needed type cast. */
template<typename T> T *get_cow_datablock(const T *orig) const
{
return (T *)get_cow_id(&orig->id);
}
/* For a given evaluated datablock get corresponding original one. */
template<typename T> T *get_orig_datablock(const T *cow) const
{
return (T *)cow->id.orig_id;
}
virtual void begin_build();
virtual void end_build();
/**
* `id_cow_self` is the user of `id_pointer`,
* see also `LibraryIDLinkCallbackData` struct definition.
*/
int foreach_id_cow_detect_need_for_update_callback(ID *id_cow_self, ID *id_pointer);
IDNode *add_id_node(ID *id);
IDNode *find_id_node(const ID *id);
TimeSourceNode *add_time_source();
ComponentNode *add_component_node(ID *id, NodeType comp_type, const char *comp_name = "");
ComponentNode *find_component_node(const ID *id, NodeType comp_type, const char *comp_name = "");
OperationNode *add_operation_node(ComponentNode *comp_node,
OperationCode opcode,
const DepsEvalOperationCb &op = nullptr,
const char *name = "",
int name_tag = -1);
OperationNode *add_operation_node(ID *id,
NodeType comp_type,
const char *comp_name,
OperationCode opcode,
const DepsEvalOperationCb &op = nullptr,
const char *name = "",
int name_tag = -1);
OperationNode *add_operation_node(ID *id,
NodeType comp_type,
OperationCode opcode,
const DepsEvalOperationCb &op = nullptr,
const char *name = "",
int name_tag = -1);
OperationNode *ensure_operation_node(ID *id,
NodeType comp_type,
const char *comp_name,
OperationCode opcode,
const DepsEvalOperationCb &op = nullptr,
const char *name = "",
int name_tag = -1);
OperationNode *ensure_operation_node(ID *id,
NodeType comp_type,
OperationCode opcode,
const DepsEvalOperationCb &op = nullptr,
const char *name = "",
int name_tag = -1);
bool has_operation_node(ID *id,
NodeType comp_type,
const char *comp_name,
OperationCode opcode,
const char *name = "",
int name_tag = -1);
bool has_operation_node(ID *id, NodeType comp_type, OperationCode opcode);
OperationNode *find_operation_node(const ID *id,
NodeType comp_type,
const char *comp_name,
OperationCode opcode,
const char *name = "",
int name_tag = -1);
OperationNode *find_operation_node(const ID *id,
NodeType comp_type,
OperationCode opcode,
const char *name = "",
int name_tag = -1);
OperationNode *find_operation_node(const OperationKey &key);
virtual void build_id(ID *id, bool force_be_visible = false);
/* Build function for ID types that do not need their own build_xxx() function. */
virtual void build_generic_id(ID *id);
virtual void build_idproperties(IDProperty *id_property);
virtual void build_scene_render(Scene *scene, ViewLayer *view_layer);
virtual void build_scene_camera(Scene *scene);
virtual void build_scene_parameters(Scene *scene);
virtual void build_scene_compositor(Scene *scene);
virtual void build_empty_object(Object *object);
virtual void build_layer_collections(ListBaseT<LayerCollection> *lb);
virtual void build_view_layer(Scene *scene,
ViewLayer *view_layer,
eDepsNode_LinkedState_Type linked_state);
virtual void build_collection(LayerCollection *from_layer_collection, Collection *collection);
virtual void build_object(int base_index,
Object *object,
eDepsNode_LinkedState_Type linked_state,
bool is_visible);
virtual void build_object_instance_collection(Object *object, bool is_object_visible);
virtual void build_object_from_layer(int base_index,
Object *object,
eDepsNode_LinkedState_Type linked_state);
virtual void build_object_flags(int base_index,
Object *object,
eDepsNode_LinkedState_Type linked_state);
virtual void build_object_modifiers(Object *object);
virtual void build_object_data(Object *object);
virtual void build_object_data_camera(Object *object);
virtual void build_object_data_geometry(Object *object);
virtual void build_object_data_geometry_datablock(ID *obdata);
virtual void build_object_data_light(Object *object);
virtual void build_object_data_lightprobe(Object *object);
virtual void build_object_data_speaker(Object *object);
virtual void build_object_data_grease_pencil(Object *object);
virtual void build_object_transform(Object *object);
virtual void build_object_constraints(Object *object);
virtual void build_object_pointcache(Object *object);
virtual void build_object_shading(Object *object);
virtual void build_object_light_linking(Object *object);
virtual void build_light_linking_collection(Collection *collection);
virtual void build_pose_constraints(Object *object, bPoseChannel *pchan, int pchan_index);
virtual void build_rigidbody(Scene *scene);
virtual void build_particle_systems(Object *object, bool is_object_visible);
virtual void build_particle_settings(ParticleSettings *part);
/**
* Build graph nodes for #AnimData block and any animated images used.
* \param id: ID-Block which hosts the #AnimData
*/
virtual void build_animdata(ID *id);
virtual void build_animdata_nlastrip_targets(ListBaseT<NlaStrip> *strips);
/**
* Build graph nodes to update the current frame in image users.
*/
virtual void build_animation_images(ID *id);
virtual void build_action(bAction *action);
virtual void build_animdata_drivers(ID *id, AnimData *adt);
/**
* Build graph node(s) for Driver
* \param id: ID-Block that driver is attached to
* \param fcurve: Driver-FCurve
* \param driver_index: Index in animation data drivers list
*/
virtual void build_driver(ID *id, FCurve *fcurve, int driver_index);
virtual void build_driver_variables(ID *id, FCurve *fcurve);
virtual void build_driver_scene_camera_variable(Scene *scene, const char *camera_path);
/* Build operations of a property value from which is read by a driver target.
*
* The driver target points to a data-block (or a sub-data-block like View Layer).
* This data-block is presented in the interface as a "Prop" and its resolved RNA pointer is
* passed here as `target_prop`.
*
* The tricky part (and a bit confusing naming) is that the driver target accesses a property of
* the `target_prop` to get its value. The property which is read to give an actual target value
* is denoted by its RNA path relative to the `target_prop`. In the interface it is called "Path"
* and here it is called `rna_path_from_target_prop`. */
virtual void build_driver_id_property(const PointerRNA &target_prop,
const char *rna_path_from_target_prop);
virtual void build_parameters(ID *id);
virtual void build_dimensions(Object *object);
/** IK Solver Eval Steps. */
virtual void build_ik_pose(Object *object, bPoseChannel *pchan, bConstraint *con);
/** Spline IK Eval Steps. */
virtual void build_splineik_pose(Object *object, bPoseChannel *pchan, bConstraint *con);
/** Pose/Armature Bones Graph. */
virtual void build_rig(Object *object);
virtual void build_armature(bArmature *armature);
virtual void build_armature_bones(ListBaseT<Bone> *bones);
virtual void build_armature_bone_collections(Span<BoneCollection *> collections);
/** Shape-keys. */
virtual void build_shapekeys(Key *key);
virtual void build_camera(Camera *camera);
virtual void build_light(Light *lamp);
virtual void build_nodetree(bNodeTree *ntree);
virtual void build_nodetree_socket(bNodeSocket *socket);
/** Recursively build graph for material. */
virtual void build_material(Material *ma);
virtual void build_materials(Material **materials, int num_materials);
virtual void build_freestyle_lineset(FreestyleLineSet *fls);
virtual void build_freestyle_linestyle(FreestyleLineStyle *linestyle);
/** Recursively build graph for texture. */
virtual void build_texture(Tex *tex);
virtual void build_image(Image *image);
/** Recursively build graph for world. */
virtual void build_world(World *world);
virtual void build_cachefile(CacheFile *cache_file);
virtual void build_mask(Mask *mask);
virtual void build_movieclip(MovieClip *clip);
virtual void build_lightprobe(LightProbe *probe);
virtual void build_speaker(Speaker *speaker);
virtual void build_sound(bSound *sound);
virtual void build_scene_sequencer(Scene *scene);
virtual void build_scene_audio(Scene *scene);
virtual void build_scene_speakers(Scene *scene, ViewLayer *view_layer);
virtual void build_vfont(VFont *vfont);
virtual Set<const ID *> get_built_ids() const;
/* Per-ID information about what was already in the dependency graph.
* Allows to re-use certain values, to speed up following evaluation. */
struct IDInfo {
/* Copy-on-written pointer of the corresponding ID. */
ID *id_cow = nullptr;
/* Mask of visible components from previous state of the
* dependency graph. */
IDComponentsMask previously_visible_components_mask = 0;
/* Special evaluation flag mask from the previous depsgraph. */
uint32_t previous_eval_flags = 0;
/* Mesh CustomData mask from the previous depsgraph. */
DEGCustomDataMeshMasks previous_customdata_masks = {};
};
protected:
/* Entry tags and non-updated operations from the previous state of the dependency graph.
* The entry tags are operations which were directly tagged, the matching operations from the
* new dependency graph will be tagged. The needs-update operations are possibly indirectly
* modified operations, whose complementary part from the new dependency graph will only be
* marked as needs-update.
* Stored before the graph is re-created so that they can be transferred over. */
Vector<PersistentOperationKey> saved_entry_tags_;
Vector<PersistentOperationKey> needs_update_operations_;
struct BuilderWalkUserData {
DepsgraphNodeBuilder *builder;
};
static void modifier_walk(void *user_data,
struct Object *object,
struct ID **idpoin,
LibraryForeachIDCallbackFlag cb_flag);
static void constraint_walk(bConstraint *constraint,
ID **idpoin,
bool is_reference,
void *user_data);
void tag_previously_tagged_nodes();
/**
* Check for IDs that need to be flushed (copy-on-eval-updated)
* because the depsgraph itself created or removed some of their evaluated dependencies.
*/
void update_invalid_cow_pointers();
/* State which demotes currently built entities. */
Scene *scene_;
ViewLayer *view_layer_;
int view_layer_index_;
/* NOTE: Collection are possibly built recursively, so be careful when
* setting the current state. */
/* Accumulated flag over the hierarchy of currently building collections.
* Denotes whether all the hierarchy from parent of `collection_` to the
* very root is visible (aka not restricted.). */
bool is_parent_collection_visible_;
/* Indexed by original ID.session_uid, values are IDInfo. */
Map<uint, IDInfo> id_info_hash_;
/* Set of IDs which were already build. Makes it easier to keep track of
* what was already built and what was not. */
BuilderMap built_map_;
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,304 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*
* Methods for constructing depsgraph's nodes
*/
#include "intern/builder/deg_builder_nodes.h"
#include <cstdlib>
#include "DNA_armature_types.h"
#include "DNA_constraint_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_listbase.h"
#include "BKE_action.hh"
#include "BKE_armature.hh"
#include "BKE_constraint.h"
#include "BKE_lib_query.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "intern/eval/deg_eval_copy_on_write.h"
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_component.hh"
#include "intern/node/deg_node_operation.hh"
namespace blender::deg {
void DepsgraphNodeBuilder::build_pose_constraints(Object *object,
bPoseChannel *pchan,
int pchan_index)
{
/* Pull indirect dependencies via constraints. */
BuilderWalkUserData data;
data.builder = this;
BKE_constraints_id_loop(&pchan->constraints, constraint_walk, IDWALK_NOP, &data);
/* Create node for constraint stack. */
Scene *scene_cow = get_cow_datablock(scene_);
Object *object_cow = get_cow_datablock(object);
add_operation_node(&object->id,
NodeType::BONE,
pchan->name,
OperationCode::BONE_CONSTRAINTS,
[scene_cow, object_cow, pchan_index](blender::Depsgraph *depsgraph) {
BKE_pose_constraints_evaluate(
depsgraph, scene_cow, object_cow, pchan_index);
});
}
void DepsgraphNodeBuilder::build_ik_pose(Object *object, bPoseChannel *pchan, bConstraint *con)
{
bKinematicConstraint *data = static_cast<bKinematicConstraint *>(con->data);
/* Find the chain's root. */
bPoseChannel *rootchan = BKE_armature_ik_solver_find_root(pchan, data);
if (rootchan == nullptr) {
return;
}
if (has_operation_node(
&object->id, NodeType::EVAL_POSE, rootchan->name, OperationCode::POSE_IK_SOLVER))
{
return;
}
int rootchan_index = BLI_findindex(&object->pose->chanbase, rootchan);
BLI_assert(rootchan_index != -1);
/* Operation node for evaluating/running IK Solver. */
Scene *scene_cow = get_cow_datablock(scene_);
Object *object_cow = get_cow_datablock(object);
add_operation_node(&object->id,
NodeType::EVAL_POSE,
rootchan->name,
OperationCode::POSE_IK_SOLVER,
[scene_cow, object_cow, rootchan_index](blender::Depsgraph *depsgraph) {
BKE_pose_iktree_evaluate(depsgraph, scene_cow, object_cow, rootchan_index);
});
}
void DepsgraphNodeBuilder::build_splineik_pose(Object *object,
bPoseChannel *pchan,
bConstraint *con)
{
bSplineIKConstraint *data = static_cast<bSplineIKConstraint *>(con->data);
/* Find the chain's root. */
bPoseChannel *rootchan = BKE_armature_splineik_solver_find_root(pchan, data);
if (has_operation_node(
&object->id, NodeType::EVAL_POSE, rootchan->name, OperationCode::POSE_SPLINE_IK_SOLVER))
{
return;
}
/* Operation node for evaluating/running Spline IK Solver.
* Store the "root bone" of this chain in the solver, so it knows where to
* start. */
int rootchan_index = BLI_findindex(&object->pose->chanbase, rootchan);
BLI_assert(rootchan_index != -1);
Scene *scene_cow = get_cow_datablock(scene_);
Object *object_cow = get_cow_datablock(object);
add_operation_node(&object->id,
NodeType::EVAL_POSE,
rootchan->name,
OperationCode::POSE_SPLINE_IK_SOLVER,
[scene_cow, object_cow, rootchan_index](blender::Depsgraph *depsgraph) {
BKE_pose_splineik_evaluate(
depsgraph, scene_cow, object_cow, rootchan_index);
});
}
/* Pose/Armature Bones Graph */
void DepsgraphNodeBuilder::build_rig(Object *object)
{
bArmature *armature = id_cast<bArmature *>(object->data);
Scene *scene_cow = get_cow_datablock(scene_);
Object *object_cow = get_cow_datablock(object);
OperationNode *op_node;
/* Animation and/or drivers linking pose-bones to base-armature used to define them.
*
* NOTE: AnimData here is really used to control animated deform properties,
* which ideally should be able to be unique across different
* instances. Eventually, we need some type of proxy/isolation
* mechanism in-between here to ensure that we can use same rig
* multiple times in same scene. */
/* Armature. */
build_armature(armature);
/* Rebuild pose if not up to date. */
if (object->pose == nullptr || (object->pose->flag & POSE_RECALC)) {
/* By definition, no need to tag depsgraph as dirty from here, so we can pass nullptr bmain. */
BKE_pose_rebuild(nullptr, object, armature, true);
}
else {
/* Ensure the pose bone indices are up to date, so that the rest of the depsgraph building code
* can use `pchan->bone_get(armature)`, which is faster than passing the object. */
BKE_pose_ensure_bone_indices(*object);
}
/* Speed optimization for animation lookups. */
if (object->pose != nullptr) {
BKE_pose_channels_hash_ensure(object->pose);
if (object->pose->flag & POSE_CONSTRAINTS_NEED_UPDATE_FLAGS) {
BKE_pose_update_constraint_flags(*object);
}
}
/**
* Pose Rig Graph
* ==============
*
* Pose Component:
* - Mainly used for referencing Bone components.
* - This is where the evaluation operations for init/exec/cleanup
* (ik) solvers live, and are later hooked up (so that they can be
* interleaved during runtime) with bone-operations they depend on/affect.
* - init_pose_eval() and cleanup_pose_eval() are absolute first and last
* steps of pose eval process. ALL bone operations must be performed
* between these two...
*
* Bone Component:
* - Used for representing each bone within the rig
* - Acts to encapsulate the evaluation operations (base matrix + parenting,
* and constraint stack) so that they can be easily found.
* - Everything else which depends on bone-results hook up to the component
* only so that we can redirect those to point at either the
* post-IK/post-constraint/post-matrix steps, as needed. */
/* Pose eval context. */
op_node = add_operation_node(&object->id,
NodeType::EVAL_POSE,
OperationCode::POSE_INIT,
[scene_cow, object_cow](blender::Depsgraph *depsgraph) {
BKE_pose_eval_init(depsgraph, scene_cow, object_cow);
});
op_node->set_as_entry();
op_node = add_operation_node(&object->id,
NodeType::EVAL_POSE,
OperationCode::POSE_INIT_IK,
[scene_cow, object_cow](blender::Depsgraph *depsgraph) {
BKE_pose_eval_init_ik(depsgraph, scene_cow, object_cow);
});
add_operation_node(&object->id,
NodeType::EVAL_POSE,
OperationCode::POSE_CLEANUP,
[scene_cow, object_cow](blender::Depsgraph *depsgraph) {
BKE_pose_eval_cleanup(depsgraph, scene_cow, object_cow);
});
op_node = add_operation_node(
&object->id,
NodeType::EVAL_POSE,
OperationCode::POSE_DONE,
[object_cow](blender::Depsgraph *depsgraph) { BKE_pose_eval_done(depsgraph, object_cow); });
op_node->set_as_exit();
/* Bones. */
int pchan_index = 0;
for (bPoseChannel &pchan : object->pose->chanbase) {
/* Node for bone evaluation. */
op_node = add_operation_node(
&object->id, NodeType::BONE, pchan.name, OperationCode::BONE_LOCAL);
op_node->set_as_entry();
/* Add a separate node for bone visibility. Getting the visibility doesn't need the pose of the
* bone to be evaluated, so drivers that target the bone's "hide" RNA property can depend on
* this operation, rather than the BONE_LOCAL node. See #152121. */
add_operation_node(&object->id, NodeType::BONE, pchan.name, OperationCode::BONE_VISIBILITY);
add_operation_node(&object->id,
NodeType::BONE,
pchan.name,
OperationCode::BONE_POSE_PARENT,
[scene_cow, object_cow, pchan_index](blender::Depsgraph *depsgraph) {
BKE_pose_eval_bone(depsgraph, scene_cow, object_cow, pchan_index);
});
/* NOTE: Dedicated noop for easier relationship construction. */
add_operation_node(&object->id, NodeType::BONE, pchan.name, OperationCode::BONE_READY);
op_node = add_operation_node(&object->id,
NodeType::BONE,
pchan.name,
OperationCode::BONE_DONE,
[object_cow, pchan_index](blender::Depsgraph *depsgraph) {
BKE_pose_bone_done(depsgraph, object_cow, pchan_index);
});
/* B-Bone shape computation - the real last step if present. */
if (check_pchan_has_bbone(object, &pchan)) {
op_node = add_operation_node(&object->id,
NodeType::BONE,
pchan.name,
OperationCode::BONE_SEGMENTS,
[object_cow, pchan_index](blender::Depsgraph *depsgraph) {
BKE_pose_eval_bbone_segments(
depsgraph, object_cow, pchan_index);
});
}
op_node->set_as_exit();
/* Custom properties. */
bool add_idprops_operation = false;
if (pchan.prop != nullptr) {
build_idproperties(pchan.prop);
add_idprops_operation = true;
}
if (pchan.system_properties != nullptr) {
build_idproperties(pchan.system_properties);
add_idprops_operation = true;
}
if (add_idprops_operation) {
add_operation_node(
&object->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EVAL, nullptr, pchan.name);
}
/* Build constraints. */
if (pchan.constraints.first != nullptr) {
build_pose_constraints(object, &pchan, pchan_index);
}
/**
* IK Solvers.
*
* - These require separate processing steps are pose-level
* to be executed between chains of bones (i.e. once the
* base transforms of a bunch of bones is done)
*
* Unsolved Issues:
* - Care is needed to ensure that multi-headed trees work out the same
* as in ik-tree building
* - Animated chain-lengths are a problem. */
for (bConstraint &con : pchan.constraints) {
switch (con.type) {
case CONSTRAINT_TYPE_KINEMATIC:
build_ik_pose(object, &pchan, &con);
break;
case CONSTRAINT_TYPE_SPLINEIK:
build_splineik_pose(object, &pchan, &con);
break;
default:
break;
}
}
/* Custom shape. */
if (pchan.custom != nullptr) {
/* NOTE: The relation builder will ensure visibility of the custom shape object. */
build_object(-1, pchan.custom, DEG_ID_LINKED_INDIRECTLY, false);
}
pchan_index++;
}
}
} // namespace blender::deg

View File

@@ -0,0 +1,87 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/builder/deg_builder_nodes.h"
#include "DNA_scene_types.h"
#include "BLI_listbase.h"
namespace blender::deg {
void DepsgraphNodeBuilder::build_scene_render(Scene *scene, ViewLayer *view_layer)
{
scene_ = scene;
view_layer_ = view_layer;
const bool build_compositor = (scene->r.scemode & R_DOCOMP);
const bool build_sequencer = (scene->r.scemode & R_DOSEQ);
IDNode *id_node = add_id_node(&scene->id);
id_node->linked_state = DEG_ID_LINKED_DIRECTLY;
add_time_source();
build_animdata(&scene->id);
build_scene_parameters(scene);
build_scene_audio(scene);
if (build_compositor) {
build_scene_compositor(scene);
}
if (build_sequencer) {
build_scene_sequencer(scene);
build_scene_speakers(scene, view_layer);
}
build_scene_camera(scene);
}
void DepsgraphNodeBuilder::build_scene_camera(Scene *scene)
{
if (scene->camera != nullptr) {
build_object(-1, scene->camera, DEG_ID_LINKED_INDIRECTLY, true);
}
for (TimeMarker &marker : scene->markers) {
if (!ELEM(marker.camera, nullptr, scene->camera)) {
build_object(-1, marker.camera, DEG_ID_LINKED_INDIRECTLY, true);
}
}
}
void DepsgraphNodeBuilder::build_scene_parameters(Scene *scene)
{
if (built_map_.check_is_built_and_tag(scene, BuilderMap::TAG_PARAMETERS)) {
return;
}
build_parameters(&scene->id);
build_idproperties(scene->id.properties);
build_idproperties(scene->id.system_properties);
add_operation_node(&scene->id, NodeType::SCENE, OperationCode::SCENE_EVAL);
for (TimeMarker &marker : scene->markers) {
build_idproperties(marker.prop);
}
}
void DepsgraphNodeBuilder::build_scene_compositor(Scene *scene)
{
if (built_map_.check_is_built_and_tag(scene, BuilderMap::TAG_SCENE_COMPOSITOR)) {
return;
}
if (scene->compositing_node_group == nullptr) {
return;
}
add_operation_node(&scene->id,
NodeType::COMPOSITOR,
OperationCode::COMPOSITOR_EVAL,
[](blender::Depsgraph * /*depsgraph*/) {
/* Empty evaluate function, but needed to make sure the operation is not
* considered a no-op. */
});
build_nodetree(scene->compositing_node_group);
}
} // namespace blender::deg

View File

@@ -0,0 +1,170 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*
* Methods for constructing depsgraph's nodes
*/
#include "intern/builder/deg_builder_nodes.h"
#include <cstdlib>
#include "DNA_collection_types.h"
#include "DNA_freestyle_types.h"
#include "DNA_layer_types.h"
#include "DNA_node_types.h"
#include "DNA_object_types.h"
#include "DNA_scene_types.h"
#include "BLI_listbase.h"
#include "BKE_layer.hh"
#include "BKE_main.hh"
#include "BKE_node.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "intern/builder/deg_builder.h"
#include "intern/depsgraph.hh"
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_component.hh"
#include "intern/node/deg_node_operation.hh"
namespace blender::deg {
void DepsgraphNodeBuilder::build_layer_collections(ListBaseT<LayerCollection> *lb)
{
const int visibility_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_HIDE_VIEWPORT :
COLLECTION_HIDE_RENDER;
for (LayerCollection &lc : *lb) {
if (lc.collection->flag & visibility_flag) {
continue;
}
if ((lc.flag & LAYER_COLLECTION_EXCLUDE) == 0) {
build_collection(&lc, lc.collection);
}
build_layer_collections(&lc.layer_collections);
}
}
void DepsgraphNodeBuilder::build_freestyle_lineset(FreestyleLineSet *fls)
{
if (fls->group != nullptr) {
build_collection(nullptr, fls->group);
}
if (fls->linestyle != nullptr) {
build_freestyle_linestyle(fls->linestyle);
}
}
void DepsgraphNodeBuilder::build_view_layer(Scene *scene,
ViewLayer *view_layer,
eDepsNode_LinkedState_Type linked_state)
{
/* NOTE: Pass view layer index of 0 since after scene evaluated copy there is
* only one view layer in there. */
view_layer_index_ = 0;
/* Scene ID block. */
IDNode *id_node = add_id_node(&scene->id);
id_node->linked_state = linked_state;
add_operation_node(&scene->id, NodeType::HIERARCHY, OperationCode::HIERARCHY);
/* Time source. */
add_time_source();
/* Setup currently building context. */
scene_ = scene;
view_layer_ = view_layer;
/* Get pointer to an evaluated version of scene ID. */
Scene *scene_cow = get_cow_datablock(scene);
/* Scene objects. */
/* NOTE: Base is used for function bindings as-is, so need to pass evaluated base,
* but object is expected to be an original one. Hence we go into some
* tricks here iterating over the view layer. */
int base_index = 0;
BKE_view_layer_synced_ensure(*bmain_, scene, view_layer);
for (Base &base : *BKE_view_layer_object_bases_get(view_layer)) {
/* object itself */
if (!need_pull_base_into_graph(&base)) {
continue;
}
/* NOTE: We consider object visible even if it's currently
* restricted by the base/restriction flags. Otherwise its drivers
* will never be evaluated.
*
* TODO(sergey): Need to go more granular on visibility checks. */
build_object(base_index, base.object, linked_state, true);
base_index++;
if (!graph_->has_animated_visibility) {
graph_->has_animated_visibility |= is_object_visibility_animated(base.object);
}
}
build_layer_collections(&view_layer->layer_collections);
build_scene_camera(scene);
/* Rigidbody. */
if (scene->rigidbody_world != nullptr) {
build_rigidbody(scene);
}
/* Scene's animation and drivers. */
if (scene->adt != nullptr) {
build_animdata(&scene->id);
}
/* World. */
if (scene->world != nullptr) {
build_world(scene->world);
}
/* Cache file. */
for (CacheFile &cachefile : bmain_->cachefiles) {
build_cachefile(&cachefile);
}
/* Masks. */
for (Mask &mask : bmain_->masks) {
build_mask(&mask);
}
/* Movie clips. */
for (MovieClip &clip : bmain_->movieclips) {
build_movieclip(&clip);
}
/* Material override. */
if (view_layer->mat_override != nullptr) {
build_material(view_layer->mat_override);
}
/* World override */
if (view_layer->world_override != nullptr) {
build_world(view_layer->world_override);
}
/* Freestyle linesets. */
for (FreestyleLineSet &fls : view_layer->freestyle_config.linesets) {
build_freestyle_lineset(&fls);
}
/* Sequencer. */
if (linked_state == DEG_ID_LINKED_DIRECTLY) {
build_scene_audio(scene);
build_scene_sequencer(scene);
}
/* Collections. */
add_operation_node(
&scene->id,
NodeType::LAYER_COLLECTIONS,
OperationCode::VIEW_LAYER_EVAL,
[view_layer_index = view_layer_index_, scene_cow](blender::Depsgraph *depsgraph) {
BKE_layer_eval_view_layer_indexed(depsgraph, scene_cow, view_layer_index);
});
/* Parameters evaluation for scene relations mainly. */
build_scene_compositor(scene);
build_scene_parameters(scene);
/* Build all set scenes. */
if (scene->set != nullptr) {
ViewLayer *set_view_layer = BKE_view_layer_default_render(scene->set);
build_view_layer(scene->set, set_view_layer, DEG_ID_LINKED_VIA_SET);
}
}
} // namespace blender::deg

View File

@@ -0,0 +1,52 @@
/* SPDX-FileCopyrightText: 2015 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/builder/deg_builder_pchanmap.h"
#include <cstdio>
#include <cstring>
namespace blender::deg {
void RootPChanMap::print_debug()
{
map_.foreach_item([](StringRefNull key, const Set<StringRefNull> &values) {
printf(" %s : { ", key.data());
for (StringRefNull val : values) {
printf("%s, ", val.data());
}
printf("}\n");
});
}
void RootPChanMap::add_bone(const char *bone, const char *root)
{
map_.lookup_or_add_default(bone).add(root);
}
bool RootPChanMap::has_common_root(const char *bone1, const char *bone2) const
{
const Set<StringRefNull> *bone1_roots = map_.lookup_ptr(bone1);
const Set<StringRefNull> *bone2_roots = map_.lookup_ptr(bone2);
if (bone1_roots == nullptr) {
// fprintf("RootPChanMap: bone1 '%s' not found (%s => %s)\n", bone1, bone1, bone2);
// print_debug();
return false;
}
if (bone2_roots == nullptr) {
// fprintf("RootPChanMap: bone2 '%s' not found (%s => %s)\n", bone2, bone1, bone2);
// print_debug();
return false;
}
return Set<StringRefNull>::Intersects(*bone1_roots, *bone2_roots);
}
} // namespace blender::deg

View File

@@ -0,0 +1,35 @@
/* SPDX-FileCopyrightText: 2015 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "BLI_map.hh"
#include "BLI_set.hh"
#include "BLI_string_ref.hh"
namespace blender::deg {
struct RootPChanMap {
/** Debug contents of map. */
void print_debug();
/** Add a mapping. */
void add_bone(const char *bone, const char *root);
/** Check if there's a common root bone between two bones. */
bool has_common_root(const char *bone1, const char *bone2) const;
protected:
/**
* The strings are only referenced by this map. Users of RootPChanMap have to make sure that the
* life-time of the strings is long enough.
*/
Map<StringRefNull, Set<StringRefNull>> map_;
};
} // namespace blender::deg

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,373 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include <cstring>
#include "intern/depsgraph_type.hh"
#include "DNA_ID.h"
#include "DNA_listBase.h"
#include "BLI_span.hh"
#include "BKE_lib_query.hh" /* For LibraryForeachIDCallbackFlag enum. */
#include "intern/builder/deg_builder.h"
#include "intern/builder/deg_builder_key.h"
#include "intern/builder/deg_builder_map.h"
#include "intern/builder/deg_builder_rna.h"
#include "intern/builder/deg_builder_stack.h"
#include "intern/depsgraph.hh"
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_component.hh"
#include "intern/node/deg_node_id.hh"
#include "intern/node/deg_node_operation.hh"
namespace blender {
struct CacheFile;
struct Camera;
struct Collection;
struct EffectorWeights;
struct FCurve;
struct FreestyleLineSet;
struct FreestyleLineStyle;
struct ID;
struct IDProperty;
struct Image;
struct Key;
struct LayerCollection;
struct Light;
struct LightProbe;
struct Main;
struct Mask;
struct Material;
struct MovieClip;
struct NlaStrip;
struct Object;
struct ParticleSettings;
struct ParticleSystem;
struct Scene;
struct Speaker;
struct Tex;
struct VFont;
struct ViewLayer;
struct World;
struct bAction;
struct bArmature;
struct bConstraint;
struct bNodeSocket;
struct bNodeTree;
struct bPoseChannel;
struct bSound;
namespace deg {
struct ComponentNode;
struct DepsNodeHandle;
struct Depsgraph;
class DepsgraphBuilderCache;
struct IDNode;
struct Node;
struct OperationNode;
struct Relation;
struct RootPChanMap;
struct TimeSourceNode;
class DepsgraphRelationBuilder : public DepsgraphBuilder {
public:
DepsgraphRelationBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache);
void begin_build();
template<typename KeyFrom, typename KeyTo>
Relation *add_relation(const KeyFrom &key_from,
const KeyTo &key_to,
const char *description,
int flags = 0);
template<typename KeyTo>
Relation *add_relation(const TimeSourceKey &key_from,
const KeyTo &key_to,
const char *description,
int flags = 0);
template<typename KeyType>
requires(!std::is_same_v<KeyType, TimeSourceKey>)
Relation *add_node_handle_relation(const KeyType &key_from,
const DepsNodeHandle *handle,
const char *description,
int flags = 0);
Relation *add_node_handle_relation(const TimeSourceKey &key_from,
const DepsNodeHandle *handle,
const char *description,
int flags = 0);
template<typename KeyTo>
Relation *add_depends_on_transform_relation(ID *id,
const KeyTo &key_to,
const char *description,
int flags = 0);
/* Adds relation from proper transformation operation to the modifier.
* Takes care of checking for possible physics solvers modifying position
* of this object. */
void add_depends_on_transform_relation(const DepsNodeHandle *handle, const char *description);
void add_customdata_mask(Object *object, const DEGCustomDataMeshMasks &customdata_masks);
void add_special_eval_flag(ID *id, uint32_t flag);
virtual void build_id(ID *id);
/* Build function for ID types that do not need their own build_xxx() function. */
virtual void build_generic_id(ID *id);
virtual void build_idproperties(IDProperty *id_property);
virtual void build_scene_camera(Scene *scene);
virtual void build_scene_render(Scene *scene, ViewLayer *view_layer);
virtual void build_scene_parameters(Scene *scene);
virtual void build_scene_compositor(Scene *scene);
virtual bool build_layer_collection(LayerCollection *layer_collection);
virtual void build_view_layer_collections(ViewLayer *view_layer);
virtual void build_view_layer(Scene *scene,
ViewLayer *view_layer,
eDepsNode_LinkedState_Type linked_state);
virtual void build_collection(LayerCollection *from_layer_collection, Collection *collection);
virtual void build_object(Object *object);
virtual void build_object_from_view_layer_base(Object *object);
virtual void build_object_layer_component_relations(Object *object);
virtual void build_object_modifiers(Object *object);
virtual void build_object_data(Object *object);
virtual void build_object_data_camera(Object *object);
virtual void build_object_data_geometry(Object *object);
virtual void build_object_data_geometry_datablock(ID *obdata);
virtual void build_object_data_empty(Object *object);
virtual void build_object_data_light(Object *object);
virtual void build_object_data_lightprobe(Object *object);
virtual void build_object_data_speaker(Object *object);
virtual void build_object_parent(Object *object);
virtual void build_object_pointcache(Object *object);
virtual void build_object_instance_collection(Object *object);
virtual void build_object_shading(Object *object);
virtual void build_object_light_linking(Object *emitter);
virtual void build_light_linking_collection(Object *emitter, Collection *collection);
virtual void build_constraints(ID *id,
NodeType component_type,
const char *component_subdata,
ListBaseT<bConstraint> *constraints,
RootPChanMap *root_map);
virtual void build_animdata(ID *id);
virtual void build_animdata_curves(ID *id);
virtual void build_animdata_fcurve_target(ID *id,
PointerRNA id_ptr,
ComponentKey &adt_key,
OperationNode *operation_from,
FCurve *fcu);
virtual void build_animdata_action_targets(ID *id,
int32_t slot_handle,
ComponentKey &adt_key,
OperationNode *operation_from,
bAction *action);
virtual void build_animdata_nlastrip_targets(ID *id,
ComponentKey &adt_key,
OperationNode *operation_from,
ListBaseT<NlaStrip> *strips);
virtual void build_animdata_drivers(ID *id);
virtual void build_animdata_force(ID *id);
virtual void build_animation_images(ID *id);
virtual void build_action(bAction *action);
virtual void build_driver(ID *id, FCurve *fcurve);
virtual void build_driver_data(ID *id, FCurve *fcurve);
virtual void build_driver_variables(ID *id, FCurve *fcurve);
virtual void build_driver_scene_camera_variable(const OperationKey &driver_key,
const RNAPathKey &self_key,
Scene *scene,
const char *rna_path);
virtual void build_driver_rna_path_variable(const OperationKey &driver_key,
const RNAPathKey &self_key,
ID *target_id,
const PointerRNA &target_prop,
const char *rna_path);
/* Build operations of a property value from which is read by a driver target.
*
* The driver target points to a data-block (or a sub-data-block like View Layer).
* This data-block is presented in the interface as a "Prop" and its resolved RNA pointer is
* passed here as `target_prop`.
*
* The tricky part (and a bit confusing naming) is that the driver target accesses a property of
* the `target_prop` to get its value. The property which is read to give an actual target value
* is denoted by its RNA path relative to the `target_prop`. In the interface it is called "Path"
* and here it is called `rna_path_from_target_prop`. */
virtual void build_driver_id_property(const PointerRNA &target_prop,
const char *rna_path_from_target_prop);
virtual void build_parameters(ID *id);
virtual void build_dimensions(Object *object);
virtual void build_world(World *world);
virtual void build_rigidbody(Scene *scene);
virtual void build_particle_systems(Object *object);
virtual void build_particle_settings(ParticleSettings *part);
virtual void build_particle_system_visualization_object(Object *object,
ParticleSystem *psys,
Object *draw_object);
virtual void build_ik_pose(Object *object,
bPoseChannel *pchan,
bConstraint *con,
RootPChanMap *root_map);
virtual void build_splineik_pose(Object *object,
bPoseChannel *pchan,
bConstraint *con,
RootPChanMap *root_map);
virtual void build_inter_ik_chains(Object *object,
const OperationKey &solver_key,
const bPoseChannel *rootchan,
const RootPChanMap *root_map);
virtual void build_rig(Object *object);
virtual void build_shapekeys(Key *key);
virtual void build_armature(bArmature *armature);
virtual void build_armature_bones(ListBaseT<Bone> *bones);
virtual void build_armature_bone_collections(Span<BoneCollection *> collections);
virtual void build_camera(Camera *camera);
virtual void build_light(Light *lamp);
virtual void build_nodetree(bNodeTree *ntree);
virtual void build_nodetree_socket(bNodeSocket *socket);
virtual void build_material(Material *ma, ID *owner = nullptr);
virtual void build_materials(ID *owner, Material **materials, int num_materials);
virtual void build_freestyle_lineset(FreestyleLineSet *fls);
virtual void build_freestyle_linestyle(FreestyleLineStyle *linestyle);
virtual void build_texture(Tex *tex);
virtual void build_image(Image *image);
virtual void build_cachefile(CacheFile *cache_file);
virtual void build_mask(Mask *mask);
virtual void build_movieclip(MovieClip *clip);
virtual void build_lightprobe(LightProbe *probe);
virtual void build_speaker(Speaker *speaker);
virtual void build_sound(bSound *sound);
virtual void build_scene_sequencer(Scene *scene);
virtual void build_scene_audio(Scene *scene);
virtual void build_scene_speakers(Scene *scene, ViewLayer *view_layer);
virtual void build_vfont(VFont *vfont);
virtual void build_nested_datablock(ID *owner, ID *id, bool flush_cow_changes);
virtual void build_nested_nodetree(ID *owner, bNodeTree *ntree);
virtual void build_nested_shapekey(ID *owner, Key *key);
void add_particle_collision_relations(const OperationKey &key,
Object *object,
Collection *collection,
const char *name);
void add_particle_forcefield_relations(const OperationKey &key,
Object *object,
ParticleSystem *psys,
EffectorWeights *eff,
bool add_absorption,
const char *name);
virtual void build_copy_on_write_relations();
virtual void build_copy_on_write_relations(IDNode *id_node);
virtual void build_driver_relations();
virtual void build_driver_relations(IDNode *id_node);
template<typename KeyType> OperationNode *find_operation_node(const KeyType &key);
Depsgraph *getGraph();
virtual Set<const ID *> get_built_ids() const;
protected:
TimeSourceNode *get_node(const TimeSourceKey &key) const;
ComponentNode *get_node(const ComponentKey &key) const;
OperationNode *get_node(const OperationKey &key) const;
Node *get_node(const RNAPathKey &key);
OperationNode *find_node(const OperationKey &key) const;
ComponentNode *find_node(const ComponentKey &key) const;
bool has_node(const ComponentKey &key) const;
bool has_node(const OperationKey &key) const;
Relation *add_time_relation(TimeSourceNode *timesrc,
Node *node_to,
const char *description,
int flags = 0);
/* Add relation which ensures visibility of `id_from` when `id_to` is visible.
* For the more detailed explanation see comment for `NodeType::VISIBILITY`. */
void add_visibility_relation(ID *id_from, ID *id_to);
Relation *add_operation_relation(OperationNode *node_from,
OperationNode *node_to,
const char *description,
int flags = 0);
template<typename KeyType>
DepsNodeHandle create_node_handle(const KeyType &key, const char *default_name = "");
/* TODO(sergey): All those is_same* functions are to be generalized. */
/* Check whether two keys corresponds to the same bone from same armature.
*
* This is used by drivers relations builder to avoid possible fake
* dependency cycle when one bone property drives another property of the
* same bone. */
template<typename KeyFrom, typename KeyTo>
bool is_same_bone_dependency(const KeyFrom &key_from, const KeyTo &key_to);
/* Similar to above, but used to check whether driver is using node from
* the same node tree as a driver variable. */
template<typename KeyFrom, typename KeyTo>
bool is_same_nodetree_node_dependency(const KeyFrom &key_from, const KeyTo &key_to);
private:
struct BuilderWalkUserData {
DepsgraphRelationBuilder *builder;
};
static void modifier_walk(void *user_data,
struct Object *object,
struct ID **idpoin,
LibraryForeachIDCallbackFlag cb_flag);
static void constraint_walk(bConstraint *con, ID **idpoin, bool is_reference, void *user_data);
/* State which demotes currently built entities. */
Scene *scene_;
BuilderMap built_map_;
RNANodeQuery rna_node_query_;
BuilderStack stack_;
};
struct DepsNodeHandle {
DepsNodeHandle(DepsgraphRelationBuilder *builder,
OperationNode *node,
const char *default_name = "")
: builder(builder), node(node), default_name(default_name)
{
BLI_assert(node != nullptr);
}
DepsgraphRelationBuilder *builder;
OperationNode *node;
const char *default_name;
};
} // namespace deg
} // namespace blender
#include "intern/builder/deg_builder_relations_impl.h" // IWYU pragma: export

View File

@@ -0,0 +1,271 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*
* Methods for constructing depsgraph relations for drivers.
*/
#include "intern/builder/deg_builder_relations_drivers.h"
#include <cstring>
#include <deque>
#include "BLI_listbase.h"
#include "DNA_anim_types.h"
#include "RNA_access.hh"
#include "RNA_path.hh"
#include "BKE_anim_data.hh"
#include "intern/builder/deg_builder_relations.h"
#include "intern/depsgraph_relation.hh"
#include "intern/node/deg_node.hh"
namespace blender::deg {
DriverDescriptor::DriverDescriptor(PointerRNA *id_ptr, FCurve *fcu)
: id_ptr_(id_ptr),
fcu_(fcu),
driver_relations_needed_(false),
pointer_rna_(),
property_rna_(nullptr),
is_array_(false)
{
driver_relations_needed_ = determine_relations_needed();
split_rna_path();
}
bool DriverDescriptor::determine_relations_needed()
{
if (fcu_->array_index > 0) {
/* Drivers on array elements always need relations. */
is_array_ = true;
return true;
}
if (!resolve_rna()) {
/* Properties that don't exist can't cause threading issues either. */
return false;
}
if (RNA_property_array_check(property_rna_)) {
/* Drivers on array elements always need relations. */
is_array_ = true;
return true;
}
/* Drivers on Booleans and Enums (when used as bit-flags) can write to the same memory location,
* so they need relations between each other. */
return ELEM(RNA_property_type(property_rna_), PROP_BOOLEAN, PROP_ENUM);
}
bool DriverDescriptor::driver_relations_needed() const
{
return driver_relations_needed_;
}
bool DriverDescriptor::is_array() const
{
return is_array_;
}
bool DriverDescriptor::is_same_array_as(const DriverDescriptor &other) const
{
if (!is_array_ || !other.is_array_) {
return false;
}
return rna_suffix == other.rna_suffix;
}
OperationKey DriverDescriptor::depsgraph_key() const
{
return OperationKey(id_ptr_->owner_id,
NodeType::PARAMETERS,
OperationCode::DRIVER,
fcu_->rna_path,
fcu_->array_index);
}
void DriverDescriptor::split_rna_path()
{
const char *last_dot = strrchr(fcu_->rna_path, '.');
if (last_dot == nullptr || last_dot[1] == '\0') {
rna_prefix = StringRef();
rna_suffix = StringRef(fcu_->rna_path);
return;
}
rna_prefix = StringRef(fcu_->rna_path, last_dot);
rna_suffix = StringRef(last_dot + 1);
}
bool DriverDescriptor::resolve_rna()
{
return RNA_path_resolve_property(id_ptr_, fcu_->rna_path, &pointer_rna_, &property_rna_);
}
static bool is_reachable(const Node *const from, const Node *const to)
{
if (from == to) {
return true;
}
/* Perform a graph walk from 'to' towards its incoming connections.
* Walking from 'from' towards its outgoing connections is 10x slower on the Spring rig. */
std::deque<const Node *> queue;
Set<const Node *> seen;
queue.push_back(to);
while (!queue.empty()) {
/* Visit the next node to inspect. */
const Node *visit = queue.back();
queue.pop_back();
if (visit == from) {
return true;
}
/* Queue all incoming relations that we haven't seen before. */
for (Relation *relation : visit->inlinks) {
const Node *prev_node = relation->from;
if (seen.add(prev_node)) {
queue.push_back(prev_node);
}
}
}
return false;
}
/* **** DepsgraphRelationBuilder functions **** */
void DepsgraphRelationBuilder::build_driver_relations()
{
for (IDNode *id_node : graph_->id_nodes) {
build_driver_relations(id_node);
}
}
void DepsgraphRelationBuilder::build_driver_relations(IDNode *id_node)
{
/* Add relations between drivers that write to the same datablock.
*
* This prevents threading issues when two separate RNA properties write to
* the same memory address. For example:
* - Drivers on individual array elements, as the animation system will write
* the whole array back to RNA even when changing individual array value.
* - Drivers on RNA properties that map to a single bit flag. Changing the RNA
* value will write the entire int containing the bit, in a non-thread-safe
* way.
*/
ID *id_orig = id_node->id_orig;
AnimData *adt = BKE_animdata_from_id(id_orig);
if (adt == nullptr) {
return;
}
/* Mapping from RNA prefix -> set of driver descriptors: */
Map<std::string, Vector<DriverDescriptor>> driver_groups;
PointerRNA id_ptr = RNA_id_pointer_create(id_orig);
for (FCurve &fcu : adt->drivers) {
if (fcu.rna_path == nullptr) {
continue;
}
DriverDescriptor driver_desc(&id_ptr, &fcu);
if (!driver_desc.driver_relations_needed()) {
continue;
}
driver_groups.lookup_or_add_default_as(driver_desc.rna_prefix).append(driver_desc);
}
for (Span<DriverDescriptor> prefix_group : driver_groups.values()) {
/* For each node in the driver group, try to connect it to another node
* in the same group without creating any cycles. */
int num_drivers = prefix_group.size();
if (num_drivers < 2) {
/* A relation requires two drivers. */
continue;
}
for (int from_index = 0; from_index < num_drivers; ++from_index) {
const DriverDescriptor &driver_from = prefix_group[from_index];
Node *op_from = get_node(driver_from.depsgraph_key());
/* Start by trying the next node in the group. */
for (int to_offset = 1; to_offset < num_drivers; ++to_offset) {
const int to_index = (from_index + to_offset) % num_drivers;
const DriverDescriptor &driver_to = prefix_group[to_index];
Node *op_to = get_node(driver_to.depsgraph_key());
/* Duplicate drivers can exist (see #78615), but cannot be distinguished by OperationKey
* and thus have the same depsgraph node. Relations between those drivers should not be
* created. This not something that is expected to happen (both the UI and the Python API
* prevent duplicate drivers), it did happen in a file and it is easy to deal with here. */
if (op_from == op_to) {
continue;
}
if (from_index < to_index && driver_from.is_same_array_as(driver_to)) {
/* This is for adding a relation like `color[0]` -> `color[1]`.
* When the search for another driver wraps around,
* we cannot blindly add relations any more. */
}
else {
/* Investigate whether this relation would create a dependency cycle.
* Example graph:
* A -> B -> C
* and investigating a potential connection C->A. Because A->C is an
* existing transitive connection, adding C->A would create a cycle. */
if (is_reachable(op_to, op_from)) {
continue;
}
/* No need to directly connect this node if there is already a transitive connection. */
if (is_reachable(op_from, op_to)) {
break;
}
}
add_operation_relation(
op_from->get_exit_operation(), op_to->get_entry_operation(), "Driver Serialization");
break;
}
}
}
}
bool data_path_maybe_shared(const ID &id, const StringRef data_path)
{
/* As it is hard to generally detect implicit sharing, this is implemented as
* a 'known to not share' list. */
/* Allow concurrent writes to custom properties. #140706 shows that this
* shouldn't be a problem in practice. */
if (data_path.startswith("[\"") && data_path.endswith("\"]")) {
return false;
}
if (GS(id.name) == ID_OB) {
const Object &ob = *reinterpret_cast<const Object *>(&id);
const bool is_thread_safe = (ob.type == OB_ARMATURE && data_path.startswith("pose.bones["));
return !is_thread_safe;
}
/* Allow concurrent writes to shape-key values. #140706 shows that this
* shouldn't be a problem in practice. */
if (GS(id.name) == ID_KE) {
const bool is_thread_safe = data_path.startswith("key_blocks[") &&
data_path.endswith("].value");
return !is_thread_safe;
}
return true;
}
} // namespace blender::deg

View File

@@ -0,0 +1,76 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "BLI_string_ref.hh"
#include "RNA_types.hh"
#include "intern/builder/deg_builder_relations.h"
namespace blender {
struct FCurve;
namespace deg {
/* Helper class for determining which relations are needed between driver evaluation nodes. */
class DriverDescriptor {
public:
/**
* Drivers are grouped by their RNA prefix. The prefix is the part of the RNA
* path up to the last dot, the suffix is the remainder of the RNA path:
*
* \code{.unparsed}
* fcu->rna_path rna_prefix rna_suffix
* ------------------------------- ---------------------- ----------
* 'color' '' 'color'
* 'rigidbody_world.time_scale' 'rigidbody_world' 'time_scale'
* 'pose.bones["master"].location' 'pose.bones["master"]' 'location'
* \endcode
*/
StringRef rna_prefix;
StringRef rna_suffix;
DriverDescriptor(PointerRNA *id_ptr, FCurve *fcu);
bool driver_relations_needed() const;
bool is_array() const;
/** Assumes that 'other' comes from the same RNA group, that is, has the same RNA path prefix. */
bool is_same_array_as(const DriverDescriptor &other) const;
OperationKey depsgraph_key() const;
private:
PointerRNA *id_ptr_;
FCurve *fcu_;
bool driver_relations_needed_;
PointerRNA pointer_rna_;
PropertyRNA *property_rna_;
bool is_array_;
bool determine_relations_needed();
void split_rna_path();
bool resolve_rna();
};
/**
* Returns whether the data at the given path may be implicitly shared (also see
* #ImplicitSharingInfo). If it is shared, writing to it through RNA will make a
* local copy that can be edited without affecting the other users.
*
* If multi-threaded writing to the path is required, one should trigger making
* the mutable copy before multi-threaded writing starts. Otherwise there is a
* race condition where each thread tries to make its own copy. The "unsharing"
* can be triggered by doing a dummy-write to it.
*/
bool data_path_maybe_shared(const ID &id, StringRef data_path);
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,214 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "intern/builder/deg_builder_relations.h"
#include "intern/node/deg_node_id.hh"
#include "intern/node/deg_node_time.hh"
#include <iostream>
#include "DNA_ID.h"
#include "DNA_rigidbody_types.h"
namespace blender::deg {
template<typename KeyType>
OperationNode *DepsgraphRelationBuilder::find_operation_node(const KeyType &key)
{
Node *node = get_node(key);
return node != nullptr ? node->get_exit_operation() : nullptr;
}
template<typename KeyFrom, typename KeyTo>
Relation *DepsgraphRelationBuilder::add_relation(const KeyFrom &key_from,
const KeyTo &key_to,
const char *description,
int flags)
{
Node *node_from = get_node(key_from);
Node *node_to = get_node(key_to);
OperationNode *op_from = node_from ? node_from->get_exit_operation() : nullptr;
OperationNode *op_to = node_to ? node_to->get_entry_operation() : nullptr;
if (op_from && op_to) {
return add_operation_relation(op_from, op_to, description, flags);
}
/* TODO(sergey): Report error in the interface. */
std::cerr << "--------------------------------------------------------------------\n";
std::cerr << "Failed to add relation \"" << description << "\"\n";
if (!op_from) {
std::cerr << "Could not find op_from: " << key_from.identifier() << "\n";
}
if (!op_to) {
std::cerr << "Could not find op_to: " << key_to.identifier() << "\n";
}
if (!stack_.is_empty()) {
std::cerr << "\nTrace:\n\n";
stack_.print_backtrace(std::cerr);
std::cerr << "\n";
}
return nullptr;
}
template<typename KeyTo>
Relation *DepsgraphRelationBuilder::add_relation(const TimeSourceKey &key_from,
const KeyTo &key_to,
const char *description,
int flags)
{
TimeSourceNode *time_from = get_node(key_from);
Node *node_to = get_node(key_to);
OperationNode *op_to = node_to ? node_to->get_entry_operation() : nullptr;
if (time_from != nullptr && op_to != nullptr) {
return add_time_relation(time_from, op_to, description, flags);
}
return nullptr;
}
template<typename KeyType>
requires(!std::is_same_v<KeyType, TimeSourceKey>)
Relation *DepsgraphRelationBuilder::add_node_handle_relation(const KeyType &key_from,
const DepsNodeHandle *handle,
const char *description,
int flags)
{
Node *node_from = get_node(key_from);
OperationNode *op_from = node_from ? node_from->get_exit_operation() : nullptr;
OperationNode *op_to = handle->node->get_entry_operation();
if (op_from != nullptr && op_to != nullptr) {
return add_operation_relation(op_from, op_to, description, flags);
}
if (!op_from) {
fprintf(stderr,
"add_node_handle_relation(%s) - Could not find op_from (%s)\n",
description,
key_from.identifier().c_str());
}
if (!op_to) {
fprintf(stderr,
"add_node_handle_relation(%s) - Could not find op_to (%s)\n",
description,
key_from.identifier().c_str());
}
return nullptr;
}
static inline bool rigidbody_object_depends_on_evaluated_geometry(const RigidBodyOb *rbo)
{
if (rbo == nullptr) {
return false;
}
if (ELEM(rbo->shape, RB_SHAPE_CONVEXH, RB_SHAPE_TRIMESH)) {
if (rbo->mesh_source != RBO_MESH_BASE) {
return true;
}
}
return false;
}
template<typename KeyTo>
Relation *DepsgraphRelationBuilder::add_depends_on_transform_relation(ID *id,
const KeyTo &key_to,
const char *description,
int flags)
{
if (GS(id->name) == ID_OB) {
Object *object = reinterpret_cast<Object *>(id);
if (rigidbody_object_depends_on_evaluated_geometry(object->rigidbody_object)) {
OperationKey transform_key(&object->id, NodeType::TRANSFORM, OperationCode::TRANSFORM_EVAL);
return add_relation(transform_key, key_to, description, flags);
}
}
ComponentKey transform_key(id, NodeType::TRANSFORM);
return add_relation(transform_key, key_to, description, flags);
}
template<typename KeyType>
DepsNodeHandle DepsgraphRelationBuilder::create_node_handle(const KeyType &key,
const char *default_name)
{
return DepsNodeHandle(this, get_node(key), default_name);
}
/* Rig compatibility: we check if bone is using local transform as a variable
* for driver on itself and ignore those relations to avoid "false-positive"
* dependency cycles.
*/
template<typename KeyFrom, typename KeyTo>
bool DepsgraphRelationBuilder::is_same_bone_dependency(const KeyFrom &key_from,
const KeyTo &key_to)
{
/* Get operations for requested keys. */
Node *node_from = get_node(key_from);
Node *node_to = get_node(key_to);
if (node_from == nullptr || node_to == nullptr) {
return false;
}
OperationNode *op_from = node_from->get_exit_operation();
OperationNode *op_to = node_to->get_entry_operation();
if (op_from == nullptr || op_to == nullptr) {
return false;
}
/* Different armatures, bone can't be the same. */
if (op_from->owner->owner != op_to->owner->owner) {
return false;
}
/* We are only interested in relations like BONE_DONE -> BONE_LOCAL... */
if (!(op_from->opcode == OperationCode::BONE_DONE && op_to->opcode == OperationCode::BONE_LOCAL))
{
return false;
}
/* ... BUT, we also need to check if it's same bone. */
if (op_from->owner->name != op_to->owner->name) {
return false;
}
return true;
}
template<typename KeyFrom, typename KeyTo>
bool DepsgraphRelationBuilder::is_same_nodetree_node_dependency(const KeyFrom &key_from,
const KeyTo &key_to)
{
/* Get operations for requested keys. */
Node *node_from = get_node(key_from);
Node *node_to = get_node(key_to);
if (node_from == nullptr || node_to == nullptr) {
return false;
}
OperationNode *op_from = node_from->get_exit_operation();
OperationNode *op_to = node_to->get_entry_operation();
if (op_from == nullptr || op_to == nullptr) {
return false;
}
/* Check if this is actually a node tree. */
if (GS(op_from->owner->owner->id_orig->name) != ID_NT) {
return false;
}
/* Different node trees. */
if (op_from->owner->owner != op_to->owner->owner) {
return false;
}
/* We are only interested in relations like BONE_DONE -> BONE_LOCAL... */
if (!(op_from->opcode == OperationCode::PARAMETERS_EVAL &&
op_to->opcode == OperationCode::PARAMETERS_EVAL))
{
return false;
}
return true;
}
} // namespace blender::deg

View File

@@ -0,0 +1,481 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*
* Methods for constructing depsgraph
*/
#include "DEG_depsgraph_debug.hh"
#include "intern/builder/deg_builder_relations.h"
#include <cstdlib>
#include <cstring> /* required for STREQ later on. */
#include "BLI_listbase.h"
#include "BLI_utildefines.h"
#include "DNA_action_types.h"
#include "DNA_armature_types.h"
#include "DNA_constraint_types.h"
#include "DNA_customdata_types.h"
#include "DNA_object_types.h"
#include "BKE_action.hh"
#include "BKE_armature.hh"
#include "BKE_constraint.h"
#include "RNA_access.hh"
#include "RNA_prototypes.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "intern/builder/deg_builder.h"
#include "intern/builder/deg_builder_cache.h"
#include "intern/builder/deg_builder_pchanmap.h"
#include "intern/debug/deg_debug.h"
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_operation.hh"
#include "intern/depsgraph_relation.hh"
#include "intern/depsgraph_type.hh"
namespace blender::deg {
/* IK Solver Eval Steps */
void DepsgraphRelationBuilder::build_ik_pose(Object *object,
bPoseChannel *pchan,
bConstraint *con,
RootPChanMap *root_map)
{
if ((con->flag & CONSTRAINT_DISABLE) != 0) {
/* Do not add disabled IK constraints to the relations. If these needs to be temporarily
* enabled, they will be added as temporary constraints during transform. */
return;
}
bKinematicConstraint *data = static_cast<bKinematicConstraint *>(con->data);
/* Attach owner to IK Solver to. */
bPoseChannel *rootchan = BKE_armature_ik_solver_find_root(pchan, data);
if (rootchan == nullptr) {
return;
}
OperationKey pchan_local_key(
&object->id, NodeType::BONE, pchan->name, OperationCode::BONE_LOCAL);
OperationKey init_ik_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_INIT_IK);
OperationKey solver_key(
&object->id, NodeType::EVAL_POSE, rootchan->name, OperationCode::POSE_IK_SOLVER);
OperationKey pose_cleanup_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_CLEANUP);
/* If any of the constraint parameters are animated, connect the relation. Since there is only
* one Init IK node per armature, this link has quite high risk of spurious dependency cycles.
*/
const bool is_itasc = (object->pose->iksolver == IKSOLVER_ITASC);
PointerRNA con_ptr = RNA_pointer_create_discrete(&object->id, RNA_Constraint, con);
if (is_itasc || cache_->isAnyPropertyAnimated(&con_ptr)) {
add_relation(pchan_local_key, init_ik_key, "IK Constraint -> Init IK Tree");
}
add_relation(init_ik_key, solver_key, "Init IK -> IK Solver");
/* Never cleanup before solver is run. */
add_relation(solver_key, pose_cleanup_key, "IK Solver -> Cleanup", RELATION_FLAG_GODMODE);
/* The ITASC solver currently accesses the target transforms in init tree :(
* TODO: Fix ITASC and remove this.
*/
OperationKey target_dependent_key = is_itasc ? init_ik_key : solver_key;
/* IK target */
/* TODO(sergey): This should get handled as part of the constraint code. */
if (data->tar != nullptr) {
/* Different object - requires its transform. */
if (data->tar != object) {
ComponentKey target_key(&data->tar->id, NodeType::TRANSFORM);
add_relation(target_key, target_dependent_key, con->name);
/* Ensure target evaluated copy is ready by the time IK tree is built just in case. */
ComponentKey target_cow_key(&data->tar->id, NodeType::COPY_ON_EVAL);
add_relation(target_cow_key,
init_ik_key,
"IK Target Copy-on-Eval -> Init IK Tree",
RELATION_CHECK_BEFORE_ADD);
}
/* Subtarget references: */
if ((data->tar->type == OB_ARMATURE) && (data->subtarget[0])) {
/* Bone - use the final transformation. */
OperationKey target_key(
&data->tar->id, NodeType::BONE, data->subtarget, OperationCode::BONE_DONE);
add_relation(target_key, target_dependent_key, con->name);
}
else if (data->subtarget[0] && ELEM(data->tar->type, OB_MESH, OB_LATTICE)) {
/* Vertex group target. */
/* NOTE: for now, we don't need to represent vertex groups
* separately. */
ComponentKey target_key(&data->tar->id, NodeType::GEOMETRY);
add_relation(target_key, target_dependent_key, con->name);
add_customdata_mask(data->tar, DEGCustomDataMeshMasks::MaskVert(CD_MASK_MDEFORMVERT));
}
if (data->tar == object && data->subtarget[0]) {
/* Prevent target's constraints from linking to anything from same
* chain that it controls. */
root_map->add_bone(data->subtarget, rootchan->name);
}
}
/* Pole Target. */
/* TODO(sergey): This should get handled as part of the constraint code. */
if (data->poletar != nullptr) {
/* Different object - requires its transform. */
if (data->poletar != object) {
ComponentKey target_key(&data->poletar->id, NodeType::TRANSFORM);
add_relation(target_key, target_dependent_key, con->name);
/* Ensure target evaluated copy is ready by the time IK tree is built just in case. */
ComponentKey target_cow_key(&data->poletar->id, NodeType::COPY_ON_EVAL);
add_relation(target_cow_key,
init_ik_key,
"IK Target Copy-on-Eval -> Init IK Tree",
RELATION_CHECK_BEFORE_ADD);
}
/* Subtarget references: */
if ((data->poletar->type == OB_ARMATURE) && (data->polesubtarget[0])) {
/* Bone - use the final transformation. */
OperationKey target_key(
&data->poletar->id, NodeType::BONE, data->polesubtarget, OperationCode::BONE_DONE);
add_relation(target_key, target_dependent_key, con->name);
}
else if (data->polesubtarget[0] && ELEM(data->poletar->type, OB_MESH, OB_LATTICE)) {
/* Vertex group target. */
/* NOTE: for now, we don't need to represent vertex groups
* separately. */
ComponentKey target_key(&data->poletar->id, NodeType::GEOMETRY);
add_relation(target_key, target_dependent_key, con->name);
add_customdata_mask(data->poletar, DEGCustomDataMeshMasks::MaskVert(CD_MASK_MDEFORMVERT));
}
}
DEG_DEBUG_PRINTF((blender::Depsgraph *)graph_,
BUILD,
"\nStarting IK Build: pchan = %s, target = (%s, %s), "
"segcount = %d\n",
pchan->name,
data->tar ? data->tar->id.name : "nullptr",
data->subtarget,
data->rootbone);
bPoseChannel *parchan = pchan;
/* Exclude tip from chain if needed. */
if (!(data->flag & CONSTRAINT_IK_TIP)) {
parchan = pchan->parent;
}
root_map->add_bone(parchan->name, rootchan->name);
OperationKey parchan_transforms_key(
&object->id, NodeType::BONE, parchan->name, OperationCode::BONE_READY);
add_relation(parchan_transforms_key, solver_key, "IK Solver Owner");
/* Walk to the chain's root. */
int segcount = 0;
while (parchan != nullptr) {
/* Make IK-solver dependent on this bone's result, since it can only run
* after the standard results of the bone are know. Validate links step
* on the bone will ensure that users of this bone only grab the result
* with IK solver results. */
if (parchan != pchan) {
OperationKey parent_key(
&object->id, NodeType::BONE, parchan->name, OperationCode::BONE_READY);
add_relation(parent_key, solver_key, "IK Chain Parent");
OperationKey bone_done_key(
&object->id, NodeType::BONE, parchan->name, OperationCode::BONE_DONE);
add_relation(solver_key, bone_done_key, "IK Chain Result");
}
else {
OperationKey final_transforms_key(
&object->id, NodeType::BONE, parchan->name, OperationCode::BONE_DONE);
add_relation(solver_key, final_transforms_key, "IK Solver Result");
}
parchan->flag |= POSE_DONE;
root_map->add_bone(parchan->name, rootchan->name);
/* continue up chain, until we reach target number of items. */
DEG_DEBUG_PRINTF((blender::Depsgraph *)graph_, BUILD, " %d = %s\n", segcount, parchan->name);
/* TODO(sergey): This is an arbitrary value, which was just following
* old code convention. */
segcount++;
if ((segcount == data->rootbone) || (segcount > 255)) {
break;
}
parchan = parchan->parent;
}
OperationKey pose_done_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_DONE);
add_relation(solver_key, pose_done_key, "PoseEval Result-Bone Link");
/* Add relation when the root of this IK chain is influenced by another IK chain. */
build_inter_ik_chains(object, solver_key, rootchan, root_map);
}
/* Spline IK Eval Steps */
void DepsgraphRelationBuilder::build_splineik_pose(Object *object,
bPoseChannel *pchan,
bConstraint *con,
RootPChanMap *root_map)
{
bSplineIKConstraint *data = static_cast<bSplineIKConstraint *>(con->data);
bPoseChannel *rootchan = BKE_armature_splineik_solver_find_root(pchan, data);
OperationKey transforms_key(&object->id, NodeType::BONE, pchan->name, OperationCode::BONE_READY);
OperationKey init_ik_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_INIT_IK);
OperationKey solver_key(
&object->id, NodeType::EVAL_POSE, rootchan->name, OperationCode::POSE_SPLINE_IK_SOLVER);
OperationKey pose_cleanup_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_CLEANUP);
/* Solver depends on initialization. */
add_relation(init_ik_key, solver_key, "Init IK -> IK Solver");
/* Never cleanup before solver is run. */
add_relation(solver_key, pose_cleanup_key, "IK Solver -> Cleanup");
/* Attach owner to IK Solver. */
add_relation(transforms_key, solver_key, "Spline IK Solver Owner", RELATION_FLAG_GODMODE);
/* Attach path dependency to solver. */
if (data->tar != nullptr) {
ComponentKey target_geometry_key(&data->tar->id, NodeType::GEOMETRY);
add_relation(target_geometry_key, solver_key, "Curve.Path -> Spline IK");
ComponentKey target_transform_key(&data->tar->id, NodeType::TRANSFORM);
add_relation(target_transform_key, solver_key, "Curve.Transform -> Spline IK");
add_special_eval_flag(&data->tar->id, DAG_EVAL_NEED_CURVE_PATH);
}
pchan->flag |= POSE_DONE;
OperationKey final_transforms_key(
&object->id, NodeType::BONE, pchan->name, OperationCode::BONE_DONE);
add_relation(solver_key, final_transforms_key, "Spline IK Result");
root_map->add_bone(pchan->name, rootchan->name);
/* Walk to the chain's root/ */
int segcount = 1;
for (bPoseChannel *parchan = pchan->parent; parchan != nullptr && segcount < data->chainlen;
parchan = parchan->parent, segcount++)
{
/* Make Spline IK solver dependent on this bone's result, since it can
* only run after the standard results of the bone are know. Validate
* links step on the bone will ensure that users of this bone only grab
* the result with IK solver results. */
OperationKey parent_key(&object->id, NodeType::BONE, parchan->name, OperationCode::BONE_READY);
add_relation(parent_key, solver_key, "Spline IK Solver Update");
OperationKey bone_done_key(
&object->id, NodeType::BONE, parchan->name, OperationCode::BONE_DONE);
add_relation(solver_key, bone_done_key, "Spline IK Solver Result");
parchan->flag |= POSE_DONE;
root_map->add_bone(parchan->name, rootchan->name);
}
OperationKey pose_done_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_DONE);
add_relation(solver_key, pose_done_key, "PoseEval Result-Bone Link");
/* Add relation when the root of this IK chain is influenced by another IK chain. */
build_inter_ik_chains(object, solver_key, rootchan, root_map);
}
void DepsgraphRelationBuilder::build_inter_ik_chains(Object *object,
const OperationKey &solver_key,
const bPoseChannel *rootchan,
const RootPChanMap *root_map)
{
bPoseChannel *deepest_root = nullptr;
const char *root_name = rootchan->name;
/* Find shared IK chain root. */
for (bPoseChannel *parchan = rootchan->parent; parchan; parchan = parchan->parent) {
if (!root_map->has_common_root(root_name, parchan->name)) {
break;
}
deepest_root = parchan;
}
if (deepest_root == nullptr) {
return;
}
OperationKey other_bone_key(
&object->id, NodeType::BONE, deepest_root->name, OperationCode::BONE_DONE);
add_relation(other_bone_key, solver_key, "IK Chain Overlap");
}
/* Pose/Armature Bones Graph */
void DepsgraphRelationBuilder::build_rig(Object *object)
{
/* Armature-Data */
bArmature *armature = id_cast<bArmature *>(object->data);
/* TODO: selection status? */
/* Attach links between pose operations. */
ComponentKey local_transform(&object->id, NodeType::TRANSFORM);
OperationKey pose_init_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_INIT);
OperationKey pose_init_ik_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_INIT_IK);
OperationKey pose_cleanup_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_CLEANUP);
OperationKey pose_done_key(&object->id, NodeType::EVAL_POSE, OperationCode::POSE_DONE);
add_relation(local_transform, pose_init_key, "Local Transform -> Pose Init");
add_relation(pose_init_key, pose_init_ik_key, "Pose Init -> Pose Init IK");
add_relation(pose_init_ik_key, pose_done_key, "Pose Init IK -> Pose Cleanup");
/* Make sure pose is up-to-date with armature updates. */
build_armature(armature);
OperationKey armature_key(&armature->id, NodeType::ARMATURE, OperationCode::ARMATURE_EVAL);
add_relation(armature_key, pose_init_key, "Data dependency");
/* Run cleanup even when there are no bones. */
add_relation(pose_init_ik_key, pose_cleanup_key, "Init -> Cleanup");
/* Relation to the instance, so that instancer can use pose of this object. */
add_relation(ComponentKey(&object->id, NodeType::EVAL_POSE),
OperationKey{&object->id, NodeType::INSTANCING, OperationCode::INSTANCE},
"Transform -> Instance");
/* IK Solvers.
*
* - These require separate processing steps are pose-level to be executed
* between chains of bones (i.e. once the base transforms of a bunch of
* bones is done).
*
* - We build relations for these before the dependencies between operations
* in the same component as it is necessary to check whether such bones
* are in the same IK chain (or else we get weird issues with either
* in-chain references, or with bones being parented to IK'd bones).
*
* Unsolved Issues:
* - Care is needed to ensure that multi-headed trees work out the same as
* in ik-tree building
* - Animated chain-lengths are a problem. */
RootPChanMap root_map;
bool pose_depends_on_local_transform = false;
for (bPoseChannel &pchan : object->pose->chanbase) {
const BuilderStack::ScopedEntry stack_entry = stack_.trace(pchan);
for (bConstraint &con : pchan.constraints) {
const BuilderStack::ScopedEntry stack_entry = stack_.trace(con);
switch (con.type) {
case CONSTRAINT_TYPE_KINEMATIC:
build_ik_pose(object, &pchan, &con, &root_map);
pose_depends_on_local_transform = true;
break;
case CONSTRAINT_TYPE_SPLINEIK:
build_splineik_pose(object, &pchan, &con, &root_map);
pose_depends_on_local_transform = true;
break;
/* Constraints which needs world's matrix for transform.
* TODO(sergey): More constraints here? */
case CONSTRAINT_TYPE_ROTLIKE:
case CONSTRAINT_TYPE_SIZELIKE:
case CONSTRAINT_TYPE_LOCLIKE:
case CONSTRAINT_TYPE_TRANSLIKE:
/* TODO(sergey): Add used space check. */
pose_depends_on_local_transform = true;
break;
default:
break;
}
}
}
// root_map.print_debug();
if (pose_depends_on_local_transform) {
/* TODO(sergey): Once partial updates are possible use relation between
* object transform and solver itself in its build function. */
ComponentKey pose_key(&object->id, NodeType::EVAL_POSE);
ComponentKey local_transform_key(&object->id, NodeType::TRANSFORM);
add_relation(local_transform_key, pose_key, "Local Transforms");
}
/* Links between operations for each bone. */
for (bPoseChannel &pchan : object->pose->chanbase) {
const BuilderStack::ScopedEntry stack_entry = stack_.trace(pchan);
build_idproperties(pchan.prop);
build_idproperties(pchan.system_properties);
OperationKey bone_local_key(
&object->id, NodeType::BONE, pchan.name, OperationCode::BONE_LOCAL);
OperationKey bone_pose_key(
&object->id, NodeType::BONE, pchan.name, OperationCode::BONE_POSE_PARENT);
OperationKey bone_ready_key(
&object->id, NodeType::BONE, pchan.name, OperationCode::BONE_READY);
OperationKey bone_done_key(&object->id, NodeType::BONE, pchan.name, OperationCode::BONE_DONE);
pchan.flag &= ~POSE_DONE;
/* Pose init to bone local. */
add_relation(pose_init_key, bone_local_key, "Pose Init - Bone Local", RELATION_FLAG_GODMODE);
/* Bone visibility (BONE_VISIBILITY) has no relationships. The node is a no-op, and it's just
* there to ensure the pose data itself is there. It does have the implicit dependency on the
* COPY_ON_EVAL node. */
/* Local to pose parenting operation. */
add_relation(bone_local_key, bone_pose_key, "Bone Local - Bone Pose");
/* Parent relation. */
if (pchan.parent != nullptr) {
OperationCode parent_key_opcode;
/* NOTE: this difference in handling allows us to prevent lockups
* while ensuring correct poses for separate chains. */
if (root_map.has_common_root(pchan.name, pchan.parent->name)) {
parent_key_opcode = OperationCode::BONE_READY;
}
else {
parent_key_opcode = OperationCode::BONE_DONE;
}
OperationKey parent_key(&object->id, NodeType::BONE, pchan.parent->name, parent_key_opcode);
add_relation(parent_key, bone_pose_key, "Parent Bone -> Child Bone");
}
/* Build constraints. */
if (pchan.constraints.first != nullptr) {
/* Build relations for indirectly linked objects. */
BuilderWalkUserData data;
data.builder = this;
BKE_constraints_id_loop(&pchan.constraints, constraint_walk, IDWALK_NOP, &data);
/* Constraints stack and constraint dependencies. */
build_constraints(&object->id, NodeType::BONE, pchan.name, &pchan.constraints, &root_map);
/* Pose -> constraints. */
OperationKey constraints_key(
&object->id, NodeType::BONE, pchan.name, OperationCode::BONE_CONSTRAINTS);
add_relation(bone_pose_key, constraints_key, "Pose -> Constraints Stack");
add_relation(bone_local_key, constraints_key, "Local -> Constraints Stack");
/* Constraints -> ready/ */
/* TODO(sergey): When constraint stack is exploded, this step should
* occur before the first IK solver. */
add_relation(constraints_key, bone_ready_key, "Constraints -> Ready");
}
else {
/* Pose -> Ready */
add_relation(bone_pose_key, bone_ready_key, "Pose -> Ready");
}
/* Bone ready -> Bone done.
* NOTE: For bones without IK, this is all that's needed.
* For IK chains however, an additional rel is created from IK
* to done, with transitive reduction removing this one. */
add_relation(bone_ready_key, bone_done_key, "Ready -> Done");
/* B-Bone shape is the real final step after Done if present. */
if (check_pchan_has_bbone(object, &pchan)) {
OperationKey bone_segments_key(
&object->id, NodeType::BONE, pchan.name, OperationCode::BONE_SEGMENTS);
/* B-Bone shape depends on the final position of the bone. */
add_relation(bone_done_key, bone_segments_key, "Done -> B-Bone Segments");
/* B-Bone shape depends on final position of handle bones. */
bPoseChannel *prev, *next;
Bone *pchan_bone = pchan.bone_get(*object);
BKE_pchan_bbone_handles_get({&pchan, pchan_bone}, &prev, &next);
if (prev) {
OperationCode opcode = OperationCode::BONE_DONE;
/* Inheriting parent roll requires access to prev handle's B-Bone properties. */
if ((pchan_bone->bbone_flag & BBONE_ADD_PARENT_END_ROLL) != 0 &&
check_pchan_has_bbone_segments(object, prev))
{
opcode = OperationCode::BONE_SEGMENTS;
}
OperationKey prev_key(&object->id, NodeType::BONE, prev->name, opcode);
add_relation(prev_key, bone_segments_key, "Prev Handle -> B-Bone Segments");
}
if (next) {
OperationKey next_key(&object->id, NodeType::BONE, next->name, OperationCode::BONE_DONE);
add_relation(next_key, bone_segments_key, "Next Handle -> B-Bone Segments");
}
/* Pose requires the B-Bone shape. */
add_relation(
bone_segments_key, pose_done_key, "PoseEval Result-Bone Link", RELATION_FLAG_GODMODE);
add_relation(bone_segments_key, pose_cleanup_key, "Cleanup dependency");
}
else {
/* Assume that all bones must be done for the pose to be ready
* (for deformers). */
add_relation(bone_done_key, pose_done_key, "PoseEval Result-Bone Link");
/* Bones must be traversed before cleanup. */
add_relation(bone_done_key, pose_cleanup_key, "Done -> Cleanup");
add_relation(bone_ready_key, pose_cleanup_key, "Ready -> Cleanup");
}
/* Custom shape. */
if (pchan.custom != nullptr) {
build_object(pchan.custom);
add_visibility_relation(&pchan.custom->id, &armature->id);
}
}
}
} // namespace blender::deg

View File

@@ -0,0 +1,93 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/builder/deg_builder_relations.h"
#include "DNA_node_types.h"
#include "DNA_scene_types.h"
#include "BKE_compositor.hh"
#include "BLI_listbase.h"
namespace blender::deg {
void DepsgraphRelationBuilder::build_scene_render(Scene *scene, ViewLayer *view_layer)
{
scene_ = scene;
const bool build_compositor = (scene->r.scemode & R_DOCOMP);
const bool build_sequencer = (scene->r.scemode & R_DOSEQ);
build_scene_parameters(scene);
build_animdata(&scene->id);
build_scene_audio(scene);
if (build_compositor) {
build_scene_compositor(scene);
}
if (build_sequencer) {
build_scene_sequencer(scene);
build_scene_speakers(scene, view_layer);
}
build_scene_camera(scene);
}
void DepsgraphRelationBuilder::build_scene_camera(Scene *scene)
{
if (scene->camera != nullptr) {
build_object(scene->camera);
}
for (TimeMarker &marker : scene->markers) {
if (!ELEM(marker.camera, nullptr, scene->camera)) {
build_object(marker.camera);
}
}
}
void DepsgraphRelationBuilder::build_scene_parameters(Scene *scene)
{
if (built_map_.check_is_built_and_tag(scene, BuilderMap::TAG_PARAMETERS)) {
return;
}
/* TODO(sergey): Trace as a scene parameters. */
build_idproperties(scene->id.properties);
build_idproperties(scene->id.system_properties);
build_parameters(&scene->id);
OperationKey parameters_eval_key(
&scene->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EXIT);
ComponentKey scene_eval_key(&scene->id, NodeType::SCENE);
add_relation(parameters_eval_key, scene_eval_key, "Parameters -> Scene Eval");
for (TimeMarker &marker : scene->markers) {
build_idproperties(marker.prop);
}
}
void DepsgraphRelationBuilder::build_scene_compositor(Scene *scene)
{
if (built_map_.check_is_built_and_tag(scene, BuilderMap::TAG_SCENE_COMPOSITOR)) {
return;
}
if (scene->compositing_node_group == nullptr) {
return;
}
ComponentKey compositor_key(&scene->id, NodeType::COMPOSITOR);
const OperationKey node_output_key(
&scene->compositing_node_group->id, NodeType::NTREE_OUTPUT, OperationCode::NTREE_OUTPUT);
this->add_relation(node_output_key, compositor_key, "NTree Output -> Compositor");
/* TODO(sergey): Trace as a scene compositor. */
build_nodetree(scene->compositing_node_group);
DepsNodeHandle handle = this->create_node_handle(node_output_key);
bke::compositor::add_depsgraph_relations(*scene,
reinterpret_cast<blender::DepsNodeHandle *>(&handle));
}
} // namespace blender::deg

View File

@@ -0,0 +1,167 @@
/* SPDX-FileCopyrightText: 2013 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*
* Methods for constructing depsgraph
*/
#include "intern/builder/deg_builder_relations.h"
#include <cstdlib>
#include <cstring> /* required for STREQ later on. */
#include "DNA_collection_types.h"
#include "DNA_scene_types.h"
#include "BLI_listbase.h"
#include "BKE_layer.hh"
#include "BKE_main.hh"
#include "BKE_node.hh"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_build.hh"
#include "intern/builder/deg_builder.h"
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_component.hh"
#include "intern/node/deg_node_id.hh"
#include "intern/node/deg_node_operation.hh"
namespace blender::deg {
bool DepsgraphRelationBuilder::build_layer_collection(LayerCollection *layer_collection)
{
const int hide_flag = (graph_->mode == DAG_EVAL_VIEWPORT) ? COLLECTION_HIDE_VIEWPORT :
COLLECTION_HIDE_RENDER;
Collection *collection = layer_collection->collection;
const bool is_collection_hidden = collection->flag & hide_flag;
const bool is_layer_collection_excluded = layer_collection->flag & LAYER_COLLECTION_EXCLUDE;
if (is_collection_hidden || is_layer_collection_excluded) {
return false;
}
build_collection(layer_collection, collection);
const ComponentKey collection_hierarchy_key{&collection->id, NodeType::HIERARCHY};
for (LayerCollection &child_layer_collection : layer_collection->layer_collections) {
if (build_layer_collection(&child_layer_collection)) {
Collection *child_collection = child_layer_collection.collection;
const ComponentKey child_collection_hierarchy_key{&child_collection->id,
NodeType::HIERARCHY};
add_relation(
collection_hierarchy_key, child_collection_hierarchy_key, "Collection hierarchy");
}
}
return true;
}
void DepsgraphRelationBuilder::build_view_layer_collections(ViewLayer *view_layer)
{
const ComponentKey scene_hierarchy_key{&scene_->id, NodeType::HIERARCHY};
for (LayerCollection &layer_collection : view_layer->layer_collections) {
if (build_layer_collection(&layer_collection)) {
Collection *collection = layer_collection.collection;
const ComponentKey collection_hierarchy_key{&collection->id, NodeType::HIERARCHY};
add_relation(scene_hierarchy_key, collection_hierarchy_key, "Scene -> Collection hierarchy");
}
}
}
void DepsgraphRelationBuilder::build_freestyle_lineset(FreestyleLineSet *fls)
{
if (fls->group != nullptr) {
build_collection(nullptr, fls->group);
}
if (fls->linestyle != nullptr) {
build_freestyle_linestyle(fls->linestyle);
}
}
void DepsgraphRelationBuilder::build_view_layer(Scene *scene,
ViewLayer *view_layer,
eDepsNode_LinkedState_Type linked_state)
{
/* Setup currently building context. */
scene_ = scene;
BKE_view_layer_synced_ensure(*bmain_, scene, view_layer);
/* Scene objects. */
/* NOTE: Nodes builder requires us to pass evaluated base because it's being
* passed to the evaluation functions. During relations builder we only
* do nullptr-pointer check of the base, so it's fine to pass original one. */
for (Base &base : *BKE_view_layer_object_bases_get(view_layer)) {
if (need_pull_base_into_graph(&base)) {
build_object_from_view_layer_base(base.object);
}
}
build_view_layer_collections(view_layer);
build_scene_camera(scene);
/* Rigidbody. */
if (scene->rigidbody_world != nullptr) {
build_rigidbody(scene);
}
/* Scene's animation and drivers. */
if (scene->adt != nullptr) {
build_animdata(&scene->id);
}
/* World. */
if (scene->world != nullptr) {
build_world(scene->world);
}
/* Cache file. */
for (CacheFile &cachefile : bmain_->cachefiles) {
build_cachefile(&cachefile);
}
/* Masks. */
for (Mask &mask : bmain_->masks) {
build_mask(&mask);
}
/* Movie clips. */
for (MovieClip &clip : bmain_->movieclips) {
build_movieclip(&clip);
}
/* Material override. */
if (view_layer->mat_override != nullptr) {
build_material(view_layer->mat_override);
}
/* World override */
if (view_layer->world_override != nullptr) {
build_world(view_layer->world_override);
}
/* Freestyle linesets. */
for (FreestyleLineSet &fls : view_layer->freestyle_config.linesets) {
build_freestyle_lineset(&fls);
}
/* Scene parameters, compositor and such. */
build_scene_compositor(scene);
build_scene_parameters(scene);
/* Make final scene evaluation dependent on view layer evaluation. */
OperationKey scene_view_layer_key(
&scene->id, NodeType::LAYER_COLLECTIONS, OperationCode::VIEW_LAYER_EVAL);
ComponentKey scene_eval_key(&scene->id, NodeType::SCENE);
add_relation(scene_view_layer_key, scene_eval_key, "View Layer -> Scene Eval");
/* Sequencer. */
if (linked_state == DEG_ID_LINKED_DIRECTLY) {
build_scene_audio(scene);
build_scene_sequencer(scene);
}
/* Build all set scenes. */
if (scene->set != nullptr) {
ViewLayer *set_view_layer = BKE_view_layer_default_render(scene->set);
build_view_layer(scene->set, set_view_layer, DEG_ID_LINKED_VIA_SET);
}
}
} // namespace blender::deg

View File

@@ -0,0 +1,100 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include <deque>
#include "intern/builder/deg_builder_remove_noop.h"
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_operation.hh"
#include "intern/debug/deg_debug.h"
#include "intern/depsgraph.hh"
#include "intern/depsgraph_relation.hh"
#include "DEG_depsgraph_debug.hh"
namespace blender::deg {
static inline bool is_unused_noop(OperationNode *op_node)
{
if (op_node == nullptr) {
return false;
}
if (op_node->flag & OperationFlag::DEPSOP_FLAG_PINNED) {
return false;
}
return op_node->is_noop() && op_node->outlinks.is_empty();
}
static inline bool is_removable_relation(const Relation *relation)
{
if (relation->from->type != NodeType::OPERATION || relation->to->type != NodeType::OPERATION) {
return true;
}
const OperationNode *operation_from = static_cast<OperationNode *>(relation->from);
const OperationNode *operation_to = static_cast<OperationNode *>(relation->to);
/* If the relation connects two different IDs there is a high risk that the removal of the
* relation will make it so visibility flushing is not possible at runtime. This happens with
* relations like the DoF on camera of custom shape on bones: such relation do not lead to an
* actual depsgraph evaluation operation as they are handled on render engine level.
*
* The indirectly linked objects could have some of their components invisible as well, so
* also keep relations which connect different components of the same object so that visibility
* tracking happens correct in those cases as well. */
return operation_from->owner == operation_to->owner;
}
void deg_graph_remove_unused_noops(Depsgraph *graph)
{
std::deque<OperationNode *> queue;
for (OperationNode *node : graph->operations) {
if (is_unused_noop(node)) {
queue.push_back(node);
}
}
Vector<Relation *> relations_to_remove;
while (!queue.empty()) {
OperationNode *to_remove = queue.front();
queue.pop_front();
for (Relation *rel_in : to_remove->inlinks) {
if (!is_removable_relation(rel_in)) {
continue;
}
Node *dependency = rel_in->from;
relations_to_remove.append(rel_in);
/* Queue parent no-op node that has now become unused. */
OperationNode *operation = dependency->get_exit_operation();
if (is_unused_noop(operation)) {
queue.push_back(operation);
}
}
/* TODO(Sybren): Remove the node itself. */
}
/* Remove the relations. */
for (Relation *relation : relations_to_remove) {
relation->unlink();
}
DEG_DEBUG_PRINTF((blender::Depsgraph *)graph,
BUILD,
"Removed %d relations to no-op nodes\n",
int(relations_to_remove.size()));
}
} // namespace blender::deg

View File

@@ -0,0 +1,18 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
namespace blender::deg {
struct Depsgraph;
/* Remove all no-op nodes that have zero outgoing relations. */
void deg_graph_remove_unused_noops(Depsgraph *graph);
} // namespace blender::deg

View File

@@ -0,0 +1,416 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/builder/deg_builder_rna.h"
#include <cstring>
#include "BLI_listbase.h"
#include "BLI_string.h"
#include "BLI_utildefines.h"
#include "DNA_action_types.h"
#include "DNA_constraint_types.h"
#include "DNA_key_types.h"
#include "DNA_object_types.h"
#include "BKE_constraint.h"
#include "RNA_access.hh"
#include "RNA_prototypes.hh"
#include "intern/builder/deg_builder.h"
#include "intern/depsgraph.hh"
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_component.hh"
#include "intern/node/deg_node_id.hh"
#include "intern/node/deg_node_operation.hh"
namespace blender::deg {
/* ********************************* ID Data ******************************** */
class RNANodeQueryIDData {
public:
explicit RNANodeQueryIDData(const ID *id) : id_(id) {}
~RNANodeQueryIDData()
{
delete constraint_to_pchan_map_;
}
const bPoseChannel *get_pchan_for_constraint(const bConstraint *constraint)
{
ensure_constraint_to_pchan_map();
return constraint_to_pchan_map_->lookup_default(constraint, nullptr);
}
void ensure_constraint_to_pchan_map()
{
if (constraint_to_pchan_map_ != nullptr) {
return;
}
BLI_assert(GS(id_->name) == ID_OB);
const Object *object = reinterpret_cast<const Object *>(id_);
constraint_to_pchan_map_ = new Map<const bConstraint *, const bPoseChannel *>();
if (object->pose != nullptr) {
for (const bPoseChannel &pchan : object->pose->chanbase) {
for (const bConstraint &constraint : pchan.constraints) {
constraint_to_pchan_map_->add_new(&constraint, &pchan);
}
}
}
}
protected:
/* ID this data corresponds to. */
const ID *id_;
/* indexed by bConstraint*, returns pose channel which contains that
* constraint. */
Map<const bConstraint *, const bPoseChannel *> *constraint_to_pchan_map_ = nullptr;
};
/* ***************************** Node Identifier **************************** */
RNANodeIdentifier::RNANodeIdentifier()
: id(nullptr),
type(NodeType::UNDEFINED),
component_name(""),
operation_code(OperationCode::OPERATION),
operation_name(),
operation_name_tag(-1)
{
}
bool RNANodeIdentifier::is_valid() const
{
return id != nullptr && type != NodeType::UNDEFINED;
}
/* ********************************** Query ********************************* */
RNANodeQuery::RNANodeQuery(Depsgraph *depsgraph, DepsgraphBuilder *builder)
: depsgraph_(depsgraph), builder_(builder)
{
}
RNANodeQuery::~RNANodeQuery() = default;
Node *RNANodeQuery::find_node(const PointerRNA *ptr,
const PropertyRNA *prop,
RNAPointerSource source)
{
const RNANodeIdentifier node_identifier = construct_node_identifier(ptr, prop, source);
if (!node_identifier.is_valid()) {
return nullptr;
}
IDNode *id_node = depsgraph_->find_id_node(node_identifier.id);
if (id_node == nullptr) {
return nullptr;
}
ComponentNode *comp_node = id_node->find_component(node_identifier.type,
node_identifier.component_name);
if (comp_node == nullptr) {
return nullptr;
}
if (node_identifier.operation_code == OperationCode::OPERATION) {
return comp_node;
}
return comp_node->find_operation(node_identifier.operation_code,
node_identifier.operation_name,
node_identifier.operation_name_tag);
}
bool RNANodeQuery::contains(const char *prop_identifier, const char *rna_path_component)
{
const char *substr = strstr(prop_identifier, rna_path_component);
if (substr == nullptr) {
return false;
}
/* If `substr != prop_identifier`, it means that the sub-string is found further in
* `prop_identifier`, and that thus index -1 is a valid memory location. */
const bool start_ok = substr == prop_identifier || substr[-1] == '.';
if (!start_ok) {
return false;
}
const size_t component_len = strlen(rna_path_component);
const bool end_ok = ELEM(substr[component_len], '\0', '.', '[');
return end_ok;
}
RNANodeIdentifier RNANodeQuery::construct_node_identifier(const PointerRNA *ptr,
const PropertyRNA *prop,
RNAPointerSource source)
{
RNANodeIdentifier node_identifier;
if (ptr->type == nullptr) {
return node_identifier;
}
/* Set default values for returns. */
node_identifier.id = ptr->owner_id;
node_identifier.component_name = "";
node_identifier.operation_code = OperationCode::OPERATION;
node_identifier.operation_name = "";
node_identifier.operation_name_tag = -1;
/* Handling of commonly known scenarios. */
if (rna_prop_affects_parameters_node(ptr, prop)) {
/* Custom properties of bones are placed in their components to improve granularity. */
if (RNA_struct_is_a(ptr->type, RNA_PoseBone)) {
const bPoseChannel *pchan = static_cast<const bPoseChannel *>(ptr->data);
node_identifier.type = NodeType::BONE;
node_identifier.component_name = pchan->name;
}
else {
node_identifier.type = NodeType::PARAMETERS;
}
node_identifier.operation_code = OperationCode::ID_PROPERTY;
node_identifier.operation_name = RNA_property_identifier(prop);
return node_identifier;
}
if (ptr->type == RNA_PoseBone) {
const bPoseChannel *pchan = static_cast<const bPoseChannel *>(ptr->data);
/* Bone - generally, we just want the bone component. */
node_identifier.type = NodeType::BONE;
node_identifier.component_name = pchan->name;
/* However check property name for special handling. */
if (prop != nullptr) {
Object *object = reinterpret_cast<Object *>(node_identifier.id);
const char *prop_name = RNA_property_identifier(prop);
/* B-Bone properties should connect to the final operation. */
if (STRPREFIX(prop_name, "bbone_")) {
if (builder_->check_pchan_has_bbone_segments(object, pchan)) {
node_identifier.operation_code = OperationCode::BONE_SEGMENTS;
}
else {
node_identifier.operation_code = OperationCode::BONE_DONE;
}
}
/* Final transform properties go to the Done node for the exit. */
else if (STR_ELEM(prop_name, "head", "tail", "length") || STRPREFIX(prop_name, "matrix")) {
if (source == RNAPointerSource::EXIT) {
node_identifier.operation_code = OperationCode::BONE_DONE;
}
}
/* Bone visibility has its own depsgraph node. */
else if (STREQ(prop_name, "hide")) {
node_identifier.operation_code = OperationCode::BONE_VISIBILITY;
}
/* And other properties can always go to the entry operation. */
else {
node_identifier.operation_code = OperationCode::BONE_LOCAL;
}
}
return node_identifier;
}
if (ptr->type == RNA_Bone) {
/* Armature-level bone mapped to Armature Eval, and thus Pose Init.
* Drivers have special code elsewhere that links them to the pose
* bone components, instead of using this generic code. */
node_identifier.type = NodeType::ARMATURE;
node_identifier.operation_code = OperationCode::ARMATURE_EVAL;
/* If trying to look up via an Object, e.g. due to lookup via
* obj.pose.bones[].bone in a driver attached to the Object,
* redirect to its data. */
if (GS(node_identifier.id->name) == ID_OB) {
node_identifier.id = id_cast<Object *>(node_identifier.id)->data;
}
return node_identifier;
}
const char *prop_identifier = prop != nullptr ?
RNA_property_identifier(const_cast<PropertyRNA *>(prop)) :
"";
if (RNA_struct_is_a(ptr->type, RNA_Constraint)) {
const Object *object = reinterpret_cast<const Object *>(ptr->owner_id);
const bConstraint *constraint = static_cast<const bConstraint *>(ptr->data);
RNANodeQueryIDData *id_data = ensure_id_data(&object->id);
/* Check whether is object or bone constraint. */
/* NOTE: Currently none of the area can address transform of an object
* at a given constraint, but for rigging one might use constraint
* influence to be used to drive some corrective shape keys or so. */
const bPoseChannel *pchan = id_data->get_pchan_for_constraint(constraint);
if (pchan == nullptr) {
node_identifier.type = NodeType::TRANSFORM;
node_identifier.operation_code = OperationCode::TRANSFORM_LOCAL;
}
else {
node_identifier.type = NodeType::BONE;
node_identifier.operation_code = OperationCode::BONE_LOCAL;
node_identifier.component_name = pchan->name;
}
return node_identifier;
}
if (ELEM(ptr->type, RNA_ConstraintTarget, RNA_ConstraintTargetBone)) {
Object *object = reinterpret_cast<Object *>(ptr->owner_id);
bConstraintTarget *tgt = static_cast<bConstraintTarget *>(ptr->data);
/* Check whether is object or bone constraint. */
bPoseChannel *pchan = nullptr;
bConstraint *con = BKE_constraint_find_from_target(object, tgt, &pchan);
if (con != nullptr) {
if (pchan != nullptr) {
node_identifier.type = NodeType::BONE;
node_identifier.operation_code = OperationCode::BONE_LOCAL;
node_identifier.component_name = pchan->name;
}
else {
node_identifier.type = NodeType::TRANSFORM;
node_identifier.operation_code = OperationCode::TRANSFORM_LOCAL;
}
return node_identifier;
}
}
else if (RNA_struct_is_a(ptr->type, RNA_Modifier) &&
(contains(prop_identifier, "show_viewport") ||
contains(prop_identifier, "show_render")))
{
node_identifier.type = NodeType::GEOMETRY;
node_identifier.operation_code = OperationCode::VISIBILITY;
return node_identifier;
}
else if (RNA_struct_is_a(ptr->type, RNA_Mesh) || RNA_struct_is_a(ptr->type, RNA_Modifier) ||
RNA_struct_is_a(ptr->type, RNA_Spline) || RNA_struct_is_a(ptr->type, RNA_TextBox) ||
RNA_struct_is_a(ptr->type, RNA_AnnotationLayer) ||
RNA_struct_is_a(ptr->type, RNA_LatticePoint) ||
RNA_struct_is_a(ptr->type, RNA_MeshUVLoop) ||
RNA_struct_is_a(ptr->type, RNA_MeshLoopColor) ||
RNA_struct_is_a(ptr->type, RNA_VertexGroupElement) ||
RNA_struct_is_a(ptr->type, RNA_ShaderFx) ||
(prop &&
RNA_property_flag(const_cast<PropertyRNA *>(prop)) & PROP_FORCE_GEOMETRY_EVAL) != 0)
{
/* When modifier is used as FROM operation this is likely referencing to
* the property (for example, modifier's influence).
* But when it's used as TO operation, this is geometry component. */
switch (source) {
case RNAPointerSource::ENTRY:
node_identifier.type = NodeType::GEOMETRY;
break;
case RNAPointerSource::EXIT:
node_identifier.type = NodeType::PARAMETERS;
node_identifier.operation_code = OperationCode::PARAMETERS_EVAL;
break;
}
return node_identifier;
}
else if (ptr->type == RNA_Object) {
/* Transforms props? */
if (prop != nullptr) {
/* TODO(sergey): How to optimize this? */
if (contains(prop_identifier, "location") || contains(prop_identifier, "matrix_basis") ||
contains(prop_identifier, "matrix_channel") ||
contains(prop_identifier, "matrix_inverse") ||
contains(prop_identifier, "matrix_local") ||
contains(prop_identifier, "matrix_parent_inverse") ||
contains(prop_identifier, "matrix_world") ||
contains(prop_identifier, "rotation_axis_angle") ||
contains(prop_identifier, "rotation_euler") ||
contains(prop_identifier, "rotation_mode") ||
contains(prop_identifier, "rotation_quaternion") || contains(prop_identifier, "scale") ||
contains(prop_identifier, "delta_location") ||
contains(prop_identifier, "delta_rotation_euler") ||
contains(prop_identifier, "delta_rotation_quaternion") ||
contains(prop_identifier, "delta_scale"))
{
node_identifier.type = NodeType::TRANSFORM;
return node_identifier;
}
if (contains(prop_identifier, "data")) {
/* We access object.data, most likely a geometry.
* Might be a bone tho. */
node_identifier.type = NodeType::GEOMETRY;
return node_identifier;
}
if (STR_ELEM(prop_identifier, "hide_viewport", "hide_render")) {
node_identifier.type = NodeType::OBJECT_FROM_LAYER;
return node_identifier;
}
if (STREQ(prop_identifier, "dimensions")) {
node_identifier.type = NodeType::PARAMETERS;
node_identifier.operation_code = OperationCode::DIMENSIONS;
return node_identifier;
}
}
}
else if (ptr->type == RNA_ShapeKey) {
KeyBlock *key_block = static_cast<KeyBlock *>(ptr->data);
node_identifier.id = ptr->owner_id;
node_identifier.type = NodeType::PARAMETERS;
node_identifier.operation_code = OperationCode::PARAMETERS_EVAL;
node_identifier.operation_name = key_block->name;
return node_identifier;
}
else if (ptr->type == RNA_Key) {
node_identifier.id = ptr->owner_id;
node_identifier.type = NodeType::GEOMETRY;
return node_identifier;
}
else if (RNA_struct_is_a(ptr->type, RNA_Strip)) {
/* Sequencer strip */
node_identifier.type = NodeType::SEQUENCER;
return node_identifier;
}
else if (RNA_struct_is_a(ptr->type, RNA_NodeSocket)) {
node_identifier.type = NodeType::NTREE_OUTPUT;
return node_identifier;
}
else if (RNA_struct_is_a(ptr->type, RNA_ShaderNode)) {
node_identifier.type = NodeType::SHADING;
return node_identifier;
}
else if (ELEM(ptr->type, RNA_Curve, RNA_TextCurve)) {
node_identifier.id = ptr->owner_id;
node_identifier.type = NodeType::GEOMETRY;
return node_identifier;
}
else if (ELEM(ptr->type, RNA_BezierSplinePoint, RNA_SplinePoint)) {
node_identifier.id = ptr->owner_id;
node_identifier.type = NodeType::GEOMETRY;
return node_identifier;
}
else if (RNA_struct_is_a(ptr->type, RNA_ImageUser)) {
if (GS(node_identifier.id->name) == ID_NT) {
node_identifier.type = NodeType::IMAGE_ANIMATION;
node_identifier.operation_code = OperationCode::IMAGE_ANIMATION;
return node_identifier;
}
}
else if (ELEM(ptr->type, RNA_MeshVertex, RNA_MeshEdge, RNA_MeshLoop, RNA_MeshPolygon)) {
node_identifier.type = NodeType::GEOMETRY;
return node_identifier;
}
if (prop != nullptr) {
/* All unknown data effectively falls under "parameter evaluation". */
node_identifier.type = NodeType::PARAMETERS;
node_identifier.operation_code = OperationCode::PARAMETERS_EVAL;
node_identifier.operation_name = "";
node_identifier.operation_name_tag = -1;
return node_identifier;
}
return node_identifier;
}
RNANodeQueryIDData *RNANodeQuery::ensure_id_data(const ID *id)
{
std::unique_ptr<RNANodeQueryIDData> &id_data = id_data_map_.lookup_or_add_cb(
id, [&]() { return std::make_unique<RNANodeQueryIDData>(id); });
return id_data.get();
}
bool rna_prop_affects_parameters_node(const PointerRNA *ptr, const PropertyRNA *prop)
{
return prop != nullptr && RNA_property_is_idprop(prop) &&
/* ID properties in the geometry nodes modifier don't affect that parameters node.
* Instead they affect the modifier and therefore the geometry node directly. */
!RNA_struct_is_a(ptr->type, RNA_NodesModifier);
}
} // namespace blender::deg

View File

@@ -0,0 +1,104 @@
/* SPDX-FileCopyrightText: 2019 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_operation.hh"
#include "BLI_map.hh"
#include <memory>
namespace blender {
struct ID;
struct PointerRNA;
struct PropertyRNA;
namespace deg {
struct Depsgraph;
struct Node;
class RNANodeQueryIDData;
class DepsgraphBuilder;
/* For queries which gives operation node or key defines whether we are
* interested in a result of the given property or whether we are linking some
* dependency to that property. */
enum class RNAPointerSource {
/* Query will return pointer to an entry operation of component which is
* responsible for evaluation of the given property. */
ENTRY,
/* Query will return pointer to an exit operation of component which is
* responsible for evaluation of the given property.
* More precisely, it will return operation at which the property is known
* to be evaluated. */
EXIT,
};
/* A helper structure which wraps all fields needed to find a node inside of
* the dependency graph. */
class RNANodeIdentifier {
public:
RNANodeIdentifier();
/* Check whether this identifier is valid and usable. */
bool is_valid() const;
ID *id;
NodeType type;
const char *component_name;
OperationCode operation_code;
const char *operation_name;
int operation_name_tag;
};
/* Helper class which performs optimized lookups of a node within a given
* dependency graph which satisfies given RNA pointer or RNA path. */
class RNANodeQuery {
public:
RNANodeQuery(Depsgraph *depsgraph, DepsgraphBuilder *builder);
~RNANodeQuery();
Node *find_node(const PointerRNA *ptr, const PropertyRNA *prop, RNAPointerSource source);
protected:
Depsgraph *depsgraph_;
DepsgraphBuilder *builder_;
/* Indexed by an ID, returns RNANodeQueryIDData associated with that ID. */
Map<const ID *, std::unique_ptr<RNANodeQueryIDData>> id_data_map_;
/* Construct identifier of the node which corresponds given configuration
* of RNA property. */
RNANodeIdentifier construct_node_identifier(const PointerRNA *ptr,
const PropertyRNA *prop,
RNAPointerSource source);
/* Make sure ID data exists for the given ID, and returns it. */
RNANodeQueryIDData *ensure_id_data(const ID *id);
/* Check whether prop_identifier contains rna_path_component.
*
* This checks more than a sub-string:
*
* prop_identifier contains(prop_identifier, "location")
* ------------------------ -------------------------------------
* location true
* ["test_location"] false
* pose["bone"].location true
* pose["bone"].location.x true
*/
static bool contains(const char *prop_identifier, const char *rna_path_component);
};
bool rna_prop_affects_parameters_node(const PointerRNA *ptr, const PropertyRNA *prop);
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,45 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "BKE_gtest_base.hh"
#include "intern/builder/deg_builder_rna.h"
#include "testing/testing.h"
namespace blender::deg::tests {
class TestableRNANodeQuery : public RNANodeQuery {
public:
static bool contains(const char *prop_identifier, const char *rna_path_component)
{
return RNANodeQuery::contains(prop_identifier, rna_path_component);
}
};
class DegBuilderRNATest : public bke::BlenderGTestBase {};
TEST_F(DegBuilderRNATest, contains)
{
EXPECT_TRUE(TestableRNANodeQuery::contains("location", "location"));
EXPECT_TRUE(TestableRNANodeQuery::contains("location.x", "location"));
EXPECT_TRUE(TestableRNANodeQuery::contains("pose.bone[\"blork\"].location", "location"));
EXPECT_TRUE(TestableRNANodeQuery::contains("pose.bone[\"blork\"].location.x", "location"));
EXPECT_TRUE(TestableRNANodeQuery::contains("pose.bone[\"blork\"].location[0]", "location"));
EXPECT_FALSE(TestableRNANodeQuery::contains("", "location"));
EXPECT_FALSE(TestableRNANodeQuery::contains("locatio", "location"));
EXPECT_FALSE(TestableRNANodeQuery::contains("locationnn", "location"));
EXPECT_FALSE(TestableRNANodeQuery::contains("test_location", "location"));
EXPECT_FALSE(TestableRNANodeQuery::contains("location_test", "location"));
EXPECT_FALSE(TestableRNANodeQuery::contains("test_location_test", "location"));
EXPECT_FALSE(TestableRNANodeQuery::contains("pose.bone[\"location\"].scale", "location"));
EXPECT_FALSE(TestableRNANodeQuery::contains("pose.bone[\"location\"].scale[0]", "location"));
}
} // namespace blender::deg::tests

View File

@@ -0,0 +1,95 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "intern/builder/deg_builder_stack.h"
#include <iomanip>
#include <ios>
#include <iostream>
#include "BKE_idtype.hh"
#include "DNA_ID.h"
#include "DNA_action_types.h"
#include "DNA_constraint_types.h"
#include "DNA_modifier_types.h"
namespace blender::deg {
/* Spacing between adjacent columns, in number of spaces. */
constexpr int kColumnSpacing = 4;
/* Width of table columns including column padding.
* The type column width is a guesstimate based on "Particle Settings" with some extra padding. */
constexpr int kPrintDepthWidth = 5 + kColumnSpacing;
constexpr int kPrintTypeWidth = 21 + kColumnSpacing;
namespace {
/* NOTE: Depth column printing is already taken care of. */
void print(std::ostream &stream, const ID *id)
{
const IDTypeInfo *id_type_info = BKE_idtype_get_info_from_id(id);
stream << std::setw(kPrintTypeWidth) << id_type_info->name << (id->name + 2) << "\n";
}
void print(std::ostream &stream, const bConstraint *constraint)
{
stream << std::setw(kPrintTypeWidth) << ("Constraint") << constraint->name << "\n";
}
void print(std::ostream &stream, const ModifierData *modifier_data)
{
stream << std::setw(kPrintTypeWidth) << ("Modifier") << modifier_data->name << "\n";
}
void print(std::ostream &stream, const bPoseChannel *pchan)
{
stream << std::setw(kPrintTypeWidth) << ("Pose Channel") << pchan->name << "\n";
}
} // namespace
void BuilderStack::print_backtrace(std::ostream &stream)
{
const std::ios_base::fmtflags old_flags(stream.flags());
stream << std::left;
stream << std::setw(kPrintDepthWidth) << "Depth" << std::setw(kPrintTypeWidth) << "Type"
<< "Name"
<< "\n";
stream << std::setw(kPrintDepthWidth) << "-----" << std::setw(kPrintTypeWidth) << "----"
<< "----"
<< "\n";
int depth = 1;
for (const Entry &entry : stack_) {
stream << std::setw(kPrintDepthWidth) << depth;
++depth;
if (entry.id_ != nullptr) {
print(stream, entry.id_);
}
else if (entry.constraint_ != nullptr) {
print(stream, entry.constraint_);
}
else if (entry.modifier_data_ != nullptr) {
print(stream, entry.modifier_data_);
}
else if (entry.pchan_ != nullptr) {
print(stream, entry.pchan_);
}
}
stream.flags(old_flags);
}
} // namespace blender::deg

View File

@@ -0,0 +1,133 @@
/* SPDX-FileCopyrightText: 2022 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "BLI_vector.hh"
namespace blender {
struct ID;
struct bConstraint;
struct bPoseChannel;
struct ModifierData;
namespace deg {
/* This class keeps track of the builder calls nesting, allowing to unroll them back and provide a
* clue about how the builder made it to its current state.
*
* The tracing is based on the builder giving a trace clues to the stack. Typical usage is:
*
* void DepsgraphRelationBuilder::my_id_builder(ID *id)
* {
* if (built_map_.check_is_built_and_tag(id)) {
* return;
* }
*
* const BuilderStack::ScopedEntry stack_entry = stack_.trace(*id);
*
* ...
* }
*/
class BuilderStack {
public:
/* Entry of the backtrace.
* A cheap-to-construct wrapper which allows to gather a proper string representation whenever
* the stack is printed. */
class Entry {
public:
explicit Entry(const ID &id) : id_(&id) {}
explicit Entry(const bConstraint &constraint) : constraint_(&constraint) {}
explicit Entry(const bPoseChannel &pchan) : pchan_(&pchan) {}
explicit Entry(const ModifierData &modifier_data) : modifier_data_(&modifier_data) {}
private:
friend class BuilderStack;
const ID *id_ = nullptr;
const bConstraint *constraint_ = nullptr;
const ModifierData *modifier_data_ = nullptr;
const bPoseChannel *pchan_ = nullptr;
};
using Stack = Vector<Entry>;
/* A helper class to provide a RAII style of tracing. It is constructed by the
* `BuilderStack::trace` (which pushes entry to the stack), and upon destruction of this object
* the corresponding entry is popped from the stack.
*
* The goal of this `ScopedEntry` is to free developers from worrying about removing entries from
* the stack whenever leaving a builder step scope. */
class ScopedEntry {
public:
/* Delete copy constructor and operator: scoped entries are only supposed to be constructed
* once and never copied. */
ScopedEntry(const ScopedEntry &other) = delete;
ScopedEntry &operator=(const ScopedEntry &other) = delete;
/* Move semantic. */
ScopedEntry(ScopedEntry &&other) noexcept : stack_(other.stack_)
{
other.stack_ = nullptr;
}
ScopedEntry &operator=(ScopedEntry &&other)
{
if (this == &other) {
return *this;
}
stack_ = other.stack_;
other.stack_ = nullptr;
return *this;
}
~ScopedEntry()
{
/* Stack will become nullptr when the entry was moved somewhere else. */
if (stack_ != nullptr) {
BLI_assert(!stack_->is_empty());
stack_->pop_last();
}
}
private:
friend BuilderStack;
explicit ScopedEntry(Stack &stack) : stack_(&stack) {}
Stack *stack_;
};
BuilderStack() = default;
~BuilderStack() = default;
bool is_empty() const
{
return stack_.is_empty();
}
void print_backtrace(std::ostream &stream);
template<class... Args> ScopedEntry trace(const Args &...args)
{
stack_.append_as(args...);
return ScopedEntry(stack_);
}
private:
Stack stack_;
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,97 @@
/* SPDX-FileCopyrightText: 2015 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#include "DEG_depsgraph_debug.hh"
#include "intern/builder/deg_builder_transitive.h"
#include "intern/node/deg_node.hh"
#include "intern/node/deg_node_component.hh"
#include "intern/node/deg_node_operation.hh"
#include "intern/debug/deg_debug.h"
#include "intern/depsgraph.hh"
#include "intern/depsgraph_relation.hh"
namespace blender::deg {
/* -------------------------------------------------- */
/* Performs a transitive reduction to remove redundant relations.
* https://en.wikipedia.org/wiki/Transitive_reduction
*
* XXX The current implementation is somewhat naive and has O(V*E) worst case
* runtime.
* A more optimized algorithm can be implemented later, e.g.
*
* http://www.sciencedirect.com/science/article/pii/0304397588900321/pdf?md5=3391e309b708b6f9cdedcd08f84f4afc&pid=1-s2.0-0304397588900321-main.pdf
*
* Care has to be taken to make sure the algorithm can handle the cyclic case
* too! (unless we can to prevent this case early on).
*/
enum {
OP_VISITED = 1,
OP_REACHABLE = 2,
};
static void deg_graph_tag_paths_recursive(Node *node)
{
if (node->custom_flags & OP_VISITED) {
return;
}
node->custom_flags |= OP_VISITED;
for (Relation *rel : node->inlinks) {
deg_graph_tag_paths_recursive(rel->from);
/* Do this only in inlinks loop, so the target node does not get
* flagged. */
rel->from->custom_flags |= OP_REACHABLE;
}
}
void deg_graph_transitive_reduction(Depsgraph *graph)
{
int num_removed_relations = 0;
Vector<Relation *> relations_to_remove;
for (OperationNode *target : graph->operations) {
/* Clear tags. */
for (OperationNode *node : graph->operations) {
node->custom_flags = 0;
}
/* Mark nodes from which we can reach the target
* start with children, so the target node and direct children are not
* flagged. */
target->custom_flags |= OP_VISITED;
for (Relation *rel : target->inlinks) {
deg_graph_tag_paths_recursive(rel->from);
}
/* Remove redundant paths to the target. */
for (Relation *rel : target->inlinks) {
if (rel->from->type == NodeType::TIMESOURCE) {
/* HACK: time source nodes don't get "custom_flags" flag
* set/cleared. */
/* TODO: there will be other types in future, so iterators above
* need modifying. */
continue;
}
if (rel->from->custom_flags & OP_REACHABLE) {
relations_to_remove.append(rel);
}
}
for (Relation *rel : relations_to_remove) {
rel->unlink();
}
num_removed_relations += relations_to_remove.size();
relations_to_remove.clear();
}
DEG_DEBUG_PRINTF(
(blender::Depsgraph *)graph, BUILD, "Removed %d relations\n", num_removed_relations);
}
} // namespace blender::deg

View File

@@ -0,0 +1,18 @@
/* SPDX-FileCopyrightText: 2015 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
namespace blender::deg {
struct Depsgraph;
/* Performs a transitive reduction to remove redundant relations. */
void deg_graph_transitive_reduction(Depsgraph *graph);
} // namespace blender::deg

View File

@@ -0,0 +1,181 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "pipeline.h"
#include "BLI_listbase.h"
#include "BLI_time.h"
#include "CLG_log.h"
#include "BKE_global.hh"
#include "DNA_scene_types.h"
#include "deg_builder_cycle.h"
#include "deg_builder_nodes.h"
#include "deg_builder_relations.h"
#include "deg_builder_transitive.h"
namespace blender {
static CLG_LogRef LOG = {"depsgraph"};
namespace deg {
static bool need_sanity_checks()
{
#if !defined(NDEBUG)
return true;
#endif
return G.debug & G_DEBUG_DEPSGRAPH_BUILD;
}
static void do_sanity_checks(const Depsgraph *graph,
const Set<const ID *> &ids_build_by_node_builder,
const Set<const ID *> &ids_build_by_relations_builder)
{
if (ids_build_by_node_builder != ids_build_by_relations_builder) {
CLOG_ERROR(&LOG, "Some IDs missed nodes or relations building.");
for (const ID *id_iter : ids_build_by_node_builder) {
if (!ids_build_by_relations_builder.contains(id_iter)) {
CLOG_ERROR(&LOG, "\t- ID '%s' was not built for relations.", id_iter->name);
}
}
for (const ID *id_iter : ids_build_by_relations_builder) {
if (!ids_build_by_node_builder.contains(id_iter)) {
CLOG_ERROR(&LOG, "\t- ID '%s' was not built for nodes.", id_iter->name);
}
}
}
for (const IDNode *id_node : graph->id_nodes) {
if (!ids_build_by_node_builder.contains(id_node->id_orig)) {
CLOG_ERROR(&LOG,
"\t+ ID '%s' is in depsgraph but was not built for nodes.",
id_node->id_orig->name);
}
if (!ids_build_by_relations_builder.contains(id_node->id_orig)) {
CLOG_ERROR(&LOG,
"\t+ ID '%s' is in depsgraph but was not built for relations.",
id_node->id_orig->name);
}
}
for (const ID *id : ids_build_by_node_builder) {
if (graph->find_id_node(id) == nullptr) {
CLOG_ERROR(&LOG, "\t* ID '%s' was built for nodes but is not in Depsgraph.", id->name);
}
}
for (const ID *id : ids_build_by_relations_builder) {
if (graph->find_id_node(id) == nullptr) {
CLOG_ERROR(&LOG, "\t* ID '%s' was built for relations but is not in Depsgraph.", id->name);
}
}
}
AbstractBuilderPipeline::AbstractBuilderPipeline(blender::Depsgraph *graph)
: deg_graph_(reinterpret_cast<Depsgraph *>(graph)),
bmain_(deg_graph_->bmain),
scene_(deg_graph_->scene),
view_layer_(deg_graph_->view_layer)
{
}
void AbstractBuilderPipeline::build()
{
double start_time = 0.0;
if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) {
start_time = BLI_time_now_seconds();
}
build_step_sanity_check();
build_step_nodes();
build_step_relations();
build_step_finalize();
if (need_sanity_checks()) {
do_sanity_checks(deg_graph_, ids_build_by_node_builder_, ids_build_by_relations_builder_);
}
if (G.debug & (G_DEBUG_DEPSGRAPH_BUILD | G_DEBUG_DEPSGRAPH_TIME)) {
printf("Depsgraph built in %f seconds.\n", BLI_time_now_seconds() - start_time);
}
}
void AbstractBuilderPipeline::build_step_sanity_check()
{
BLI_assert(BLI_findindex(&scene_->view_layers, view_layer_) != -1);
BLI_assert(deg_graph_->scene == scene_);
BLI_assert(deg_graph_->view_layer == view_layer_);
}
void AbstractBuilderPipeline::build_step_nodes()
{
/* Generate all the nodes in the graph first */
std::unique_ptr<DepsgraphNodeBuilder> node_builder = construct_node_builder();
node_builder->begin_build();
build_nodes(*node_builder);
node_builder->end_build();
if (need_sanity_checks()) {
ids_build_by_node_builder_ = node_builder->get_built_ids();
}
}
void AbstractBuilderPipeline::build_step_relations()
{
/* Hook up relationships between operations - to determine evaluation order. */
std::unique_ptr<DepsgraphRelationBuilder> relation_builder = construct_relation_builder();
relation_builder->begin_build();
build_relations(*relation_builder);
relation_builder->build_copy_on_write_relations();
relation_builder->build_driver_relations();
if (need_sanity_checks()) {
ids_build_by_relations_builder_ = relation_builder->get_built_ids();
}
}
void AbstractBuilderPipeline::build_step_finalize()
{
/* Detect and solve cycles. */
deg_graph_detect_cycles(deg_graph_);
/* Simplify the graph by removing redundant relations (to optimize
* traversal later). */
/* TODO: it would be useful to have an option to disable this in cases where
* it is causing trouble. */
if (G.debug_value == 799) {
deg_graph_transitive_reduction(deg_graph_);
}
/* Store pointers to commonly used evaluated datablocks. */
deg_graph_->scene_cow = reinterpret_cast<Scene *>(
deg_graph_->get_cow_id(&deg_graph_->scene->id));
/* Flush visibility layer and re-schedule nodes for update. */
deg_graph_build_finalize(bmain_, deg_graph_);
DEG_graph_tag_on_visible_update(reinterpret_cast<blender::Depsgraph *>(deg_graph_), false);
#if 0
if (!DEG_debug_consistency_check(deg_graph_)) {
printf("Consistency validation failed, ABORTING!\n");
abort();
}
#endif
/* Relations are up to date. */
deg_graph_->need_update_relations = false;
}
std::unique_ptr<DepsgraphNodeBuilder> AbstractBuilderPipeline::construct_node_builder()
{
return std::make_unique<DepsgraphNodeBuilder>(bmain_, deg_graph_, &builder_cache_);
}
std::unique_ptr<DepsgraphRelationBuilder> AbstractBuilderPipeline::construct_relation_builder()
{
return std::make_unique<DepsgraphRelationBuilder>(bmain_, deg_graph_, &builder_cache_);
}
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,66 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "deg_builder_cache.h"
#include "BLI_set.hh"
namespace blender {
struct Depsgraph;
struct Main;
struct Scene;
struct ViewLayer;
namespace deg {
struct Depsgraph;
class DepsgraphNodeBuilder;
class DepsgraphRelationBuilder;
/* Base class for Depsgraph Builder pipelines.
*
* Basically it runs through the following steps:
* - sanity check
* - build nodes
* - build relations
* - finalize
*/
class AbstractBuilderPipeline {
public:
AbstractBuilderPipeline(blender::Depsgraph *graph);
virtual ~AbstractBuilderPipeline() = default;
void build();
protected:
Depsgraph *deg_graph_;
Main *bmain_;
Scene *scene_;
ViewLayer *view_layer_;
DepsgraphBuilderCache builder_cache_;
virtual std::unique_ptr<DepsgraphNodeBuilder> construct_node_builder();
virtual std::unique_ptr<DepsgraphRelationBuilder> construct_relation_builder();
virtual void build_step_sanity_check();
void build_step_nodes();
void build_step_relations();
void build_step_finalize();
virtual void build_nodes(DepsgraphNodeBuilder &node_builder) = 0;
virtual void build_relations(DepsgraphRelationBuilder &relation_builder) = 0;
Set<const ID *> ids_build_by_node_builder_;
Set<const ID *> ids_build_by_relations_builder_;
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,60 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "pipeline_all_objects.h"
#include "intern/builder/deg_builder_nodes.h"
#include "intern/builder/deg_builder_relations.h"
#include "intern/depsgraph.hh"
#include "DNA_layer_types.h"
namespace blender::deg {
namespace {
class AllObjectsNodeBuilder : public DepsgraphNodeBuilder {
public:
AllObjectsNodeBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache)
: DepsgraphNodeBuilder(bmain, graph, cache)
{
}
bool need_pull_base_into_graph(const Base * /*base*/) override
{
return true;
}
};
class AllObjectsRelationBuilder : public DepsgraphRelationBuilder {
public:
AllObjectsRelationBuilder(Main *bmain, Depsgraph *graph, DepsgraphBuilderCache *cache)
: DepsgraphRelationBuilder(bmain, graph, cache)
{
}
bool need_pull_base_into_graph(const Base * /*base*/) override
{
return true;
}
};
} // namespace
AllObjectsBuilderPipeline::AllObjectsBuilderPipeline(blender::Depsgraph *graph)
: ViewLayerBuilderPipeline(graph)
{
}
std::unique_ptr<DepsgraphNodeBuilder> AllObjectsBuilderPipeline::construct_node_builder()
{
return std::make_unique<AllObjectsNodeBuilder>(bmain_, deg_graph_, &builder_cache_);
}
std::unique_ptr<DepsgraphRelationBuilder> AllObjectsBuilderPipeline::construct_relation_builder()
{
return std::make_unique<AllObjectsRelationBuilder>(bmain_, deg_graph_, &builder_cache_);
}
} // namespace blender::deg

View File

@@ -0,0 +1,27 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "pipeline_view_layer.h"
namespace blender::deg {
/* Builds a dependency graph that contains all objects in the view layer.
* This is contrary to the regular ViewLayerBuilderPipeline, which is limited to visible objects
* (and their dependencies). */
class AllObjectsBuilderPipeline : public ViewLayerBuilderPipeline {
public:
AllObjectsBuilderPipeline(blender::Depsgraph *graph);
protected:
std::unique_ptr<DepsgraphNodeBuilder> construct_node_builder() override;
std::unique_ptr<DepsgraphRelationBuilder> construct_relation_builder() override;
};
} // namespace blender::deg

View File

@@ -0,0 +1,59 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "pipeline_compositor.h"
#include "intern/builder/deg_builder_nodes.h"
#include "intern/builder/deg_builder_relations.h"
#include "intern/depsgraph.hh"
namespace blender::deg {
namespace {
class CompositorDepsgraphNodeBuilder : public DepsgraphNodeBuilder {
public:
using DepsgraphNodeBuilder::DepsgraphNodeBuilder;
void build_idproperties(IDProperty * /*id_property*/) override {}
};
class CompositorDepsgraphRelationBuilder : public DepsgraphRelationBuilder {
public:
using DepsgraphRelationBuilder::DepsgraphRelationBuilder;
void build_idproperties(IDProperty * /*id_property*/) override {}
};
} // namespace
CompositorBuilderPipeline::CompositorBuilderPipeline(blender::Depsgraph *graph)
: AbstractBuilderPipeline(graph)
{
deg_graph_->is_render_pipeline_depsgraph = true;
}
std::unique_ptr<DepsgraphNodeBuilder> CompositorBuilderPipeline::construct_node_builder()
{
return std::make_unique<CompositorDepsgraphNodeBuilder>(bmain_, deg_graph_, &builder_cache_);
}
std::unique_ptr<DepsgraphRelationBuilder> CompositorBuilderPipeline::construct_relation_builder()
{
return std::make_unique<CompositorDepsgraphRelationBuilder>(bmain_, deg_graph_, &builder_cache_);
}
void CompositorBuilderPipeline::build_nodes(DepsgraphNodeBuilder &node_builder)
{
node_builder.build_scene_render(scene_, view_layer_);
node_builder.build_scene_compositor(scene_);
}
void CompositorBuilderPipeline::build_relations(DepsgraphRelationBuilder &relation_builder)
{
relation_builder.build_scene_render(scene_, view_layer_);
relation_builder.build_scene_compositor(scene_);
}
} // namespace blender::deg

View File

@@ -0,0 +1,32 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "pipeline.h"
namespace blender {
struct bNodeTree;
namespace deg {
class CompositorBuilderPipeline : public AbstractBuilderPipeline {
public:
CompositorBuilderPipeline(blender::Depsgraph *graph);
protected:
std::unique_ptr<DepsgraphNodeBuilder> construct_node_builder() override;
std::unique_ptr<DepsgraphRelationBuilder> construct_relation_builder() override;
void build_nodes(DepsgraphNodeBuilder &node_builder) override;
void build_relations(DepsgraphRelationBuilder &relation_builder) override;
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,125 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "pipeline_from_collection.h"
#include "BKE_collection.hh"
#include "DNA_layer_types.h"
#include "DEG_depsgraph.hh"
#include "DEG_depsgraph_query.hh"
#include "intern/builder/deg_builder_nodes.h"
#include "intern/builder/deg_builder_relations.h"
#include "intern/depsgraph.hh"
namespace blender::deg {
namespace {
class DepsgraphFromCollectionIDsFilter {
public:
DepsgraphFromCollectionIDsFilter(const Set<ID *> &ids) : ids_(ids) {}
bool contains(ID *id)
{
return ids_.contains(id);
}
protected:
const Set<ID *> &ids_;
};
class DepsgraphFromCollectionIDsNodeBuilder : public DepsgraphNodeBuilder {
public:
DepsgraphFromCollectionIDsNodeBuilder(Main *bmain,
Depsgraph *graph,
DepsgraphBuilderCache *cache,
const Set<ID *> &ids)
: DepsgraphNodeBuilder(bmain, graph, cache), filter_(ids)
{
}
bool need_pull_base_into_graph(const Base *base) override
{
if (!filter_.contains(&base->object->id)) {
return false;
}
return DepsgraphNodeBuilder::need_pull_base_into_graph(base);
}
protected:
DepsgraphFromCollectionIDsFilter filter_;
};
class DepsgraphFromCollectionIDsRelationBuilder : public DepsgraphRelationBuilder {
public:
DepsgraphFromCollectionIDsRelationBuilder(Main *bmain,
Depsgraph *graph,
DepsgraphBuilderCache *cache,
const Set<ID *> &ids)
: DepsgraphRelationBuilder(bmain, graph, cache), filter_(ids)
{
}
bool need_pull_base_into_graph(const Base *base) override
{
if (!filter_.contains(&base->object->id)) {
return false;
}
return DepsgraphRelationBuilder::need_pull_base_into_graph(base);
}
protected:
DepsgraphFromCollectionIDsFilter filter_;
};
} // namespace
FromCollectionBuilderPipeline::FromCollectionBuilderPipeline(blender::Depsgraph *graph,
Collection *collection)
: AbstractBuilderPipeline(graph)
{
Base *base = BKE_collection_or_layer_objects(
*DEG_get_bmain(graph), scene_, view_layer_, collection);
const int base_flag = (deg_graph_->mode == DAG_EVAL_RENDER) ? BASE_ENABLED_RENDER :
BASE_ENABLED_VIEWPORT;
for (; base; base = base->next) {
if (!deg_graph_->use_visibility_optimization || (base->flag & base_flag)) {
ids_.add(&base->object->id);
}
}
}
std::unique_ptr<DepsgraphNodeBuilder> FromCollectionBuilderPipeline::construct_node_builder()
{
return std::make_unique<DepsgraphFromCollectionIDsNodeBuilder>(
bmain_, deg_graph_, &builder_cache_, ids_);
}
std::unique_ptr<DepsgraphRelationBuilder> FromCollectionBuilderPipeline::
construct_relation_builder()
{
return std::make_unique<DepsgraphFromCollectionIDsRelationBuilder>(
bmain_, deg_graph_, &builder_cache_, ids_);
}
void FromCollectionBuilderPipeline::build_nodes(DepsgraphNodeBuilder &node_builder)
{
node_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY);
for (ID *id : ids_) {
node_builder.build_id(id, true);
}
}
void FromCollectionBuilderPipeline::build_relations(DepsgraphRelationBuilder &relation_builder)
{
relation_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY);
for (ID *id : ids_) {
relation_builder.build_id(id);
}
}
} // namespace blender::deg

View File

@@ -0,0 +1,45 @@
/* SPDX-FileCopyrightText: 2024 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "pipeline.h"
namespace blender {
struct Collection;
namespace deg {
/* Optimized builders for dependency graph built from a given Collection.
*
* General notes:
*
* - We pull in all bases if their objects are in the set of IDs. This allows to have proper
* visibility and other flags assigned to the objects.
* All other bases (the ones which points to object which is outside of the set of IDs) are
* completely ignored.
*/
class FromCollectionBuilderPipeline : public AbstractBuilderPipeline {
public:
FromCollectionBuilderPipeline(blender::Depsgraph *graph, Collection *collection);
protected:
std::unique_ptr<DepsgraphNodeBuilder> construct_node_builder() override;
std::unique_ptr<DepsgraphRelationBuilder> construct_relation_builder() override;
void build_nodes(DepsgraphNodeBuilder &node_builder) override;
void build_relations(DepsgraphRelationBuilder &relation_builder) override;
private:
Set<ID *> ids_;
};
} // namespace deg
} // namespace blender

View File

@@ -0,0 +1,111 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "pipeline_from_ids.h"
#include "DNA_layer_types.h"
#include "intern/builder/deg_builder_nodes.h"
#include "intern/builder/deg_builder_relations.h"
#include "intern/depsgraph.hh"
namespace blender::deg {
namespace {
class DepsgraphFromIDsFilter {
public:
DepsgraphFromIDsFilter(Span<ID *> ids)
{
ids_.add_multiple(ids);
}
bool contains(ID *id)
{
return ids_.contains(id);
}
protected:
Set<ID *> ids_;
};
class DepsgraphFromIDsNodeBuilder : public DepsgraphNodeBuilder {
public:
DepsgraphFromIDsNodeBuilder(Main *bmain,
Depsgraph *graph,
DepsgraphBuilderCache *cache,
Span<ID *> ids)
: DepsgraphNodeBuilder(bmain, graph, cache), filter_(ids)
{
}
bool need_pull_base_into_graph(const Base *base) override
{
if (!filter_.contains(&base->object->id)) {
return false;
}
return DepsgraphNodeBuilder::need_pull_base_into_graph(base);
}
protected:
DepsgraphFromIDsFilter filter_;
};
class DepsgraphFromIDsRelationBuilder : public DepsgraphRelationBuilder {
public:
DepsgraphFromIDsRelationBuilder(Main *bmain,
Depsgraph *graph,
DepsgraphBuilderCache *cache,
Span<ID *> ids)
: DepsgraphRelationBuilder(bmain, graph, cache), filter_(ids)
{
}
bool need_pull_base_into_graph(const Base *base) override
{
if (!filter_.contains(&base->object->id)) {
return false;
}
return DepsgraphRelationBuilder::need_pull_base_into_graph(base);
}
protected:
DepsgraphFromIDsFilter filter_;
};
} // namespace
FromIDsBuilderPipeline::FromIDsBuilderPipeline(blender::Depsgraph *graph, Span<ID *> ids)
: AbstractBuilderPipeline(graph), ids_(ids)
{
}
std::unique_ptr<DepsgraphNodeBuilder> FromIDsBuilderPipeline::construct_node_builder()
{
return std::make_unique<DepsgraphFromIDsNodeBuilder>(bmain_, deg_graph_, &builder_cache_, ids_);
}
std::unique_ptr<DepsgraphRelationBuilder> FromIDsBuilderPipeline::construct_relation_builder()
{
return std::make_unique<DepsgraphFromIDsRelationBuilder>(
bmain_, deg_graph_, &builder_cache_, ids_);
}
void FromIDsBuilderPipeline::build_nodes(DepsgraphNodeBuilder &node_builder)
{
node_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY);
for (ID *id : ids_) {
node_builder.build_id(id, true);
}
}
void FromIDsBuilderPipeline::build_relations(DepsgraphRelationBuilder &relation_builder)
{
relation_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY);
for (ID *id : ids_) {
relation_builder.build_id(id);
}
}
} // namespace blender::deg

View File

@@ -0,0 +1,39 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "pipeline.h"
namespace blender::deg {
/* Optimized builders for dependency graph built from a given set of IDs.
*
* General notes:
*
* - We pull in all bases if their objects are in the set of IDs. This allows to have proper
* visibility and other flags assigned to the objects.
* All other bases (the ones which points to object which is outside of the set of IDs) are
* completely ignored.
*/
class FromIDsBuilderPipeline : public AbstractBuilderPipeline {
Span<ID *> ids_;
public:
FromIDsBuilderPipeline(blender::Depsgraph *graph, Span<ID *> ids);
protected:
std::unique_ptr<DepsgraphNodeBuilder> construct_node_builder() override;
std::unique_ptr<DepsgraphRelationBuilder> construct_relation_builder() override;
void build_nodes(DepsgraphNodeBuilder &node_builder) override;
void build_relations(DepsgraphRelationBuilder &relation_builder) override;
};
} // namespace blender::deg

View File

@@ -0,0 +1,57 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "pipeline_render.h"
#include "intern/builder/deg_builder_nodes.h"
#include "intern/builder/deg_builder_relations.h"
#include "intern/depsgraph.hh"
namespace blender::deg {
namespace {
class RenderDepsgraphNodeBuilder : public DepsgraphNodeBuilder {
public:
using DepsgraphNodeBuilder::DepsgraphNodeBuilder;
void build_idproperties(IDProperty * /*id_property*/) override {}
};
class RenderDepsgraphRelationBuilder : public DepsgraphRelationBuilder {
public:
using DepsgraphRelationBuilder::DepsgraphRelationBuilder;
void build_idproperties(IDProperty * /*id_property*/) override {}
};
} // namespace
RenderBuilderPipeline::RenderBuilderPipeline(blender::Depsgraph *graph)
: AbstractBuilderPipeline(graph)
{
deg_graph_->is_render_pipeline_depsgraph = true;
}
std::unique_ptr<DepsgraphNodeBuilder> RenderBuilderPipeline::construct_node_builder()
{
return std::make_unique<RenderDepsgraphNodeBuilder>(bmain_, deg_graph_, &builder_cache_);
}
std::unique_ptr<DepsgraphRelationBuilder> RenderBuilderPipeline::construct_relation_builder()
{
return std::make_unique<RenderDepsgraphRelationBuilder>(bmain_, deg_graph_, &builder_cache_);
}
void RenderBuilderPipeline::build_nodes(DepsgraphNodeBuilder &node_builder)
{
node_builder.build_scene_render(scene_, view_layer_);
}
void RenderBuilderPipeline::build_relations(DepsgraphRelationBuilder &relation_builder)
{
relation_builder.build_scene_render(scene_, view_layer_);
}
} // namespace blender::deg

View File

@@ -0,0 +1,27 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "pipeline.h"
namespace blender::deg {
class RenderBuilderPipeline : public AbstractBuilderPipeline {
public:
RenderBuilderPipeline(blender::Depsgraph *graph);
protected:
std::unique_ptr<DepsgraphNodeBuilder> construct_node_builder() override;
std::unique_ptr<DepsgraphRelationBuilder> construct_relation_builder() override;
void build_nodes(DepsgraphNodeBuilder &node_builder) override;
void build_relations(DepsgraphRelationBuilder &relation_builder) override;
};
} // namespace blender::deg

View File

@@ -0,0 +1,27 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
#include "pipeline_view_layer.h"
#include "intern/builder/deg_builder_nodes.h"
#include "intern/builder/deg_builder_relations.h"
namespace blender::deg {
ViewLayerBuilderPipeline::ViewLayerBuilderPipeline(blender::Depsgraph *graph)
: AbstractBuilderPipeline(graph)
{
}
void ViewLayerBuilderPipeline::build_nodes(DepsgraphNodeBuilder &node_builder)
{
node_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY);
}
void ViewLayerBuilderPipeline::build_relations(DepsgraphRelationBuilder &relation_builder)
{
relation_builder.build_view_layer(scene_, view_layer_, DEG_ID_LINKED_DIRECTLY);
}
} // namespace blender::deg

View File

@@ -0,0 +1,24 @@
/* SPDX-FileCopyrightText: 2020 Blender Authors
*
* SPDX-License-Identifier: GPL-2.0-or-later */
/** \file
* \ingroup depsgraph
*/
#pragma once
#include "pipeline.h"
namespace blender::deg {
class ViewLayerBuilderPipeline : public AbstractBuilderPipeline {
public:
ViewLayerBuilderPipeline(blender::Depsgraph *graph);
protected:
void build_nodes(DepsgraphNodeBuilder &node_builder) override;
void build_relations(DepsgraphRelationBuilder &relation_builder) override;
};
} // namespace blender::deg