Add Chromium-only Blender WebEngine parity work
This commit is contained in:
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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(°_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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user