Add Chromium-only Blender WebEngine parity work
This commit is contained in:
189
blender-5.2.0/source/blender/depsgraph/CMakeLists.txt
Normal file
189
blender-5.2.0/source/blender/depsgraph/CMakeLists.txt
Normal file
@@ -0,0 +1,189 @@
|
||||
# SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
#
|
||||
# SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
set(INC
|
||||
PUBLIC .
|
||||
../animrig
|
||||
../makesrna
|
||||
../modifiers
|
||||
../nodes
|
||||
# RNA_prototypes.hh
|
||||
${CMAKE_BINARY_DIR}/source/blender/makesrna
|
||||
)
|
||||
|
||||
set(INC_SYS
|
||||
)
|
||||
|
||||
set(SRC
|
||||
intern/builder/deg_builder.cc
|
||||
intern/builder/deg_builder_cache.cc
|
||||
intern/builder/deg_builder_cycle.cc
|
||||
intern/builder/deg_builder_key.cc
|
||||
intern/builder/deg_builder_key.h
|
||||
intern/builder/deg_builder_map.cc
|
||||
intern/builder/deg_builder_nodes.cc
|
||||
intern/builder/deg_builder_nodes_rig.cc
|
||||
intern/builder/deg_builder_nodes_scene.cc
|
||||
intern/builder/deg_builder_nodes_view_layer.cc
|
||||
intern/builder/deg_builder_pchanmap.cc
|
||||
intern/builder/deg_builder_relations.cc
|
||||
intern/builder/deg_builder_relations_drivers.cc
|
||||
intern/builder/deg_builder_relations_rig.cc
|
||||
intern/builder/deg_builder_relations_scene.cc
|
||||
intern/builder/deg_builder_relations_view_layer.cc
|
||||
intern/builder/deg_builder_remove_noop.cc
|
||||
intern/builder/deg_builder_rna.cc
|
||||
intern/builder/deg_builder_stack.cc
|
||||
intern/builder/deg_builder_transitive.cc
|
||||
intern/builder/pipeline.cc
|
||||
intern/builder/pipeline_all_objects.cc
|
||||
intern/builder/pipeline_compositor.cc
|
||||
intern/builder/pipeline_from_collection.cc
|
||||
intern/builder/pipeline_from_ids.cc
|
||||
intern/builder/pipeline_render.cc
|
||||
intern/builder/pipeline_view_layer.cc
|
||||
intern/debug/deg_debug.cc
|
||||
intern/debug/deg_debug_relations_graphviz.cc
|
||||
intern/debug/deg_debug_stats_gnuplot.cc
|
||||
intern/eval/deg_eval.cc
|
||||
intern/eval/deg_eval_copy_on_write.cc
|
||||
intern/eval/deg_eval_flush.cc
|
||||
intern/eval/deg_eval_runtime_backup.cc
|
||||
intern/eval/deg_eval_runtime_backup_animation.cc
|
||||
intern/eval/deg_eval_runtime_backup_modifier.cc
|
||||
intern/eval/deg_eval_runtime_backup_movieclip.cc
|
||||
intern/eval/deg_eval_runtime_backup_object.cc
|
||||
intern/eval/deg_eval_runtime_backup_pose.cc
|
||||
intern/eval/deg_eval_runtime_backup_scene.cc
|
||||
intern/eval/deg_eval_runtime_backup_sequence.cc
|
||||
intern/eval/deg_eval_runtime_backup_sequencer.cc
|
||||
intern/eval/deg_eval_runtime_backup_sound.cc
|
||||
intern/eval/deg_eval_runtime_backup_volume.cc
|
||||
intern/eval/deg_eval_stats.cc
|
||||
intern/eval/deg_eval_visibility.cc
|
||||
intern/eval/deg_eval_visibility.h
|
||||
intern/node/deg_node.cc
|
||||
intern/node/deg_node_component.cc
|
||||
intern/node/deg_node_factory.cc
|
||||
intern/node/deg_node_id.cc
|
||||
intern/node/deg_node_operation.cc
|
||||
intern/node/deg_node_time.cc
|
||||
intern/depsgraph.cc
|
||||
intern/depsgraph_build.cc
|
||||
intern/depsgraph_debug.cc
|
||||
intern/depsgraph_eval.cc
|
||||
intern/depsgraph_light_linking.cc
|
||||
intern/depsgraph_light_linking.hh
|
||||
intern/depsgraph_physics.cc
|
||||
intern/depsgraph_query.cc
|
||||
intern/depsgraph_query_foreach.cc
|
||||
intern/depsgraph_query_iter.cc
|
||||
intern/depsgraph_registry.cc
|
||||
intern/depsgraph_relation.cc
|
||||
intern/depsgraph_tag.cc
|
||||
intern/depsgraph_type.cc
|
||||
intern/depsgraph_update.cc
|
||||
intern/depsgraph_writeback_sync.cc
|
||||
|
||||
DEG_depsgraph.hh
|
||||
DEG_depsgraph_build.hh
|
||||
DEG_depsgraph_debug.hh
|
||||
DEG_depsgraph_light_linking.hh
|
||||
DEG_depsgraph_physics.hh
|
||||
DEG_depsgraph_query.hh
|
||||
DEG_depsgraph_writeback_sync.hh
|
||||
|
||||
intern/builder/deg_builder.h
|
||||
intern/builder/deg_builder_cache.h
|
||||
intern/builder/deg_builder_cycle.h
|
||||
intern/builder/deg_builder_map.h
|
||||
intern/builder/deg_builder_nodes.h
|
||||
intern/builder/deg_builder_pchanmap.h
|
||||
intern/builder/deg_builder_relations.h
|
||||
intern/builder/deg_builder_relations_drivers.h
|
||||
intern/builder/deg_builder_relations_impl.h
|
||||
intern/builder/deg_builder_remove_noop.h
|
||||
intern/builder/deg_builder_rna.h
|
||||
intern/builder/deg_builder_stack.h
|
||||
intern/builder/deg_builder_transitive.h
|
||||
intern/builder/pipeline.h
|
||||
intern/builder/pipeline_all_objects.h
|
||||
intern/builder/pipeline_compositor.h
|
||||
intern/builder/pipeline_from_collection.h
|
||||
intern/builder/pipeline_from_ids.h
|
||||
intern/builder/pipeline_render.h
|
||||
intern/builder/pipeline_view_layer.h
|
||||
intern/debug/deg_debug.h
|
||||
intern/eval/deg_eval.h
|
||||
intern/eval/deg_eval_copy_on_write.h
|
||||
intern/eval/deg_eval_flush.h
|
||||
intern/eval/deg_eval_runtime_backup.h
|
||||
intern/eval/deg_eval_runtime_backup_animation.h
|
||||
intern/eval/deg_eval_runtime_backup_modifier.h
|
||||
intern/eval/deg_eval_runtime_backup_movieclip.h
|
||||
intern/eval/deg_eval_runtime_backup_object.h
|
||||
intern/eval/deg_eval_runtime_backup_pose.h
|
||||
intern/eval/deg_eval_runtime_backup_scene.h
|
||||
intern/eval/deg_eval_runtime_backup_sequence.h
|
||||
intern/eval/deg_eval_runtime_backup_sequencer.h
|
||||
intern/eval/deg_eval_runtime_backup_sound.h
|
||||
intern/eval/deg_eval_runtime_backup_volume.h
|
||||
intern/eval/deg_eval_stats.h
|
||||
intern/node/deg_node.hh
|
||||
intern/node/deg_node_component.hh
|
||||
intern/node/deg_node_factory.hh
|
||||
intern/node/deg_node_factory_impl.hh
|
||||
intern/node/deg_node_id.hh
|
||||
intern/node/deg_node_operation.hh
|
||||
intern/node/deg_node_time.hh
|
||||
intern/depsgraph.hh
|
||||
intern/depsgraph_physics.hh
|
||||
intern/depsgraph_registry.hh
|
||||
intern/depsgraph_relation.hh
|
||||
intern/depsgraph_tag.hh
|
||||
intern/depsgraph_type.hh
|
||||
intern/depsgraph_update.hh
|
||||
)
|
||||
|
||||
set(LIB
|
||||
PRIVATE bf::blenkernel
|
||||
PRIVATE bf::blenlib
|
||||
PRIVATE bf::bmesh
|
||||
PRIVATE bf::dna
|
||||
PRIVATE bf::draw
|
||||
PRIVATE bf::functions
|
||||
PRIVATE bf::imbuf
|
||||
PRIVATE bf::intern::atomic
|
||||
PRIVATE bf::intern::clog
|
||||
PRIVATE bf::intern::guardedalloc
|
||||
PRIVATE bf::sequencer
|
||||
PRIVATE bf::windowmanager
|
||||
PRIVATE bf::dependencies::optional::audaspace
|
||||
)
|
||||
|
||||
if(WITH_PYTHON)
|
||||
add_definitions(-DWITH_PYTHON)
|
||||
list(APPEND INC
|
||||
../python
|
||||
)
|
||||
endif()
|
||||
|
||||
blender_add_lib(bf_depsgraph "${SRC}" "${INC}" "${INC_SYS}" "${LIB}")
|
||||
add_library(bf::depsgraph ALIAS bf_depsgraph)
|
||||
|
||||
# RNA_prototypes.hh
|
||||
add_dependencies(bf_depsgraph bf_rna)
|
||||
|
||||
if(WITH_GTESTS)
|
||||
set(TEST_INC
|
||||
)
|
||||
set(TEST_SRC
|
||||
intern/builder/deg_builder_rna_test.cc
|
||||
)
|
||||
set(TEST_LIB
|
||||
${LIB}
|
||||
bf_depsgraph
|
||||
)
|
||||
blender_add_test_suite_lib(depsgraph "${TEST_SRC}" "${INC};${TEST_INC}" "${INC_SYS}" "${TEST_LIB}")
|
||||
endif()
|
||||
296
blender-5.2.0/source/blender/depsgraph/DEG_depsgraph.hh
Normal file
296
blender-5.2.0/source/blender/depsgraph/DEG_depsgraph.hh
Normal file
@@ -0,0 +1,296 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Public API for Depsgraph
|
||||
*
|
||||
* Dependency Graph
|
||||
* ================
|
||||
*
|
||||
* The dependency graph tracks relations between various pieces of data in
|
||||
* a Blender file, but mainly just those which make up scene data. It is used
|
||||
* to determine the set of operations need to ensure that all data has been
|
||||
* correctly evaluated in response to changes, based on dependencies and visibility
|
||||
* of affected data.
|
||||
* Evaluation Engine
|
||||
* =================
|
||||
*
|
||||
* The evaluation takes the operation-nodes the Depsgraph has tagged for updating,
|
||||
* and schedules them up for being evaluated/executed such that the all dependency
|
||||
* relationship constraints are satisfied.
|
||||
*/
|
||||
|
||||
/* ************************************************* */
|
||||
/* Forward-defined typedefs for core types
|
||||
* - These are used in all depsgraph code and by all callers of Depsgraph API...
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* Dependency Graph */
|
||||
struct Depsgraph;
|
||||
|
||||
/* ------------------------------------------------ */
|
||||
|
||||
struct Main;
|
||||
struct Scene;
|
||||
struct ViewLayer;
|
||||
|
||||
enum eEvaluationMode {
|
||||
DAG_EVAL_VIEWPORT = 0, /* evaluate for OpenGL viewport */
|
||||
DAG_EVAL_RENDER = 1, /* evaluate for render purposes */
|
||||
};
|
||||
|
||||
/* DagNode->eval_flags */
|
||||
enum {
|
||||
/* Regardless to curve->path animation flag path is to be evaluated anyway,
|
||||
* to meet dependencies with such a things as curve modifier and other guys
|
||||
* who're using curve deform, where_on_path and so. */
|
||||
DAG_EVAL_NEED_CURVE_PATH = (1 << 0),
|
||||
/* A shrinkwrap modifier or constraint targeting this mesh needs information
|
||||
* about non-manifold boundary edges for the Target Normal Project mode. */
|
||||
DAG_EVAL_NEED_SHRINKWRAP_BOUNDARY = (1 << 1),
|
||||
};
|
||||
|
||||
/* ************************************************ */
|
||||
/* Depsgraph API */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name CRUD
|
||||
* \{ */
|
||||
|
||||
/* Get main depsgraph instance from context! */
|
||||
|
||||
/**
|
||||
* Create new Depsgraph instance.
|
||||
*
|
||||
* TODO: what arguments are needed here? What's the building-graph entry point?
|
||||
*/
|
||||
Depsgraph *DEG_graph_new(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode);
|
||||
|
||||
/**
|
||||
* Replace the "owner" pointers (currently Main/Scene/ViewLayer) of this depsgraph.
|
||||
* Used for:
|
||||
* - Undo steps when we do want to re-use the old depsgraph data as much as possible.
|
||||
* - Rendering where we want to re-use objects between different view layers.
|
||||
*/
|
||||
void DEG_graph_replace_owners(Depsgraph *depsgraph,
|
||||
Main *bmain,
|
||||
Scene *scene,
|
||||
ViewLayer *view_layer);
|
||||
|
||||
/** Free graph's contents and graph itself. */
|
||||
void DEG_graph_free(Depsgraph *graph);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Node Types Registry
|
||||
* \{ */
|
||||
|
||||
/** Register all node types. */
|
||||
void DEG_register_node_types();
|
||||
|
||||
/** Free node type registry on exit. */
|
||||
void DEG_free_node_types();
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Update Tagging
|
||||
* \{ */
|
||||
|
||||
/** Tag dependency graph for updates when visible scenes/layers changes. */
|
||||
void DEG_graph_tag_on_visible_update(Depsgraph *depsgraph, bool do_time);
|
||||
|
||||
/** Tag all dependency graphs for update when visible scenes/layers changes. */
|
||||
void DEG_tag_on_visible_update(Main *bmain, bool do_time);
|
||||
|
||||
/**
|
||||
* \note Will return NULL if the flag is not known, allowing to gracefully handle situations
|
||||
* when recalc flag has been removed.
|
||||
*/
|
||||
const char *DEG_update_tag_as_string(IDRecalcFlag flag);
|
||||
|
||||
/** Tag given ID for an update in all the dependency graphs. */
|
||||
void DEG_id_tag_update(ID *id, unsigned int flags);
|
||||
void DEG_id_tag_update_ex(Main *bmain, ID *id, unsigned int flags);
|
||||
|
||||
void DEG_graph_id_tag_update(Main *bmain, Depsgraph *depsgraph, ID *id, unsigned int flags);
|
||||
|
||||
/**
|
||||
* Tag the given ID for an update in the given depsgraph even though it evaluated state might not
|
||||
* have changed. This can be used when some data is required that is generated as a side effect of
|
||||
* the evaluation. For example, baking an intermediate geometry in geometry nodes needs this,
|
||||
* because the to-be-baked geometry has to be recomputed even though no input changed and the final
|
||||
* geometry also did not change.
|
||||
*/
|
||||
void DEG_id_tag_update_for_side_effect_request(Depsgraph *depsgraph, ID *id, unsigned int flags);
|
||||
|
||||
/** Tag all dependency graphs when time has changed. */
|
||||
void DEG_time_tag_update(Main *bmain);
|
||||
|
||||
/** Tag a dependency graph when time has changed. */
|
||||
void DEG_graph_time_tag_update(Depsgraph *depsgraph);
|
||||
|
||||
/**
|
||||
* Mark a particular data-block type as having changing.
|
||||
* This does not cause any updates but is used by external
|
||||
* render engines to detect if for example a data-block was removed.
|
||||
*/
|
||||
void DEG_graph_id_type_tag(Depsgraph *depsgraph, short id_type);
|
||||
void DEG_id_type_tag(Main *bmain, short id_type);
|
||||
|
||||
/**
|
||||
* Set a depsgraph to flush updates to editors. This would be done
|
||||
* for viewport depsgraphs, but not render or export depsgraph for example.
|
||||
*/
|
||||
void DEG_enable_editors_update(Depsgraph *depsgraph);
|
||||
|
||||
/** Check if something was changed in the database and inform editors about this. */
|
||||
void DEG_editors_update(Depsgraph *depsgraph, bool time);
|
||||
|
||||
/** Clear recalc flags after editors or renderers have handled updates. */
|
||||
void DEG_ids_clear_recalc(Depsgraph *depsgraph, bool backup);
|
||||
|
||||
/**
|
||||
* Restore recalc flags, backed up by a previous call to #DEG_ids_clear_recalc.
|
||||
* This also clears the backup.
|
||||
*/
|
||||
void DEG_ids_restore_recalc(Depsgraph *depsgraph);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* ************************************************ */
|
||||
/* Evaluation Engine API */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Graph Evaluation
|
||||
* \{ */
|
||||
|
||||
enum DepsgraphEvaluateSyncWriteback {
|
||||
DEG_EVALUATE_SYNC_WRITEBACK_NO,
|
||||
/**
|
||||
* Allow writing back to original data after depsgraph evaluation. The change to original data
|
||||
* may add new ID relations and may tag the depsgraph as changed again.
|
||||
*/
|
||||
DEG_EVALUATE_SYNC_WRITEBACK_YES,
|
||||
};
|
||||
|
||||
/**
|
||||
* Frame changed recalculation entry point.
|
||||
*
|
||||
* \note The frame-change happened for root scene that graph belongs to.
|
||||
*/
|
||||
void DEG_evaluate_on_framechange(
|
||||
Depsgraph *graph,
|
||||
float frame,
|
||||
DepsgraphEvaluateSyncWriteback sync_writeback = DEG_EVALUATE_SYNC_WRITEBACK_NO);
|
||||
|
||||
/**
|
||||
* Data changed recalculation entry point.
|
||||
* Evaluate all nodes tagged for updating.
|
||||
*/
|
||||
void DEG_evaluate_on_refresh(
|
||||
Depsgraph *graph,
|
||||
DepsgraphEvaluateSyncWriteback sync_writeback = DEG_EVALUATE_SYNC_WRITEBACK_NO);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Editors Integration
|
||||
*
|
||||
* Mechanism to allow editors to be informed of depsgraph updates,
|
||||
* to do their own updates based on changes.
|
||||
* \{ */
|
||||
|
||||
struct DEGEditorUpdateContext {
|
||||
Main *bmain;
|
||||
Depsgraph *depsgraph;
|
||||
Scene *scene;
|
||||
ViewLayer *view_layer;
|
||||
};
|
||||
|
||||
using DEG_EditorUpdateIDCb = void (*)(const DEGEditorUpdateContext *update_ctx, ID *id);
|
||||
using DEG_EditorUpdateSceneCb = void (*)(const DEGEditorUpdateContext *update_ctx, bool updated);
|
||||
|
||||
/** Set callbacks which are being called when depsgraph changes. */
|
||||
void DEG_editors_set_update_cb(DEG_EditorUpdateIDCb id_func, DEG_EditorUpdateSceneCb scene_func);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Evaluation
|
||||
* \{ */
|
||||
|
||||
bool DEG_is_evaluating(const Depsgraph *depsgraph);
|
||||
|
||||
bool DEG_is_active(const Depsgraph *depsgraph);
|
||||
void DEG_make_active(Depsgraph *depsgraph);
|
||||
void DEG_make_inactive(Depsgraph *depsgraph);
|
||||
|
||||
/* Returns the number of times the graph has been evaluated. */
|
||||
uint64_t DEG_get_update_count(const Depsgraph *depsgraph);
|
||||
|
||||
/**
|
||||
* Disable the visibility optimization making it so IDs which affect hidden objects or disabled
|
||||
* modifiers are still evaluated.
|
||||
*
|
||||
* For example, this ensures that an object which is needed by a modifier is ignoring checks about
|
||||
* whether the object is hidden or the modifier is disabled. */
|
||||
void DEG_disable_visibility_optimization(Depsgraph *depsgraph);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Evaluation Debug
|
||||
* \{ */
|
||||
|
||||
void DEG_debug_print_begin(Depsgraph *depsgraph);
|
||||
|
||||
void DEG_debug_print_eval(Depsgraph *depsgraph,
|
||||
const char *function_name,
|
||||
const char *object_name,
|
||||
const void *object_address);
|
||||
|
||||
void DEG_debug_print_eval_subdata(Depsgraph *depsgraph,
|
||||
const char *function_name,
|
||||
const char *object_name,
|
||||
const void *object_address,
|
||||
const char *subdata_comment,
|
||||
const char *subdata_name,
|
||||
const void *subdata_address);
|
||||
|
||||
void DEG_debug_print_eval_subdata_index(Depsgraph *depsgraph,
|
||||
const char *function_name,
|
||||
const char *object_name,
|
||||
const void *object_address,
|
||||
const char *subdata_comment,
|
||||
const char *subdata_name,
|
||||
const void *subdata_address,
|
||||
int subdata_index);
|
||||
|
||||
void DEG_debug_print_eval_parent_typed(Depsgraph *depsgraph,
|
||||
const char *function_name,
|
||||
const char *object_name,
|
||||
const void *object_address,
|
||||
const char *parent_comment,
|
||||
const char *parent_name,
|
||||
const void *parent_address);
|
||||
|
||||
void DEG_debug_print_eval_time(Depsgraph *depsgraph,
|
||||
const char *function_name,
|
||||
const char *object_name,
|
||||
const void *object_address,
|
||||
float time);
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
194
blender-5.2.0/source/blender/depsgraph/DEG_depsgraph_build.hh
Normal file
194
blender-5.2.0/source/blender/depsgraph/DEG_depsgraph_build.hh
Normal file
@@ -0,0 +1,194 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Public API for Depsgraph
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_span.hh"
|
||||
|
||||
/* ************************************************* */
|
||||
|
||||
/* Dependency Graph */
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* ------------------------------------------------ */
|
||||
|
||||
struct CacheFile;
|
||||
struct Collection;
|
||||
struct CustomData_MeshMasks;
|
||||
struct Depsgraph;
|
||||
struct DepsNodeHandle;
|
||||
struct ID;
|
||||
struct Main;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
struct bNodeTree;
|
||||
struct VFont;
|
||||
|
||||
/* Graph Building -------------------------------- */
|
||||
|
||||
/** Build depsgraph for the given scene layer, and dump results in given graph container. */
|
||||
void DEG_graph_build_from_view_layer(Depsgraph *graph);
|
||||
|
||||
/**
|
||||
* Build depsgraph for all objects (so also invisible ones) in the given view layer.
|
||||
*/
|
||||
void DEG_graph_build_for_all_objects(Depsgraph *graph);
|
||||
|
||||
/**
|
||||
* Special version of builder which produces dependency graph suitable for the render pipeline.
|
||||
* It will contain sequencer and compositor (if needed) and all their dependencies.
|
||||
*/
|
||||
void DEG_graph_build_for_render_pipeline(Depsgraph *graph);
|
||||
|
||||
/**
|
||||
* Builds minimal dependency graph for compositor preview.
|
||||
*/
|
||||
void DEG_graph_build_for_compositor_preview(Depsgraph *graph);
|
||||
|
||||
/**
|
||||
* Builds the minimal dependency graph needed for evaluation of all IDs within the Collection.
|
||||
*/
|
||||
void DEG_graph_build_from_collection(Depsgraph *graph, Collection *collection);
|
||||
|
||||
/**
|
||||
* Builds the minimal dependency graph needed for evaluation of the given IDs.
|
||||
*/
|
||||
void DEG_graph_build_from_ids(Depsgraph *graph, Span<ID *> ids);
|
||||
|
||||
/** Tag relations from the given graph for update. */
|
||||
void DEG_graph_tag_relations_update(Depsgraph *graph);
|
||||
|
||||
/** Create or update relations in the specified graph. */
|
||||
void DEG_graph_relations_update(Depsgraph *graph);
|
||||
|
||||
/** Tag all relations in the database for update. */
|
||||
void DEG_relations_tag_update(Main *bmain);
|
||||
|
||||
/* Add Dependencies ----------------------------- */
|
||||
|
||||
/**
|
||||
* Handle for components to define their dependencies from callbacks.
|
||||
* This is generated by the depsgraph and passed to dependency callbacks
|
||||
* as a symbolic reference to the current DepsNode.
|
||||
* All relations will be defined in reference to that node.
|
||||
*/
|
||||
enum eDepsSceneComponentType {
|
||||
/* Parameters Component - Default when nothing else fits
|
||||
* (i.e. just SDNA property setting). */
|
||||
DEG_SCENE_COMP_PARAMETERS,
|
||||
/* Animation Component
|
||||
* TODO(sergey): merge in with parameters? */
|
||||
DEG_SCENE_COMP_ANIMATION,
|
||||
/* Sequencer Component (Scene Only). */
|
||||
DEG_SCENE_COMP_SEQUENCER,
|
||||
/* Compositor Component. */
|
||||
DEG_SCENE_COMP_COMPOSITOR,
|
||||
};
|
||||
|
||||
enum eDepsObjectComponentType {
|
||||
/* Used in query API, to denote which component caller is interested in. */
|
||||
DEG_OB_COMP_ANY,
|
||||
|
||||
/* Parameters Component - Default when nothing else fits
|
||||
* (i.e. just SDNA property setting). */
|
||||
DEG_OB_COMP_PARAMETERS,
|
||||
/* Animation Component.
|
||||
*
|
||||
* TODO(sergey): merge in with parameters? */
|
||||
DEG_OB_COMP_ANIMATION,
|
||||
/* Transform Component (Parenting/Constraints) */
|
||||
DEG_OB_COMP_TRANSFORM,
|
||||
/* Geometry Component (#Mesh / #Curves, etc.). */
|
||||
DEG_OB_COMP_GEOMETRY,
|
||||
|
||||
/* Evaluation-Related Outer Types (with Sub-data) */
|
||||
|
||||
/* Pose Component - Owner/Container of Bones Eval */
|
||||
DEG_OB_COMP_EVAL_POSE,
|
||||
/* Bone Component - Child/Sub-component of Pose */
|
||||
DEG_OB_COMP_BONE,
|
||||
|
||||
/* Material Shading Component */
|
||||
DEG_OB_COMP_SHADING,
|
||||
/* Cache Component */
|
||||
DEG_OB_COMP_CACHE,
|
||||
};
|
||||
|
||||
void DEG_add_scene_relation(DepsNodeHandle *node_handle,
|
||||
Scene *scene,
|
||||
eDepsSceneComponentType component,
|
||||
const char *description);
|
||||
void DEG_add_scene_camera_relation(DepsNodeHandle *node_handle,
|
||||
Scene *scene,
|
||||
eDepsObjectComponentType component,
|
||||
const char *description);
|
||||
void DEG_add_object_relation(DepsNodeHandle *node_handle,
|
||||
Object *object,
|
||||
eDepsObjectComponentType component,
|
||||
const char *description);
|
||||
void DEG_add_collection_geometry_relation(DepsNodeHandle *node_handle,
|
||||
Collection *collection,
|
||||
const char *description);
|
||||
void DEG_add_collection_geometry_customdata_mask(DepsNodeHandle *node_handle,
|
||||
Collection *collection,
|
||||
const CustomData_MeshMasks *masks);
|
||||
void DEG_add_node_tree_output_relation(DepsNodeHandle *node_handle,
|
||||
bNodeTree *node_tree,
|
||||
const char *description);
|
||||
void DEG_add_bone_relation(DepsNodeHandle *handle,
|
||||
Object *object,
|
||||
const char *bone_name,
|
||||
eDepsObjectComponentType component,
|
||||
const char *description);
|
||||
void DEG_add_object_cache_relation(DepsNodeHandle *handle,
|
||||
CacheFile *cache_file,
|
||||
eDepsObjectComponentType component,
|
||||
const char *description);
|
||||
void DEG_add_vfont_relation(DepsNodeHandle *handle, VFont *vfont, const char *description);
|
||||
/**
|
||||
* Adds relation from #DEG_OPCODE_GENERIC_DATABLOCK_UPDATE of a given ID.
|
||||
* Is used for such entities as textures and images.
|
||||
*/
|
||||
void DEG_add_generic_id_relation(DepsNodeHandle *node_handle, ID *id, const char *description);
|
||||
|
||||
/**
|
||||
* Special function which is used from modifiers' #update_depsgraph() callback
|
||||
* to indicate that the modifier needs to know transformation of the object
|
||||
* which that modifier belongs to.
|
||||
* This function will take care of checking which operation is required to
|
||||
* have transformation for the modifier, taking into account possible simulation solvers.
|
||||
*/
|
||||
void DEG_add_depends_on_transform_relation(DepsNodeHandle *node_handle, const char *description);
|
||||
|
||||
/**
|
||||
* Adds relations from the given component of a given object to the given node
|
||||
* handle AND the component to the point cache component of the node's ID.
|
||||
*/
|
||||
void DEG_add_object_pointcache_relation(DepsNodeHandle *node_handle,
|
||||
Object *object,
|
||||
eDepsObjectComponentType component,
|
||||
const char *description);
|
||||
|
||||
void DEG_add_time_source_relation(DepsNodeHandle *node_handle, const char *description);
|
||||
|
||||
void DEG_add_special_eval_flag(DepsNodeHandle *handle, ID *id, uint32_t flag);
|
||||
void DEG_add_customdata_mask(DepsNodeHandle *handle,
|
||||
Object *object,
|
||||
const CustomData_MeshMasks *masks);
|
||||
|
||||
ID *DEG_get_id_from_handle(DepsNodeHandle *node_handle);
|
||||
Depsgraph *DEG_get_graph_from_handle(DepsNodeHandle *node_handle);
|
||||
|
||||
bool DEG_object_has_geometry_component(Object *object);
|
||||
|
||||
/* ************************************************ */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,83 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Public API for Querying and Filtering Depsgraph
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
|
||||
#include "BLI_string_ref.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Depsgraph;
|
||||
struct Main;
|
||||
struct Scene;
|
||||
struct ViewLayer;
|
||||
|
||||
/* ------------------------------------------------ */
|
||||
|
||||
/* NOTE: Those flags are same bit-mask as #G.debug_flags */
|
||||
|
||||
void DEG_debug_flags_set(Depsgraph *depsgraph, int flags);
|
||||
int DEG_debug_flags_get(const Depsgraph *depsgraph);
|
||||
|
||||
void DEG_debug_name_set(Depsgraph *depsgraph, const char *name);
|
||||
const char *DEG_debug_name_get(Depsgraph *depsgraph);
|
||||
|
||||
/* ------------------------------------------------ */
|
||||
|
||||
/**
|
||||
* Obtain simple statistics about the complexity of the depsgraph.
|
||||
* \param[out] r_outer: The number of outer nodes in the graph.
|
||||
* \param[out] r_operations: The number of operation nodes in the graph.
|
||||
* \param[out] r_relations: The number of relations between (executable) nodes in the graph.
|
||||
*/
|
||||
void DEG_stats_simple(const Depsgraph *graph,
|
||||
size_t *r_outer,
|
||||
size_t *r_operations,
|
||||
size_t *r_relations);
|
||||
|
||||
/* ************************************************ */
|
||||
/* Diagram-Based Graph Debugging */
|
||||
|
||||
std::string DEG_debug_graph_to_dot(const Depsgraph &graph, StringRef label);
|
||||
|
||||
void DEG_debug_stats_gnuplot(const Depsgraph *graph,
|
||||
FILE *fp,
|
||||
const char *label,
|
||||
const char *output_filename);
|
||||
|
||||
/* ************************************************ */
|
||||
|
||||
/** Compare two dependency graphs. */
|
||||
bool DEG_debug_compare(const Depsgraph *graph1, const Depsgraph *graph2);
|
||||
|
||||
/** Check that dependencies in the graph are really up to date. */
|
||||
bool DEG_debug_graph_relations_validate(Depsgraph *graph,
|
||||
Main *bmain,
|
||||
Scene *scene,
|
||||
ViewLayer *view_layer);
|
||||
|
||||
/** Perform consistency check on the graph. */
|
||||
bool DEG_debug_consistency_check(Depsgraph *graph);
|
||||
|
||||
/**
|
||||
* Convert bit-flag representation of recalculation flags to string
|
||||
* that consists of human-readable names of recalculation bits that are set in the given mask.
|
||||
*
|
||||
* If flags == 0 the result is "NONE".
|
||||
*
|
||||
* Example:
|
||||
* DEG_stringify_recalc_flags(ID_RECALC_GEOMETRY | ID_RECALC_SHADING) -> "GEOMETRY, SHADING".
|
||||
*/
|
||||
std::string DEG_stringify_recalc_flags(unsigned int flags);
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,22 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Depsgraph;
|
||||
struct Object;
|
||||
namespace deg::light_linking {
|
||||
|
||||
/* Set runtime light linking data on evaluated object. */
|
||||
void eval_runtime_data(const Depsgraph *depsgraph, Object &object_eval);
|
||||
|
||||
} // namespace deg::light_linking
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,60 @@
|
||||
/* SPDX-FileCopyrightText: 2018 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Physics utilities for effectors and collision.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Collection;
|
||||
struct CollisionRelation;
|
||||
struct DepsNodeHandle;
|
||||
struct Depsgraph;
|
||||
struct EffectorRelation;
|
||||
struct EffectorWeights;
|
||||
struct ModifierData;
|
||||
struct Object;
|
||||
|
||||
enum ePhysicsCollisionType {
|
||||
DEG_PHYSICS_COLLISION = 0,
|
||||
DEG_PHYSICS_SMOKE_COLLISION = 1,
|
||||
DEG_PHYSICS_DYNAMIC_BRUSH = 2,
|
||||
DEG_PHYSICS_COLLISION_NUM = 3,
|
||||
};
|
||||
|
||||
/* Get collision/effector relations from collection or entire scene. These
|
||||
* created during depsgraph relations building and should only be accessed
|
||||
* during evaluation. */
|
||||
ListBaseT<EffectorRelation> *DEG_get_effector_relations(const Depsgraph *depsgraph,
|
||||
Collection *collection);
|
||||
ListBaseT<CollisionRelation> *DEG_get_collision_relations(const Depsgraph *depsgraph,
|
||||
Collection *collection,
|
||||
unsigned int modifier_type);
|
||||
|
||||
/* Build collision/effector relations for depsgraph. */
|
||||
using DEG_CollobjFilterFunction = bool (*)(Object *obj, ModifierData *md);
|
||||
|
||||
void DEG_add_collision_relations(DepsNodeHandle *handle,
|
||||
Object *object,
|
||||
Collection *collection,
|
||||
unsigned int modifier_type,
|
||||
DEG_CollobjFilterFunction filter_function,
|
||||
const char *name);
|
||||
void DEG_add_forcefield_relations(DepsNodeHandle *handle,
|
||||
Object *object,
|
||||
EffectorWeights *eff,
|
||||
bool add_absorption,
|
||||
int skip_forcefield,
|
||||
const char *name);
|
||||
|
||||
} // namespace blender
|
||||
442
blender-5.2.0/source/blender/depsgraph/DEG_depsgraph_query.hh
Normal file
442
blender-5.2.0/source/blender/depsgraph/DEG_depsgraph_query.hh
Normal file
@@ -0,0 +1,442 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Public API for Querying Depsgraph.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_iterator.h"
|
||||
#include "BLI_set.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
|
||||
/* Needed for the instance iterator. */
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "BKE_duplilist.hh"
|
||||
#include "BKE_object_types.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct BLI_Iterator;
|
||||
struct CustomData_MeshMasks;
|
||||
struct Depsgraph;
|
||||
struct DupliObject;
|
||||
struct ID;
|
||||
struct PointerRNA;
|
||||
struct Scene;
|
||||
struct ViewLayer;
|
||||
struct ViewerPath;
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name DEG input data
|
||||
* \{ */
|
||||
|
||||
/** Get scene that depsgraph was built for. */
|
||||
Scene *DEG_get_input_scene(const Depsgraph *graph);
|
||||
|
||||
/** Get view layer that depsgraph was built for. */
|
||||
ViewLayer *DEG_get_input_view_layer(const Depsgraph *graph);
|
||||
|
||||
/** Get bmain that depsgraph was built for. */
|
||||
Main *DEG_get_bmain(const Depsgraph *graph);
|
||||
|
||||
/** Get evaluation mode that depsgraph was built for. */
|
||||
eEvaluationMode DEG_get_mode(const Depsgraph *graph);
|
||||
|
||||
/** Get time that depsgraph is being evaluated or was last evaluated at. */
|
||||
float DEG_get_ctime(const Depsgraph *graph);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name DEG evaluated data
|
||||
* \{ */
|
||||
|
||||
/** Check if given ID type was tagged for update. */
|
||||
bool DEG_id_type_updated(const Depsgraph *depsgraph, short id_type);
|
||||
bool DEG_id_type_any_updated(const Depsgraph *depsgraph);
|
||||
|
||||
/** Check if given ID type is present in the depsgraph */
|
||||
bool DEG_id_type_any_exists(const Depsgraph *depsgraph, short id_type);
|
||||
|
||||
/** Get additional evaluation flags for the given ID. */
|
||||
uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, const ID *id);
|
||||
|
||||
/** Get additional mesh CustomData_MeshMasks flags for the given object. */
|
||||
void DEG_get_customdata_mask_for_object(const Depsgraph *graph,
|
||||
Object *object,
|
||||
CustomData_MeshMasks *r_mask);
|
||||
|
||||
/**
|
||||
* Get scene at its evaluated state.
|
||||
*
|
||||
* Technically, this is a copied-on-written and fully evaluated version of the input scene.
|
||||
* This function will check that the data-block has been expanded (and copied) from the original
|
||||
* one. Assert will happen if it's not.
|
||||
*/
|
||||
Scene *DEG_get_evaluated_scene(const Depsgraph *graph);
|
||||
|
||||
/**
|
||||
* Get view layer at its evaluated state.
|
||||
* This is a shortcut for accessing active view layer from evaluated scene.
|
||||
*/
|
||||
ViewLayer *DEG_get_evaluated_view_layer(const Depsgraph *graph);
|
||||
|
||||
/** Get evaluated version of given ID data-block. */
|
||||
ID *DEG_get_evaluated_id(const Depsgraph *depsgraph, ID *id);
|
||||
const ID *DEG_get_evaluated_id(const Depsgraph *depsgraph, const ID *id);
|
||||
|
||||
template<typename T> T *DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
|
||||
{
|
||||
static_assert(dna::is_ID_v<T>);
|
||||
return reinterpret_cast<T *>(DEG_get_evaluated_id(depsgraph, reinterpret_cast<ID *>(id)));
|
||||
}
|
||||
|
||||
template<typename T> const T *DEG_get_evaluated(const Depsgraph *depsgraph, const T *id)
|
||||
{
|
||||
static_assert(dna::is_ID_v<T>);
|
||||
return reinterpret_cast<const T *>(
|
||||
DEG_get_evaluated_id(depsgraph, reinterpret_cast<const ID *>(id)));
|
||||
}
|
||||
|
||||
/** Get evaluated version of data pointed to by RNA pointer */
|
||||
void DEG_get_evaluated_rna_pointer(const Depsgraph *depsgraph,
|
||||
PointerRNA *ptr,
|
||||
PointerRNA *r_ptr_eval);
|
||||
|
||||
/** Get original version of given evaluated ID data-block. */
|
||||
ID *DEG_get_original_id(ID *id);
|
||||
const ID *DEG_get_original_id(const ID *id);
|
||||
|
||||
template<typename T> T *DEG_get_original(T *id)
|
||||
{
|
||||
static_assert(dna::is_ID_v<T>);
|
||||
return reinterpret_cast<T *>(DEG_get_original_id(reinterpret_cast<ID *>(id)));
|
||||
}
|
||||
|
||||
template<typename T> const T *DEG_get_original(const T *id)
|
||||
{
|
||||
static_assert(dna::is_ID_v<T>);
|
||||
return reinterpret_cast<const T *>(DEG_get_original_id(reinterpret_cast<const ID *>(id)));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the depsgraph that owns the given ID. This is efficient because the depsgraph is cached on
|
||||
* the ID.
|
||||
*
|
||||
* Only IDs that use the copy-on-eval mechanism of depsgraph evaluation have a corresponding
|
||||
* depsgraph. Original IDs as well as temporary IDs e.g. generated by geometry nodes do not have a
|
||||
* corresponding depsgraph, so null is returned here for these IDs.
|
||||
*/
|
||||
Depsgraph *DEG_get_depsgraph_by_id(const ID &id);
|
||||
|
||||
/**
|
||||
* Check whether given ID is an original.
|
||||
*
|
||||
* Original IDs are considered all the IDs which are not covered by copy-on-evaluation system and
|
||||
* are not out-of-main localized data-blocks.
|
||||
*/
|
||||
bool DEG_is_original_id(const ID *id);
|
||||
|
||||
template<typename T> bool DEG_is_original(const T *id)
|
||||
{
|
||||
static_assert(dna::is_ID_v<T>);
|
||||
return DEG_is_original_id(reinterpret_cast<const ID *>(id));
|
||||
}
|
||||
|
||||
/* Opposite of the above (`DEG_is_original_*`).
|
||||
*
|
||||
* If the data-block is not original it must be evaluated, and vice versa. */
|
||||
|
||||
bool DEG_is_evaluated_id(const ID *id);
|
||||
|
||||
template<typename T> bool DEG_is_evaluated(const T *id)
|
||||
{
|
||||
static_assert(dna::is_ID_v<T>);
|
||||
return DEG_is_evaluated_id(reinterpret_cast<const ID *>(id));
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether depsgraph is fully evaluated. This includes the following checks:
|
||||
* - Relations are up-to-date.
|
||||
* - Nothing is tagged for update.
|
||||
*/
|
||||
bool DEG_is_fully_evaluated(const Depsgraph *depsgraph);
|
||||
|
||||
/**
|
||||
* Check every component of the data-block is evaluated. For example, an object disabled in the
|
||||
* viewport is not fully evaluated, even though the copy-on-eval data-block is created.
|
||||
*/
|
||||
bool DEG_id_is_fully_evaluated(const Depsgraph *depsgraph, const ID *id_eval);
|
||||
|
||||
/**
|
||||
* Returns false when the objects geometry is not fully evaluated in its depsgraph yet. In this
|
||||
* case, the geometry must not be accessed. Otherwise returns true when geometry is fully evaluated
|
||||
* or the object does not belong to any specific depsgraph.
|
||||
*
|
||||
* The result of this function is non deterministic when multi-threading is used because the
|
||||
* depsgraph nodes are not totally ordered. When the depsgraph contains all correct relations and
|
||||
* there are no cycles, the result should always be `true` here though. So it does not break
|
||||
* determinism when there are no dependency graph cycles.
|
||||
*/
|
||||
bool DEG_object_geometry_is_evaluated(const Object &object);
|
||||
|
||||
/**
|
||||
* Same as #DEG_object_geometry_is_evaluated
|
||||
* but for the transformation of the object.
|
||||
*/
|
||||
bool DEG_object_transform_is_evaluated(const Object &object);
|
||||
|
||||
/**
|
||||
* Same as #DEG_object_geometry_is_evaluated
|
||||
* but for the geometry of all objects in the collection.
|
||||
*/
|
||||
bool DEG_collection_geometry_is_evaluated(const Collection &collection);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name DEG object iterators
|
||||
* \{ */
|
||||
|
||||
enum DegIterFlag {
|
||||
DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY = (1 << 0),
|
||||
DEG_ITER_OBJECT_FLAG_LINKED_INDIRECTLY = (1 << 1),
|
||||
DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET = (1 << 2),
|
||||
DEG_ITER_OBJECT_FLAG_VISIBLE = (1 << 3),
|
||||
DEG_ITER_OBJECT_FLAG_DUPLI = (1 << 4),
|
||||
};
|
||||
ENUM_OPERATORS(DegIterFlag)
|
||||
|
||||
struct DEGObjectIterSettings {
|
||||
Depsgraph *depsgraph;
|
||||
/**
|
||||
* Bit-field of the #DegIterFlag.
|
||||
*
|
||||
* NOTE: Be careful with #DEG_ITER_OBJECT_FLAG_LINKED_INDIRECTLY objects.
|
||||
* Although they are available they have no overrides (collection_properties)
|
||||
* and will crash if you try to access it.
|
||||
*/
|
||||
uint32_t flags;
|
||||
|
||||
/**
|
||||
* When set, the final evaluated geometry of the corresponding object is omitted. Instead the
|
||||
* geometry for the viewer path included in the iterator.
|
||||
*/
|
||||
const ViewerPath *viewer_path;
|
||||
|
||||
/**
|
||||
* If not empty, the iterator should only return objects that are in this list (or their
|
||||
* instances are in it). Pointers in this span should be the original data-block.
|
||||
*/
|
||||
Set<const Object *> *included_objects;
|
||||
};
|
||||
|
||||
/**
|
||||
* Flags to get objects for draw manager and final render.
|
||||
*/
|
||||
#define DEG_OBJECT_ITER_FOR_RENDER_ENGINE_FLAGS \
|
||||
blender::DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY | blender::DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET | \
|
||||
blender::DEG_ITER_OBJECT_FLAG_VISIBLE | blender::DEG_ITER_OBJECT_FLAG_DUPLI
|
||||
|
||||
struct DEGObjectIterData {
|
||||
DEGObjectIterSettings *settings;
|
||||
Depsgraph *graph;
|
||||
int flag;
|
||||
|
||||
eEvaluationMode eval_mode;
|
||||
|
||||
/** Object whose preview instead of evaluated geometry should be part of the iterator. */
|
||||
Object *object_orig_with_preview;
|
||||
|
||||
Object *next_object;
|
||||
|
||||
/* **** Iteration over dupli-list. *** */
|
||||
|
||||
/* Object which created the dupli-list. */
|
||||
Object *dupli_parent;
|
||||
/* List of duplicated objects. */
|
||||
DupliList dupli_list;
|
||||
/* Next duplicated object to step into. */
|
||||
DupliObject *dupli_object_next;
|
||||
/* The dupli_list index of dupli_object_next. */
|
||||
int dupli_object_next_index;
|
||||
/* Corresponds to current object: current iterator object is evaluated from
|
||||
* this duplicated object. */
|
||||
DupliObject *dupli_object_current;
|
||||
/* Temporary storage to report fully populated DNA to the render engine or
|
||||
* other users of the iterator. */
|
||||
Object temp_dupli_object;
|
||||
bke::ObjectRuntime temp_dupli_object_runtime;
|
||||
|
||||
/* **** Iteration over ID nodes **** */
|
||||
size_t id_node_index;
|
||||
size_t num_id_nodes;
|
||||
|
||||
/* Copy the current/next data and move the DupliList. */
|
||||
void transfer_from(DEGObjectIterData &other);
|
||||
};
|
||||
|
||||
void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data);
|
||||
void DEG_iterator_objects_next(BLI_Iterator *iter);
|
||||
void DEG_iterator_objects_end(BLI_Iterator *iter);
|
||||
|
||||
#define DEG_OBJECT_ITER_BEGIN(settings_, instance_) \
|
||||
{ \
|
||||
DEGObjectIterData data_ = { \
|
||||
(settings_), \
|
||||
(settings_)->depsgraph, \
|
||||
int((settings_)->flags), \
|
||||
}; \
|
||||
\
|
||||
ITER_BEGIN (DEG_iterator_objects_begin, \
|
||||
DEG_iterator_objects_next, \
|
||||
DEG_iterator_objects_end, \
|
||||
&data_, \
|
||||
Object *, \
|
||||
instance_)
|
||||
|
||||
#define DEG_OBJECT_ITER_END \
|
||||
ITER_END; \
|
||||
} \
|
||||
((void)0)
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name DEG object iterators - Manual dupli iteration
|
||||
* Helper functions for handling dupli instances iteration manually (ie. without passing
|
||||
* DEG_ITER_OBJECT_FLAG_DUPLI to DEGObjectIterSettings::flags)
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Returns true if the object should be visible on the given context.
|
||||
*/
|
||||
bool DEG_iterator_object_is_visible(eEvaluationMode eval_mode, const Object *ob);
|
||||
|
||||
/**
|
||||
* Returns true if the dupli instance should be visible on the given context.
|
||||
*/
|
||||
bool DEG_iterator_dupli_is_visible(const DupliObject *dupli, eEvaluationMode eval_mode);
|
||||
|
||||
namespace evil {
|
||||
/**
|
||||
* WARNING: DON'T USE!!!
|
||||
*
|
||||
* These functions are exposed publicly as a temporary measure while we figure out how to fully get
|
||||
* rid of temporary objects in the Draw module (See #144811).
|
||||
*
|
||||
* DON'T ADD NEW USE CASES FOR THESE FUNCTIONS.
|
||||
*/
|
||||
|
||||
/**
|
||||
* WARNING: DON'T USE!!!
|
||||
* Generates a temporary object for a given dupli instance.
|
||||
*
|
||||
* Returns true if the resulting object should be visible, otherwise the temp object should be
|
||||
* considered invalid.
|
||||
* NOTE: DEG_iterator_temp_object_free_properties should be called regardless.
|
||||
*
|
||||
* \param do_matrix_setup: If false, the temp_object won't have valid
|
||||
* object_to_world/world_to_object matrices, and the OB_NEG_SCALE flag will never be set.
|
||||
*/
|
||||
[[nodiscard]] bool DEG_iterator_temp_object_from_dupli(const Object *dupli_parent,
|
||||
const DupliObject *dupli,
|
||||
eEvaluationMode eval_mode,
|
||||
bool do_matrix_setup,
|
||||
Object *r_temp_object,
|
||||
bke::ObjectRuntime *r_temp_runtime);
|
||||
|
||||
/**
|
||||
* WARNING: DON'T USE!!!
|
||||
* Frees any property allocated when calling DEG_iterator_temp_object_from_dupli.
|
||||
*/
|
||||
void DEG_iterator_temp_object_free_properties(const DupliObject *dupli, Object *temp_object);
|
||||
} // namespace evil
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name DEG ID iterators
|
||||
* \{ */
|
||||
|
||||
struct DEGIDIterData {
|
||||
Depsgraph *graph;
|
||||
bool only_updated;
|
||||
|
||||
size_t id_node_index;
|
||||
size_t num_id_nodes;
|
||||
};
|
||||
|
||||
void DEG_iterator_ids_begin(BLI_Iterator *iter, DEGIDIterData *data);
|
||||
void DEG_iterator_ids_next(BLI_Iterator *iter);
|
||||
void DEG_iterator_ids_end(BLI_Iterator *iter);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name DEG traversal
|
||||
* \{ */
|
||||
|
||||
using DEGForeachIDCallback = FunctionRef<void(ID *id)>;
|
||||
using DEGForeachIDComponentCallback =
|
||||
FunctionRef<void(ID *id, eDepsObjectComponentType component)>;
|
||||
|
||||
/**
|
||||
* \note Modifies runtime flags in depsgraph nodes,
|
||||
* so can not be used in parallel. Keep an eye on that!
|
||||
*/
|
||||
void DEG_foreach_ancestor_ID(const Depsgraph *depsgraph,
|
||||
const ID *id,
|
||||
DEGForeachIDCallback callback);
|
||||
void DEG_foreach_dependent_ID(const Depsgraph *depsgraph,
|
||||
const ID *id,
|
||||
DEGForeachIDCallback callback);
|
||||
|
||||
/**
|
||||
* Starts traversal from given component of the given ID, invokes callback for every other
|
||||
* component which is directly on indirectly dependent on the source one.
|
||||
*/
|
||||
enum {
|
||||
/* Ignore transform solvers which depends on multiple inputs and affects final transform.
|
||||
* Is used for cases like snapping objects which are part of a rigid body simulation:
|
||||
* without this there will be "false-positive" dependencies between transform components of
|
||||
* objects:
|
||||
*
|
||||
* object 1 transform before solver ---> solver ------> object 1 final transform
|
||||
* object 2 transform before solver -----^ \------> object 2 final transform
|
||||
*/
|
||||
DEG_FOREACH_COMPONENT_IGNORE_TRANSFORM_SOLVERS = (1 << 0),
|
||||
};
|
||||
void DEG_foreach_dependent_ID_component(const Depsgraph *depsgraph,
|
||||
const ID *id,
|
||||
eDepsObjectComponentType source_component_type,
|
||||
int flags,
|
||||
DEGForeachIDComponentCallback callback);
|
||||
|
||||
void DEG_foreach_ID(const Depsgraph *depsgraph, DEGForeachIDCallback callback);
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name DEG query evaluation timings
|
||||
* \{ */
|
||||
|
||||
/**
|
||||
* Return the last evaluation time of \a depsgraph in seconds or #std::nullopt if \a depsgraph
|
||||
* hasn't been (fully) evaluated.
|
||||
*/
|
||||
std::optional<double> DEG_get_last_evaluation_time(const Depsgraph *depsgraph);
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,34 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#pragma once
|
||||
|
||||
/** \file
|
||||
* \ingroup bke
|
||||
*
|
||||
* This file provides an API that can be used to modify original (as opposed to evaluated)
|
||||
* data-blocks after depsgraph evaluation. For some data (e.g. animated properties), this is done
|
||||
* during depsgraph evaluation. However, this is not possible in all cases. For example, if the
|
||||
* change to the original data adds a new relation between data-blocks, a user-count (#ID.us) has
|
||||
* to be increased. This counter is not atomic and can therefore not be modified arbitrarily from
|
||||
* different threads.
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Depsgraph;
|
||||
|
||||
namespace deg::sync_writeback {
|
||||
|
||||
/**
|
||||
* Add a writeback task during depsgraph evaluation. The given function is called after depsgraph
|
||||
* evaluation is done if the depsgraph is active. It is allowed to change original data blocks and
|
||||
* even to add new relations.
|
||||
*/
|
||||
void add(Depsgraph &depsgraph, std::function<void()> fn);
|
||||
|
||||
} // namespace deg::sync_writeback
|
||||
} // namespace blender
|
||||
@@ -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
|
||||
@@ -0,0 +1,81 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#include "intern/debug/deg_debug.h"
|
||||
|
||||
#include "BLI_console.h"
|
||||
#include "BLI_hash.h"
|
||||
#include "BLI_string.h"
|
||||
#include "BLI_time.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
DepsgraphDebug::DepsgraphDebug() : flags(G.debug), graph_evaluation_start_time_(0) {}
|
||||
|
||||
bool DepsgraphDebug::do_time_debug() const
|
||||
{
|
||||
return ((G.debug & G_DEBUG_DEPSGRAPH_TIME) != 0);
|
||||
}
|
||||
|
||||
void DepsgraphDebug::begin_graph_evaluation()
|
||||
{
|
||||
const double current_time = BLI_time_now_seconds();
|
||||
|
||||
graph_evaluation_start_time_ = current_time;
|
||||
}
|
||||
|
||||
void DepsgraphDebug::end_graph_evaluation()
|
||||
{
|
||||
const double graph_eval_end_time = BLI_time_now_seconds();
|
||||
graph_evaluation_total_time_ = graph_eval_end_time - graph_evaluation_start_time_;
|
||||
|
||||
if (!do_time_debug()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (name.empty()) {
|
||||
printf("Depsgraph updated in %f seconds.\n", graph_evaluation_total_time_);
|
||||
}
|
||||
else {
|
||||
printf("Depsgraph [%s] updated in %f seconds.\n", name.c_str(), graph_evaluation_total_time_);
|
||||
}
|
||||
}
|
||||
|
||||
double DepsgraphDebug::total_evaluation_time() const
|
||||
{
|
||||
return graph_evaluation_total_time_;
|
||||
}
|
||||
|
||||
bool terminal_do_color()
|
||||
{
|
||||
return (G.debug & G_DEBUG_DEPSGRAPH_PRETTY) != 0;
|
||||
}
|
||||
|
||||
std::string color_for_pointer(const void *pointer)
|
||||
{
|
||||
if (!terminal_do_color()) {
|
||||
return "";
|
||||
}
|
||||
int r, g, b;
|
||||
BLI_hash_pointer_to_color(pointer, &r, &g, &b);
|
||||
char buffer[64];
|
||||
SNPRINTF(buffer, TRUECOLOR_ANSI_COLOR_FORMAT, r, g, b);
|
||||
return std::string(buffer);
|
||||
}
|
||||
|
||||
std::string color_end()
|
||||
{
|
||||
if (!terminal_do_color()) {
|
||||
return "";
|
||||
}
|
||||
return std::string(TRUECOLOR_ANSI_COLOR_FINISH);
|
||||
}
|
||||
|
||||
} // namespace blender::deg
|
||||
@@ -0,0 +1,72 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "BKE_global.hh" // IWYU pragma: keep
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
class DepsgraphDebug {
|
||||
public:
|
||||
DepsgraphDebug();
|
||||
|
||||
bool do_time_debug() const;
|
||||
|
||||
void begin_graph_evaluation();
|
||||
void end_graph_evaluation();
|
||||
|
||||
double total_evaluation_time() const;
|
||||
|
||||
/* NOTE: Corresponds to G_DEBUG_DEPSGRAPH_* flags. */
|
||||
int flags;
|
||||
|
||||
/* Name of this dependency graph (is used for debug prints, helping to distinguish graphs
|
||||
* created for different view layer). */
|
||||
std::string name;
|
||||
|
||||
protected:
|
||||
/* Maximum number of counters used to calculate frame rate of depsgraph update. */
|
||||
static const constexpr int MAX_FPS_COUNTERS = 64;
|
||||
|
||||
/* Point in time when last graph evaluation began.
|
||||
* Is initialized from begin_graph_evaluation().
|
||||
*/
|
||||
double graph_evaluation_start_time_;
|
||||
/* Total time of the last evaluation. */
|
||||
double graph_evaluation_total_time_;
|
||||
};
|
||||
|
||||
#define DEG_DEBUG_PRINTF(depsgraph, type, ...) \
|
||||
do { \
|
||||
if (DEG_debug_flags_get(depsgraph) & G_DEBUG_DEPSGRAPH_##type) { \
|
||||
DEG_debug_print_begin(depsgraph); \
|
||||
fprintf(stdout, __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DEG_GLOBAL_DEBUG_PRINTF(type, ...) \
|
||||
do { \
|
||||
if (G.debug & G_DEBUG_DEPSGRAPH_##type) { \
|
||||
fprintf(stdout, __VA_ARGS__); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DEG_ERROR_PRINTF(...) \
|
||||
do { \
|
||||
fprintf(stderr, __VA_ARGS__); \
|
||||
fflush(stderr); \
|
||||
} while (0)
|
||||
|
||||
bool terminal_do_color();
|
||||
std::string color_for_pointer(const void *pointer);
|
||||
std::string color_end();
|
||||
|
||||
} // namespace blender::deg
|
||||
@@ -0,0 +1,529 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Implementation of tools for debugging the depsgraph
|
||||
*/
|
||||
|
||||
#include <cstdarg>
|
||||
|
||||
#include "BLI_dot_export.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_debug.hh"
|
||||
|
||||
#include "intern/depsgraph.hh"
|
||||
#include "intern/depsgraph_relation.hh"
|
||||
|
||||
#include "intern/node/deg_node_component.hh"
|
||||
#include "intern/node/deg_node_id.hh"
|
||||
#include "intern/node/deg_node_operation.hh"
|
||||
#include "intern/node/deg_node_time.hh"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* ****************** */
|
||||
/* Graphviz Debugging */
|
||||
|
||||
namespace deg {
|
||||
|
||||
/* Only one should be enabled, defines whether graphviz nodes
|
||||
* get colored by individual types or classes.
|
||||
*/
|
||||
#define COLOR_SCHEME_NODE_CLASS 1
|
||||
// #define COLOR_SCHEME_NODE_TYPE 2
|
||||
|
||||
static const char *deg_debug_graphviz_fontname = "helvetica";
|
||||
static float deg_debug_graphviz_graph_label_size = 20.0f;
|
||||
static float deg_debug_graphviz_node_label_size = 14.0f;
|
||||
static const int deg_debug_max_colors = 12;
|
||||
#ifdef COLOR_SCHEME_NODE_TYPE
|
||||
static const char *deg_debug_colors[] = {
|
||||
"#a6cee3",
|
||||
"#1f78b4",
|
||||
"#b2df8a",
|
||||
"#33a02c",
|
||||
"#fb9a99",
|
||||
"#e31a1c",
|
||||
"#fdbf6f",
|
||||
"#ff7f00",
|
||||
"#cab2d6",
|
||||
"#6a3d9a",
|
||||
"#ffff99",
|
||||
"#b15928",
|
||||
"#ff00ff",
|
||||
};
|
||||
#endif
|
||||
static const char *deg_debug_colors_light[] = {
|
||||
"#8dd3c7",
|
||||
"#ffffb3",
|
||||
"#bebada",
|
||||
"#fb8072",
|
||||
"#80b1d3",
|
||||
"#fdb462",
|
||||
"#b3de69",
|
||||
"#fccde5",
|
||||
"#d9d9d9",
|
||||
"#bc80bd",
|
||||
"#ccebc5",
|
||||
"#ffed6f",
|
||||
"#ff00ff",
|
||||
};
|
||||
|
||||
#ifdef COLOR_SCHEME_NODE_TYPE
|
||||
static const int deg_debug_node_type_color_map[][2] = {
|
||||
{NodeType::TIMESOURCE, 0},
|
||||
{NodeType::ID_REF, 1},
|
||||
|
||||
/* Outer Types */
|
||||
{NodeType::PARAMETERS, 2},
|
||||
{NodeType::ANIMATION, 4},
|
||||
{NodeType::TRANSFORM, 5},
|
||||
{NodeType::GEOMETRY, 6},
|
||||
{NodeType::SEQUENCER, 7},
|
||||
{NodeType::SHADING, 8},
|
||||
{NodeType::CACHE, 9},
|
||||
{NodeType::POINT_CACHE, 10},
|
||||
{NodeType::LAYER_COLLECTIONS, 11},
|
||||
{NodeType::COPY_ON_EVAL, 12},
|
||||
{-1, 0},
|
||||
};
|
||||
#endif
|
||||
|
||||
static int deg_debug_node_color_index(const Node *node)
|
||||
{
|
||||
#ifdef COLOR_SCHEME_NODE_CLASS
|
||||
/* Some special types. */
|
||||
switch (node->type) {
|
||||
case NodeType::ID_REF:
|
||||
return 5;
|
||||
case NodeType::OPERATION: {
|
||||
OperationNode *op_node = static_cast<OperationNode *>(const_cast<Node *>(node));
|
||||
if (op_node->is_noop()) {
|
||||
if (op_node->flag & OperationFlag::DEPSOP_FLAG_PINNED) {
|
||||
return 7;
|
||||
}
|
||||
return 8;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
/* Do others based on class. */
|
||||
switch (node->get_class()) {
|
||||
case NodeClass::OPERATION:
|
||||
return 4;
|
||||
case NodeClass::COMPONENT:
|
||||
return 1;
|
||||
default:
|
||||
return 9;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef COLOR_SCHEME_NODE_TYPE
|
||||
const int (*pair)[2];
|
||||
for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; pair++) {
|
||||
if ((*pair)[0] == node->type) {
|
||||
return (*pair)[1];
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
#endif
|
||||
}
|
||||
|
||||
struct DotExportContext {
|
||||
bool show_tags;
|
||||
dot_export::DirectedGraph &digraph;
|
||||
Map<const Node *, dot_export::Node *> nodes_map;
|
||||
Map<const Node *, dot_export::Cluster *> clusters_map;
|
||||
};
|
||||
|
||||
static void deg_debug_graphviz_legend_color(const char *name,
|
||||
const char *color,
|
||||
std::stringstream &ss)
|
||||
{
|
||||
|
||||
ss << "<TR>";
|
||||
ss << "<TD>" << name << "</TD>";
|
||||
ss << "<TD BGCOLOR=\"" << color << "\"></TD>";
|
||||
ss << "</TR>";
|
||||
}
|
||||
|
||||
static void deg_debug_graphviz_legend(DotExportContext &ctx)
|
||||
{
|
||||
dot_export::Node &legend_node = ctx.digraph.new_node("");
|
||||
legend_node.attributes.set("rank", "sink");
|
||||
legend_node.attributes.set("shape", "none");
|
||||
legend_node.attributes.set("margin", 0);
|
||||
|
||||
std::stringstream ss;
|
||||
ss << "<";
|
||||
ss << R"(<TABLE BORDER="0" CELLBORDER="1" CELLSPACING="0" CELLPADDING="4">)";
|
||||
ss << "<TR><TD COLSPAN=\"2\"><B>Legend</B></TD></TR>";
|
||||
|
||||
#ifdef COLOR_SCHEME_NODE_CLASS
|
||||
const char **colors = deg_debug_colors_light;
|
||||
deg_debug_graphviz_legend_color("Operation", colors[4], ss);
|
||||
deg_debug_graphviz_legend_color("Component", colors[1], ss);
|
||||
deg_debug_graphviz_legend_color("ID Node", colors[5], ss);
|
||||
deg_debug_graphviz_legend_color("NOOP", colors[8], ss);
|
||||
deg_debug_graphviz_legend_color("Pinned OP", colors[7], ss);
|
||||
#endif
|
||||
|
||||
#ifdef COLOR_SCHEME_NODE_TYPE
|
||||
const int (*pair)[2];
|
||||
for (pair = deg_debug_node_type_color_map; (*pair)[0] >= 0; pair++) {
|
||||
DepsNodeFactory *nti = type_get_factory((NodeType)(*pair)[0]);
|
||||
deg_debug_graphviz_legend_color(
|
||||
ctx, nti->tname().c_str(), deg_debug_colors_light[(*pair)[1] % deg_debug_max_colors], ss);
|
||||
}
|
||||
#endif
|
||||
|
||||
ss << "</TABLE>";
|
||||
ss << ">";
|
||||
legend_node.attributes.set("label", ss.str());
|
||||
legend_node.attributes.set("fontname", deg_debug_graphviz_fontname);
|
||||
}
|
||||
|
||||
static void deg_debug_graphviz_node_color(DotExportContext &ctx,
|
||||
const Node *node,
|
||||
dot_export::Attributes &dot_attributes)
|
||||
{
|
||||
const char *color_default = "black";
|
||||
const char *color_modified = "orangered4";
|
||||
const char *color_update = "dodgerblue3";
|
||||
const char *color = color_default;
|
||||
if (ctx.show_tags) {
|
||||
if (node->get_class() == NodeClass::OPERATION) {
|
||||
OperationNode *op_node = static_cast<OperationNode *>(const_cast<Node *>(node));
|
||||
if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) {
|
||||
color = color_modified;
|
||||
}
|
||||
else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
|
||||
color = color_update;
|
||||
}
|
||||
}
|
||||
}
|
||||
dot_attributes.set("color", color);
|
||||
}
|
||||
|
||||
static void deg_debug_graphviz_node_penwidth(DotExportContext &ctx,
|
||||
const Node *node,
|
||||
dot_export::Attributes &dot_attributes)
|
||||
{
|
||||
float penwidth_default = 1.0f;
|
||||
float penwidth_modified = 4.0f;
|
||||
float penwidth_update = 4.0f;
|
||||
float penwidth = penwidth_default;
|
||||
if (ctx.show_tags) {
|
||||
if (node->get_class() == NodeClass::OPERATION) {
|
||||
OperationNode *op_node = static_cast<OperationNode *>(const_cast<Node *>(node));
|
||||
if (op_node->flag & DEPSOP_FLAG_DIRECTLY_MODIFIED) {
|
||||
penwidth = penwidth_modified;
|
||||
}
|
||||
else if (op_node->flag & DEPSOP_FLAG_NEEDS_UPDATE) {
|
||||
penwidth = penwidth_update;
|
||||
}
|
||||
}
|
||||
}
|
||||
dot_attributes.set("penwidth", penwidth);
|
||||
}
|
||||
|
||||
static void deg_debug_graphviz_node_fillcolor(const Node *node,
|
||||
dot_export::Attributes &dot_attributes)
|
||||
{
|
||||
const char *defaultcolor = "gainsboro";
|
||||
int color_index = deg_debug_node_color_index(node);
|
||||
const char *fillcolor = color_index < 0 ?
|
||||
defaultcolor :
|
||||
deg_debug_colors_light[color_index % deg_debug_max_colors];
|
||||
dot_attributes.set("fillcolor", fillcolor);
|
||||
}
|
||||
|
||||
static void deg_debug_graphviz_relation_color(const Relation *rel, dot_export::DirectedEdge &edge)
|
||||
{
|
||||
const char *color_default = "black";
|
||||
const char *color_cyclic = "red4"; /* The color of crime scene. */
|
||||
const char *color_godmode = "blue4"; /* The color of beautiful sky. */
|
||||
const char *color = color_default;
|
||||
if (rel->flag & RELATION_FLAG_CYCLIC) {
|
||||
color = color_cyclic;
|
||||
}
|
||||
else if (rel->flag & RELATION_FLAG_GODMODE) {
|
||||
color = color_godmode;
|
||||
}
|
||||
edge.attributes.set("color", color);
|
||||
}
|
||||
|
||||
static void deg_debug_graphviz_relation_style(const Relation *rel, dot_export::DirectedEdge &edge)
|
||||
{
|
||||
const char *style_default = "solid";
|
||||
const char *style_no_flush = "dashed";
|
||||
const char *style_flush_user_only = "dotted";
|
||||
const char *style = style_default;
|
||||
if (rel->flag & RELATION_FLAG_NO_FLUSH) {
|
||||
style = style_no_flush;
|
||||
}
|
||||
if (rel->flag & RELATION_FLAG_FLUSH_USER_EDIT_ONLY) {
|
||||
style = style_flush_user_only;
|
||||
}
|
||||
edge.attributes.set("style", style);
|
||||
}
|
||||
|
||||
static void deg_debug_graphviz_relation_arrowhead(const Relation *rel,
|
||||
dot_export::DirectedEdge &edge)
|
||||
{
|
||||
const char *shape_default = "normal";
|
||||
const char *shape_no_cow = "box";
|
||||
const char *shape = shape_default;
|
||||
if (rel->from->get_class() == NodeClass::OPERATION &&
|
||||
rel->to->get_class() == NodeClass::OPERATION)
|
||||
{
|
||||
OperationNode *op_from = static_cast<OperationNode *>(rel->from);
|
||||
OperationNode *op_to = static_cast<OperationNode *>(rel->to);
|
||||
if (op_from->owner->type == NodeType::COPY_ON_EVAL &&
|
||||
/* The #ID::recalc flag depends on run-time state which is not valid at this point in time.
|
||||
* Pass in all flags although there may be a better way to represent this. */
|
||||
!op_to->owner->need_tag_cow_before_update(ID_RECALC_ALL))
|
||||
{
|
||||
shape = shape_no_cow;
|
||||
}
|
||||
}
|
||||
edge.attributes.set("arrowhead", shape);
|
||||
}
|
||||
|
||||
static void deg_debug_graphviz_node_style(DotExportContext &ctx,
|
||||
const Node *node,
|
||||
dot_export::Attributes &dot_attributes)
|
||||
{
|
||||
StringRef base_style = "filled"; /* default style */
|
||||
if (ctx.show_tags) {
|
||||
if (node->get_class() == NodeClass::OPERATION) {
|
||||
OperationNode *op_node = static_cast<OperationNode *>(const_cast<Node *>(node));
|
||||
if (op_node->flag & (DEPSOP_FLAG_DIRECTLY_MODIFIED | DEPSOP_FLAG_NEEDS_UPDATE)) {
|
||||
base_style = "striped";
|
||||
}
|
||||
}
|
||||
}
|
||||
switch (node->get_class()) {
|
||||
case NodeClass::GENERIC:
|
||||
dot_attributes.set("style", base_style);
|
||||
break;
|
||||
case NodeClass::COMPONENT:
|
||||
dot_attributes.set("style", base_style);
|
||||
break;
|
||||
case NodeClass::OPERATION:
|
||||
dot_attributes.set("style", base_style + ",rounded");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void deg_debug_graphviz_node_single(DotExportContext &ctx,
|
||||
const Node *node,
|
||||
dot_export::Cluster *parent_cluster)
|
||||
{
|
||||
std::string name = node->identifier();
|
||||
|
||||
dot_export::Node &dot_node = ctx.digraph.new_node(name);
|
||||
ctx.nodes_map.add_new(node, &dot_node);
|
||||
dot_node.set_parent_cluster(parent_cluster);
|
||||
dot_node.attributes.set("fontname", deg_debug_graphviz_fontname);
|
||||
dot_node.attributes.set("frontsize", deg_debug_graphviz_node_label_size);
|
||||
dot_node.attributes.set("shape", "box");
|
||||
|
||||
deg_debug_graphviz_node_style(ctx, node, dot_node.attributes);
|
||||
deg_debug_graphviz_node_color(ctx, node, dot_node.attributes);
|
||||
deg_debug_graphviz_node_fillcolor(node, dot_node.attributes);
|
||||
deg_debug_graphviz_node_penwidth(ctx, node, dot_node.attributes);
|
||||
}
|
||||
|
||||
static dot_export::Cluster °_debug_graphviz_node_cluster_create(
|
||||
DotExportContext &ctx, const Node *node, dot_export::Cluster *parent_cluster)
|
||||
{
|
||||
std::string name = node->identifier();
|
||||
dot_export::Cluster &cluster = ctx.digraph.new_cluster(name);
|
||||
cluster.set_parent_cluster(parent_cluster);
|
||||
cluster.attributes.set("fontname", deg_debug_graphviz_fontname);
|
||||
cluster.attributes.set("fontsize", deg_debug_graphviz_node_label_size);
|
||||
cluster.attributes.set("margin", 16);
|
||||
deg_debug_graphviz_node_style(ctx, node, cluster.attributes);
|
||||
deg_debug_graphviz_node_color(ctx, node, cluster.attributes);
|
||||
deg_debug_graphviz_node_fillcolor(node, cluster.attributes);
|
||||
deg_debug_graphviz_node_penwidth(ctx, node, cluster.attributes);
|
||||
/* dummy node, so we can add edges between clusters */
|
||||
dot_export::Node &dot_node = ctx.digraph.new_node("");
|
||||
dot_node.attributes.set("shape", "point");
|
||||
dot_node.attributes.set("style", "invis");
|
||||
dot_node.set_parent_cluster(&cluster);
|
||||
ctx.nodes_map.add_new(node, &dot_node);
|
||||
ctx.clusters_map.add_new(node, &cluster);
|
||||
return cluster;
|
||||
}
|
||||
|
||||
static void deg_debug_graphviz_graph_nodes(DotExportContext &ctx, const Depsgraph *graph);
|
||||
static void deg_debug_graphviz_graph_relations(DotExportContext &ctx, const Depsgraph *graph);
|
||||
|
||||
static void deg_debug_graphviz_node(DotExportContext &ctx,
|
||||
const Node *node,
|
||||
dot_export::Cluster *parent_cluster)
|
||||
{
|
||||
switch (node->type) {
|
||||
case NodeType::ID_REF: {
|
||||
const IDNode *id_node = static_cast<const IDNode *>(node);
|
||||
if (id_node->components.is_empty()) {
|
||||
deg_debug_graphviz_node_single(ctx, node, parent_cluster);
|
||||
}
|
||||
else {
|
||||
dot_export::Cluster &cluster = deg_debug_graphviz_node_cluster_create(
|
||||
ctx, node, parent_cluster);
|
||||
for (const ComponentNode *comp : id_node->components.values()) {
|
||||
deg_debug_graphviz_node(ctx, comp, &cluster);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NodeType::PARAMETERS:
|
||||
case NodeType::ANIMATION:
|
||||
case NodeType::TRANSFORM:
|
||||
case NodeType::GEOMETRY:
|
||||
case NodeType::SEQUENCER:
|
||||
case NodeType::COMPOSITOR:
|
||||
case NodeType::EVAL_POSE:
|
||||
case NodeType::BONE:
|
||||
case NodeType::SHADING:
|
||||
case NodeType::CACHE:
|
||||
case NodeType::POINT_CACHE:
|
||||
case NodeType::IMAGE_ANIMATION:
|
||||
case NodeType::LAYER_COLLECTIONS:
|
||||
case NodeType::PARTICLE_SYSTEM:
|
||||
case NodeType::PARTICLE_SETTINGS:
|
||||
case NodeType::COPY_ON_EVAL:
|
||||
case NodeType::OBJECT_FROM_LAYER:
|
||||
case NodeType::HIERARCHY:
|
||||
case NodeType::BATCH_CACHE:
|
||||
case NodeType::INSTANCING:
|
||||
case NodeType::SYNCHRONIZATION:
|
||||
case NodeType::AUDIO:
|
||||
case NodeType::ARMATURE:
|
||||
case NodeType::GENERIC_DATABLOCK:
|
||||
case NodeType::SCENE:
|
||||
case NodeType::VISIBILITY:
|
||||
case NodeType::NTREE_OUTPUT:
|
||||
case NodeType::NTREE_GEOMETRY_PREPROCESS: {
|
||||
ComponentNode *comp_node = static_cast<ComponentNode *>(const_cast<Node *>(node));
|
||||
if (comp_node->operations.is_empty()) {
|
||||
deg_debug_graphviz_node_single(ctx, node, parent_cluster);
|
||||
}
|
||||
else {
|
||||
dot_export::Cluster &cluster = deg_debug_graphviz_node_cluster_create(
|
||||
ctx, node, parent_cluster);
|
||||
for (Node *op_node : comp_node->operations) {
|
||||
deg_debug_graphviz_node(ctx, op_node, &cluster);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case NodeType::UNDEFINED:
|
||||
case NodeType::TIMESOURCE:
|
||||
case NodeType::OPERATION:
|
||||
deg_debug_graphviz_node_single(ctx, node, parent_cluster);
|
||||
break;
|
||||
case NodeType::NUM_TYPES:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void deg_debug_graphviz_node_relations(DotExportContext &ctx, const Node *node)
|
||||
{
|
||||
for (Relation *rel : node->inlinks) {
|
||||
float penwidth = 2.0f;
|
||||
|
||||
const Node *head = rel->to; /* same as node */
|
||||
const Node *tail = rel->from;
|
||||
dot_export::Node &dot_tail = *ctx.nodes_map.lookup(tail);
|
||||
dot_export::Node &dot_head = *ctx.nodes_map.lookup(head);
|
||||
|
||||
dot_export::DirectedEdge &edge = ctx.digraph.new_edge(dot_tail, dot_head);
|
||||
|
||||
/* NOTE: without label an id seem necessary to avoid bugs in graphviz/dot. */
|
||||
edge.attributes.set("id", rel->name);
|
||||
deg_debug_graphviz_relation_color(rel, edge);
|
||||
deg_debug_graphviz_relation_style(rel, edge);
|
||||
deg_debug_graphviz_relation_arrowhead(rel, edge);
|
||||
edge.attributes.set("penwidth", penwidth);
|
||||
|
||||
/* NOTE: edge from node to our own cluster is not possible and gives graphviz
|
||||
* warning, avoid this here by just linking directly to the invisible
|
||||
* placeholder node. */
|
||||
dot_export::Cluster *tail_cluster = ctx.clusters_map.lookup_default(tail, nullptr);
|
||||
if (tail_cluster != nullptr && tail_cluster->contains(dot_head)) {
|
||||
edge.attributes.set("ltail", tail_cluster->name());
|
||||
}
|
||||
dot_export::Cluster *head_cluster = ctx.clusters_map.lookup_default(head, nullptr);
|
||||
if (head_cluster != nullptr && head_cluster->contains(dot_tail)) {
|
||||
edge.attributes.set("lhead", head_cluster->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void deg_debug_graphviz_graph_nodes(DotExportContext &ctx, const Depsgraph *graph)
|
||||
{
|
||||
for (Node *node : graph->id_nodes) {
|
||||
deg_debug_graphviz_node(ctx, node, nullptr);
|
||||
}
|
||||
TimeSourceNode *time_source = graph->find_time_source();
|
||||
if (time_source != nullptr) {
|
||||
deg_debug_graphviz_node(ctx, time_source, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
static void deg_debug_graphviz_graph_relations(DotExportContext &ctx, const Depsgraph *graph)
|
||||
{
|
||||
for (IDNode *id_node : graph->id_nodes) {
|
||||
for (ComponentNode *comp_node : id_node->components.values()) {
|
||||
for (OperationNode *op_node : comp_node->operations) {
|
||||
deg_debug_graphviz_node_relations(ctx, op_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TimeSourceNode *time_source = graph->find_time_source();
|
||||
if (time_source != nullptr) {
|
||||
deg_debug_graphviz_node_relations(ctx, time_source);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace deg
|
||||
|
||||
std::string DEG_debug_graph_to_dot(const Depsgraph &graph, const StringRef label)
|
||||
{
|
||||
const deg::Depsgraph °_graph = reinterpret_cast<const deg::Depsgraph &>(graph);
|
||||
|
||||
dot_export::DirectedGraph digraph;
|
||||
deg::DotExportContext ctx{false, digraph};
|
||||
|
||||
digraph.set_rankdir(dot_export::Attr_rankdir::LeftToRight);
|
||||
digraph.attributes.set("compound", "true");
|
||||
digraph.attributes.set("labelloc", "t");
|
||||
digraph.attributes.set("fontsize", deg::deg_debug_graphviz_graph_label_size);
|
||||
digraph.attributes.set("fontname", deg::deg_debug_graphviz_fontname);
|
||||
digraph.attributes.set("label", label);
|
||||
digraph.attributes.set("splines", "ortho");
|
||||
digraph.attributes.set("overlap", "scalexy");
|
||||
|
||||
deg::deg_debug_graphviz_graph_nodes(ctx, °_graph);
|
||||
deg::deg_debug_graphviz_graph_relations(ctx, °_graph);
|
||||
|
||||
deg::deg_debug_graphviz_legend(ctx);
|
||||
|
||||
return digraph.to_dot_string();
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,154 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#include "DEG_depsgraph_debug.hh"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cstdarg>
|
||||
|
||||
#include "BLI_compiler_attrs.h"
|
||||
#include "BLI_math_base.h"
|
||||
|
||||
#include "intern/depsgraph.hh"
|
||||
#include "intern/node/deg_node_id.hh"
|
||||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
#define NL "\r\n"
|
||||
|
||||
namespace deg {
|
||||
namespace {
|
||||
|
||||
struct DebugContext {
|
||||
FILE *file;
|
||||
const Depsgraph *graph;
|
||||
const char *label;
|
||||
const char *output_filename;
|
||||
};
|
||||
|
||||
struct StatsEntry {
|
||||
const IDNode *id_node;
|
||||
double time;
|
||||
};
|
||||
|
||||
/* TODO(sergey): De-duplicate with graphviz relation debugger. */
|
||||
void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...) ATTR_PRINTF_FORMAT(2, 3);
|
||||
void deg_debug_fprintf(const DebugContext &ctx, const char *fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
vfprintf(ctx.file, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
inline double get_node_time(const DebugContext & /*ctx*/, const Node *node)
|
||||
{
|
||||
/* TODO(sergey): Figure out a nice way to define which exact time
|
||||
* we want to show. */
|
||||
return node->stats.current_time;
|
||||
}
|
||||
|
||||
bool stat_entry_comparator(const StatsEntry &a, const StatsEntry &b)
|
||||
{
|
||||
return a.time > b.time;
|
||||
}
|
||||
|
||||
std::string gnuplotify_id_code(const std::string &name)
|
||||
{
|
||||
return std::string("") + name[0] + name[1];
|
||||
}
|
||||
|
||||
std::string gnuplotify_name(const std::string &name)
|
||||
{
|
||||
std::string result;
|
||||
const int length = name.length();
|
||||
for (int i = 0; i < length; i++) {
|
||||
const char ch = name[i];
|
||||
if (ch == '_') {
|
||||
result += R"(\\\)";
|
||||
}
|
||||
result += ch;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void write_stats_data(const DebugContext &ctx)
|
||||
{
|
||||
/* Fill in array of all stats which are to be displayed. */
|
||||
Vector<StatsEntry> stats;
|
||||
stats.reserve(ctx.graph->id_nodes.size());
|
||||
for (const IDNode *id_node : ctx.graph->id_nodes) {
|
||||
const double time = get_node_time(ctx, id_node);
|
||||
if (time == 0.0) {
|
||||
continue;
|
||||
}
|
||||
StatsEntry entry;
|
||||
entry.id_node = id_node;
|
||||
entry.time = time;
|
||||
stats.append(entry);
|
||||
}
|
||||
/* Sort the data. */
|
||||
std::ranges::sort(stats, stat_entry_comparator);
|
||||
/* We limit number of entries, otherwise things become unreadable. */
|
||||
stats.resize(min_ii(stats.size(), 32));
|
||||
std::reverse(stats.begin(), stats.end());
|
||||
/* Print data to the file stream. */
|
||||
deg_debug_fprintf(ctx, "$data << EOD" NL);
|
||||
for (const StatsEntry &entry : stats) {
|
||||
deg_debug_fprintf(ctx,
|
||||
"\"[%s] %s\",%f" NL,
|
||||
gnuplotify_id_code(entry.id_node->id_orig->name).c_str(),
|
||||
gnuplotify_name(entry.id_node->id_orig->name + 2).c_str(),
|
||||
entry.time);
|
||||
}
|
||||
deg_debug_fprintf(ctx, "EOD" NL);
|
||||
}
|
||||
|
||||
void deg_debug_stats_gnuplot(const DebugContext &ctx)
|
||||
{
|
||||
/* Data itself. */
|
||||
write_stats_data(ctx);
|
||||
/* Optional label. */
|
||||
if (ctx.label && ctx.label[0]) {
|
||||
deg_debug_fprintf(ctx, "set title \"%s\"" NL, ctx.label);
|
||||
}
|
||||
/* Rest of the commands.
|
||||
* TODO(sergey): Need to decide on the resolution somehow. */
|
||||
deg_debug_fprintf(ctx, "set terminal pngcairo size 1920,1080" NL);
|
||||
deg_debug_fprintf(ctx, "set output \"%s\"" NL, ctx.output_filename);
|
||||
deg_debug_fprintf(ctx, "set grid" NL);
|
||||
deg_debug_fprintf(ctx, "set datafile separator ','" NL);
|
||||
deg_debug_fprintf(ctx, "set style fill solid" NL);
|
||||
deg_debug_fprintf(ctx,
|
||||
"plot \"$data\" using "
|
||||
"($2*0.5):0:($2*0.5):(0.2):yticlabels(1) "
|
||||
"with boxxyerrorbars t '' lt rgb \"#406090\"" NL);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace deg
|
||||
|
||||
void DEG_debug_stats_gnuplot(const Depsgraph *depsgraph,
|
||||
FILE *fp,
|
||||
const char *label,
|
||||
const char *output_filename)
|
||||
{
|
||||
if (depsgraph == nullptr) {
|
||||
return;
|
||||
}
|
||||
deg::DebugContext ctx;
|
||||
ctx.file = fp;
|
||||
ctx.graph = reinterpret_cast<deg::Depsgraph *>(const_cast<Depsgraph *>(depsgraph));
|
||||
ctx.label = label;
|
||||
ctx.output_filename = output_filename;
|
||||
deg::deg_debug_stats_gnuplot(ctx);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
362
blender-5.2.0/source/blender/depsgraph/intern/depsgraph.cc
Normal file
362
blender-5.2.0/source/blender/depsgraph/intern/depsgraph.cc
Normal file
@@ -0,0 +1,362 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Core routines for how the Depsgraph works.
|
||||
*/
|
||||
|
||||
#include "intern/depsgraph.hh" /* own include */
|
||||
|
||||
#include <cstring>
|
||||
#include <type_traits>
|
||||
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_idtype.hh"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_debug.hh"
|
||||
|
||||
#include "intern/depsgraph_physics.hh"
|
||||
#include "intern/depsgraph_registry.hh"
|
||||
#include "intern/depsgraph_relation.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_factory.hh"
|
||||
#include "intern/node/deg_node_id.hh"
|
||||
#include "intern/node/deg_node_operation.hh"
|
||||
#include "intern/node/deg_node_time.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace deg {
|
||||
|
||||
Depsgraph::Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode)
|
||||
: time_source(nullptr),
|
||||
has_animated_visibility(false),
|
||||
need_update_relations(true),
|
||||
need_update_nodes_visibility(true),
|
||||
need_tag_id_on_graph_visibility_update(true),
|
||||
need_tag_id_on_graph_visibility_time_update(false),
|
||||
bmain(bmain),
|
||||
scene(scene),
|
||||
view_layer(view_layer),
|
||||
mode(mode),
|
||||
frame(BKE_scene_frame_get(scene)),
|
||||
ctime(BKE_scene_ctime_get(scene)),
|
||||
scene_cow(nullptr),
|
||||
is_active(false),
|
||||
use_visibility_optimization(true),
|
||||
is_evaluating(false),
|
||||
is_render_pipeline_depsgraph(false),
|
||||
use_editors_update(false),
|
||||
physics_relations_effector(nullptr),
|
||||
update_count(0),
|
||||
sync_writeback(DEG_EVALUATE_SYNC_WRITEBACK_NO)
|
||||
{
|
||||
BLI_spin_init(&lock);
|
||||
memset(id_type_updated, 0, sizeof(id_type_updated));
|
||||
memset(id_type_updated_backup, 0, sizeof(id_type_updated_backup));
|
||||
memset(id_type_exist, 0, sizeof(id_type_exist));
|
||||
memset(physics_relations_collision, 0, sizeof(physics_relations_collision));
|
||||
|
||||
add_time_source();
|
||||
}
|
||||
|
||||
Depsgraph::~Depsgraph()
|
||||
{
|
||||
clear_id_nodes();
|
||||
delete time_source;
|
||||
BLI_spin_end(&lock);
|
||||
}
|
||||
|
||||
/* Node Management ---------------------------- */
|
||||
|
||||
TimeSourceNode *Depsgraph::add_time_source()
|
||||
{
|
||||
if (time_source == nullptr) {
|
||||
DepsNodeFactory *factory = type_get_factory(NodeType::TIMESOURCE);
|
||||
time_source = static_cast<TimeSourceNode *>(factory->create_node(nullptr, "", "Time Source"));
|
||||
}
|
||||
return time_source;
|
||||
}
|
||||
|
||||
TimeSourceNode *Depsgraph::find_time_source() const
|
||||
{
|
||||
return time_source;
|
||||
}
|
||||
|
||||
void Depsgraph::tag_time_source()
|
||||
{
|
||||
time_source->tag_update(this, DEG_UPDATE_SOURCE_TIME);
|
||||
}
|
||||
|
||||
IDNode *Depsgraph::find_id_node(const ID *id) const
|
||||
{
|
||||
return id_hash.lookup_default(id, nullptr);
|
||||
}
|
||||
|
||||
IDNode *Depsgraph::add_id_node(ID *id, ID *id_cow_hint)
|
||||
{
|
||||
BLI_assert((id->tag & ID_TAG_COPIED_ON_EVAL) == 0);
|
||||
IDNode *id_node = find_id_node(id);
|
||||
if (!id_node) {
|
||||
DepsNodeFactory *factory = type_get_factory(NodeType::ID_REF);
|
||||
id_node = static_cast<IDNode *>(factory->create_node(id, "", id->name));
|
||||
id_node->init_copy_on_write(id_cow_hint);
|
||||
/* Register node in ID hash.
|
||||
*
|
||||
* NOTE: We address ID nodes by the original ID pointer they are
|
||||
* referencing to. */
|
||||
id_hash.add_new(id, id_node);
|
||||
id_nodes.append(id_node);
|
||||
|
||||
id_type_exist[BKE_idtype_idcode_to_index(GS(id->name))] = 1;
|
||||
}
|
||||
return id_node;
|
||||
}
|
||||
|
||||
template<typename FilterFunc>
|
||||
static void clear_id_nodes_conditional(Depsgraph::IDDepsNodes *id_nodes, const FilterFunc &filter)
|
||||
{
|
||||
for (IDNode *id_node : *id_nodes) {
|
||||
if (id_node->id_cow == nullptr) {
|
||||
/* This means builder "stole" ownership of the evaluated
|
||||
* datablock for its own dirty needs. */
|
||||
continue;
|
||||
}
|
||||
if (id_node->id_cow == id_node->id_orig) {
|
||||
/* Evaluated copy is not needed for this ID type.
|
||||
*
|
||||
* NOTE: Is important to not de-reference the original datablock here because it might be
|
||||
* freed already (happens during main database free when some IDs are freed prior to a
|
||||
* scene). */
|
||||
continue;
|
||||
}
|
||||
if (!deg_eval_copy_is_expanded(id_node->id_cow)) {
|
||||
continue;
|
||||
}
|
||||
const ID_Type id_type = GS(id_node->id_cow->name);
|
||||
if (filter(id_type)) {
|
||||
id_node->destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Depsgraph::clear_id_nodes()
|
||||
{
|
||||
/* Free memory used by ID nodes. */
|
||||
|
||||
/* Stupid workaround to ensure we free IDs in a proper order. */
|
||||
clear_id_nodes_conditional(&id_nodes, [](ID_Type id_type) { return id_type == ID_SCE; });
|
||||
clear_id_nodes_conditional(&id_nodes, [](ID_Type id_type) { return id_type != ID_PA; });
|
||||
|
||||
for (IDNode *id_node : id_nodes) {
|
||||
delete id_node;
|
||||
}
|
||||
/* Clear containers. */
|
||||
id_hash.clear();
|
||||
id_nodes.clear();
|
||||
/* Clear physics relation caches. */
|
||||
clear_physics_relations(this);
|
||||
|
||||
light_linking_cache.clear();
|
||||
}
|
||||
|
||||
Relation *Depsgraph::add_new_relation(Node *from, Node *to, const char *description, int flags)
|
||||
{
|
||||
Relation *rel = nullptr;
|
||||
if (flags & RELATION_CHECK_BEFORE_ADD) {
|
||||
rel = check_nodes_connected(from, to, description);
|
||||
}
|
||||
if (rel != nullptr) {
|
||||
rel->flag |= flags;
|
||||
return rel;
|
||||
}
|
||||
|
||||
#ifndef NDEBUG
|
||||
if (from->type == NodeType::OPERATION && to->type == NodeType::OPERATION) {
|
||||
OperationNode *operation_from = static_cast<OperationNode *>(from);
|
||||
OperationNode *operation_to = static_cast<OperationNode *>(to);
|
||||
BLI_assert(operation_to->owner->type != NodeType::COPY_ON_EVAL ||
|
||||
operation_from->owner->type == NodeType::COPY_ON_EVAL);
|
||||
}
|
||||
#endif
|
||||
|
||||
/* Create new relation, and add it to the graph. The type must be trivially destructible for
|
||||
* `.release()` to be okay. If it weren't, we could store the relations with #destruct_ptr on
|
||||
* either the `inlinks` or `outlinks`. But since so many #Relation structs are allocated, it's
|
||||
* probably better for it be a simple type anyway. */
|
||||
static_assert(std::is_trivially_destructible_v<Relation>);
|
||||
rel = this->build_allocator.construct<Relation>(from, to, description).release();
|
||||
from->outlinks.append(rel);
|
||||
to->inlinks.append(rel);
|
||||
rel->flag |= flags;
|
||||
return rel;
|
||||
}
|
||||
|
||||
Relation *Depsgraph::check_nodes_connected(const Node *from,
|
||||
const Node *to,
|
||||
const char *description)
|
||||
{
|
||||
for (Relation *rel : from->outlinks) {
|
||||
BLI_assert(rel->from == from);
|
||||
if (rel->to != to) {
|
||||
continue;
|
||||
}
|
||||
if (description != nullptr && !STREQ(rel->name, description)) {
|
||||
continue;
|
||||
}
|
||||
return rel;
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
/* Low level tagging -------------------------------------- */
|
||||
|
||||
void Depsgraph::add_entry_tag(OperationNode *node)
|
||||
{
|
||||
/* Sanity check. */
|
||||
if (node == nullptr) {
|
||||
return;
|
||||
}
|
||||
/* Add to graph-level set of directly modified nodes to start searching
|
||||
* from.
|
||||
* NOTE: this is necessary since we have several thousand nodes to play
|
||||
* with. */
|
||||
entry_tags.add(node);
|
||||
}
|
||||
|
||||
void Depsgraph::clear_all_nodes()
|
||||
{
|
||||
clear_id_nodes();
|
||||
delete time_source;
|
||||
time_source = nullptr;
|
||||
/* Memory used by the build allocator is now unused. Rebuild it from scratch. */
|
||||
std::destroy_at(&this->build_allocator);
|
||||
new (&this->build_allocator) LinearAllocator<>();
|
||||
}
|
||||
|
||||
ID *Depsgraph::get_cow_id(const ID *id_orig) const
|
||||
{
|
||||
IDNode *id_node = find_id_node(id_orig);
|
||||
if (id_node == nullptr) {
|
||||
/* This function is used from places where we expect ID to be either
|
||||
* already a copy-on-evaluation version or have a corresponding copy-on-evaluation
|
||||
* version.
|
||||
*
|
||||
* We try to enforce that in debug builds, for release we play a bit
|
||||
* safer game here. */
|
||||
if ((id_orig->tag & ID_TAG_COPIED_ON_EVAL) == 0) {
|
||||
/* TODO(sergey): This is nice sanity check to have, but it fails
|
||||
* in following situations:
|
||||
*
|
||||
* - Material has link to texture, which is not needed by new
|
||||
* shading system and hence can be ignored at construction.
|
||||
* - Object or mesh has material at a slot which is not used (for
|
||||
* example, object has material slot by materials are set to
|
||||
* object data). */
|
||||
// BLI_assert_msg(0, "Request for non-existing copy-on-evaluation ID");
|
||||
}
|
||||
return const_cast<ID *>(id_orig);
|
||||
}
|
||||
return id_node->id_cow;
|
||||
}
|
||||
|
||||
} // namespace deg
|
||||
|
||||
/* **************** */
|
||||
/* Public Graph API */
|
||||
|
||||
Depsgraph *DEG_graph_new(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode)
|
||||
{
|
||||
deg::Depsgraph *deg_depsgraph = new deg::Depsgraph(bmain, scene, view_layer, mode);
|
||||
deg::register_graph(deg_depsgraph);
|
||||
return reinterpret_cast<Depsgraph *>(deg_depsgraph);
|
||||
}
|
||||
|
||||
void DEG_graph_replace_owners(Depsgraph *depsgraph,
|
||||
Main *bmain,
|
||||
Scene *scene,
|
||||
ViewLayer *view_layer)
|
||||
{
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
|
||||
|
||||
const bool do_update_register = deg_graph->bmain != bmain;
|
||||
if (do_update_register && deg_graph->bmain != nullptr) {
|
||||
deg::unregister_graph(deg_graph);
|
||||
}
|
||||
|
||||
deg_graph->bmain = bmain;
|
||||
deg_graph->scene = scene;
|
||||
deg_graph->view_layer = view_layer;
|
||||
|
||||
if (do_update_register) {
|
||||
deg::register_graph(deg_graph);
|
||||
}
|
||||
}
|
||||
|
||||
void DEG_graph_free(Depsgraph *graph)
|
||||
{
|
||||
if (graph == nullptr) {
|
||||
return;
|
||||
}
|
||||
using deg::Depsgraph;
|
||||
deg::Depsgraph *deg_depsgraph = reinterpret_cast<deg::Depsgraph *>(graph);
|
||||
deg::unregister_graph(deg_depsgraph);
|
||||
delete deg_depsgraph;
|
||||
}
|
||||
|
||||
bool DEG_is_evaluating(const Depsgraph *depsgraph)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
|
||||
return deg_graph->is_evaluating;
|
||||
}
|
||||
|
||||
bool DEG_is_active(const Depsgraph *depsgraph)
|
||||
{
|
||||
if (depsgraph == nullptr) {
|
||||
/* Happens for such cases as work object in what_does_obaction(),
|
||||
* and sine render pipeline parts. Shouldn't really be accepting
|
||||
* nullptr depsgraph, but is quite hard to get proper one in those
|
||||
* cases. */
|
||||
return false;
|
||||
}
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
|
||||
return deg_graph->is_active;
|
||||
}
|
||||
|
||||
void DEG_make_active(Depsgraph *depsgraph)
|
||||
{
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
|
||||
deg_graph->is_active = true;
|
||||
/* TODO(sergey): Copy data from evaluated state to original. */
|
||||
}
|
||||
|
||||
void DEG_make_inactive(Depsgraph *depsgraph)
|
||||
{
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
|
||||
deg_graph->is_active = false;
|
||||
}
|
||||
|
||||
void DEG_disable_visibility_optimization(Depsgraph *depsgraph)
|
||||
{
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
|
||||
deg_graph->use_visibility_optimization = false;
|
||||
}
|
||||
|
||||
uint64_t DEG_get_update_count(const Depsgraph *depsgraph)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
|
||||
return deg_graph->update_count;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
206
blender-5.2.0/source/blender/depsgraph/intern/depsgraph.hh
Normal file
206
blender-5.2.0/source/blender/depsgraph/intern/depsgraph.hh
Normal file
@@ -0,0 +1,206 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Data-types for internal use in the Depsgraph
|
||||
*
|
||||
* All of these data-types are only really used within the "core" depsgraph.
|
||||
* In particular, node types declared here form the structure of operations
|
||||
* in the graph.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
#include <functional>
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "DNA_ID.h" /* for ID_Type and INDEX_ID_MAX */
|
||||
|
||||
#include "BLI_linear_allocator.hh"
|
||||
#include "BLI_mutex.hh"
|
||||
#include "BLI_set.hh"
|
||||
#include "BLI_threads.h" /* for SpinLock */
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_physics.hh"
|
||||
|
||||
#include "intern/debug/deg_debug.h"
|
||||
#include "intern/depsgraph_light_linking.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ID;
|
||||
struct Scene;
|
||||
struct ViewLayer;
|
||||
|
||||
namespace deg {
|
||||
|
||||
struct IDNode;
|
||||
struct Node;
|
||||
struct OperationNode;
|
||||
struct Relation;
|
||||
struct TimeSourceNode;
|
||||
|
||||
/* Dependency Graph object */
|
||||
struct Depsgraph {
|
||||
using OperationNodes = Vector<OperationNode *>;
|
||||
using IDDepsNodes = Vector<IDNode *>;
|
||||
|
||||
Depsgraph(Main *bmain, Scene *scene, ViewLayer *view_layer, eEvaluationMode mode);
|
||||
~Depsgraph();
|
||||
|
||||
TimeSourceNode *add_time_source();
|
||||
TimeSourceNode *find_time_source() const;
|
||||
void tag_time_source();
|
||||
|
||||
IDNode *find_id_node(const ID *id) const;
|
||||
IDNode *add_id_node(ID *id, ID *id_cow_hint = nullptr);
|
||||
void clear_id_nodes();
|
||||
|
||||
/** Add new relationship between two nodes. */
|
||||
Relation *add_new_relation(Node *from, Node *to, const char *description, int flags = 0);
|
||||
|
||||
/* Check whether two nodes are connected by relation with given
|
||||
* description. Description might be nullptr to check ANY relation between
|
||||
* given nodes. */
|
||||
Relation *check_nodes_connected(const Node *from, const Node *to, const char *description);
|
||||
|
||||
/* Tag a specific node as needing updates. */
|
||||
void add_entry_tag(OperationNode *node);
|
||||
|
||||
/* Clear storage used by all nodes. */
|
||||
void clear_all_nodes();
|
||||
|
||||
/* Copy-on-Write Functionality ........ */
|
||||
|
||||
/* For given original ID get ID which is created by copy-on-evaluation system. */
|
||||
ID *get_cow_id(const ID *id_orig) const;
|
||||
|
||||
/* Core Graph Functionality ........... */
|
||||
|
||||
/**
|
||||
* Used to decrease the cost of allocating many small structs when building the graph. This is a
|
||||
* viable strategy because the graph is rebuilt from scratch rather than changed in-place.
|
||||
*/
|
||||
LinearAllocator<> build_allocator;
|
||||
|
||||
/* <ID : IDNode> mapping from ID blocks to nodes representing these
|
||||
* blocks, used for quick lookups. */
|
||||
Map<const ID *, IDNode *> id_hash;
|
||||
|
||||
/* Ordered list of ID nodes, order matches ID allocation order.
|
||||
* Used for faster iteration, especially for areas which are critical to
|
||||
* keep exact order of iteration. */
|
||||
IDDepsNodes id_nodes;
|
||||
|
||||
/* Top-level time source node. */
|
||||
TimeSourceNode *time_source;
|
||||
|
||||
/* The graph contains data-blocks whose visibility depends on evaluation (driven or animated). */
|
||||
bool has_animated_visibility;
|
||||
|
||||
/* Indicates whether relations needs to be updated. */
|
||||
bool need_update_relations;
|
||||
|
||||
/* Indicates whether indirect effect of nodes on a directly visible ones needs to be updated. */
|
||||
bool need_update_nodes_visibility;
|
||||
|
||||
/* Indicated whether IDs in this graph are to be tagged as if they first appear visible, with
|
||||
* an optional tag for their animation (time) update. */
|
||||
bool need_tag_id_on_graph_visibility_update;
|
||||
bool need_tag_id_on_graph_visibility_time_update;
|
||||
|
||||
/* Indicates which ID types were updated. */
|
||||
char id_type_updated[INDEX_ID_MAX];
|
||||
/* Accumulate id type updates from multiple update passes. */
|
||||
char id_type_updated_backup[INDEX_ID_MAX];
|
||||
|
||||
/* Indicates type of IDs present in the depsgraph. */
|
||||
char id_type_exist[INDEX_ID_MAX];
|
||||
|
||||
/* Quick-Access Temp Data ............. */
|
||||
|
||||
/* Nodes which have been tagged as "directly modified". */
|
||||
Set<OperationNode *> entry_tags;
|
||||
|
||||
/* Convenience Data ................... */
|
||||
|
||||
/* XXX: should be collected after building (if actually needed?) */
|
||||
/* All operation nodes, sorted in order of single-thread traversal order. */
|
||||
OperationNodes operations;
|
||||
|
||||
/* Spin lock for threading-critical operations.
|
||||
* Mainly used by graph evaluation. */
|
||||
SpinLock lock;
|
||||
|
||||
/* Main, scene, layer, mode this dependency graph is built for. */
|
||||
Main *bmain;
|
||||
Scene *scene;
|
||||
ViewLayer *view_layer;
|
||||
eEvaluationMode mode;
|
||||
|
||||
/* Time at which dependency graph is being or was last evaluated.
|
||||
* frame is the value before, and ctime the value after time remapping. */
|
||||
float frame;
|
||||
float ctime;
|
||||
|
||||
/* Evaluated version of datablocks we access a lot.
|
||||
* Stored here to save us form doing hash lookup. */
|
||||
Scene *scene_cow;
|
||||
|
||||
/* Active dependency graph is a dependency graph which is used by the
|
||||
* currently active window. When dependency graph is active, it is allowed
|
||||
* for evaluation functions to write animation f-curve result, drivers
|
||||
* result and other selective things (object matrix?) to original object.
|
||||
*
|
||||
* This way we simplify operators, which don't need to worry about where
|
||||
* to read stuff from. */
|
||||
bool is_active;
|
||||
|
||||
/* Optimize out evaluation of operations which affect hidden objects or disabled modifiers. */
|
||||
bool use_visibility_optimization;
|
||||
|
||||
DepsgraphDebug debug;
|
||||
|
||||
bool is_evaluating;
|
||||
|
||||
/* Is set to truth for dependency graph which are used for post-processing (compositor and
|
||||
* sequencer).
|
||||
* Such dependency graph needs all view layers (so render pipeline can access names), but it
|
||||
* does not need any bases. */
|
||||
bool is_render_pipeline_depsgraph;
|
||||
|
||||
/* Notify editors about changes to IDs in this depsgraph. */
|
||||
bool use_editors_update;
|
||||
|
||||
/* Cached list of colliders/effectors for collections and the scene
|
||||
* created along with relations, for fast lookup during evaluation. */
|
||||
Map<const ID *, ListBaseT<EffectorRelation> *> *physics_relations_effector;
|
||||
Map<const ID *, ListBaseT<CollisionRelation> *>
|
||||
*physics_relations_collision[DEG_PHYSICS_COLLISION_NUM];
|
||||
|
||||
light_linking::Cache light_linking_cache;
|
||||
|
||||
/* The number of times this graph has been evaluated. */
|
||||
uint64_t update_count;
|
||||
|
||||
/* If this mode does not allow writing back to original data any callbacks will be discarded. */
|
||||
DepsgraphEvaluateSyncWriteback sync_writeback;
|
||||
/**
|
||||
* Stores functions that can be called after depsgraph evaluation to writeback some changes to
|
||||
* original data. Also see `DEG_depsgraph_writeback_sync.hh`.
|
||||
*/
|
||||
Vector<std::function<void()>> sync_writeback_callbacks;
|
||||
/** Needs to be locked when adding a writeback callback during evaluation. */
|
||||
Mutex sync_writeback_callbacks_mutex;
|
||||
|
||||
MEM_CXX_CLASS_ALLOC_FUNCS("Depsgraph");
|
||||
};
|
||||
|
||||
} // namespace deg
|
||||
} // namespace blender
|
||||
358
blender-5.2.0/source/blender/depsgraph/intern/depsgraph_build.cc
Normal file
358
blender-5.2.0/source/blender/depsgraph/intern/depsgraph_build.cc
Normal file
@@ -0,0 +1,358 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Methods for constructing depsgraph.
|
||||
*/
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_cachefile_types.h"
|
||||
#include "DNA_camera_types.h"
|
||||
#include "DNA_collection_types.h"
|
||||
#include "DNA_layer_types.h"
|
||||
#include "DNA_node_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
#include "DNA_vfont_types.h"
|
||||
|
||||
#include "BKE_collection.hh"
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_main.hh"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
#include "DEG_depsgraph_debug.hh"
|
||||
|
||||
#include "builder/deg_builder_relations.h"
|
||||
#include "builder/pipeline_all_objects.h"
|
||||
#include "builder/pipeline_compositor.h"
|
||||
#include "builder/pipeline_from_collection.h"
|
||||
#include "builder/pipeline_from_ids.h"
|
||||
#include "builder/pipeline_render.h"
|
||||
#include "builder/pipeline_view_layer.h"
|
||||
|
||||
#include "intern/debug/deg_debug.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"
|
||||
|
||||
#include "intern/depsgraph_registry.hh"
|
||||
#include "intern/depsgraph_relation.hh"
|
||||
#include "intern/depsgraph_tag.hh"
|
||||
#include "intern/depsgraph_type.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* ****************** */
|
||||
/* External Build API */
|
||||
|
||||
static deg::NodeType deg_build_scene_component_type(eDepsSceneComponentType component)
|
||||
{
|
||||
switch (component) {
|
||||
case DEG_SCENE_COMP_PARAMETERS:
|
||||
return deg::NodeType::PARAMETERS;
|
||||
case DEG_SCENE_COMP_ANIMATION:
|
||||
return deg::NodeType::ANIMATION;
|
||||
case DEG_SCENE_COMP_SEQUENCER:
|
||||
return deg::NodeType::SEQUENCER;
|
||||
case DEG_SCENE_COMP_COMPOSITOR:
|
||||
return deg::NodeType::COMPOSITOR;
|
||||
}
|
||||
return deg::NodeType::UNDEFINED;
|
||||
}
|
||||
|
||||
static deg::DepsNodeHandle *get_node_handle(DepsNodeHandle *node_handle)
|
||||
{
|
||||
return reinterpret_cast<deg::DepsNodeHandle *>(node_handle);
|
||||
}
|
||||
|
||||
void DEG_add_scene_relation(DepsNodeHandle *node_handle,
|
||||
Scene *scene,
|
||||
eDepsSceneComponentType component,
|
||||
const char *description)
|
||||
{
|
||||
deg::NodeType type = deg_build_scene_component_type(component);
|
||||
deg::ComponentKey comp_key(&scene->id, type);
|
||||
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
|
||||
deg_node_handle->builder->add_node_handle_relation(comp_key, deg_node_handle, description);
|
||||
}
|
||||
|
||||
static void add_camera_parameters_relation(DepsNodeHandle *node_handle,
|
||||
Camera *camera,
|
||||
const char *description)
|
||||
{
|
||||
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
|
||||
deg::ComponentKey parameters_key(&camera->id, deg::NodeType::PARAMETERS);
|
||||
deg_node_handle->builder->add_node_handle_relation(parameters_key, deg_node_handle, description);
|
||||
}
|
||||
|
||||
void DEG_add_scene_camera_relation(DepsNodeHandle *node_handle,
|
||||
Scene *scene,
|
||||
eDepsObjectComponentType component,
|
||||
const char *description)
|
||||
{
|
||||
if (scene->camera != nullptr) {
|
||||
DEG_add_object_relation(node_handle, scene->camera, component, description);
|
||||
if (scene->camera->type == OB_CAMERA) {
|
||||
add_camera_parameters_relation(
|
||||
node_handle, reinterpret_cast<Camera *>(scene->camera->data), description);
|
||||
}
|
||||
}
|
||||
|
||||
/* Like DepsgraphNodeBuilder::build_scene_camera(), we also need to account for other cameras
|
||||
* referenced by markers. */
|
||||
for (TimeMarker &marker : scene->markers) {
|
||||
if (!ELEM(marker.camera, nullptr, scene->camera)) {
|
||||
DEG_add_object_relation(node_handle, marker.camera, component, description);
|
||||
if (marker.camera->type == OB_CAMERA) {
|
||||
add_camera_parameters_relation(
|
||||
node_handle, reinterpret_cast<Camera *>(marker.camera->data), description);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DEG_add_object_relation(DepsNodeHandle *node_handle,
|
||||
Object *object,
|
||||
eDepsObjectComponentType component,
|
||||
const char *description)
|
||||
{
|
||||
deg::NodeType type = deg::nodeTypeFromObjectComponent(component);
|
||||
deg::ComponentKey comp_key(&object->id, type);
|
||||
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
|
||||
deg_node_handle->builder->add_node_handle_relation(comp_key, deg_node_handle, description);
|
||||
}
|
||||
|
||||
bool DEG_object_has_geometry_component(Object *object)
|
||||
{
|
||||
return deg::geometry_tag_to_component(&object->id) == deg::NodeType::GEOMETRY;
|
||||
}
|
||||
|
||||
void DEG_add_collection_geometry_relation(DepsNodeHandle *node_handle,
|
||||
Collection *collection,
|
||||
const char *description)
|
||||
{
|
||||
deg::OperationKey operation_key{
|
||||
&collection->id, deg::NodeType::GEOMETRY, deg::OperationCode::GEOMETRY_EVAL_DONE};
|
||||
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
|
||||
deg_node_handle->builder->add_node_handle_relation(operation_key, deg_node_handle, description);
|
||||
}
|
||||
|
||||
void DEG_add_collection_geometry_customdata_mask(DepsNodeHandle *node_handle,
|
||||
Collection *collection,
|
||||
const CustomData_MeshMasks *masks)
|
||||
{
|
||||
FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (collection, ob) {
|
||||
DEG_add_customdata_mask(node_handle, ob, masks);
|
||||
if (ob->type == OB_EMPTY && ob->instance_collection != nullptr) {
|
||||
DEG_add_collection_geometry_customdata_mask(node_handle, ob->instance_collection, masks);
|
||||
}
|
||||
}
|
||||
FOREACH_COLLECTION_OBJECT_RECURSIVE_END;
|
||||
}
|
||||
|
||||
void DEG_add_node_tree_output_relation(DepsNodeHandle *node_handle,
|
||||
bNodeTree *node_tree,
|
||||
const char *description)
|
||||
{
|
||||
deg::OperationKey ntree_output_key(
|
||||
&node_tree->id, deg::NodeType::NTREE_OUTPUT, deg::OperationCode::NTREE_OUTPUT);
|
||||
deg::OperationKey ntree_preprocess_key(&node_tree->id,
|
||||
deg::NodeType::NTREE_GEOMETRY_PREPROCESS,
|
||||
deg::OperationCode::NTREE_GEOMETRY_PREPROCESS);
|
||||
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
|
||||
deg_node_handle->builder->add_node_handle_relation(
|
||||
ntree_output_key, deg_node_handle, description);
|
||||
deg_node_handle->builder->add_node_handle_relation(
|
||||
ntree_preprocess_key, deg_node_handle, description, deg::RELATION_FLAG_NO_FLUSH);
|
||||
}
|
||||
|
||||
void DEG_add_object_cache_relation(DepsNodeHandle *node_handle,
|
||||
CacheFile *cache_file,
|
||||
eDepsObjectComponentType component,
|
||||
const char *description)
|
||||
{
|
||||
deg::NodeType type = deg::nodeTypeFromObjectComponent(component);
|
||||
deg::ComponentKey comp_key(&cache_file->id, type);
|
||||
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
|
||||
deg_node_handle->builder->add_node_handle_relation(comp_key, deg_node_handle, description);
|
||||
}
|
||||
|
||||
void DEG_add_bone_relation(DepsNodeHandle *node_handle,
|
||||
Object *object,
|
||||
const char *bone_name,
|
||||
eDepsObjectComponentType component,
|
||||
const char *description)
|
||||
{
|
||||
deg::NodeType type = deg::nodeTypeFromObjectComponent(component);
|
||||
deg::ComponentKey comp_key(&object->id, type, bone_name);
|
||||
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
|
||||
deg_node_handle->builder->add_node_handle_relation(comp_key, deg_node_handle, description);
|
||||
}
|
||||
|
||||
void DEG_add_vfont_relation(DepsNodeHandle *handle, VFont *vfont, const char *description)
|
||||
{
|
||||
deg::OperationKey operation_key(
|
||||
&vfont->id, deg::NodeType::PARAMETERS, deg::OperationCode::PARAMETERS_EXIT);
|
||||
deg::DepsNodeHandle *deg_node_handle = get_node_handle(handle);
|
||||
deg_node_handle->builder->add_node_handle_relation(operation_key, deg_node_handle, description);
|
||||
}
|
||||
|
||||
void DEG_add_object_pointcache_relation(DepsNodeHandle *node_handle,
|
||||
Object *object,
|
||||
eDepsObjectComponentType component,
|
||||
const char *description)
|
||||
{
|
||||
deg::NodeType type = deg::nodeTypeFromObjectComponent(component);
|
||||
deg::ComponentKey comp_key(&object->id, type);
|
||||
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
|
||||
deg::DepsgraphRelationBuilder *relation_builder = deg_node_handle->builder;
|
||||
/* Add relation from source to the node handle. */
|
||||
relation_builder->add_node_handle_relation(comp_key, deg_node_handle, description);
|
||||
/* Node deduct point cache component and connect source to it. */
|
||||
ID *id = DEG_get_id_from_handle(node_handle);
|
||||
deg::ComponentKey point_cache_key(id, deg::NodeType::POINT_CACHE);
|
||||
deg::Relation *rel = relation_builder->add_relation(comp_key, point_cache_key, "Point Cache");
|
||||
if (rel != nullptr) {
|
||||
rel->flag |= deg::RELATION_FLAG_FLUSH_USER_EDIT_ONLY;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error in point cache relation from %s to ^%s.\n", object->id.name, id->name);
|
||||
}
|
||||
}
|
||||
|
||||
void DEG_add_generic_id_relation(DepsNodeHandle *node_handle, ID *id, const char *description)
|
||||
{
|
||||
deg::OperationKey operation_key(
|
||||
id, deg::NodeType::GENERIC_DATABLOCK, deg::OperationCode::GENERIC_DATABLOCK_UPDATE);
|
||||
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
|
||||
deg_node_handle->builder->add_node_handle_relation(operation_key, deg_node_handle, description);
|
||||
}
|
||||
|
||||
void DEG_add_depends_on_transform_relation(DepsNodeHandle *node_handle, const char *description)
|
||||
{
|
||||
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
|
||||
deg_node_handle->builder->add_depends_on_transform_relation(deg_node_handle, description);
|
||||
}
|
||||
|
||||
void DEG_add_time_source_relation(DepsNodeHandle *node_handle, const char *description)
|
||||
{
|
||||
const deg::TimeSourceKey time_src_key;
|
||||
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
|
||||
deg_node_handle->builder->add_node_handle_relation(time_src_key, deg_node_handle, description);
|
||||
}
|
||||
|
||||
void DEG_add_special_eval_flag(DepsNodeHandle *node_handle, ID *id, uint32_t flag)
|
||||
{
|
||||
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
|
||||
deg_node_handle->builder->add_special_eval_flag(id, flag);
|
||||
}
|
||||
|
||||
void DEG_add_customdata_mask(DepsNodeHandle *node_handle,
|
||||
Object *object,
|
||||
const CustomData_MeshMasks *masks)
|
||||
{
|
||||
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
|
||||
deg_node_handle->builder->add_customdata_mask(object, deg::DEGCustomDataMeshMasks(masks));
|
||||
}
|
||||
|
||||
ID *DEG_get_id_from_handle(DepsNodeHandle *node_handle)
|
||||
{
|
||||
deg::DepsNodeHandle *deg_handle = get_node_handle(node_handle);
|
||||
return deg_handle->node->owner->owner->id_orig;
|
||||
}
|
||||
|
||||
Depsgraph *DEG_get_graph_from_handle(DepsNodeHandle *node_handle)
|
||||
{
|
||||
deg::DepsNodeHandle *deg_node_handle = get_node_handle(node_handle);
|
||||
deg::DepsgraphRelationBuilder *relation_builder = deg_node_handle->builder;
|
||||
return reinterpret_cast<Depsgraph *>(relation_builder->getGraph());
|
||||
}
|
||||
|
||||
/* ******************** */
|
||||
/* Graph Building API's */
|
||||
|
||||
void DEG_graph_build_from_view_layer(Depsgraph *graph)
|
||||
{
|
||||
deg::ViewLayerBuilderPipeline builder(graph);
|
||||
builder.build();
|
||||
}
|
||||
|
||||
void DEG_graph_build_for_all_objects(Depsgraph *graph)
|
||||
{
|
||||
deg::AllObjectsBuilderPipeline builder(graph);
|
||||
builder.build();
|
||||
}
|
||||
|
||||
void DEG_graph_build_for_render_pipeline(Depsgraph *graph)
|
||||
{
|
||||
deg::RenderBuilderPipeline builder(graph);
|
||||
builder.build();
|
||||
}
|
||||
|
||||
void DEG_graph_build_for_compositor_preview(Depsgraph *graph)
|
||||
{
|
||||
deg::CompositorBuilderPipeline builder(graph);
|
||||
builder.build();
|
||||
}
|
||||
|
||||
void DEG_graph_build_from_ids(Depsgraph *graph, Span<ID *> ids)
|
||||
{
|
||||
deg::FromIDsBuilderPipeline builder(graph, ids);
|
||||
builder.build();
|
||||
}
|
||||
|
||||
void DEG_graph_build_from_collection(Depsgraph *graph, Collection *collection)
|
||||
{
|
||||
deg::FromCollectionBuilderPipeline builder(graph, collection);
|
||||
builder.build();
|
||||
}
|
||||
|
||||
void DEG_graph_tag_relations_update(Depsgraph *graph)
|
||||
{
|
||||
DEG_DEBUG_PRINTF(graph, TAG, "%s: Tagging relations for update.\n", __func__);
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
|
||||
deg_graph->need_update_relations = true;
|
||||
|
||||
/* NOTE: When relations are updated, it's quite possible that we've got new bases in the scene.
|
||||
* This means, we need to re-create flat array of bases in view layer. */
|
||||
/* TODO(sergey): It is expected that bases manipulation tags scene for update to tag bases array
|
||||
* for re-creation. Once it is ensured to happen from all places this implicit tag can be
|
||||
* removed. */
|
||||
deg::IDNode *id_node = deg_graph->find_id_node(°_graph->scene->id);
|
||||
if (id_node != nullptr) {
|
||||
graph_id_tag_update(deg_graph->bmain,
|
||||
deg_graph,
|
||||
°_graph->scene->id,
|
||||
ID_RECALC_BASE_FLAGS | ID_RECALC_HIERARCHY,
|
||||
deg::DEG_UPDATE_SOURCE_RELATIONS);
|
||||
}
|
||||
}
|
||||
|
||||
void DEG_graph_relations_update(Depsgraph *graph)
|
||||
{
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
|
||||
if (!deg_graph->need_update_relations) {
|
||||
/* Graph is up to date, nothing to do. */
|
||||
return;
|
||||
}
|
||||
DEG_graph_build_from_view_layer(graph);
|
||||
}
|
||||
|
||||
void DEG_relations_tag_update(Main *bmain)
|
||||
{
|
||||
DEG_GLOBAL_DEBUG_PRINTF(TAG, "%s: Tagging relations for update.\n", __func__);
|
||||
for (deg::Depsgraph *depsgraph : deg::get_all_registered_graphs(bmain)) {
|
||||
DEG_graph_tag_relations_update(reinterpret_cast<Depsgraph *>(depsgraph));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
396
blender-5.2.0/source/blender/depsgraph/intern/depsgraph_debug.cc
Normal file
396
blender-5.2.0/source/blender/depsgraph/intern/depsgraph_debug.cc
Normal file
@@ -0,0 +1,396 @@
|
||||
/* SPDX-FileCopyrightText: 2014 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Implementation of tools for debugging the depsgraph
|
||||
*/
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
#include "DEG_depsgraph_debug.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "intern/debug/deg_debug.h"
|
||||
#include "intern/depsgraph.hh"
|
||||
#include "intern/depsgraph_relation.hh"
|
||||
#include "intern/node/deg_node_component.hh"
|
||||
#include "intern/node/deg_node_id.hh"
|
||||
#include "intern/node/deg_node_time.hh"
|
||||
|
||||
#include "BLI_math_bits.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void DEG_debug_flags_set(Depsgraph *depsgraph, int flags)
|
||||
{
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
|
||||
deg_graph->debug.flags = flags;
|
||||
}
|
||||
|
||||
int DEG_debug_flags_get(const Depsgraph *depsgraph)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
|
||||
return deg_graph->debug.flags;
|
||||
}
|
||||
|
||||
void DEG_debug_name_set(Depsgraph *depsgraph, const char *name)
|
||||
{
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
|
||||
deg_graph->debug.name = name;
|
||||
}
|
||||
|
||||
const char *DEG_debug_name_get(Depsgraph *depsgraph)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
|
||||
return deg_graph->debug.name.c_str();
|
||||
}
|
||||
|
||||
bool DEG_debug_compare(const Depsgraph *graph1, const Depsgraph *graph2)
|
||||
{
|
||||
BLI_assert(graph1 != nullptr);
|
||||
BLI_assert(graph2 != nullptr);
|
||||
const deg::Depsgraph *deg_graph1 = reinterpret_cast<const deg::Depsgraph *>(graph1);
|
||||
const deg::Depsgraph *deg_graph2 = reinterpret_cast<const deg::Depsgraph *>(graph2);
|
||||
if (deg_graph1->operations.size() != deg_graph2->operations.size()) {
|
||||
return false;
|
||||
}
|
||||
/* TODO(sergey): Currently we only do real stupid check,
|
||||
* which is fast but which isn't 100% reliable.
|
||||
*
|
||||
* Would be cool to make it more robust, but it's good enough
|
||||
* for now. Also, proper graph check is actually NP-complex
|
||||
* problem. */
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DEG_debug_graph_relations_validate(Depsgraph *graph,
|
||||
Main *bmain,
|
||||
Scene *scene,
|
||||
ViewLayer *view_layer)
|
||||
{
|
||||
Depsgraph *temp_depsgraph = DEG_graph_new(bmain, scene, view_layer, DEG_get_mode(graph));
|
||||
bool valid = true;
|
||||
DEG_graph_build_from_view_layer(temp_depsgraph);
|
||||
if (!DEG_debug_compare(temp_depsgraph, graph)) {
|
||||
fprintf(stderr, "ERROR! Depsgraph wasn't tagged for update when it should have!\n");
|
||||
BLI_assert_msg(0, "This should not happen!");
|
||||
valid = false;
|
||||
}
|
||||
DEG_graph_free(temp_depsgraph);
|
||||
return valid;
|
||||
}
|
||||
|
||||
bool DEG_debug_consistency_check(Depsgraph *graph)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
||||
/* Validate links exists in both directions. */
|
||||
for (deg::OperationNode *node : deg_graph->operations) {
|
||||
for (deg::Relation *rel : node->outlinks) {
|
||||
int counter1 = 0;
|
||||
for (deg::Relation *tmp_rel : node->outlinks) {
|
||||
if (tmp_rel == rel) {
|
||||
counter1++;
|
||||
}
|
||||
}
|
||||
int counter2 = 0;
|
||||
for (deg::Relation *tmp_rel : rel->to->inlinks) {
|
||||
if (tmp_rel == rel) {
|
||||
counter2++;
|
||||
}
|
||||
}
|
||||
if (counter1 != counter2) {
|
||||
printf(
|
||||
"Relation exists in outgoing direction but not in "
|
||||
"incoming (%d vs. %d).\n",
|
||||
counter1,
|
||||
counter2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (deg::OperationNode *node : deg_graph->operations) {
|
||||
for (deg::Relation *rel : node->inlinks) {
|
||||
int counter1 = 0;
|
||||
for (deg::Relation *tmp_rel : node->inlinks) {
|
||||
if (tmp_rel == rel) {
|
||||
counter1++;
|
||||
}
|
||||
}
|
||||
int counter2 = 0;
|
||||
for (deg::Relation *tmp_rel : rel->from->outlinks) {
|
||||
if (tmp_rel == rel) {
|
||||
counter2++;
|
||||
}
|
||||
}
|
||||
if (counter1 != counter2) {
|
||||
printf("Relation exists in incoming direction but not in outcoming (%d vs. %d).\n",
|
||||
counter1,
|
||||
counter2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Validate node valency calculated in both directions. */
|
||||
for (deg::OperationNode *node : deg_graph->operations) {
|
||||
node->num_links_pending = 0;
|
||||
node->custom_flags = 0;
|
||||
}
|
||||
|
||||
for (deg::OperationNode *node : deg_graph->operations) {
|
||||
if (node->custom_flags) {
|
||||
printf("Node %s is twice in the operations!\n", node->identifier().c_str());
|
||||
return false;
|
||||
}
|
||||
for (deg::Relation *rel : node->outlinks) {
|
||||
if (rel->to->type == deg::NodeType::OPERATION) {
|
||||
deg::OperationNode *to = static_cast<deg::OperationNode *>(rel->to);
|
||||
BLI_assert(to->num_links_pending < to->inlinks.size());
|
||||
++to->num_links_pending;
|
||||
}
|
||||
}
|
||||
node->custom_flags = 1;
|
||||
}
|
||||
|
||||
for (deg::OperationNode *node : deg_graph->operations) {
|
||||
int num_links_pending = 0;
|
||||
for (deg::Relation *rel : node->inlinks) {
|
||||
if (rel->from->type == deg::NodeType::OPERATION) {
|
||||
num_links_pending++;
|
||||
}
|
||||
}
|
||||
if (node->num_links_pending != num_links_pending) {
|
||||
printf("Valency mismatch: %s, %u != %d\n",
|
||||
node->identifier().c_str(),
|
||||
node->num_links_pending,
|
||||
num_links_pending);
|
||||
printf("Number of inlinks: %d\n", int(node->inlinks.size()));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------ */
|
||||
|
||||
void DEG_stats_simple(const Depsgraph *graph,
|
||||
size_t *r_outer,
|
||||
size_t *r_operations,
|
||||
size_t *r_relations)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
||||
|
||||
/* number of operations */
|
||||
if (r_operations) {
|
||||
/* All operations should be in this list, allowing us to count the total
|
||||
* number of nodes. */
|
||||
*r_operations = deg_graph->operations.size();
|
||||
}
|
||||
|
||||
/* Count number of outer nodes and/or relations between these. */
|
||||
if (r_outer || r_relations) {
|
||||
size_t tot_outer = 0;
|
||||
size_t tot_rels = 0;
|
||||
|
||||
for (deg::IDNode *id_node : deg_graph->id_nodes) {
|
||||
tot_outer++;
|
||||
for (deg::ComponentNode *comp_node : id_node->components.values()) {
|
||||
tot_outer++;
|
||||
for (deg::OperationNode *op_node : comp_node->operations) {
|
||||
tot_rels += op_node->inlinks.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deg::TimeSourceNode *time_source = deg_graph->find_time_source();
|
||||
if (time_source != nullptr) {
|
||||
tot_rels += time_source->inlinks.size();
|
||||
}
|
||||
|
||||
if (r_relations) {
|
||||
*r_relations = tot_rels;
|
||||
}
|
||||
if (r_outer) {
|
||||
*r_outer = tot_outer;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static std::string depsgraph_name_for_logging(Depsgraph *depsgraph)
|
||||
{
|
||||
const char *name = DEG_debug_name_get(depsgraph);
|
||||
if (name[0] == '\0') {
|
||||
return "";
|
||||
}
|
||||
return "[" + std::string(name) + "]: ";
|
||||
}
|
||||
|
||||
void DEG_debug_print_begin(Depsgraph *depsgraph)
|
||||
{
|
||||
fprintf(stdout, "%s", depsgraph_name_for_logging(depsgraph).c_str());
|
||||
}
|
||||
|
||||
void DEG_debug_print_eval(Depsgraph *depsgraph,
|
||||
const char *function_name,
|
||||
const char *object_name,
|
||||
const void *object_address)
|
||||
{
|
||||
if ((DEG_debug_flags_get(depsgraph) & G_DEBUG_DEPSGRAPH_EVAL) == 0) {
|
||||
return;
|
||||
}
|
||||
fprintf(stdout,
|
||||
"%s%s on %s %s(%p)%s\n",
|
||||
depsgraph_name_for_logging(depsgraph).c_str(),
|
||||
function_name,
|
||||
object_name,
|
||||
deg::color_for_pointer(object_address).c_str(),
|
||||
object_address,
|
||||
deg::color_end().c_str());
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void DEG_debug_print_eval_subdata(Depsgraph *depsgraph,
|
||||
const char *function_name,
|
||||
const char *object_name,
|
||||
const void *object_address,
|
||||
const char *subdata_comment,
|
||||
const char *subdata_name,
|
||||
const void *subdata_address)
|
||||
{
|
||||
if ((DEG_debug_flags_get(depsgraph) & G_DEBUG_DEPSGRAPH_EVAL) == 0) {
|
||||
return;
|
||||
}
|
||||
fprintf(stdout,
|
||||
"%s%s on %s %s(%p)%s %s %s %s(%p)%s\n",
|
||||
depsgraph_name_for_logging(depsgraph).c_str(),
|
||||
function_name,
|
||||
object_name,
|
||||
deg::color_for_pointer(object_address).c_str(),
|
||||
object_address,
|
||||
deg::color_end().c_str(),
|
||||
subdata_comment,
|
||||
subdata_name,
|
||||
deg::color_for_pointer(subdata_address).c_str(),
|
||||
subdata_address,
|
||||
deg::color_end().c_str());
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void DEG_debug_print_eval_subdata_index(Depsgraph *depsgraph,
|
||||
const char *function_name,
|
||||
const char *object_name,
|
||||
const void *object_address,
|
||||
const char *subdata_comment,
|
||||
const char *subdata_name,
|
||||
const void *subdata_address,
|
||||
const int subdata_index)
|
||||
{
|
||||
if ((DEG_debug_flags_get(depsgraph) & G_DEBUG_DEPSGRAPH_EVAL) == 0) {
|
||||
return;
|
||||
}
|
||||
fprintf(stdout,
|
||||
"%s%s on %s %s(%p)%s %s %s[%d] %s(%p)%s\n",
|
||||
depsgraph_name_for_logging(depsgraph).c_str(),
|
||||
function_name,
|
||||
object_name,
|
||||
deg::color_for_pointer(object_address).c_str(),
|
||||
object_address,
|
||||
deg::color_end().c_str(),
|
||||
subdata_comment,
|
||||
subdata_name,
|
||||
subdata_index,
|
||||
deg::color_for_pointer(subdata_address).c_str(),
|
||||
subdata_address,
|
||||
deg::color_end().c_str());
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void DEG_debug_print_eval_parent_typed(Depsgraph *depsgraph,
|
||||
const char *function_name,
|
||||
const char *object_name,
|
||||
const void *object_address,
|
||||
const char *parent_comment,
|
||||
const char *parent_name,
|
||||
const void *parent_address)
|
||||
{
|
||||
if ((DEG_debug_flags_get(depsgraph) & G_DEBUG_DEPSGRAPH_EVAL) == 0) {
|
||||
return;
|
||||
}
|
||||
fprintf(stdout,
|
||||
"%s%s on %s %s(%p) [%s] %s %s %s(%p)%s\n",
|
||||
depsgraph_name_for_logging(depsgraph).c_str(),
|
||||
function_name,
|
||||
object_name,
|
||||
deg::color_for_pointer(object_address).c_str(),
|
||||
object_address,
|
||||
deg::color_end().c_str(),
|
||||
parent_comment,
|
||||
parent_name,
|
||||
deg::color_for_pointer(parent_address).c_str(),
|
||||
parent_address,
|
||||
deg::color_end().c_str());
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void DEG_debug_print_eval_time(Depsgraph *depsgraph,
|
||||
const char *function_name,
|
||||
const char *object_name,
|
||||
const void *object_address,
|
||||
float time)
|
||||
{
|
||||
if ((DEG_debug_flags_get(depsgraph) & G_DEBUG_DEPSGRAPH_EVAL) == 0) {
|
||||
return;
|
||||
}
|
||||
fprintf(stdout,
|
||||
"%s%s on %s %s(%p)%s at time %f\n",
|
||||
depsgraph_name_for_logging(depsgraph).c_str(),
|
||||
function_name,
|
||||
object_name,
|
||||
deg::color_for_pointer(object_address).c_str(),
|
||||
object_address,
|
||||
deg::color_end().c_str(),
|
||||
time);
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
static std::string stringify_append_bit(const std::string &str, IDRecalcFlag tag)
|
||||
{
|
||||
const char *tag_name = DEG_update_tag_as_string(tag);
|
||||
if (tag_name == nullptr) {
|
||||
return str;
|
||||
}
|
||||
std::string result = str;
|
||||
if (!result.empty()) {
|
||||
result += ", ";
|
||||
}
|
||||
result += tag_name;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string DEG_stringify_recalc_flags(uint flags)
|
||||
{
|
||||
if (flags == 0) {
|
||||
return "NONE";
|
||||
}
|
||||
std::string result;
|
||||
uint current_flag = flags;
|
||||
/* Special cases to avoid ALL flags from being split into individual bits. */
|
||||
if ((current_flag & ID_RECALC_PSYS_ALL) == ID_RECALC_PSYS_ALL) {
|
||||
result = stringify_append_bit(result, ID_RECALC_PSYS_ALL);
|
||||
}
|
||||
/* Handle all the rest of the flags. */
|
||||
while (current_flag != 0) {
|
||||
IDRecalcFlag tag = IDRecalcFlag(1 << bitscan_forward_clear_uint(¤t_flag));
|
||||
result = stringify_append_bit(result, tag);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,92 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Evaluation engine entry-points for Depsgraph Engine.
|
||||
*/
|
||||
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
#include "DEG_depsgraph_writeback_sync.hh"
|
||||
|
||||
#include "intern/eval/deg_eval.h"
|
||||
#include "intern/eval/deg_eval_flush.h"
|
||||
|
||||
#include "intern/node/deg_node.hh"
|
||||
#include "intern/node/deg_node_operation.hh"
|
||||
|
||||
#include "intern/depsgraph.hh"
|
||||
#include "intern/depsgraph_tag.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
static void deg_flush_updates_and_refresh(deg::Depsgraph *deg_graph,
|
||||
const DepsgraphEvaluateSyncWriteback sync_writeback)
|
||||
{
|
||||
BLI_assert(!deg_graph->need_update_relations);
|
||||
|
||||
/* Update the time on the cow scene. */
|
||||
if (deg_graph->scene_cow) {
|
||||
BKE_scene_frame_set(deg_graph->scene_cow, deg_graph->frame);
|
||||
}
|
||||
|
||||
deg::graph_tag_ids_for_visible_update(deg_graph);
|
||||
deg::deg_graph_flush_updates(deg_graph);
|
||||
BLI_assert(deg_graph->sync_writeback_callbacks.is_empty());
|
||||
deg_graph->sync_writeback = sync_writeback;
|
||||
deg::deg_evaluate_on_refresh(deg_graph);
|
||||
|
||||
if ((deg_graph->sync_writeback == DEG_EVALUATE_SYNC_WRITEBACK_YES) && deg_graph->is_active) {
|
||||
for (std::function<void()> &fn : deg_graph->sync_writeback_callbacks) {
|
||||
fn();
|
||||
}
|
||||
}
|
||||
deg_graph->sync_writeback = DEG_EVALUATE_SYNC_WRITEBACK_NO;
|
||||
deg_graph->sync_writeback_callbacks.clear();
|
||||
}
|
||||
|
||||
void DEG_evaluate_on_refresh(Depsgraph *graph, const DepsgraphEvaluateSyncWriteback sync_writeback)
|
||||
{
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
|
||||
const Scene *scene = DEG_get_input_scene(graph);
|
||||
const float frame = BKE_scene_frame_get(scene);
|
||||
const float ctime = BKE_scene_ctime_get(scene);
|
||||
|
||||
if (deg_graph->frame != frame || ctime != deg_graph->ctime) {
|
||||
deg_graph->tag_time_source();
|
||||
deg_graph->frame = frame;
|
||||
deg_graph->ctime = ctime;
|
||||
}
|
||||
else if (scene->id.recalc & ID_RECALC_FRAME_CHANGE) {
|
||||
/* Comparing depsgraph & scene frame fails in the case of undo,
|
||||
* since the undo state is stored before updates from the frame change have been applied.
|
||||
* In this case reading back the undo state will behave as if no updates on frame change
|
||||
* is needed as the #Depsgraph.ctime & frame will match the values in the input scene.
|
||||
* Use #ID_RECALC_FRAME_CHANGE to detect that recalculation is necessary. see: #66913. */
|
||||
deg_graph->tag_time_source();
|
||||
}
|
||||
|
||||
deg_flush_updates_and_refresh(deg_graph, sync_writeback);
|
||||
}
|
||||
|
||||
void DEG_evaluate_on_framechange(Depsgraph *graph,
|
||||
float frame,
|
||||
const DepsgraphEvaluateSyncWriteback sync_writeback)
|
||||
{
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(graph);
|
||||
const Scene *scene = DEG_get_input_scene(graph);
|
||||
|
||||
deg_graph->tag_time_source();
|
||||
deg_graph->frame = frame;
|
||||
deg_graph->ctime = BKE_scene_frame_to_ctime(scene, frame);
|
||||
deg_flush_updates_and_refresh(deg_graph, sync_writeback);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,493 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Light linking utilities. */
|
||||
|
||||
#include "intern/depsgraph_light_linking.hh"
|
||||
|
||||
#include "MEM_guardedalloc.h"
|
||||
|
||||
#include "BLI_hash.hh"
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_map.hh"
|
||||
|
||||
#include "DNA_collection_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "DEG_depsgraph_light_linking.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "intern/depsgraph.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Public C++ API
|
||||
* \{ */
|
||||
|
||||
namespace deg::light_linking {
|
||||
|
||||
void eval_runtime_data(const ::blender::Depsgraph *depsgraph, Object &object_eval)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
|
||||
deg_graph->light_linking_cache.eval_runtime_data(object_eval);
|
||||
}
|
||||
|
||||
} // namespace deg::light_linking
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Internal builder API
|
||||
* \{ */
|
||||
|
||||
namespace {
|
||||
|
||||
/* Check whether the ID is suitable to be an input of the dependency graph. */
|
||||
/* TODO(sergey): Move the function and check to a more generic place. */
|
||||
#ifndef NDEBUG
|
||||
bool is_valid_input_id(const ID &id)
|
||||
{
|
||||
return (id.tag & ID_TAG_LOCALIZED) || DEG_is_original(&id);
|
||||
}
|
||||
#endif
|
||||
|
||||
} // namespace
|
||||
|
||||
namespace deg::light_linking {
|
||||
|
||||
using LightSet = internal::LightSet;
|
||||
using EmitterData = internal::EmitterData;
|
||||
using EmitterDataMap = internal::EmitterDataMap;
|
||||
using EmitterSetMembership = internal::EmitterSetMembership;
|
||||
using LinkingData = internal::LinkingData;
|
||||
|
||||
namespace internal {
|
||||
|
||||
namespace {
|
||||
|
||||
/* Helper class which takes care of allocating an unique light set IDs, performing checks for
|
||||
* overflows. */
|
||||
class LightSetIDManager {
|
||||
using LightSet = internal::LightSet;
|
||||
|
||||
public:
|
||||
explicit LightSetIDManager(const Scene &scene) : scene_(scene) {}
|
||||
|
||||
bool get(const LightSet &light_set, uint64_t &id)
|
||||
{
|
||||
/* Performance note.
|
||||
*
|
||||
* Always ensure the light set data exists in the map, even when an overflow happens. This has
|
||||
* a downside of potentially higher memory usage and when there are many emitters with light
|
||||
* linking, but it avoids distinct lookup + add fore the normal cases. */
|
||||
|
||||
const uint64_t light_set_id = light_set_id_map_.lookup_or_add_cb(light_set, [&]() {
|
||||
const uint64_t new_light_set_id = next_light_set_id_++;
|
||||
|
||||
if (new_light_set_id == LightSet::MAX_ID + 1) {
|
||||
printf("Maximum number of light linking sets (%d) exceeded scene \"%s\".\n",
|
||||
LightSet::MAX_ID + 1,
|
||||
scene_.id.name + 2);
|
||||
}
|
||||
|
||||
return new_light_set_id;
|
||||
});
|
||||
|
||||
id = light_set_id;
|
||||
|
||||
return id <= LightSet::MAX_ID;
|
||||
}
|
||||
|
||||
private:
|
||||
const Scene &scene_;
|
||||
|
||||
/* Next unique ID of a light set. */
|
||||
uint64_t next_light_set_id_ = LightSet::DEFAULT_ID + 1;
|
||||
|
||||
/* Map from a link set to its original id. */
|
||||
Map<internal::LightSet, uint64_t> light_set_id_map_;
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
/* LightSet */
|
||||
|
||||
bool LightSet::operator==(const LightSet &other) const
|
||||
{
|
||||
return include_collection_mask == other.include_collection_mask &&
|
||||
exclude_collection_mask == other.exclude_collection_mask;
|
||||
}
|
||||
|
||||
uint64_t LightSet::hash() const
|
||||
{
|
||||
return get_default_hash(get_default_hash(include_collection_mask),
|
||||
get_default_hash(exclude_collection_mask));
|
||||
}
|
||||
|
||||
/* EmitterSetMembership */
|
||||
|
||||
uint64_t EmitterSetMembership::get_mask() const
|
||||
{
|
||||
const uint64_t effective_included_mask = included_sets_mask ? included_sets_mask :
|
||||
SET_MEMBERSHIP_ALL;
|
||||
return effective_included_mask & ~excluded_sets_mask;
|
||||
}
|
||||
|
||||
/* EmitterDataMap. */
|
||||
|
||||
void EmitterDataMap::clear()
|
||||
{
|
||||
emitter_data_map_.clear();
|
||||
next_collection_id_ = 0;
|
||||
}
|
||||
|
||||
EmitterData *EmitterDataMap::ensure_data_if_possible(const Scene &scene, const Object &emitter)
|
||||
{
|
||||
BLI_assert(is_valid_input_id(emitter.id));
|
||||
|
||||
const Collection *collection = get_collection(emitter);
|
||||
BLI_assert(collection);
|
||||
|
||||
/* Performance note.
|
||||
*
|
||||
* Always ensure the emitter data exists in the map, even when an overflow happens. This has a
|
||||
* downside of potentially higher memory usage when there are many emitters with light linking,
|
||||
* but it avoids distinct lookup + add fore the normal cases.
|
||||
*
|
||||
* On the API level the function always returns nullptr on overflow, so it is more of an internal
|
||||
* behavior. */
|
||||
|
||||
EmitterData &emitter_data = emitter_data_map_.lookup_or_add_cb(collection, [&]() {
|
||||
const uint64_t collection_id = next_collection_id_++;
|
||||
|
||||
EmitterData new_emitter_data;
|
||||
|
||||
if (collection_id > EmitterData::MAX_COLLECTION_ID) {
|
||||
if (collection_id == EmitterData::MAX_COLLECTION_ID + 1) {
|
||||
printf("Maximum number of light linking collections (%d) exceeded in scene \"%s\".\n",
|
||||
EmitterData::MAX_COLLECTION_ID + 1,
|
||||
scene.id.name + 2);
|
||||
}
|
||||
new_emitter_data.collection_mask = 0;
|
||||
}
|
||||
else {
|
||||
new_emitter_data.collection_mask = uint64_t(1) << collection_id;
|
||||
}
|
||||
|
||||
return new_emitter_data;
|
||||
});
|
||||
|
||||
if (!emitter_data.collection_mask) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return &emitter_data;
|
||||
}
|
||||
|
||||
const EmitterData *EmitterDataMap::get_data(const Object &emitter) const
|
||||
{
|
||||
const Collection *collection_eval = get_collection(emitter);
|
||||
|
||||
if (!collection_eval) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Collection *collection_orig = DEG_get_original(collection_eval);
|
||||
|
||||
return emitter_data_map_.lookup_ptr(collection_orig);
|
||||
}
|
||||
|
||||
bool EmitterDataMap::can_skip_emitter(const Object &emitter) const
|
||||
{
|
||||
BLI_assert(is_valid_input_id(emitter.id));
|
||||
|
||||
const Collection *collection = get_collection(emitter);
|
||||
|
||||
if (!collection) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return emitter_data_map_.contains(collection);
|
||||
}
|
||||
|
||||
/* LinkingData */
|
||||
|
||||
void LinkingData::clear()
|
||||
{
|
||||
light_linked_sets_.clear();
|
||||
object_light_sets_.clear();
|
||||
}
|
||||
|
||||
void LinkingData::link_object(const EmitterData &emitter_data,
|
||||
const eCollectionLightLinkingState link_state,
|
||||
const Object &object)
|
||||
{
|
||||
LightSet &light_set = ensure_light_set_for(object);
|
||||
|
||||
switch (link_state) {
|
||||
case COLLECTION_LIGHT_LINKING_STATE_INCLUDE:
|
||||
light_set.include_collection_mask |= emitter_data.collection_mask;
|
||||
light_set.exclude_collection_mask &= ~emitter_data.collection_mask;
|
||||
break;
|
||||
|
||||
case COLLECTION_LIGHT_LINKING_STATE_EXCLUDE:
|
||||
light_set.exclude_collection_mask |= emitter_data.collection_mask;
|
||||
light_set.include_collection_mask &= ~emitter_data.collection_mask;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LightSet &LinkingData::ensure_light_set_for(const Object &object)
|
||||
{
|
||||
BLI_assert(is_valid_input_id(object.id));
|
||||
|
||||
return light_linked_sets_.lookup_or_add_as(&object);
|
||||
}
|
||||
|
||||
void LinkingData::clear_after_build()
|
||||
{
|
||||
light_linked_sets_.clear();
|
||||
}
|
||||
|
||||
void LinkingData::end_build(const Scene &scene, EmitterDataMap &emitter_data_map)
|
||||
{
|
||||
LightSetIDManager light_set_id_manager(scene);
|
||||
|
||||
for (const auto it : light_linked_sets_.items()) {
|
||||
const Object *receiver = it.key;
|
||||
LightSet &light_set = it.value;
|
||||
|
||||
uint64_t light_set_id;
|
||||
if (!light_set_id_manager.get(light_set, light_set_id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const uint64_t light_set_mask = uint64_t(1) << light_set_id;
|
||||
|
||||
object_light_sets_.add(receiver, light_set_id);
|
||||
|
||||
update_emitters_membership(emitter_data_map, light_set, light_set_mask);
|
||||
}
|
||||
|
||||
clear_after_build();
|
||||
}
|
||||
|
||||
void LinkingData::update_emitters_membership(EmitterDataMap &emitter_data_map,
|
||||
const LightSet &light_set,
|
||||
const uint64_t light_set_mask)
|
||||
{
|
||||
for (EmitterData &emitter_data : emitter_data_map.values()) {
|
||||
EmitterSetMembership &set_membership = get_emitter_set_membership(emitter_data);
|
||||
|
||||
if (emitter_data.collection_mask & light_set.include_collection_mask) {
|
||||
set_membership.included_sets_mask |= light_set_mask;
|
||||
}
|
||||
if (emitter_data.collection_mask & light_set.exclude_collection_mask) {
|
||||
set_membership.excluded_sets_mask |= light_set_mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t LinkingData::get_light_set_for(const Object &object) const
|
||||
{
|
||||
const Object *object_orig = DEG_get_original(&object);
|
||||
return object_light_sets_.lookup_default(object_orig, LightSet::DEFAULT_ID);
|
||||
}
|
||||
|
||||
} // namespace internal
|
||||
|
||||
namespace {
|
||||
|
||||
/* Iterate over all objects of the collection and invoke the given callback with two arguments:
|
||||
* the given collection light linking settings, and the object (passed as reference).
|
||||
*
|
||||
* Note that if an object is reachable from multiple children collection the callback is invoked
|
||||
* for all of them. */
|
||||
template<class Proc>
|
||||
void foreach_light_collection_object_inner(const CollectionLightLinking &collection_light_linking,
|
||||
const Collection &collection,
|
||||
Proc &&callback)
|
||||
{
|
||||
for (const CollectionChild &collection_child : collection.children) {
|
||||
foreach_light_collection_object_inner(
|
||||
collection_light_linking, *collection_child.collection, callback);
|
||||
}
|
||||
|
||||
for (const CollectionObject &collection_object : collection.gobject) {
|
||||
callback(collection_light_linking, *collection_object.ob);
|
||||
}
|
||||
}
|
||||
|
||||
/* Iterate over all objects of the collection and invoke the given callback with two arguments:
|
||||
* CollectionLightLinking and the actual Object (passed as reference).
|
||||
*
|
||||
* The CollectionLightLinking denotes the effective light linking settings of the object. It comes
|
||||
* from the first level of hierarchy from the given collection.
|
||||
*
|
||||
* Note that if an object is reachable from multiple children collection the callback is invoked
|
||||
* for all of them. */
|
||||
template<class Proc>
|
||||
void foreach_light_collection_object(const Collection &collection, Proc &&callback)
|
||||
{
|
||||
for (const CollectionChild &collection_child : collection.children) {
|
||||
foreach_light_collection_object_inner(
|
||||
collection_child.light_linking, *collection_child.collection, callback);
|
||||
}
|
||||
|
||||
for (const CollectionObject &collection_object : collection.gobject) {
|
||||
callback(collection_object.light_linking, *collection_object.ob);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void Cache::clear()
|
||||
{
|
||||
light_emitter_data_map_.clear();
|
||||
shadow_emitter_data_map_.clear();
|
||||
|
||||
light_linking_.clear();
|
||||
shadow_linking_.clear();
|
||||
}
|
||||
|
||||
void Cache::add_emitter(const Scene &scene, const Object &emitter)
|
||||
{
|
||||
BLI_assert(is_valid_input_id(emitter.id));
|
||||
|
||||
add_light_linking_emitter(scene, emitter);
|
||||
add_shadow_linking_emitter(scene, emitter);
|
||||
}
|
||||
|
||||
void Cache::add_light_linking_emitter(const Scene &scene, const Object &emitter)
|
||||
{
|
||||
BLI_assert(is_valid_input_id(emitter.id));
|
||||
|
||||
if (light_emitter_data_map_.can_skip_emitter(emitter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const EmitterData *light_emitter_data = light_emitter_data_map_.ensure_data_if_possible(scene,
|
||||
emitter);
|
||||
if (light_emitter_data) {
|
||||
foreach_light_collection_object(
|
||||
*emitter.light_linking->receiver_collection,
|
||||
[&](const CollectionLightLinking &collection_light_linking, const Object &receiver) {
|
||||
add_receiver_object(*light_emitter_data, collection_light_linking, receiver);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Cache::add_shadow_linking_emitter(const Scene &scene, const Object &emitter)
|
||||
{
|
||||
BLI_assert(is_valid_input_id(emitter.id));
|
||||
|
||||
if (shadow_emitter_data_map_.can_skip_emitter(emitter)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const EmitterData *shadow_emitter_data = shadow_emitter_data_map_.ensure_data_if_possible(
|
||||
scene, emitter);
|
||||
if (shadow_emitter_data) {
|
||||
foreach_light_collection_object(
|
||||
*emitter.light_linking->blocker_collection,
|
||||
[&](const CollectionLightLinking &collection_light_linking, const Object &receiver) {
|
||||
add_blocker_object(*shadow_emitter_data, collection_light_linking, receiver);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void Cache::add_receiver_object(const EmitterData &emitter_data,
|
||||
const CollectionLightLinking &collection_light_linking,
|
||||
const Object &receiver)
|
||||
{
|
||||
BLI_assert(is_valid_input_id(receiver.id));
|
||||
|
||||
light_linking_.link_object(
|
||||
emitter_data, eCollectionLightLinkingState(collection_light_linking.link_state), receiver);
|
||||
}
|
||||
|
||||
void Cache::add_blocker_object(const EmitterData &emitter_data,
|
||||
const CollectionLightLinking &collection_light_linking,
|
||||
const Object &blocker)
|
||||
{
|
||||
BLI_assert(is_valid_input_id(blocker.id));
|
||||
|
||||
shadow_linking_.link_object(
|
||||
emitter_data, eCollectionLightLinkingState(collection_light_linking.link_state), blocker);
|
||||
}
|
||||
|
||||
void Cache::end_build(const Scene &scene)
|
||||
{
|
||||
if (!has_light_linking()) {
|
||||
return;
|
||||
}
|
||||
|
||||
light_linking_.end_build(scene, light_emitter_data_map_);
|
||||
shadow_linking_.end_build(scene, shadow_emitter_data_map_);
|
||||
}
|
||||
|
||||
void Cache::eval_runtime_data(Object &object_eval) const
|
||||
{
|
||||
static const LightLinkingRuntime runtime_no_links = {
|
||||
EmitterSetMembership::SET_MEMBERSHIP_ALL, EmitterSetMembership::SET_MEMBERSHIP_ALL, 0, 0};
|
||||
|
||||
if (!has_light_linking()) {
|
||||
/* No light linking used in the scene, still reset to default on objects that have
|
||||
* allocated light linking data structures since we can't free them here. */
|
||||
if (object_eval.light_linking) {
|
||||
object_eval.light_linking->runtime = runtime_no_links;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/* Receiver and blocker configuration. */
|
||||
LightLinkingRuntime runtime = {};
|
||||
runtime.receiver_light_set = light_linking_.get_light_set_for(object_eval);
|
||||
runtime.blocker_shadow_set = shadow_linking_.get_light_set_for(object_eval);
|
||||
|
||||
/* Emitter configuration. */
|
||||
const EmitterData *light_emitter_data = light_emitter_data_map_.get_data(object_eval);
|
||||
if (light_emitter_data) {
|
||||
runtime.light_set_membership = light_emitter_data->light_membership.get_mask();
|
||||
}
|
||||
else {
|
||||
runtime.light_set_membership = EmitterSetMembership::SET_MEMBERSHIP_ALL;
|
||||
}
|
||||
|
||||
const EmitterData *shadow_emitter_data = shadow_emitter_data_map_.get_data(object_eval);
|
||||
if (shadow_emitter_data) {
|
||||
runtime.shadow_set_membership = shadow_emitter_data->shadow_membership.get_mask();
|
||||
}
|
||||
else {
|
||||
runtime.shadow_set_membership = EmitterSetMembership::SET_MEMBERSHIP_ALL;
|
||||
}
|
||||
|
||||
const bool need_runtime = (memcmp(&runtime, &runtime_no_links, sizeof(runtime)) != 0);
|
||||
|
||||
/* Assign, allocating light linking on demand if needed. */
|
||||
if (object_eval.light_linking) {
|
||||
object_eval.light_linking->runtime = runtime;
|
||||
if (!need_runtime) {
|
||||
/* Note that this will only remove lazily allocated light_linking on the evaluated object,
|
||||
* as an empty light_linking is not allowed on the original object. */
|
||||
BKE_light_linking_free_if_empty(&object_eval);
|
||||
}
|
||||
}
|
||||
else if (need_runtime) {
|
||||
BKE_light_linking_ensure(&object_eval);
|
||||
object_eval.light_linking->runtime = runtime;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace deg::light_linking
|
||||
|
||||
/** \} */
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,284 @@
|
||||
/* SPDX-FileCopyrightText: 2023 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
#include "BLI_map.hh"
|
||||
|
||||
#include "BKE_light_linking.h" /* LightLinkingType */
|
||||
|
||||
#include "DNA_collection_types.h" /* eCollectionLightLinkingState */
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Collection;
|
||||
struct CollectionLightLinking;
|
||||
struct Object;
|
||||
struct Scene;
|
||||
|
||||
namespace deg::light_linking {
|
||||
|
||||
namespace internal {
|
||||
|
||||
/* Set of light as seen from a receiver perspective. */
|
||||
class LightSet {
|
||||
public:
|
||||
/* Maximum possible identifier of a light set. The identifier is 0-based.
|
||||
* The limitation is imposed by the fact that its identifier is converted to a bitmask. */
|
||||
static constexpr int MAX_ID = 63;
|
||||
|
||||
/* Identifier of a light set which is not explicitly linked to anything. */
|
||||
static constexpr int DEFAULT_ID = 0;
|
||||
|
||||
bool operator==(const LightSet &other) const;
|
||||
bool operator!=(const LightSet &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
uint64_t hash() const;
|
||||
|
||||
/* Lights which are explicitly included/excluded into the light set.
|
||||
*
|
||||
* The light is denoted as a bit mask of a light linking collection. This mask is allocated for
|
||||
* every unique light linking collection on an emitter. */
|
||||
uint64_t include_collection_mask = 0;
|
||||
uint64_t exclude_collection_mask = 0;
|
||||
};
|
||||
|
||||
class EmitterSetMembership {
|
||||
public:
|
||||
/* Bitmask which indicates the emitter belongs to all light sets. */
|
||||
static constexpr uint64_t SET_MEMBERSHIP_ALL = ~uint64_t(0);
|
||||
|
||||
/* Get final membership mask in the light sets, considering its inclusion and exclusion. */
|
||||
uint64_t get_mask() const;
|
||||
|
||||
/* Bit masks of the emitter membership in the light sets. */
|
||||
uint64_t included_sets_mask = 0;
|
||||
uint64_t excluded_sets_mask = 0;
|
||||
};
|
||||
|
||||
/* Packed information about emitter.
|
||||
* Emitter is actually corresponding to a light linking collection on an object. */
|
||||
class EmitterData {
|
||||
public:
|
||||
/* Maximum possible identifier of a light linking collection. The identifier is 0-based.
|
||||
* The limitation is imposed by the fact that its identifier is converted to a bitmask. */
|
||||
static constexpr int MAX_COLLECTION_ID = 63;
|
||||
|
||||
/* Mask of a light linking collection this emitter uses in its configuration.
|
||||
* A single bit is set in this bit-field which corresponds to an identifier of a light linking
|
||||
* collection in the scene. */
|
||||
uint64_t collection_mask = 0;
|
||||
|
||||
/* Membership masks for the light and shadow linking. */
|
||||
EmitterSetMembership light_membership;
|
||||
EmitterSetMembership shadow_membership;
|
||||
};
|
||||
|
||||
/* Helper class which deals with keeping per-emitter data. */
|
||||
class EmitterDataMap {
|
||||
using MapType = Map<const Collection *, EmitterData>;
|
||||
|
||||
public:
|
||||
explicit EmitterDataMap(const LightLinkingType link_type) : link_type_(link_type) {}
|
||||
|
||||
/* Returns true if there is no information about emitters at all. */
|
||||
bool is_empty() const
|
||||
{
|
||||
return emitter_data_map_.is_empty();
|
||||
}
|
||||
|
||||
/* Entirely clear the state, become ready for a new light linking relations build. */
|
||||
void clear();
|
||||
|
||||
/* Ensure that the data exists for the given emitter.
|
||||
* The emitter must be original and have light linking collection.
|
||||
*
|
||||
* Note that there is limited number of emitters possible within a scene, When this number is
|
||||
* exceeded an error is printed and a nullptr is returned. */
|
||||
EmitterData *ensure_data_if_possible(const Scene &scene, const Object &emitter);
|
||||
|
||||
/* Get emitter data for the given original or evaluated object.
|
||||
* If the light linking is not configured for this emitted nullptr is returned. */
|
||||
const EmitterData *get_data(const Object &emitter) const;
|
||||
|
||||
/* Returns true if the underlying data of the light linking emitter has been handled, and there
|
||||
* is no need to handle the emitter.
|
||||
* The emitter must be original object. */
|
||||
bool can_skip_emitter(const Object &emitter) const;
|
||||
|
||||
/* Returns an iterator over all emitter data in the map. */
|
||||
MapType::MutableValueIterator values()
|
||||
{
|
||||
return emitter_data_map_.values();
|
||||
}
|
||||
|
||||
private:
|
||||
/* Get linked collection depending on whether this is emitter information for light or shadow
|
||||
* linking. */
|
||||
/* TODO(sergey): Check whether template specialization is preferred here. */
|
||||
const Collection *get_collection(const Object &emitter) const
|
||||
{
|
||||
return BKE_light_linking_collection_get(&emitter, link_type_);
|
||||
}
|
||||
|
||||
LightLinkingType link_type_ = LIGHT_LINKING_RECEIVER;
|
||||
|
||||
/* Emitter-centric information: indexed by an original emitter object, contains accumulated
|
||||
* information about emitter. */
|
||||
MapType emitter_data_map_;
|
||||
|
||||
/* Next unique light linking collection ID. */
|
||||
uint64_t next_collection_id_ = 0;
|
||||
};
|
||||
|
||||
/* Common part of receiver (for light linking) and blocker (for shadow lining) data. */
|
||||
class LinkingData {
|
||||
public:
|
||||
explicit LinkingData(const LightLinkingType link_type) : link_type_(link_type) {}
|
||||
|
||||
/* Entirely clear the state, become ready for a new light linking relations build. */
|
||||
void clear();
|
||||
|
||||
/* Link the given object with the given light linking state. */
|
||||
void link_object(const EmitterData &emitter_data,
|
||||
eCollectionLightLinkingState link_state,
|
||||
const Object &object);
|
||||
|
||||
/* Compute unique sets of emitters used by receivers or blockers.
|
||||
*
|
||||
* This must be called at the end of depsgraph relations build after all emitters have been
|
||||
* added, and before runtime data can be set as part of evaluation. */
|
||||
void end_build(const Scene &scene, EmitterDataMap &emitter_data_map);
|
||||
|
||||
/* Get an unique index the given object is receiving light or casting shadow from.
|
||||
* The object can either be original or evaluated.
|
||||
*
|
||||
* If the object is not linked to any emitter LightSet::DEFAULT_ID is returned. */
|
||||
uint64_t get_light_set_for(const Object &object) const;
|
||||
|
||||
private:
|
||||
/* Ensure that the light set exists for the given receiver/blocker object.
|
||||
* The object must be original. */
|
||||
LightSet &ensure_light_set_for(const Object &object);
|
||||
|
||||
/* Update the emitter light/shadow set membership after the final unique light set identifier
|
||||
* is known.
|
||||
* The light_set_mask consists of a single bit set corresponding to the light set index. */
|
||||
void update_emitters_membership(EmitterDataMap &emitter_data_map,
|
||||
const LightSet &light_set,
|
||||
uint64_t light_set_mask);
|
||||
|
||||
/* Clear data which is only needed during the build. */
|
||||
void clear_after_build();
|
||||
|
||||
/* Get light set membership information of the emitter data depending whether this linking
|
||||
* data is a light or shadow linking. */
|
||||
/* TODO(sergey): Check whether template specialization is preferred here. */
|
||||
EmitterSetMembership &get_emitter_set_membership(EmitterData &emitter_data) const
|
||||
{
|
||||
if (link_type_ == LIGHT_LINKING_BLOCKER) {
|
||||
return emitter_data.shadow_membership;
|
||||
}
|
||||
|
||||
return emitter_data.light_membership;
|
||||
}
|
||||
|
||||
LightLinkingType link_type_ = LIGHT_LINKING_RECEIVER;
|
||||
|
||||
/* Receiver/blocker-centric view of light sets: indexed by an original receiver object, contains
|
||||
* light set which defines from which emitters it receives light from or casts shadow when is lit
|
||||
* ny.
|
||||
*
|
||||
* NOTE: Only available during build. */
|
||||
Map<const Object *, LightSet> light_linked_sets_;
|
||||
|
||||
/* Map from an original receiver/blocker object: map to index of light set for this
|
||||
* receiver/blocker. */
|
||||
/* TODO(sergey): What is the generic term for receiver and blocker which is less generic than
|
||||
* object? */
|
||||
Map<const Object *, uint64_t> object_light_sets_;
|
||||
};
|
||||
|
||||
} // namespace internal
|
||||
|
||||
/* Cached light linking evaluation data.
|
||||
*
|
||||
* This cache is only valid within a specific dependency graph, hence the dependency graph is
|
||||
* expected to own this cache.
|
||||
*
|
||||
* This cache takes care of making it efficient to lookup emitter masks, emitters which affect
|
||||
* given receiver and so on. */
|
||||
class Cache {
|
||||
using EmitterData = internal::EmitterData;
|
||||
using EmitterDataMap = internal::EmitterDataMap;
|
||||
using LinkingData = internal::LinkingData;
|
||||
|
||||
public:
|
||||
/* Entirely clear the cache.
|
||||
* Should be called whenever the dependency graph is being re-built, in the beginning of the
|
||||
* build process. */
|
||||
void clear();
|
||||
|
||||
/* Add emitter to the cache.
|
||||
*
|
||||
* This call does nothing if the emitter does not have light configured linking (as in, if it
|
||||
* has light linking collection set to nullptr).
|
||||
*
|
||||
* The emitter must be original. This is asserted, but in release builds passing evaluated
|
||||
* object leads to an undefined behavior. */
|
||||
void add_emitter(const Scene &scene, const Object &emitter);
|
||||
|
||||
/* Compute unique sets of emitters used by receivers and blockers.
|
||||
*
|
||||
* This must be called at the end of depsgraph relations build after all emitters have been
|
||||
* added, and before runtime data can be set as part of evaluation. */
|
||||
void end_build(const Scene &scene);
|
||||
|
||||
/* Set runtime light linking data on evaluated object. */
|
||||
void eval_runtime_data(Object &object_eval) const;
|
||||
|
||||
private:
|
||||
/* Add emitter information specific for light and shadow linking. */
|
||||
void add_light_linking_emitter(const Scene &scene, const Object &emitter);
|
||||
void add_shadow_linking_emitter(const Scene &scene, const Object &emitter);
|
||||
|
||||
/* Add receiver or blocker object with the given light linking configuration.
|
||||
*
|
||||
* The term receiver here is meant in a wider meaning of it. For the light linking it is a
|
||||
* receiver of light, but for the shadow linking is it actually a shadow caster. */
|
||||
void add_receiver_object(const EmitterData &emitter_data,
|
||||
const CollectionLightLinking &collection_light_linking,
|
||||
const Object &receiver);
|
||||
void add_blocker_object(const EmitterData &emitter_data,
|
||||
const CollectionLightLinking &collection_light_linking,
|
||||
const Object &blocker);
|
||||
|
||||
/* Returns true if there is light linking configuration in the scene. */
|
||||
bool has_light_linking() const
|
||||
{
|
||||
return !light_emitter_data_map_.is_empty() || !shadow_emitter_data_map_.is_empty();
|
||||
}
|
||||
|
||||
/* Per-emitter light and shadow linking information. */
|
||||
EmitterDataMap light_emitter_data_map_{LIGHT_LINKING_RECEIVER};
|
||||
EmitterDataMap shadow_emitter_data_map_{LIGHT_LINKING_BLOCKER};
|
||||
|
||||
/* Light and shadow linking data. */
|
||||
LinkingData light_linking_{LIGHT_LINKING_RECEIVER};
|
||||
LinkingData shadow_linking_{LIGHT_LINKING_BLOCKER};
|
||||
};
|
||||
|
||||
} // namespace deg::light_linking
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,317 @@
|
||||
/* SPDX-FileCopyrightText: 2018 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Physics utilities for effectors and collision.
|
||||
*/
|
||||
|
||||
#include "intern/depsgraph_physics.hh"
|
||||
|
||||
#include "BLI_enum_flags.hh"
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "BKE_collision.h"
|
||||
#include "BKE_dynamicpaint.h"
|
||||
#include "BKE_effect.h"
|
||||
#include "BKE_modifier.hh"
|
||||
#include "BKE_object.hh"
|
||||
|
||||
#include "DNA_collection_types.h"
|
||||
#include "DNA_object_force_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
|
||||
#include "DEG_depsgraph_build.hh"
|
||||
#include "DEG_depsgraph_physics.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "depsgraph.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
/*************************** Evaluation Query API *****************************/
|
||||
|
||||
static ePhysicsCollisionType modifier_to_collision_type(uint modifier_type)
|
||||
{
|
||||
switch (modifier_type) {
|
||||
case eModifierType_Collision:
|
||||
return DEG_PHYSICS_COLLISION;
|
||||
case eModifierType_Fluid:
|
||||
return DEG_PHYSICS_SMOKE_COLLISION;
|
||||
case eModifierType_DynamicPaint:
|
||||
return DEG_PHYSICS_DYNAMIC_BRUSH;
|
||||
}
|
||||
|
||||
BLI_assert_msg(0, "Unknown collision modifier type");
|
||||
return DEG_PHYSICS_COLLISION_NUM;
|
||||
}
|
||||
/* Get ID from an ID type object, in a safe manner. This means that object can be nullptr,
|
||||
* in which case the function returns nullptr.
|
||||
*/
|
||||
template<class T> static ID *object_id_safe(T *object)
|
||||
{
|
||||
if (object == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
return &object->id;
|
||||
}
|
||||
|
||||
ListBaseT<EffectorRelation> *DEG_get_effector_relations(const Depsgraph *graph,
|
||||
Collection *collection)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
||||
Map<const ID *, ListBaseT<EffectorRelation> *> *hash = deg_graph->physics_relations_effector;
|
||||
if (hash == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
/* NOTE: nullptr is a valid lookup key here as it means that the relation is not bound to a
|
||||
* specific collection. */
|
||||
ID *collection_orig = DEG_get_original(object_id_safe(collection));
|
||||
return hash->lookup_default(collection_orig, nullptr);
|
||||
}
|
||||
|
||||
ListBaseT<CollisionRelation> *DEG_get_collision_relations(const Depsgraph *graph,
|
||||
Collection *collection,
|
||||
uint modifier_type)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
||||
const ePhysicsCollisionType type = modifier_to_collision_type(modifier_type);
|
||||
Map<const ID *, ListBaseT<CollisionRelation> *> *hash =
|
||||
deg_graph->physics_relations_collision[type];
|
||||
if (hash == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
/* NOTE: nullptr is a valid lookup key here as it means that the relation is not bound to a
|
||||
* specific collection. */
|
||||
ID *collection_orig = DEG_get_original(object_id_safe(collection));
|
||||
return hash->lookup_default(collection_orig, nullptr);
|
||||
}
|
||||
|
||||
/********************** Depsgraph Building API ************************/
|
||||
|
||||
/**
|
||||
* Flags to store point-cache relations which have been calculated.
|
||||
* This avoid adding relations multiple times.
|
||||
*
|
||||
* \note This could be replaced by bit-shifting #eDepsObjectComponentType values,
|
||||
* although this would limit them to integer size.
|
||||
*/
|
||||
enum class CollisionComponentFlag : uint8_t {
|
||||
None = 0,
|
||||
/** #DEG_OB_COMP_TRANSFORM is set. */
|
||||
Transform = 1 << 0,
|
||||
/** #DEG_OB_COMP_GEOMETRY is set. */
|
||||
Geometry = 1 << 1,
|
||||
/** #DEG_OB_COMP_EVAL_POSE is set. */
|
||||
EvalPose = 1 << 2,
|
||||
};
|
||||
ENUM_OPERATORS(CollisionComponentFlag);
|
||||
|
||||
void DEG_add_collision_relations(DepsNodeHandle *handle,
|
||||
Object *object,
|
||||
Collection *collection,
|
||||
uint modifier_type,
|
||||
DEG_CollobjFilterFunction filter_function,
|
||||
const char *name)
|
||||
{
|
||||
Depsgraph *depsgraph = DEG_get_graph_from_handle(handle);
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
|
||||
ListBaseT<CollisionRelation> *relations = build_collision_relations(
|
||||
deg_graph, collection, modifier_type);
|
||||
|
||||
/* Expand tag objects, matching: #BKE_object_modifier_update_subframe behavior. */
|
||||
|
||||
/* NOTE: #eModifierType_Fluid should be included,
|
||||
* leave out for the purpose of validating the fix for dynamic paint only. */
|
||||
const bool use_recursive_parents = (modifier_type == eModifierType_DynamicPaint);
|
||||
|
||||
Map<Object *, CollisionComponentFlag> *object_component_map = nullptr;
|
||||
if (use_recursive_parents) {
|
||||
object_component_map = MEM_new<Map<Object *, CollisionComponentFlag>>(__func__);
|
||||
}
|
||||
|
||||
for (CollisionRelation &relation : *relations) {
|
||||
Object *ob1 = relation.ob;
|
||||
if (ob1 == object) {
|
||||
continue;
|
||||
}
|
||||
if (filter_function &&
|
||||
!filter_function(ob1, BKE_modifiers_findby_type(ob1, ModifierType(modifier_type))))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (use_recursive_parents) {
|
||||
/* Add relations for `ob1` and other objects it references,
|
||||
* using `object_component_map` to avoid redundant calls.
|
||||
*
|
||||
* When #BKE_object_modifier_update_subframe is used by a modifier,
|
||||
* it's important the depsgraph tags objects this modifier uses.
|
||||
*
|
||||
* Without this, access to objects is not thread-safe, see: #142137.
|
||||
*
|
||||
* NOTE(@ideasman42): #BKE_object_modifier_update_subframe calls
|
||||
* #BKE_animsys_evaluate_animdata, depending on the object type.
|
||||
* Equivalent relations could be added here.
|
||||
* This was not done and there are no bug reports relating to this,
|
||||
* so leave as-is unless the current code is failing in a real world scenario. */
|
||||
|
||||
BKE_object_modifier_update_subframe_only_callback(
|
||||
ob1,
|
||||
true,
|
||||
OBJECT_MODIFIER_UPDATE_SUBFRAME_RECURSION_DEFAULT,
|
||||
modifier_type,
|
||||
[&handle, &name, &object_component_map](Object *ob, const bool update_mesh) {
|
||||
CollisionComponentFlag &update_flag = object_component_map->lookup_or_add_default(ob);
|
||||
{
|
||||
constexpr CollisionComponentFlag test_flag = CollisionComponentFlag::Transform;
|
||||
if (!flag_is_set(update_flag, test_flag)) {
|
||||
update_flag |= test_flag;
|
||||
DEG_add_object_pointcache_relation(handle, ob, DEG_OB_COMP_TRANSFORM, name);
|
||||
}
|
||||
}
|
||||
if (update_mesh) {
|
||||
constexpr CollisionComponentFlag test_flag = CollisionComponentFlag::Geometry;
|
||||
if (!flag_is_set(update_flag, test_flag)) {
|
||||
update_flag |= test_flag;
|
||||
DEG_add_object_pointcache_relation(handle, ob, DEG_OB_COMP_GEOMETRY, name);
|
||||
}
|
||||
}
|
||||
if (ob->type == OB_ARMATURE) {
|
||||
constexpr CollisionComponentFlag test_flag = CollisionComponentFlag::EvalPose;
|
||||
if (!flag_is_set(update_flag, test_flag)) {
|
||||
update_flag |= test_flag;
|
||||
DEG_add_object_pointcache_relation(handle, ob, DEG_OB_COMP_EVAL_POSE, name);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
DEG_add_object_pointcache_relation(handle, ob1, DEG_OB_COMP_TRANSFORM, name);
|
||||
DEG_add_object_pointcache_relation(handle, ob1, DEG_OB_COMP_GEOMETRY, name);
|
||||
}
|
||||
|
||||
if (use_recursive_parents) {
|
||||
MEM_delete(object_component_map);
|
||||
}
|
||||
}
|
||||
|
||||
void DEG_add_forcefield_relations(DepsNodeHandle *handle,
|
||||
Object *object,
|
||||
EffectorWeights *effector_weights,
|
||||
bool add_absorption,
|
||||
int skip_forcefield,
|
||||
const char *name)
|
||||
{
|
||||
Depsgraph *depsgraph = DEG_get_graph_from_handle(handle);
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
|
||||
ListBaseT<EffectorRelation> *relations = build_effector_relations(deg_graph,
|
||||
effector_weights->group);
|
||||
for (EffectorRelation &relation : *relations) {
|
||||
if (relation.ob == object) {
|
||||
continue;
|
||||
}
|
||||
if (relation.pd->forcefield == skip_forcefield) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Relation to forcefield object, optionally including geometry.
|
||||
* Use special point cache relations for automatic cache clearing. */
|
||||
DEG_add_object_pointcache_relation(handle, relation.ob, DEG_OB_COMP_TRANSFORM, name);
|
||||
|
||||
if (relation.psys || ELEM(relation.pd->shape, PFIELD_SHAPE_SURFACE, PFIELD_SHAPE_POINTS) ||
|
||||
relation.pd->forcefield == PFIELD_GUIDE)
|
||||
{
|
||||
/* TODO(sergey): Consider going more granular with more dedicated
|
||||
* particle system operation. */
|
||||
DEG_add_object_pointcache_relation(handle, relation.ob, DEG_OB_COMP_GEOMETRY, name);
|
||||
}
|
||||
|
||||
/* Smoke flow relations. */
|
||||
if (relation.pd->forcefield == PFIELD_FLUIDFLOW && relation.pd->f_source != nullptr) {
|
||||
DEG_add_object_pointcache_relation(
|
||||
handle, relation.pd->f_source, DEG_OB_COMP_TRANSFORM, "Fluid Force Domain");
|
||||
DEG_add_object_pointcache_relation(
|
||||
handle, relation.pd->f_source, DEG_OB_COMP_GEOMETRY, "Fluid Force Domain");
|
||||
}
|
||||
|
||||
/* Absorption forces need collision relation. */
|
||||
if (add_absorption && (relation.pd->flag & PFIELD_VISIBILITY)) {
|
||||
DEG_add_collision_relations(
|
||||
handle, object, nullptr, eModifierType_Collision, nullptr, "Force Absorption");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************** Internal API ********************************/
|
||||
|
||||
namespace deg {
|
||||
|
||||
ListBaseT<EffectorRelation> *build_effector_relations(Depsgraph *graph, Collection *collection)
|
||||
{
|
||||
Map<const ID *, ListBaseT<EffectorRelation> *> *hash = graph->physics_relations_effector;
|
||||
if (hash == nullptr) {
|
||||
graph->physics_relations_effector = new Map<const ID *, ListBaseT<EffectorRelation> *>();
|
||||
hash = graph->physics_relations_effector;
|
||||
}
|
||||
/* If collection is nullptr still use it as a key.
|
||||
* In this case the BKE_effector_relations_create() will create relates for all bases in the
|
||||
* view layer.
|
||||
*/
|
||||
ID *collection_id = object_id_safe(collection);
|
||||
return hash->lookup_or_add_cb(collection_id, [&]() {
|
||||
::blender::Depsgraph *depsgraph = reinterpret_cast<::blender::Depsgraph *>(graph);
|
||||
return BKE_effector_relations_create(depsgraph, graph->scene, graph->view_layer, collection);
|
||||
});
|
||||
}
|
||||
|
||||
ListBaseT<CollisionRelation> *build_collision_relations(Depsgraph *graph,
|
||||
Collection *collection,
|
||||
uint modifier_type)
|
||||
{
|
||||
const ePhysicsCollisionType type = modifier_to_collision_type(modifier_type);
|
||||
Map<const ID *, ListBaseT<CollisionRelation> *> *hash = graph->physics_relations_collision[type];
|
||||
if (hash == nullptr) {
|
||||
graph->physics_relations_collision[type] =
|
||||
new Map<const ID *, ListBaseT<CollisionRelation> *>();
|
||||
hash = graph->physics_relations_collision[type];
|
||||
}
|
||||
/* If collection is nullptr still use it as a key.
|
||||
* In this case the BKE_collision_relations_create() will create relates for all bases in the
|
||||
* view layer.
|
||||
*/
|
||||
ID *collection_id = object_id_safe(collection);
|
||||
return hash->lookup_or_add_cb(collection_id, [&]() {
|
||||
::blender::Depsgraph *depsgraph = reinterpret_cast<::blender::Depsgraph *>(graph);
|
||||
return BKE_collision_relations_create(depsgraph, collection, modifier_type);
|
||||
});
|
||||
}
|
||||
|
||||
void clear_physics_relations(Depsgraph *graph)
|
||||
{
|
||||
if (graph->physics_relations_effector) {
|
||||
for (ListBaseT<EffectorRelation> *list : graph->physics_relations_effector->values()) {
|
||||
BKE_effector_relations_free(list);
|
||||
}
|
||||
delete graph->physics_relations_effector;
|
||||
graph->physics_relations_effector = nullptr;
|
||||
}
|
||||
|
||||
for (int i = 0; i < DEG_PHYSICS_COLLISION_NUM; i++) {
|
||||
Map<const ID *, ListBaseT<CollisionRelation> *> *hash = graph->physics_relations_collision[i];
|
||||
if (hash) {
|
||||
for (ListBaseT<CollisionRelation> *list : hash->values()) {
|
||||
BKE_collision_relations_free(list);
|
||||
}
|
||||
delete hash;
|
||||
graph->physics_relations_collision[i] = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace deg
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,30 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_listBase.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Collection;
|
||||
struct CollisionRelation;
|
||||
struct EffectorRelation;
|
||||
|
||||
namespace deg {
|
||||
|
||||
struct Depsgraph;
|
||||
|
||||
ListBaseT<EffectorRelation> *build_effector_relations(Depsgraph *graph, Collection *collection);
|
||||
ListBaseT<CollisionRelation> *build_collision_relations(Depsgraph *graph,
|
||||
Collection *collection,
|
||||
unsigned int modifier_type);
|
||||
void clear_physics_relations(Depsgraph *graph);
|
||||
|
||||
} // namespace deg
|
||||
} // namespace blender
|
||||
413
blender-5.2.0/source/blender/depsgraph/intern/depsgraph_query.cc
Normal file
413
blender-5.2.0/source/blender/depsgraph/intern/depsgraph_query.cc
Normal file
@@ -0,0 +1,413 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Implementation of Querying API
|
||||
*/
|
||||
|
||||
#include <cstring> /* XXX: `memcpy`. */
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "BKE_action.hh" /* XXX: BKE_pose_channel_find_name */
|
||||
#include "BKE_idtype.hh"
|
||||
#include "BKE_lib_id.hh"
|
||||
#include "BKE_main.hh"
|
||||
|
||||
#include "DNA_layer_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_path.hh"
|
||||
#include "RNA_prototypes.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "intern/depsgraph.hh"
|
||||
#include "intern/eval/deg_eval_copy_on_write.h"
|
||||
#include "intern/node/deg_node_component.hh"
|
||||
#include "intern/node/deg_node_id.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace deg {
|
||||
|
||||
static const ID *get_original_id(const ID *id)
|
||||
{
|
||||
if (id == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (id->orig_id == nullptr) {
|
||||
return id;
|
||||
}
|
||||
BLI_assert((id->tag & ID_TAG_COPIED_ON_EVAL) != 0);
|
||||
return id_cast<ID *>(id->orig_id);
|
||||
}
|
||||
|
||||
static ID *get_original_id(ID *id)
|
||||
{
|
||||
const ID *const_id = id;
|
||||
return const_cast<ID *>(get_original_id(const_id));
|
||||
}
|
||||
|
||||
static const ID *get_evaluated_id(const Depsgraph *deg_graph, const ID *id)
|
||||
{
|
||||
if (id == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
/* TODO(sergey): This is a duplicate of Depsgraph::get_cow_id(),
|
||||
* but here we never do assert, since we don't know nature of the
|
||||
* incoming ID data-block. */
|
||||
const IDNode *id_node = deg_graph->find_id_node(id);
|
||||
if (id_node == nullptr) {
|
||||
return id;
|
||||
}
|
||||
return id_node->id_cow;
|
||||
}
|
||||
|
||||
static ID *get_evaluated_id(const Depsgraph *deg_graph, ID *id)
|
||||
{
|
||||
const ID *const_id = id;
|
||||
return const_cast<ID *>(get_evaluated_id(deg_graph, const_id));
|
||||
}
|
||||
|
||||
} // namespace deg
|
||||
|
||||
Scene *DEG_get_input_scene(const Depsgraph *graph)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
||||
return deg_graph->scene;
|
||||
}
|
||||
|
||||
ViewLayer *DEG_get_input_view_layer(const Depsgraph *graph)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
||||
return deg_graph->view_layer;
|
||||
}
|
||||
|
||||
Main *DEG_get_bmain(const Depsgraph *graph)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
||||
return deg_graph->bmain;
|
||||
}
|
||||
|
||||
eEvaluationMode DEG_get_mode(const Depsgraph *graph)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
||||
return deg_graph->mode;
|
||||
}
|
||||
|
||||
float DEG_get_ctime(const Depsgraph *graph)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
||||
return deg_graph->ctime;
|
||||
}
|
||||
|
||||
bool DEG_id_type_updated(const Depsgraph *graph, short id_type)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
||||
return deg_graph->id_type_updated[BKE_idtype_idcode_to_index(id_type)] != 0;
|
||||
}
|
||||
|
||||
bool DEG_id_type_any_updated(const Depsgraph *graph)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
||||
|
||||
/* Loop over all ID types. */
|
||||
for (char id_type_index : deg_graph->id_type_updated) {
|
||||
if (id_type_index) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DEG_id_type_any_exists(const Depsgraph *depsgraph, short id_type)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
|
||||
return deg_graph->id_type_exist[BKE_idtype_idcode_to_index(id_type)] != 0;
|
||||
}
|
||||
|
||||
uint32_t DEG_get_eval_flags_for_id(const Depsgraph *graph, const ID *id)
|
||||
{
|
||||
if (graph == nullptr) {
|
||||
/* Happens when converting objects to mesh from a python script
|
||||
* after modifying scene graph.
|
||||
*
|
||||
* Currently harmless because it's only called for temporary
|
||||
* objects which are out of the DAG anyway. */
|
||||
return 0;
|
||||
}
|
||||
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
||||
const deg::IDNode *id_node = deg_graph->find_id_node(deg::get_original_id(id));
|
||||
if (id_node == nullptr) {
|
||||
/* TODO(sergey): Does it mean we need to check set scene? */
|
||||
return 0;
|
||||
}
|
||||
|
||||
return id_node->eval_flags;
|
||||
}
|
||||
|
||||
void DEG_get_customdata_mask_for_object(const Depsgraph *graph,
|
||||
Object *ob,
|
||||
CustomData_MeshMasks *r_mask)
|
||||
{
|
||||
if (graph == nullptr) {
|
||||
/* Happens when converting objects to mesh from a python script
|
||||
* after modifying scene graph.
|
||||
*
|
||||
* Currently harmless because it's only called for temporary
|
||||
* objects which are out of the DAG anyway. */
|
||||
return;
|
||||
}
|
||||
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
||||
const deg::IDNode *id_node = deg_graph->find_id_node(DEG_get_original(&ob->id));
|
||||
if (id_node == nullptr) {
|
||||
/* TODO(sergey): Does it mean we need to check set scene? */
|
||||
return;
|
||||
}
|
||||
|
||||
r_mask->vmask |= id_node->customdata_masks.vert_mask;
|
||||
r_mask->emask |= id_node->customdata_masks.edge_mask;
|
||||
r_mask->fmask |= id_node->customdata_masks.face_mask;
|
||||
r_mask->lmask |= id_node->customdata_masks.loop_mask;
|
||||
r_mask->pmask |= id_node->customdata_masks.poly_mask;
|
||||
}
|
||||
|
||||
Scene *DEG_get_evaluated_scene(const Depsgraph *graph)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
||||
Scene *scene_cow = deg_graph->scene_cow;
|
||||
/* TODO(sergey): Shall we expand data-block here? Or is it OK to assume
|
||||
* that caller is OK with just a pointer in case scene is not updated yet? */
|
||||
BLI_assert(scene_cow != nullptr && deg::deg_eval_copy_is_expanded(&scene_cow->id));
|
||||
return scene_cow;
|
||||
}
|
||||
|
||||
ViewLayer *DEG_get_evaluated_view_layer(const Depsgraph *graph)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(graph);
|
||||
Scene *scene_cow = DEG_get_evaluated_scene(graph);
|
||||
if (scene_cow == nullptr) {
|
||||
return nullptr; /* Happens with new, not-yet-built/evaluated graphs. */
|
||||
}
|
||||
/* Do name-based lookup. */
|
||||
/* TODO(sergey): Can this be optimized? */
|
||||
ViewLayer *view_layer_orig = deg_graph->view_layer;
|
||||
ViewLayer *view_layer_cow = static_cast<ViewLayer *>(
|
||||
BLI_findstring(&scene_cow->view_layers, view_layer_orig->name, offsetof(ViewLayer, name)));
|
||||
BLI_assert(view_layer_cow != nullptr);
|
||||
return view_layer_cow;
|
||||
}
|
||||
|
||||
ID *DEG_get_evaluated_id(const Depsgraph *depsgraph, ID *id)
|
||||
{
|
||||
return deg::get_evaluated_id(reinterpret_cast<const deg::Depsgraph *>(depsgraph), id);
|
||||
}
|
||||
|
||||
const ID *DEG_get_evaluated_id(const Depsgraph *depsgraph, const ID *id)
|
||||
{
|
||||
return DEG_get_evaluated_id(depsgraph, const_cast<ID *>(id));
|
||||
}
|
||||
|
||||
void DEG_get_evaluated_rna_pointer(const Depsgraph *depsgraph,
|
||||
PointerRNA *ptr,
|
||||
PointerRNA *r_ptr_eval)
|
||||
{
|
||||
if ((ptr == nullptr) || (r_ptr_eval == nullptr)) {
|
||||
return;
|
||||
}
|
||||
ID *orig_id = ptr->owner_id;
|
||||
ID *cow_id = DEG_get_evaluated_id(depsgraph, orig_id);
|
||||
if (ptr->owner_id == ptr->data) {
|
||||
/* For ID pointers, it's easy... */
|
||||
r_ptr_eval->owner_id = cow_id;
|
||||
r_ptr_eval->data = static_cast<void *>(cow_id);
|
||||
r_ptr_eval->type = ptr->type;
|
||||
}
|
||||
else if (ptr->type == RNA_PoseBone) {
|
||||
/* HACK: Since bone keyframing is quite commonly used,
|
||||
* speed things up for this case by doing a special lookup
|
||||
* for bones */
|
||||
const Object *ob_eval = id_cast<Object *>(cow_id);
|
||||
bPoseChannel *pchan = static_cast<bPoseChannel *>(ptr->data);
|
||||
bPoseChannel *pchan_eval = BKE_pose_channel_find_name(ob_eval->pose, pchan->name);
|
||||
r_ptr_eval->owner_id = cow_id;
|
||||
r_ptr_eval->data = pchan_eval;
|
||||
r_ptr_eval->type = ptr->type;
|
||||
}
|
||||
else {
|
||||
/* For everything else, try to get RNA Path of the BMain-pointer,
|
||||
* then use that to look up what the evaluated one should be
|
||||
* given the evaluated ID pointer as the new lookup point */
|
||||
/* TODO: Find a faster alternative, or implement support for other
|
||||
* common types too above (e.g. modifiers) */
|
||||
if (const std::optional<std::string> path = RNA_path_from_ID_to_struct(ptr)) {
|
||||
PointerRNA cow_id_ptr = RNA_id_pointer_create(cow_id);
|
||||
if (!RNA_path_resolve(&cow_id_ptr, path->c_str(), r_ptr_eval, nullptr)) {
|
||||
/* Couldn't find evaluated copy of data */
|
||||
fprintf(stderr,
|
||||
"%s: Couldn't resolve RNA path ('%s') relative to evaluated ID (%p) for '%s'\n",
|
||||
__func__,
|
||||
path->c_str(),
|
||||
static_cast<void *>(cow_id),
|
||||
orig_id->name);
|
||||
}
|
||||
}
|
||||
else {
|
||||
/* Path resolution failed - XXX: Hide this behind a debug flag */
|
||||
fprintf(stderr,
|
||||
"%s: Couldn't get RNA path for %s relative to %s\n",
|
||||
__func__,
|
||||
RNA_struct_identifier(ptr->type),
|
||||
orig_id->name);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ID *DEG_get_original_id(ID *id)
|
||||
{
|
||||
return deg::get_original_id(id);
|
||||
}
|
||||
|
||||
const ID *DEG_get_original_id(const ID *id)
|
||||
{
|
||||
return deg::get_original_id(id);
|
||||
}
|
||||
|
||||
Depsgraph *DEG_get_depsgraph_by_id(const ID &id)
|
||||
{
|
||||
return id.runtime->depsgraph;
|
||||
}
|
||||
|
||||
bool DEG_is_original_id(const ID *id)
|
||||
{
|
||||
/* Some explanation of the logic.
|
||||
*
|
||||
* What we want here is to be able to tell whether given ID is a result of dependency graph
|
||||
* evaluation or not.
|
||||
*
|
||||
* All the data-blocks which are created by copy-on-evaluation mechanism will have will be tagged
|
||||
* with ID_TAG_COPIED_ON_EVAL tag. Those data-blocks can not be original.
|
||||
*
|
||||
* Modifier stack evaluation might create special data-blocks which have all the modifiers
|
||||
* applied, and those will be tagged with ID_TAG_COPIED_ON_EVAL_FINAL_RESULT. Such data-blocks
|
||||
* can not be original as well.
|
||||
*
|
||||
* Localization is usually happening from evaluated data-block, or will have some special pointer
|
||||
* magic which will make them to act as evaluated.
|
||||
*
|
||||
* NOTE: We consider ID evaluated if ANY of those flags is set. We do NOT require ALL of them. */
|
||||
if (id->tag & (ID_TAG_COPIED_ON_EVAL | ID_TAG_COPIED_ON_EVAL_FINAL_RESULT | ID_TAG_LOCALIZED)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DEG_is_evaluated_id(const ID *id)
|
||||
{
|
||||
return !DEG_is_original_id(id);
|
||||
}
|
||||
|
||||
bool DEG_is_fully_evaluated(const Depsgraph *depsgraph)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
|
||||
/* Check whether relations are up to date. */
|
||||
if (deg_graph->need_update_relations) {
|
||||
return false;
|
||||
}
|
||||
/* Check whether IDs are up to date. */
|
||||
if (!deg_graph->entry_tags.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool DEG_id_is_fully_evaluated(const Depsgraph *depsgraph, const ID *id_eval)
|
||||
{
|
||||
const deg::Depsgraph *deg_graph = reinterpret_cast<const deg::Depsgraph *>(depsgraph);
|
||||
/* Only us the original ID pointer to look up the IDNode, do not dereference it. */
|
||||
const ID *id_orig = deg::get_original_id(id_eval);
|
||||
const deg::IDNode *id_node = deg_graph->find_id_node(id_orig);
|
||||
if (!id_node) {
|
||||
return false;
|
||||
}
|
||||
for (deg::ComponentNode *component : id_node->components.values()) {
|
||||
for (deg::OperationNode *operation : component->operations) {
|
||||
if (operation->flag & deg::DEPSOP_FLAG_NEEDS_UPDATE) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool operation_needs_update(const ID &id,
|
||||
const deg::NodeType component_type,
|
||||
const deg::OperationCode opcode)
|
||||
{
|
||||
const Depsgraph *depsgraph = DEG_get_depsgraph_by_id(id);
|
||||
if (!depsgraph) {
|
||||
return false;
|
||||
}
|
||||
const deg::Depsgraph °_graph = *reinterpret_cast<const deg::Depsgraph *>(depsgraph);
|
||||
/* Only us the original ID pointer to look up the IDNode, do not dereference it. */
|
||||
const ID *id_orig = deg::get_original_id(&id);
|
||||
if (!id_orig) {
|
||||
return false;
|
||||
}
|
||||
const deg::IDNode *id_node = deg_graph.find_id_node(id_orig);
|
||||
if (!id_node) {
|
||||
return false;
|
||||
};
|
||||
const deg::ComponentNode *component_node = id_node->find_component(component_type);
|
||||
if (!component_node) {
|
||||
return false;
|
||||
}
|
||||
const deg::OperationNode *operation_node = component_node->find_operation(opcode);
|
||||
if (!operation_node) {
|
||||
return false;
|
||||
}
|
||||
/* Technically, there is potential for a race condition here, because the depsgraph evaluation
|
||||
* might update this flag, but it's very unlikely to cause issues right now. Maybe this should
|
||||
* become an atomic eventually. */
|
||||
const bool needs_update = operation_node->flag & deg::DEPSOP_FLAG_NEEDS_UPDATE;
|
||||
return needs_update;
|
||||
}
|
||||
|
||||
bool DEG_object_geometry_is_evaluated(const Object &object)
|
||||
{
|
||||
return !operation_needs_update(
|
||||
object.id, deg::NodeType::GEOMETRY, deg::OperationCode::GEOMETRY_EVAL);
|
||||
}
|
||||
|
||||
bool DEG_object_transform_is_evaluated(const Object &object)
|
||||
{
|
||||
return !operation_needs_update(
|
||||
object.id, deg::NodeType::TRANSFORM, deg::OperationCode::TRANSFORM_FINAL);
|
||||
}
|
||||
|
||||
bool DEG_collection_geometry_is_evaluated(const Collection &collection)
|
||||
{
|
||||
return !operation_needs_update(
|
||||
collection.id, deg::NodeType::GEOMETRY, deg::OperationCode::GEOMETRY_EVAL_DONE);
|
||||
}
|
||||
|
||||
std::optional<double> DEG_get_last_evaluation_time(const Depsgraph *depsgraph)
|
||||
{
|
||||
if (!DEG_is_fully_evaluated(depsgraph)) {
|
||||
return std::nullopt;
|
||||
}
|
||||
const deg::Depsgraph °_graph = *reinterpret_cast<const deg::Depsgraph *>(depsgraph);
|
||||
return deg_graph.debug.total_evaluation_time();
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,276 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Implementation of Querying and Filtering API's
|
||||
*/
|
||||
|
||||
#include <deque>
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "intern/depsgraph.hh"
|
||||
#include "intern/depsgraph_relation.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 TRAVERSAL ********************* */
|
||||
|
||||
namespace deg {
|
||||
namespace {
|
||||
|
||||
using TraversalQueue = std::deque<OperationNode *>;
|
||||
|
||||
using DEGForeachOperation = void (*)(OperationNode *, void *);
|
||||
|
||||
bool deg_foreach_needs_visit(const OperationNode *op_node, const int flags)
|
||||
{
|
||||
if (flags & DEG_FOREACH_COMPONENT_IGNORE_TRANSFORM_SOLVERS) {
|
||||
if (op_node->opcode == OperationCode::RIGIDBODY_SIM) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void deg_foreach_dependent_operation(const Depsgraph * /*graph*/,
|
||||
const IDNode *target_id_node,
|
||||
eDepsObjectComponentType source_component_type,
|
||||
int flags,
|
||||
DEGForeachOperation callback,
|
||||
void *user_data)
|
||||
{
|
||||
if (target_id_node == nullptr) {
|
||||
/* TODO(sergey): Shall we inform or assert here about attempt to start
|
||||
* iterating over non-existing ID? */
|
||||
return;
|
||||
}
|
||||
/* Start with scheduling all operations from ID node. */
|
||||
TraversalQueue queue;
|
||||
Set<OperationNode *> scheduled;
|
||||
for (ComponentNode *comp_node : target_id_node->components.values()) {
|
||||
if (comp_node->type == NodeType::VISIBILITY) {
|
||||
/* Visibility component is only used internally. It is not to be reporting dependencies to
|
||||
* the outer world. */
|
||||
continue;
|
||||
}
|
||||
|
||||
if (source_component_type != DEG_OB_COMP_ANY &&
|
||||
nodeTypeToObjectComponent(comp_node->type) != source_component_type)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
for (OperationNode *op_node : comp_node->operations) {
|
||||
if (!deg_foreach_needs_visit(op_node, flags)) {
|
||||
continue;
|
||||
}
|
||||
queue.push_back(op_node);
|
||||
scheduled.add(op_node);
|
||||
}
|
||||
}
|
||||
/* Process the queue. */
|
||||
while (!queue.empty()) {
|
||||
/* get next operation node to process. */
|
||||
OperationNode *op_node = queue.front();
|
||||
queue.pop_front();
|
||||
for (;;) {
|
||||
callback(op_node, user_data);
|
||||
/* Schedule outgoing operation nodes. */
|
||||
if (op_node->outlinks.size() == 1) {
|
||||
OperationNode *to_node = static_cast<OperationNode *>(op_node->outlinks[0]->to);
|
||||
if (!scheduled.contains(to_node) && deg_foreach_needs_visit(to_node, flags)) {
|
||||
scheduled.add_new(to_node);
|
||||
op_node = to_node;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (Relation *rel : op_node->outlinks) {
|
||||
OperationNode *to_node = static_cast<OperationNode *>(rel->to);
|
||||
if (!scheduled.contains(to_node) && deg_foreach_needs_visit(to_node, flags)) {
|
||||
queue.push_front(to_node);
|
||||
scheduled.add_new(to_node);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ForeachIDComponentData {
|
||||
DEGForeachIDComponentCallback callback;
|
||||
IDNode *target_id_node;
|
||||
Set<ComponentNode *> visited;
|
||||
};
|
||||
|
||||
void deg_foreach_dependent_component_callback(OperationNode *op_node, void *user_data_v)
|
||||
{
|
||||
ForeachIDComponentData *user_data = reinterpret_cast<ForeachIDComponentData *>(user_data_v);
|
||||
ComponentNode *comp_node = op_node->owner;
|
||||
IDNode *id_node = comp_node->owner;
|
||||
if (id_node != user_data->target_id_node && !user_data->visited.contains(comp_node)) {
|
||||
user_data->callback(id_node->id_orig, nodeTypeToObjectComponent(comp_node->type));
|
||||
user_data->visited.add_new(comp_node);
|
||||
}
|
||||
}
|
||||
|
||||
void deg_foreach_dependent_ID_component(const Depsgraph *graph,
|
||||
const ID *id,
|
||||
eDepsObjectComponentType source_component_type,
|
||||
int flags,
|
||||
DEGForeachIDComponentCallback callback)
|
||||
{
|
||||
ForeachIDComponentData data;
|
||||
data.callback = callback;
|
||||
data.target_id_node = graph->find_id_node(id);
|
||||
deg_foreach_dependent_operation(graph,
|
||||
data.target_id_node,
|
||||
source_component_type,
|
||||
flags,
|
||||
deg_foreach_dependent_component_callback,
|
||||
&data);
|
||||
}
|
||||
|
||||
struct ForeachIDData {
|
||||
DEGForeachIDCallback callback;
|
||||
IDNode *target_id_node;
|
||||
Set<IDNode *> visited;
|
||||
};
|
||||
|
||||
void deg_foreach_dependent_ID_callback(OperationNode *op_node, void *user_data_v)
|
||||
{
|
||||
ForeachIDData *user_data = reinterpret_cast<ForeachIDData *>(user_data_v);
|
||||
ComponentNode *comp_node = op_node->owner;
|
||||
IDNode *id_node = comp_node->owner;
|
||||
if (id_node != user_data->target_id_node && !user_data->visited.contains(id_node)) {
|
||||
user_data->callback(id_node->id_orig);
|
||||
user_data->visited.add_new(id_node);
|
||||
}
|
||||
}
|
||||
|
||||
void deg_foreach_dependent_ID(const Depsgraph *graph, const ID *id, DEGForeachIDCallback callback)
|
||||
{
|
||||
ForeachIDData data;
|
||||
data.callback = callback;
|
||||
data.target_id_node = graph->find_id_node(id);
|
||||
deg_foreach_dependent_operation(
|
||||
graph, data.target_id_node, DEG_OB_COMP_ANY, 0, deg_foreach_dependent_ID_callback, &data);
|
||||
}
|
||||
|
||||
void deg_foreach_ancestor_ID(const Depsgraph *graph, const ID *id, DEGForeachIDCallback callback)
|
||||
{
|
||||
/* Start with getting ID node from the graph. */
|
||||
IDNode *target_id_node = graph->find_id_node(id);
|
||||
if (target_id_node == nullptr) {
|
||||
/* TODO(sergey): Shall we inform or assert here about attempt to start
|
||||
* iterating over non-existing ID? */
|
||||
return;
|
||||
}
|
||||
/* Start with scheduling all operations from ID node. */
|
||||
TraversalQueue queue;
|
||||
Set<OperationNode *> scheduled;
|
||||
for (ComponentNode *comp_node : target_id_node->components.values()) {
|
||||
for (OperationNode *op_node : comp_node->operations) {
|
||||
queue.push_back(op_node);
|
||||
scheduled.add(op_node);
|
||||
}
|
||||
}
|
||||
Set<IDNode *> visited;
|
||||
visited.add_new(target_id_node);
|
||||
/* Process the queue. */
|
||||
while (!queue.empty()) {
|
||||
/* get next operation node to process. */
|
||||
OperationNode *op_node = queue.front();
|
||||
queue.pop_front();
|
||||
for (;;) {
|
||||
/* Check whether we need to inform callee about corresponding ID node. */
|
||||
ComponentNode *comp_node = op_node->owner;
|
||||
IDNode *id_node = comp_node->owner;
|
||||
if (!visited.contains(id_node)) {
|
||||
/* TODO(sergey): Is it orig or evaluated? */
|
||||
callback(id_node->id_orig);
|
||||
visited.add_new(id_node);
|
||||
}
|
||||
/* Schedule incoming operation nodes. */
|
||||
if (op_node->inlinks.size() == 1) {
|
||||
Node *from = op_node->inlinks[0]->from;
|
||||
if (from->get_class() == NodeClass::OPERATION) {
|
||||
OperationNode *from_node = static_cast<OperationNode *>(from);
|
||||
if (scheduled.add(from_node)) {
|
||||
op_node = from_node;
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (Relation *rel : op_node->inlinks) {
|
||||
Node *from = rel->from;
|
||||
if (from->get_class() == NodeClass::OPERATION) {
|
||||
OperationNode *from_node = static_cast<OperationNode *>(from);
|
||||
if (scheduled.add(from_node)) {
|
||||
queue.push_front(from_node);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void deg_foreach_id(const Depsgraph *depsgraph, DEGForeachIDCallback callback)
|
||||
{
|
||||
for (const IDNode *id_node : depsgraph->id_nodes) {
|
||||
callback(id_node->id_orig);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
} // namespace deg
|
||||
|
||||
void DEG_foreach_dependent_ID(const Depsgraph *depsgraph,
|
||||
const ID *id,
|
||||
DEGForeachIDCallback callback)
|
||||
{
|
||||
deg::deg_foreach_dependent_ID(reinterpret_cast<const deg::Depsgraph *>(depsgraph), id, callback);
|
||||
}
|
||||
|
||||
void DEG_foreach_dependent_ID_component(const Depsgraph *depsgraph,
|
||||
const ID *id,
|
||||
eDepsObjectComponentType source_component_type,
|
||||
int flags,
|
||||
DEGForeachIDComponentCallback callback)
|
||||
{
|
||||
deg::deg_foreach_dependent_ID_component(reinterpret_cast<const deg::Depsgraph *>(depsgraph),
|
||||
id,
|
||||
source_component_type,
|
||||
flags,
|
||||
callback);
|
||||
}
|
||||
|
||||
void DEG_foreach_ancestor_ID(const Depsgraph *depsgraph,
|
||||
const ID *id,
|
||||
DEGForeachIDCallback callback)
|
||||
{
|
||||
deg::deg_foreach_ancestor_ID(reinterpret_cast<const deg::Depsgraph *>(depsgraph), id, callback);
|
||||
}
|
||||
|
||||
void DEG_foreach_ID(const Depsgraph *depsgraph, DEGForeachIDCallback callback)
|
||||
{
|
||||
deg::deg_foreach_id(reinterpret_cast<const deg::Depsgraph *>(depsgraph), callback);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,558 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Implementation of Querying and Filtering API's
|
||||
*/
|
||||
|
||||
/* Silence warnings from copying deprecated fields. */
|
||||
#define DNA_DEPRECATED_ALLOW
|
||||
|
||||
#include "BKE_duplilist.hh"
|
||||
#include "BKE_idprop.hh"
|
||||
#include "BKE_layer.hh"
|
||||
#include "BKE_modifier.hh"
|
||||
#include "BKE_node.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_object_types.hh"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_math_matrix.h"
|
||||
#include "BLI_math_vector.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#include "intern/depsgraph.hh"
|
||||
#include "intern/node/deg_node_id.hh"
|
||||
|
||||
#ifndef NDEBUG
|
||||
# include "intern/eval/deg_eval_copy_on_write.h"
|
||||
#endif
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* If defined, all working data will be set to an invalid state, helping
|
||||
* to catch issues when areas accessing data which is considered to be no
|
||||
* longer available. */
|
||||
#undef INVALIDATE_WORK_DATA
|
||||
|
||||
#ifndef NDEBUG
|
||||
# define INVALIDATE_WORK_DATA
|
||||
#endif
|
||||
|
||||
using evil::DEG_iterator_temp_object_free_properties;
|
||||
using evil::DEG_iterator_temp_object_from_dupli;
|
||||
|
||||
/* ************************ DEG ITERATORS ********************* */
|
||||
|
||||
namespace {
|
||||
|
||||
void deg_invalidate_iterator_work_data(DEGObjectIterData *data)
|
||||
{
|
||||
#ifdef INVALIDATE_WORK_DATA
|
||||
BLI_assert(data != nullptr);
|
||||
memset(static_cast<void *>(&data->temp_dupli_object), 0xff, sizeof(data->temp_dupli_object));
|
||||
#else
|
||||
(void)data;
|
||||
#endif
|
||||
}
|
||||
|
||||
bool deg_object_hide_original(eEvaluationMode eval_mode, const Object *ob, const DupliObject *dob)
|
||||
{
|
||||
/* Automatic hiding if this object is being instanced on verts/faces/frames
|
||||
* by its parent. Ideally this should not be needed, but due to the wrong
|
||||
* dependency direction in the data design there is no way to keep the object
|
||||
* visible otherwise. The better solution eventually would be for objects
|
||||
* to specify which object they instance, instead of through parenting.
|
||||
*
|
||||
* This function should not be used for meta-balls. They have custom visibility rules, as hiding
|
||||
* the base meta-ball will also hide all the other balls in the group. */
|
||||
if (eval_mode == DAG_EVAL_RENDER || dob) {
|
||||
const int hide_original_types = OB_DUPLIVERTS | OB_DUPLIFACES;
|
||||
|
||||
if (!dob || !(dob->type & hide_original_types)) {
|
||||
if (ob->parent && (ob->parent->transflag & hide_original_types)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void deg_iterator_duplis_init(DEGObjectIterData *data, Object *object)
|
||||
{
|
||||
/* This should have been set to default values at `DEG_iterator_objects_begin`, or at the end of
|
||||
* the previous `deg_iterator_duplis_step` cycle. */
|
||||
BLI_assert(data->dupli_parent == nullptr);
|
||||
BLI_assert(data->dupli_object_next == nullptr);
|
||||
BLI_assert(data->dupli_object_next_index == -1);
|
||||
if (data->dupli_list.is_empty()) {
|
||||
return;
|
||||
}
|
||||
data->dupli_parent = object;
|
||||
data->dupli_object_next = &data->dupli_list.first();
|
||||
data->dupli_object_next_index = 0;
|
||||
}
|
||||
|
||||
/* Returns false when iterator is exhausted. */
|
||||
bool deg_iterator_duplis_step(DEGObjectIterData *data)
|
||||
{
|
||||
if (data->dupli_list.is_empty()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while (data->dupli_object_next != nullptr) {
|
||||
DupliObject *dob = data->dupli_object_next;
|
||||
Object *obd = dob->ob;
|
||||
|
||||
if (++data->dupli_object_next_index < data->dupli_list.size()) {
|
||||
data->dupli_object_next = &data->dupli_list[data->dupli_object_next_index];
|
||||
}
|
||||
else {
|
||||
data->dupli_object_next = nullptr;
|
||||
data->dupli_object_next_index = -1;
|
||||
}
|
||||
|
||||
if (!DEG_iterator_dupli_is_visible(dob, data->eval_mode)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
/* TODO: Can this be removed? included_objects is already passed to object_duplilist(). */
|
||||
DEGObjectIterSettings *settings = data->settings;
|
||||
if (settings->included_objects) {
|
||||
Object *object_orig = DEG_get_original(obd);
|
||||
if (!settings->included_objects->contains(object_orig)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (data->dupli_object_current) {
|
||||
DEG_iterator_temp_object_free_properties(data->dupli_object_current,
|
||||
&data->temp_dupli_object);
|
||||
}
|
||||
|
||||
data->dupli_object_current = dob;
|
||||
|
||||
if (DEG_iterator_temp_object_from_dupli(data->dupli_parent,
|
||||
data->dupli_object_current,
|
||||
data->eval_mode,
|
||||
true,
|
||||
&data->temp_dupli_object,
|
||||
&data->temp_dupli_object_runtime))
|
||||
{
|
||||
data->next_object = &data->temp_dupli_object;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
/* Even if the `dupli_list` is not empty, it may happen that none of its entry is displayed (e.g.
|
||||
* #DEG_iterator_dupli_is_visible return `false` for all of the duplis). In such cases,
|
||||
* `dupli_object_current` will also be `nullptr`, and nothing needs to be freed here.
|
||||
* See also #149673 for a reproducible case. */
|
||||
if (data->dupli_object_current) {
|
||||
DEG_iterator_temp_object_free_properties(data->dupli_object_current, &data->temp_dupli_object);
|
||||
}
|
||||
data->dupli_list.clear();
|
||||
data->dupli_parent = nullptr;
|
||||
data->dupli_object_next = nullptr;
|
||||
data->dupli_object_next_index = -1;
|
||||
data->dupli_object_current = nullptr;
|
||||
deg_invalidate_iterator_work_data(data);
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Returns false when iterator is exhausted. */
|
||||
bool deg_iterator_objects_step(DEGObjectIterData *data)
|
||||
{
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(data->graph);
|
||||
|
||||
for (; data->id_node_index < data->num_id_nodes; data->id_node_index++) {
|
||||
deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
|
||||
|
||||
/* Use the build time visibility so that the ID is not appearing/disappearing throughout
|
||||
* animation export. */
|
||||
if (!id_node->is_visible_on_build) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const ID_Type id_type = GS(id_node->id_orig->name);
|
||||
|
||||
if (id_type != ID_OB) {
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (id_node->linked_state) {
|
||||
case deg::DEG_ID_LINKED_DIRECTLY:
|
||||
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_DIRECTLY) == 0) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case deg::DEG_ID_LINKED_VIA_SET:
|
||||
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_VIA_SET) == 0) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
case deg::DEG_ID_LINKED_INDIRECTLY:
|
||||
if ((data->flag & DEG_ITER_OBJECT_FLAG_LINKED_INDIRECTLY) == 0) {
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
Object *object = id_cast<Object *>(id_node->id_cow);
|
||||
Object *object_orig = DEG_get_original(object);
|
||||
|
||||
DEGObjectIterSettings *settings = data->settings;
|
||||
if (settings->included_objects) {
|
||||
if (!settings->included_objects->contains(object_orig)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
/* NOTE: The object might be invisible after the latest depsgraph evaluation, in which case
|
||||
* going into its evaluated state might not be safe. For example, its evaluated mesh state
|
||||
* might point to a freed data-block if the mesh is animated.
|
||||
* So it is required to perform the visibility checks prior to looking into any deeper into the
|
||||
* object. */
|
||||
|
||||
BLI_assert(deg::deg_eval_copy_is_expanded(&object->id));
|
||||
|
||||
object->runtime->select_id = object_orig->runtime->select_id;
|
||||
|
||||
const bool use_preview = object_orig == data->object_orig_with_preview;
|
||||
if (use_preview) {
|
||||
object_duplilist_preview(data->graph, object, data->settings->viewer_path, data->dupli_list);
|
||||
deg_iterator_duplis_init(data, object);
|
||||
data->id_node_index++;
|
||||
return true;
|
||||
}
|
||||
|
||||
int ob_visibility = OB_VISIBLE_ALL;
|
||||
if (data->flag & DEG_ITER_OBJECT_FLAG_VISIBLE) {
|
||||
ob_visibility = BKE_object_visibility(object, data->eval_mode);
|
||||
|
||||
if (!DEG_iterator_object_is_visible(data->eval_mode, object)) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if (ob_visibility & OB_VISIBLE_INSTANCES) {
|
||||
if ((data->flag & DEG_ITER_OBJECT_FLAG_DUPLI) &&
|
||||
((object->transflag & OB_DUPLI) || object->runtime->geometry_set_eval != nullptr))
|
||||
{
|
||||
BLI_assert(deg::deg_validate_eval_copy_datablock(&object->id));
|
||||
object_duplilist(data->graph, object, data->settings->included_objects, data->dupli_list);
|
||||
deg_iterator_duplis_init(data, object);
|
||||
}
|
||||
}
|
||||
|
||||
if (ob_visibility & (OB_VISIBLE_SELF | OB_VISIBLE_PARTICLES)) {
|
||||
BLI_assert(deg::deg_validate_eval_copy_datablock(&object->id));
|
||||
data->next_object = object;
|
||||
}
|
||||
data->id_node_index++;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void DEGObjectIterData::transfer_from(DEGObjectIterData &other)
|
||||
{
|
||||
BLI_assert(this != &other);
|
||||
|
||||
this->settings = other.settings;
|
||||
this->graph = other.graph;
|
||||
this->flag = other.flag;
|
||||
this->eval_mode = other.eval_mode;
|
||||
this->object_orig_with_preview = other.object_orig_with_preview;
|
||||
this->next_object = other.next_object;
|
||||
this->dupli_parent = other.dupli_parent;
|
||||
this->dupli_list = std::move(other.dupli_list);
|
||||
this->dupli_object_next = other.dupli_object_next;
|
||||
this->dupli_object_next_index = other.dupli_object_next_index;
|
||||
this->dupli_object_current = other.dupli_object_current;
|
||||
this->temp_dupli_object = dna::shallow_copy(other.temp_dupli_object);
|
||||
this->temp_dupli_object_runtime = other.temp_dupli_object_runtime;
|
||||
this->temp_dupli_object.runtime = &temp_dupli_object_runtime;
|
||||
this->id_node_index = other.id_node_index;
|
||||
this->num_id_nodes = other.num_id_nodes;
|
||||
}
|
||||
|
||||
static Object *find_object_with_preview_geometry(const ViewerPath &viewer_path)
|
||||
{
|
||||
if (viewer_path.path.is_empty()) {
|
||||
return nullptr;
|
||||
}
|
||||
const ViewerPathElem *elem = static_cast<const ViewerPathElem *>(viewer_path.path.first);
|
||||
if (elem->type != VIEWER_PATH_ELEM_TYPE_ID) {
|
||||
return nullptr;
|
||||
}
|
||||
const IDViewerPathElem *id_elem = reinterpret_cast<const IDViewerPathElem *>(elem);
|
||||
if (id_elem->id == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (GS(id_elem->id->name) != ID_OB) {
|
||||
return nullptr;
|
||||
}
|
||||
Object *object = reinterpret_cast<Object *>(id_elem->id);
|
||||
if (elem->next->type != VIEWER_PATH_ELEM_TYPE_MODIFIER) {
|
||||
return nullptr;
|
||||
}
|
||||
const ModifierViewerPathElem *modifier_elem = reinterpret_cast<const ModifierViewerPathElem *>(
|
||||
elem->next);
|
||||
ModifierData *md = BKE_modifiers_findby_persistent_uid(object, modifier_elem->modifier_uid);
|
||||
if (md == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
if (!(md->mode & eModifierMode_Realtime)) {
|
||||
return nullptr;
|
||||
}
|
||||
return reinterpret_cast<Object *>(id_elem->id);
|
||||
}
|
||||
|
||||
void DEG_iterator_objects_begin(BLI_Iterator *iter, DEGObjectIterData *data)
|
||||
{
|
||||
Depsgraph *depsgraph = data->graph;
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
|
||||
const size_t num_id_nodes = deg_graph->id_nodes.size();
|
||||
|
||||
iter->data = data;
|
||||
|
||||
if (num_id_nodes == 0) {
|
||||
iter->valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
data->next_object = nullptr;
|
||||
data->dupli_parent = nullptr;
|
||||
data->dupli_list.clear();
|
||||
data->dupli_object_next = nullptr;
|
||||
data->dupli_object_next_index = -1;
|
||||
data->dupli_object_current = nullptr;
|
||||
data->id_node_index = 0;
|
||||
data->num_id_nodes = num_id_nodes;
|
||||
data->eval_mode = DEG_get_mode(depsgraph);
|
||||
deg_invalidate_iterator_work_data(data);
|
||||
|
||||
/* Determine if the preview of any object should be in the iterator. */
|
||||
const ViewerPath *viewer_path = data->settings->viewer_path;
|
||||
if (viewer_path != nullptr) {
|
||||
data->object_orig_with_preview = find_object_with_preview_geometry(*viewer_path);
|
||||
}
|
||||
|
||||
DEG_iterator_objects_next(iter);
|
||||
}
|
||||
|
||||
void DEG_iterator_objects_next(BLI_Iterator *iter)
|
||||
{
|
||||
DEGObjectIterData *data = static_cast<DEGObjectIterData *>(iter->data);
|
||||
while (true) {
|
||||
if (data->next_object != nullptr) {
|
||||
iter->current = data->next_object;
|
||||
data->next_object = nullptr;
|
||||
return;
|
||||
}
|
||||
if (deg_iterator_duplis_step(data)) {
|
||||
continue;
|
||||
}
|
||||
if (deg_iterator_objects_step(data)) {
|
||||
continue;
|
||||
}
|
||||
iter->valid = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void DEG_iterator_objects_end(BLI_Iterator *iter)
|
||||
{
|
||||
DEGObjectIterData *data = static_cast<DEGObjectIterData *>(iter->data);
|
||||
if (data != nullptr) {
|
||||
/* Force crash in case the iterator data is referenced and accessed down
|
||||
* the line. (#51718) */
|
||||
deg_invalidate_iterator_work_data(data);
|
||||
}
|
||||
}
|
||||
|
||||
/* ************************ DEG ID ITERATOR ********************* */
|
||||
|
||||
static void DEG_iterator_ids_step(BLI_Iterator *iter, deg::IDNode *id_node, bool only_updated)
|
||||
{
|
||||
ID *id_cow = id_node->id_cow;
|
||||
|
||||
/* Use the build time visibility so that the ID is not appearing/disappearing throughout
|
||||
* animation export.
|
||||
* When the dependency graph is asked for updates report all IDs, as the user of those updates
|
||||
* might need to react to updates coming from IDs which do change visibility throughout the
|
||||
* life-time of the graph. */
|
||||
if (!only_updated && !id_node->is_visible_on_build) {
|
||||
iter->skip = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (only_updated && !(id_cow->recalc & ID_RECALC_ALL)) {
|
||||
/* Node-tree is considered part of the data-block. */
|
||||
bNodeTree *ntree = bke::node_tree_from_id(id_cow);
|
||||
if (ntree == nullptr) {
|
||||
iter->skip = true;
|
||||
return;
|
||||
}
|
||||
if ((ntree->id.recalc & ID_RECALC_NTREE_OUTPUT) == 0) {
|
||||
iter->skip = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
iter->current = id_cow;
|
||||
iter->skip = false;
|
||||
}
|
||||
|
||||
void DEG_iterator_ids_begin(BLI_Iterator *iter, DEGIDIterData *data)
|
||||
{
|
||||
Depsgraph *depsgraph = data->graph;
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
|
||||
const size_t num_id_nodes = deg_graph->id_nodes.size();
|
||||
|
||||
iter->data = data;
|
||||
|
||||
if ((num_id_nodes == 0) || (data->only_updated && !DEG_id_type_any_updated(depsgraph))) {
|
||||
iter->valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
data->id_node_index = 0;
|
||||
data->num_id_nodes = num_id_nodes;
|
||||
|
||||
deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
|
||||
DEG_iterator_ids_step(iter, id_node, data->only_updated);
|
||||
|
||||
if (iter->skip) {
|
||||
DEG_iterator_ids_next(iter);
|
||||
}
|
||||
}
|
||||
|
||||
void DEG_iterator_ids_next(BLI_Iterator *iter)
|
||||
{
|
||||
DEGIDIterData *data = static_cast<DEGIDIterData *>(iter->data);
|
||||
Depsgraph *depsgraph = data->graph;
|
||||
deg::Depsgraph *deg_graph = reinterpret_cast<deg::Depsgraph *>(depsgraph);
|
||||
|
||||
do {
|
||||
iter->skip = false;
|
||||
|
||||
++data->id_node_index;
|
||||
if (data->id_node_index == data->num_id_nodes) {
|
||||
iter->valid = false;
|
||||
return;
|
||||
}
|
||||
|
||||
deg::IDNode *id_node = deg_graph->id_nodes[data->id_node_index];
|
||||
DEG_iterator_ids_step(iter, id_node, data->only_updated);
|
||||
} while (iter->skip);
|
||||
}
|
||||
|
||||
void DEG_iterator_ids_end(BLI_Iterator * /*iter*/) {}
|
||||
|
||||
bool DEG_iterator_object_is_visible(eEvaluationMode eval_mode, const Object *ob)
|
||||
{
|
||||
if (ob->type == OB_MBALL) {
|
||||
return true;
|
||||
}
|
||||
return !deg_object_hide_original(eval_mode, ob, nullptr);
|
||||
}
|
||||
|
||||
bool DEG_iterator_dupli_is_visible(const DupliObject *dupli, eEvaluationMode eval_mode)
|
||||
{
|
||||
if (dupli->no_draw) {
|
||||
return false;
|
||||
}
|
||||
if (dupli->ob_data && GS(dupli->ob_data->name) == ID_MB) {
|
||||
return false;
|
||||
}
|
||||
if (dupli->ob->type != OB_MBALL && deg_object_hide_original(eval_mode, dupli->ob, dupli)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool evil::DEG_iterator_temp_object_from_dupli(const Object *dupli_parent,
|
||||
const DupliObject *dupli,
|
||||
eEvaluationMode eval_mode,
|
||||
bool do_matrix_setup,
|
||||
Object *r_temp_object,
|
||||
bke::ObjectRuntime *r_temp_runtime)
|
||||
{
|
||||
*r_temp_object = dna::shallow_copy(*dupli->ob);
|
||||
r_temp_object->runtime = r_temp_runtime;
|
||||
*r_temp_object->runtime = *dupli->ob->runtime;
|
||||
|
||||
r_temp_object->base_flag = dupli_parent->base_flag | BASE_FROM_DUPLI;
|
||||
r_temp_object->base_local_view_bits = dupli_parent->base_local_view_bits;
|
||||
r_temp_object->runtime->local_collections_bits = dupli_parent->runtime->local_collections_bits;
|
||||
r_temp_object->dt = std::min(r_temp_object->dt, dupli_parent->dt);
|
||||
copy_v4_v4(r_temp_object->color, dupli_parent->color);
|
||||
r_temp_object->runtime->select_id = dupli_parent->runtime->select_id;
|
||||
if (dupli->ob->data != dupli->ob_data) {
|
||||
BKE_object_replace_data_on_shallow_copy(r_temp_object, dupli->ob_data);
|
||||
}
|
||||
|
||||
/* Duplicated elements shouldn't care whether their original collection is visible or not. */
|
||||
r_temp_object->base_flag |= BASE_ENABLED_AND_MAYBE_VISIBLE_IN_VIEWPORT;
|
||||
|
||||
/* TODO: Could this be computed in DEG_iterator_dupli_is_visible,
|
||||
* before setting up the shallow copy? */
|
||||
int ob_visibility = BKE_object_visibility(r_temp_object, eval_mode);
|
||||
if ((ob_visibility & (OB_VISIBLE_SELF | OB_VISIBLE_PARTICLES)) == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (do_matrix_setup) {
|
||||
/* This could be avoided by refactoring make_dupli() in order to track all negative scaling
|
||||
* recursively. */
|
||||
bool is_neg_scale = is_negative_m4(dupli->mat);
|
||||
SET_FLAG_FROM_TEST(r_temp_object->transflag, is_neg_scale, OB_NEG_SCALE);
|
||||
|
||||
copy_m4_m4(r_temp_object->runtime->object_to_world.ptr(), dupli->mat);
|
||||
invert_m4_m4(r_temp_object->runtime->world_to_object.ptr(),
|
||||
r_temp_object->object_to_world().ptr());
|
||||
}
|
||||
|
||||
BLI_assert(deg::deg_validate_eval_copy_datablock(&r_temp_object->id));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static void ensure_id_properties_freed(const IDProperty *dupli_idprops,
|
||||
IDProperty **temp_dupli_idprops)
|
||||
{
|
||||
if (*temp_dupli_idprops == nullptr) {
|
||||
/* No ID properties in temp data-block -- no leak is possible. */
|
||||
return;
|
||||
}
|
||||
if (*temp_dupli_idprops == dupli_idprops) {
|
||||
/* Temp copy of object did not modify ID properties. */
|
||||
return;
|
||||
}
|
||||
/* Free memory which is owned by temporary storage which is about to get overwritten. */
|
||||
IDP_FreeProperty(*temp_dupli_idprops);
|
||||
*temp_dupli_idprops = nullptr;
|
||||
}
|
||||
|
||||
void evil::DEG_iterator_temp_object_free_properties(const DupliObject *dupli, Object *temp_object)
|
||||
{
|
||||
ensure_id_properties_freed(dupli->ob->id.properties, &temp_object->id.properties);
|
||||
ensure_id_properties_freed(dupli->ob->id.system_properties, &temp_object->id.system_properties);
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,75 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#include "BLI_map.hh"
|
||||
#include "BLI_mutex.hh"
|
||||
#include "BLI_vector_set.hh"
|
||||
|
||||
#include "intern/depsgraph_registry.hh"
|
||||
|
||||
#include "intern/depsgraph.hh"
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
/* Global registry for dependency graphs associated with a main database.
|
||||
*
|
||||
* Threads may add or remove depsgraphs for different mains concurrently
|
||||
* (for example for preview rendering), but not the same main. */
|
||||
|
||||
/* Use pointer for map value to ensure span returned by get_all_registered_graphs
|
||||
* remains unchanged as other mains are added or removed. */
|
||||
using GraphSetPtr = std::unique_ptr<VectorSet<Depsgraph *>>;
|
||||
struct GraphRegistry {
|
||||
Map<Main *, GraphSetPtr> map;
|
||||
Mutex mutex;
|
||||
};
|
||||
|
||||
static GraphRegistry &get_graph_registry()
|
||||
{
|
||||
static GraphRegistry graph_registry;
|
||||
return graph_registry;
|
||||
}
|
||||
|
||||
void register_graph(Depsgraph *depsgraph)
|
||||
{
|
||||
GraphRegistry &graph_registry = get_graph_registry();
|
||||
Main *bmain = depsgraph->bmain;
|
||||
|
||||
std::lock_guard lock{graph_registry.mutex};
|
||||
graph_registry.map
|
||||
.lookup_or_add_cb(bmain, []() { return std::make_unique<VectorSet<Depsgraph *>>(); })
|
||||
->add_new(depsgraph);
|
||||
}
|
||||
|
||||
void unregister_graph(Depsgraph *depsgraph)
|
||||
{
|
||||
Main *bmain = depsgraph->bmain;
|
||||
GraphRegistry &graph_registry = get_graph_registry();
|
||||
|
||||
std::lock_guard lock{graph_registry.mutex};
|
||||
GraphSetPtr &graphs = graph_registry.map.lookup(bmain);
|
||||
graphs->remove(depsgraph);
|
||||
|
||||
/* If this was the last depsgraph associated with the main, remove the main entry as well. */
|
||||
if (graphs->is_empty()) {
|
||||
graph_registry.map.remove(bmain);
|
||||
}
|
||||
}
|
||||
|
||||
Span<Depsgraph *> get_all_registered_graphs(Main *bmain)
|
||||
{
|
||||
GraphRegistry &graph_registry = get_graph_registry();
|
||||
std::lock_guard lock{graph_registry.mutex};
|
||||
GraphSetPtr *graphs = graph_registry.map.lookup_ptr(bmain);
|
||||
if (graphs) {
|
||||
return **graphs;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
} // namespace blender::deg
|
||||
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "BLI_span.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Main;
|
||||
|
||||
namespace deg {
|
||||
|
||||
struct Depsgraph;
|
||||
|
||||
void register_graph(Depsgraph *depsgraph);
|
||||
void unregister_graph(Depsgraph *depsgraph);
|
||||
Span<Depsgraph *> get_all_registered_graphs(Main *bmain);
|
||||
|
||||
} // namespace deg
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,23 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#include "intern/depsgraph_relation.hh" /* own include */
|
||||
|
||||
#include "intern/node/deg_node.hh"
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
void Relation::unlink()
|
||||
{
|
||||
/* Sanity check. */
|
||||
BLI_assert(from != nullptr && to != nullptr);
|
||||
from->outlinks.remove_first_occurrence_and_reorder(this);
|
||||
to->inlinks.remove_first_occurrence_and_reorder(this);
|
||||
}
|
||||
|
||||
} // namespace blender::deg
|
||||
@@ -0,0 +1,51 @@
|
||||
/* SPDX-FileCopyrightText: 2020 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
struct Node;
|
||||
|
||||
/* Settings/Tags on Relationship.
|
||||
* NOTE: Is a bitmask, allowing accumulation. */
|
||||
enum RelationFlag {
|
||||
/* "cyclic" link - when detecting cycles, this relationship was the one
|
||||
* which triggers a cyclic relationship to exist in the graph. */
|
||||
RELATION_FLAG_CYCLIC = (1 << 0),
|
||||
/* Update flush will not go through this relation. */
|
||||
RELATION_FLAG_NO_FLUSH = (1 << 1),
|
||||
/* Only flush along the relation is update comes from a node which was
|
||||
* affected by user input. */
|
||||
RELATION_FLAG_FLUSH_USER_EDIT_ONLY = (1 << 2),
|
||||
/* The relation can not be killed by the cyclic dependencies solver. */
|
||||
RELATION_FLAG_GODMODE = (1 << 4),
|
||||
/* Relation will check existence before being added. */
|
||||
RELATION_CHECK_BEFORE_ADD = (1 << 5),
|
||||
/* The relation does not participate in visibility checks. */
|
||||
RELATION_NO_VISIBILITY_CHANGE = (1 << 6),
|
||||
};
|
||||
|
||||
/* B depends on A (A -> B) */
|
||||
struct Relation {
|
||||
Relation(Node *from, Node *to, const char *description) : from(from), to(to), name(description)
|
||||
{
|
||||
}
|
||||
|
||||
void unlink();
|
||||
|
||||
/* the nodes in the relationship (since this is shared between the nodes) */
|
||||
Node *from; /* A */
|
||||
Node *to; /* B */
|
||||
|
||||
/* relationship attributes */
|
||||
const char *name; /* label for debugging */
|
||||
int flag = 0; /* Bitmask of RelationFlag. */
|
||||
};
|
||||
|
||||
} // namespace blender::deg
|
||||
1009
blender-5.2.0/source/blender/depsgraph/intern/depsgraph_tag.cc
Normal file
1009
blender-5.2.0/source/blender/depsgraph/intern/depsgraph_tag.cc
Normal file
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,35 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#include "intern/node/deg_node.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ID;
|
||||
struct Main;
|
||||
|
||||
namespace deg {
|
||||
|
||||
struct Depsgraph;
|
||||
|
||||
/* Get type of a node which corresponds to a ID_RECALC_GEOMETRY tag. */
|
||||
NodeType geometry_tag_to_component(const ID *id);
|
||||
|
||||
/* Tag given ID for an update in all registered dependency graphs. */
|
||||
void id_tag_update(Main *bmain, ID *id, unsigned int flags, eUpdateSource update_source);
|
||||
|
||||
/* Tag given ID for an update with in a given dependency graph. */
|
||||
void graph_id_tag_update(
|
||||
Main *bmain, Depsgraph *graph, ID *id, unsigned int flags, eUpdateSource update_source);
|
||||
|
||||
/* Tag IDs of the graph for the visibility update tags.
|
||||
* Will do nothing if the graph is not tagged for visibility update. */
|
||||
void graph_tag_ids_for_visible_update(Depsgraph *graph);
|
||||
|
||||
} // namespace deg
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,43 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Defines and code for core node types.
|
||||
*/
|
||||
|
||||
#include <cstdlib> /* for BLI_assert() */
|
||||
|
||||
#include "DNA_customdata_types.h"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
#include "intern/depsgraph_type.hh"
|
||||
#include "intern/node/deg_node.hh"
|
||||
#include "intern/node/deg_node_component.hh"
|
||||
#include "intern/node/deg_node_operation.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
void DEG_register_node_types()
|
||||
{
|
||||
/* register node types */
|
||||
deg::deg_register_base_depsnodes();
|
||||
deg::deg_register_component_depsnodes();
|
||||
deg::deg_register_operation_depsnodes();
|
||||
}
|
||||
|
||||
void DEG_free_node_types() {}
|
||||
|
||||
deg::DEGCustomDataMeshMasks::DEGCustomDataMeshMasks(const CustomData_MeshMasks *other)
|
||||
: vert_mask(other->vmask),
|
||||
edge_mask(other->emask),
|
||||
face_mask(other->fmask),
|
||||
loop_mask(other->lmask),
|
||||
poly_mask(other->pmask)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
134
blender-5.2.0/source/blender/depsgraph/intern/depsgraph_type.hh
Normal file
134
blender-5.2.0/source/blender/depsgraph/intern/depsgraph_type.hh
Normal file
@@ -0,0 +1,134 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Data-types for internal use in the Depsgraph
|
||||
*
|
||||
* All of these data-types are only really used within the "core" depsgraph.
|
||||
* In particular, node types declared here form the structure of operations
|
||||
* in the graph.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstdint>
|
||||
|
||||
namespace blender {
|
||||
|
||||
/* TODO(sergey): Ideally we'll just use char* and statically allocated strings
|
||||
* to avoid any possible overhead caused by string (re)allocation/formatting. */
|
||||
|
||||
struct CustomData_MeshMasks;
|
||||
|
||||
namespace deg {
|
||||
|
||||
/* Source of the dependency graph node update tag.
|
||||
*
|
||||
* NOTE: This is a bit mask, so accumulation of sources is possible.
|
||||
*
|
||||
* TODO(sergey): Find a better place for this. */
|
||||
enum eUpdateSource {
|
||||
/* Update is caused by a time change. */
|
||||
DEG_UPDATE_SOURCE_TIME = (1 << 0),
|
||||
/* Update caused by user directly or indirectly influencing the node. */
|
||||
DEG_UPDATE_SOURCE_USER_EDIT = (1 << 1),
|
||||
/* Update is happening as a special response for the relations update. */
|
||||
DEG_UPDATE_SOURCE_RELATIONS = (1 << 2),
|
||||
/* Update is happening due to visibility change. */
|
||||
DEG_UPDATE_SOURCE_VISIBILITY = (1 << 3),
|
||||
/**
|
||||
* Update is happening because some side effect of the reevaluation is necessary. For example,
|
||||
* baking intermediate geometry nodes happens during normal evaluation but does not actually
|
||||
* change the final result. For baking we still want to reevaluate it.
|
||||
*/
|
||||
DEG_UPDATE_SOURCE_SIDE_EFFECT_REQUEST = (1 << 4),
|
||||
};
|
||||
|
||||
/* C++ wrapper around DNA's CustomData_MeshMasks struct. */
|
||||
struct DEGCustomDataMeshMasks {
|
||||
uint64_t vert_mask;
|
||||
uint64_t edge_mask;
|
||||
uint64_t face_mask;
|
||||
uint64_t loop_mask;
|
||||
uint64_t poly_mask;
|
||||
|
||||
DEGCustomDataMeshMasks() : vert_mask(0), edge_mask(0), face_mask(0), loop_mask(0), poly_mask(0)
|
||||
{
|
||||
}
|
||||
|
||||
explicit DEGCustomDataMeshMasks(const CustomData_MeshMasks *other);
|
||||
|
||||
DEGCustomDataMeshMasks &operator|=(const DEGCustomDataMeshMasks &other)
|
||||
{
|
||||
this->vert_mask |= other.vert_mask;
|
||||
this->edge_mask |= other.edge_mask;
|
||||
this->face_mask |= other.face_mask;
|
||||
this->loop_mask |= other.loop_mask;
|
||||
this->poly_mask |= other.poly_mask;
|
||||
return *this;
|
||||
}
|
||||
|
||||
DEGCustomDataMeshMasks operator|(const DEGCustomDataMeshMasks &other) const
|
||||
{
|
||||
DEGCustomDataMeshMasks result;
|
||||
result.vert_mask = this->vert_mask | other.vert_mask;
|
||||
result.edge_mask = this->edge_mask | other.edge_mask;
|
||||
result.face_mask = this->face_mask | other.face_mask;
|
||||
result.loop_mask = this->loop_mask | other.loop_mask;
|
||||
result.poly_mask = this->poly_mask | other.poly_mask;
|
||||
return result;
|
||||
}
|
||||
|
||||
bool operator==(const DEGCustomDataMeshMasks &other) const
|
||||
{
|
||||
return (this->vert_mask == other.vert_mask && this->edge_mask == other.edge_mask &&
|
||||
this->face_mask == other.face_mask && this->loop_mask == other.loop_mask &&
|
||||
this->poly_mask == other.poly_mask);
|
||||
}
|
||||
|
||||
bool operator!=(const DEGCustomDataMeshMasks &other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
static DEGCustomDataMeshMasks MaskVert(const uint64_t vert_mask)
|
||||
{
|
||||
DEGCustomDataMeshMasks result;
|
||||
result.vert_mask = vert_mask;
|
||||
return result;
|
||||
}
|
||||
|
||||
static DEGCustomDataMeshMasks MaskEdge(const uint64_t edge_mask)
|
||||
{
|
||||
DEGCustomDataMeshMasks result;
|
||||
result.edge_mask = edge_mask;
|
||||
return result;
|
||||
}
|
||||
|
||||
static DEGCustomDataMeshMasks MaskFace(const uint64_t face_mask)
|
||||
{
|
||||
DEGCustomDataMeshMasks result;
|
||||
result.face_mask = face_mask;
|
||||
return result;
|
||||
}
|
||||
|
||||
static DEGCustomDataMeshMasks MaskLoop(const uint64_t loop_mask)
|
||||
{
|
||||
DEGCustomDataMeshMasks result;
|
||||
result.loop_mask = loop_mask;
|
||||
return result;
|
||||
}
|
||||
|
||||
static DEGCustomDataMeshMasks MaskPoly(const uint64_t poly_mask)
|
||||
{
|
||||
DEGCustomDataMeshMasks result;
|
||||
result.poly_mask = poly_mask;
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace deg
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,42 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#include "intern/depsgraph_update.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
namespace deg {
|
||||
|
||||
static DEG_EditorUpdateIDCb deg_editor_update_id_cb = nullptr;
|
||||
static DEG_EditorUpdateSceneCb deg_editor_update_scene_cb = nullptr;
|
||||
|
||||
void deg_editors_id_update(const DEGEditorUpdateContext *update_ctx, ID *id)
|
||||
{
|
||||
if (deg_editor_update_id_cb != nullptr) {
|
||||
deg_editor_update_id_cb(update_ctx, id);
|
||||
}
|
||||
}
|
||||
|
||||
void deg_editors_scene_update(const DEGEditorUpdateContext *update_ctx, bool updated)
|
||||
{
|
||||
if (deg_editor_update_scene_cb != nullptr) {
|
||||
deg_editor_update_scene_cb(update_ctx, updated);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace deg
|
||||
|
||||
void DEG_editors_set_update_cb(DEG_EditorUpdateIDCb id_func, DEG_EditorUpdateSceneCb scene_func)
|
||||
{
|
||||
deg::deg_editor_update_id_cb = id_func;
|
||||
deg::deg_editor_update_scene_cb = scene_func;
|
||||
}
|
||||
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,23 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct DEGEditorUpdateContext;
|
||||
struct ID;
|
||||
|
||||
namespace deg {
|
||||
|
||||
void deg_editors_id_update(const DEGEditorUpdateContext *update_ctx, ID *id);
|
||||
|
||||
void deg_editors_scene_update(const DEGEditorUpdateContext *update_ctx, bool updated);
|
||||
|
||||
} // namespace deg
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,25 @@
|
||||
/* SPDX-FileCopyrightText: 2024 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
#include <mutex>
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_writeback_sync.hh"
|
||||
|
||||
#include "depsgraph.hh"
|
||||
|
||||
namespace blender::deg::sync_writeback {
|
||||
|
||||
void add(blender::Depsgraph &depsgraph, std::function<void()> fn)
|
||||
{
|
||||
deg::Depsgraph °_graph = reinterpret_cast<deg::Depsgraph &>(depsgraph);
|
||||
if (!deg_graph.is_active || deg_graph.sync_writeback == DEG_EVALUATE_SYNC_WRITEBACK_NO) {
|
||||
return;
|
||||
}
|
||||
|
||||
std::lock_guard lock{deg_graph.sync_writeback_callbacks_mutex};
|
||||
deg_graph.sync_writeback_callbacks.append(std::move(fn));
|
||||
}
|
||||
|
||||
} // namespace blender::deg::sync_writeback
|
||||
518
blender-5.2.0/source/blender/depsgraph/intern/eval/deg_eval.cc
Normal file
518
blender-5.2.0/source/blender/depsgraph/intern/eval/deg_eval.cc
Normal file
@@ -0,0 +1,518 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Evaluation engine entry-points for Depsgraph Engine.
|
||||
*/
|
||||
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
|
||||
#include "intern/eval/deg_eval.h"
|
||||
|
||||
#include "BLI_function_ref.hh"
|
||||
#include "BLI_gsqueue.h"
|
||||
#include "BLI_task.h"
|
||||
#include "BLI_time.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
|
||||
#include "DNA_modifier_types.h"
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_scene_types.h"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_query.hh"
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
# include "BPY_extern.hh"
|
||||
#endif
|
||||
|
||||
#include "atomic_ops.h"
|
||||
|
||||
#include "intern/depsgraph.hh"
|
||||
#include "intern/depsgraph_relation.hh"
|
||||
#include "intern/depsgraph_tag.hh"
|
||||
#include "intern/eval/deg_eval_copy_on_write.h"
|
||||
#include "intern/eval/deg_eval_flush.h"
|
||||
#include "intern/eval/deg_eval_stats.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 "intern/node/deg_node_operation.hh"
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
namespace {
|
||||
|
||||
struct DepsgraphEvalState;
|
||||
|
||||
void deg_task_run_func(TaskPool *pool, void *taskdata);
|
||||
|
||||
void schedule_children(DepsgraphEvalState *state,
|
||||
OperationNode *node,
|
||||
FunctionRef<void(OperationNode *node)> schedule_fn);
|
||||
|
||||
/* Denotes which part of dependency graph is being evaluated. */
|
||||
enum class EvaluationStage {
|
||||
/* Stage 1: Only Copy-on-Write operations are to be evaluated, prior to anything else.
|
||||
* This allows other operations to access its dependencies when there is a dependency cycle
|
||||
* involved. */
|
||||
COPY_ON_EVAL,
|
||||
|
||||
/* Evaluate actual ID nodes visibility based on the current state of animation and drivers. */
|
||||
DYNAMIC_VISIBILITY,
|
||||
|
||||
/* Threaded evaluation of all possible operations. */
|
||||
THREADED_EVALUATION,
|
||||
|
||||
/* Workaround for areas which can not be evaluated in threads.
|
||||
*
|
||||
* For example, meta-balls, which are iterating over all bases and are requesting dupli-lists
|
||||
* to see whether there are meta-balls inside. */
|
||||
SINGLE_THREADED_WORKAROUND,
|
||||
};
|
||||
|
||||
struct DepsgraphEvalState {
|
||||
Depsgraph *graph;
|
||||
bool do_stats;
|
||||
EvaluationStage stage;
|
||||
bool need_update_pending_parents = true;
|
||||
bool need_single_thread_pass = false;
|
||||
};
|
||||
|
||||
void evaluate_node(const DepsgraphEvalState *state, OperationNode *operation_node)
|
||||
{
|
||||
blender::Depsgraph *depsgraph = reinterpret_cast<blender::Depsgraph *>(state->graph);
|
||||
|
||||
/* Sanity checks. */
|
||||
BLI_assert_msg(!operation_node->is_noop(), "NOOP nodes should not actually be scheduled");
|
||||
/* Perform operation. */
|
||||
if (state->do_stats) {
|
||||
const double start_time = BLI_time_now_seconds();
|
||||
operation_node->evaluate(depsgraph);
|
||||
operation_node->stats.current_time += BLI_time_now_seconds() - start_time;
|
||||
}
|
||||
else {
|
||||
operation_node->evaluate(depsgraph);
|
||||
}
|
||||
|
||||
/* Clear the flag early on, allowing partial updates without re-evaluating the same node multiple
|
||||
* times.
|
||||
* This is a thread-safe modification as the node's flags are only read for a non-scheduled nodes
|
||||
* and this node has been scheduled. */
|
||||
operation_node->flag &= ~DEPSOP_FLAG_CLEAR_ON_EVAL;
|
||||
}
|
||||
|
||||
void deg_task_run_func(TaskPool *pool, void *taskdata)
|
||||
{
|
||||
void *userdata_v = BLI_task_pool_user_data(pool);
|
||||
DepsgraphEvalState *state = static_cast<DepsgraphEvalState *>(userdata_v);
|
||||
|
||||
/* Evaluate node. */
|
||||
OperationNode *operation_node = reinterpret_cast<OperationNode *>(taskdata);
|
||||
evaluate_node(state, operation_node);
|
||||
|
||||
/* Schedule children. */
|
||||
schedule_children(state, operation_node, [&](OperationNode *node) {
|
||||
BLI_task_pool_push(pool, deg_task_run_func, node, false, nullptr);
|
||||
});
|
||||
}
|
||||
|
||||
bool check_operation_node_visible(const DepsgraphEvalState *state, OperationNode *op_node)
|
||||
{
|
||||
const ComponentNode *comp_node = op_node->owner;
|
||||
/* Special case for copy-on-eval component: it is to be always evaluated, to keep copied
|
||||
* "database" in a consistent state. */
|
||||
if (comp_node->type == NodeType::COPY_ON_EVAL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Special case for dynamic visibility pass: the actual visibility is not yet known, so limit to
|
||||
* only operations which affects visibility. */
|
||||
if (state->stage == EvaluationStage::DYNAMIC_VISIBILITY) {
|
||||
return op_node->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY;
|
||||
}
|
||||
|
||||
return comp_node->affects_visible_id;
|
||||
}
|
||||
|
||||
void calculate_pending_parents_for_node(const DepsgraphEvalState *state, OperationNode *node)
|
||||
{
|
||||
/* Update counters, applies for both visible and invisible IDs. */
|
||||
node->num_links_pending = 0;
|
||||
node->scheduled = false;
|
||||
/* Invisible IDs requires no pending operations. */
|
||||
if (!check_operation_node_visible(state, node)) {
|
||||
return;
|
||||
}
|
||||
/* No need to bother with anything if node is not tagged for update. */
|
||||
if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) == 0) {
|
||||
return;
|
||||
}
|
||||
for (Relation *rel : node->inlinks) {
|
||||
if (rel->from->type == NodeType::OPERATION && (rel->flag & RELATION_FLAG_CYCLIC) == 0) {
|
||||
OperationNode *from = static_cast<OperationNode *>(rel->from);
|
||||
/* TODO(sergey): This is how old layer system was checking for the
|
||||
* calculation, but how is it possible that visible object depends
|
||||
* on an invisible? This is something what is prohibited after
|
||||
* deg_graph_build_flush_layers(). */
|
||||
if (!check_operation_node_visible(state, from)) {
|
||||
continue;
|
||||
}
|
||||
/* No need to wait for operation which is up to date. */
|
||||
if ((from->flag & DEPSOP_FLAG_NEEDS_UPDATE) == 0) {
|
||||
continue;
|
||||
}
|
||||
++node->num_links_pending;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void calculate_pending_parents_if_needed(DepsgraphEvalState *state)
|
||||
{
|
||||
if (!state->need_update_pending_parents) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (OperationNode *node : state->graph->operations) {
|
||||
calculate_pending_parents_for_node(state, node);
|
||||
}
|
||||
|
||||
state->need_update_pending_parents = false;
|
||||
}
|
||||
|
||||
void initialize_execution(DepsgraphEvalState *state, Depsgraph *graph)
|
||||
{
|
||||
/* Clear tags and other things which needs to be clear. */
|
||||
if (state->do_stats) {
|
||||
for (OperationNode *node : graph->operations) {
|
||||
node->stats.reset_current();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool is_metaball_object_operation(const OperationNode *operation_node)
|
||||
{
|
||||
const ComponentNode *component_node = operation_node->owner;
|
||||
const IDNode *id_node = component_node->owner;
|
||||
/* This runs after the COPY_ON_EVAL stage which creates id_cow. */
|
||||
BLI_assert(id_node->id_cow);
|
||||
if (GS(id_node->id_cow->name) != ID_OB) {
|
||||
return false;
|
||||
}
|
||||
const Object *object = reinterpret_cast<const Object *>(id_node->id_cow);
|
||||
return object->type == OB_MBALL;
|
||||
}
|
||||
|
||||
/* Simulation modifiers with sub-frames (fluid domain, dynamic paint canvas) perform direct updates
|
||||
* of other objects, which can cause race conditions over certain data (#115636). Unless and until
|
||||
* sub-steps are fully supported in depsgraph evaluation such objects must use single-threaded
|
||||
* evaluation. */
|
||||
bool is_modifier_subframe_operation(const OperationNode *operation_node)
|
||||
{
|
||||
const ComponentNode *component_node = operation_node->owner;
|
||||
const IDNode *id_node = component_node->owner;
|
||||
/* This runs after the COPY_ON_EVAL stage which creates id_cow. */
|
||||
BLI_assert(id_node->id_cow);
|
||||
if (GS(id_node->id_cow->name) != ID_OB) {
|
||||
return false;
|
||||
}
|
||||
const Object *object = reinterpret_cast<const Object *>(id_node->id_cow);
|
||||
for (const ModifierData &md : object->modifiers) {
|
||||
if (md.type == eModifierType_Fluid) {
|
||||
const auto &fmd = reinterpret_cast<const FluidModifierData &>(md);
|
||||
if (fmd.type == MOD_FLUID_TYPE_DOMAIN) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if (md.type == eModifierType_DynamicPaint) {
|
||||
const auto &dmd = reinterpret_cast<const DynamicPaintModifierData &>(md);
|
||||
if (dmd.type == MOD_DYNAMICPAINT_TYPE_CANVAS) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool need_evaluate_operation_at_stage(DepsgraphEvalState *state,
|
||||
const OperationNode *operation_node)
|
||||
{
|
||||
const ComponentNode *component_node = operation_node->owner;
|
||||
switch (state->stage) {
|
||||
case EvaluationStage::COPY_ON_EVAL:
|
||||
return (component_node->type == NodeType::COPY_ON_EVAL);
|
||||
|
||||
case EvaluationStage::DYNAMIC_VISIBILITY:
|
||||
return operation_node->flag & OperationFlag::DEPSOP_FLAG_AFFECTS_VISIBILITY;
|
||||
|
||||
case EvaluationStage::THREADED_EVALUATION:
|
||||
if (is_metaball_object_operation(operation_node)) {
|
||||
state->need_single_thread_pass = true;
|
||||
return false;
|
||||
}
|
||||
if (is_modifier_subframe_operation(operation_node)) {
|
||||
state->need_single_thread_pass = true;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
|
||||
case EvaluationStage::SINGLE_THREADED_WORKAROUND:
|
||||
return true;
|
||||
}
|
||||
BLI_assert_msg(0, "Unhandled evaluation stage, should never happen.");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Schedule a node if it needs evaluation.
|
||||
* dec_parents: Decrement pending parents count, true when child nodes are
|
||||
* scheduled after a task has been completed.
|
||||
*/
|
||||
void schedule_node(DepsgraphEvalState *state,
|
||||
OperationNode *node,
|
||||
bool dec_parents,
|
||||
const FunctionRef<void(OperationNode *node)> schedule_fn)
|
||||
{
|
||||
/* No need to schedule nodes of invisible ID. */
|
||||
if (!check_operation_node_visible(state, node)) {
|
||||
return;
|
||||
}
|
||||
/* No need to schedule operations which are not tagged for update, they are
|
||||
* considered to be up to date. */
|
||||
if ((node->flag & DEPSOP_FLAG_NEEDS_UPDATE) == 0) {
|
||||
return;
|
||||
}
|
||||
/* TODO(sergey): This is not strictly speaking safe to read
|
||||
* num_links_pending. */
|
||||
if (dec_parents) {
|
||||
BLI_assert(node->num_links_pending > 0);
|
||||
atomic_sub_and_fetch_uint32(&node->num_links_pending, 1);
|
||||
}
|
||||
/* Cal not schedule operation while its dependencies are not yet
|
||||
* evaluated. */
|
||||
if (node->num_links_pending != 0) {
|
||||
return;
|
||||
}
|
||||
/* During the copy-on-eval stage only schedule copy-on-eval nodes. */
|
||||
if (!need_evaluate_operation_at_stage(state, node)) {
|
||||
return;
|
||||
}
|
||||
/* Actually schedule the node. */
|
||||
bool is_scheduled = atomic_fetch_and_or_uint8(reinterpret_cast<uint8_t *>(&node->scheduled),
|
||||
uint8_t(true));
|
||||
if (!is_scheduled) {
|
||||
if (node->is_noop()) {
|
||||
/* Clear flags to avoid affecting subsequent update propagation.
|
||||
* For normal nodes these are cleared when it is evaluated. */
|
||||
node->flag &= ~DEPSOP_FLAG_CLEAR_ON_EVAL;
|
||||
|
||||
/* skip NOOP node, schedule children right away */
|
||||
schedule_children(state, node, schedule_fn);
|
||||
}
|
||||
else {
|
||||
/* children are scheduled once this task is completed */
|
||||
schedule_fn(node);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void schedule_graph(DepsgraphEvalState *state,
|
||||
const FunctionRef<void(OperationNode *node)> schedule_fn)
|
||||
{
|
||||
for (OperationNode *node : state->graph->operations) {
|
||||
schedule_node(state, node, false, schedule_fn);
|
||||
}
|
||||
}
|
||||
|
||||
void schedule_children(DepsgraphEvalState *state,
|
||||
OperationNode *node,
|
||||
const FunctionRef<void(OperationNode *node)> schedule_fn)
|
||||
{
|
||||
for (Relation *rel : node->outlinks) {
|
||||
OperationNode *child = static_cast<OperationNode *>(rel->to);
|
||||
BLI_assert(child->type == NodeType::OPERATION);
|
||||
if (child->scheduled) {
|
||||
/* Happens when having cyclic dependencies. */
|
||||
continue;
|
||||
}
|
||||
schedule_node(state, child, (rel->flag & RELATION_FLAG_CYCLIC) == 0, schedule_fn);
|
||||
}
|
||||
}
|
||||
|
||||
/* Evaluate given stage of the dependency graph evaluation using multiple threads.
|
||||
*
|
||||
* NOTE: Will assign the `state->stage` to the given stage. */
|
||||
void evaluate_graph_threaded_stage(DepsgraphEvalState *state,
|
||||
TaskPool *task_pool,
|
||||
const EvaluationStage stage)
|
||||
{
|
||||
state->stage = stage;
|
||||
|
||||
calculate_pending_parents_if_needed(state);
|
||||
|
||||
schedule_graph(state, [&](OperationNode *node) {
|
||||
BLI_task_pool_push(task_pool, deg_task_run_func, node, false, nullptr);
|
||||
});
|
||||
BLI_task_pool_work_and_wait(task_pool);
|
||||
}
|
||||
|
||||
/* Evaluate remaining operations of the dependency graph in a single threaded manner. */
|
||||
void evaluate_graph_single_threaded_if_needed(DepsgraphEvalState *state)
|
||||
{
|
||||
if (!state->need_single_thread_pass) {
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_assert(!state->need_update_pending_parents);
|
||||
|
||||
state->stage = EvaluationStage::SINGLE_THREADED_WORKAROUND;
|
||||
|
||||
GSQueue *evaluation_queue = BLI_gsqueue_new(sizeof(OperationNode *));
|
||||
auto schedule_node_to_queue = [&](OperationNode *node) {
|
||||
BLI_gsqueue_push(evaluation_queue, &node);
|
||||
};
|
||||
schedule_graph(state, schedule_node_to_queue);
|
||||
|
||||
while (!BLI_gsqueue_is_empty(evaluation_queue)) {
|
||||
OperationNode *operation_node;
|
||||
BLI_gsqueue_pop(evaluation_queue, &operation_node);
|
||||
|
||||
evaluate_node(state, operation_node);
|
||||
schedule_children(state, operation_node, schedule_node_to_queue);
|
||||
}
|
||||
|
||||
BLI_gsqueue_free(evaluation_queue);
|
||||
}
|
||||
|
||||
void depsgraph_ensure_view_layer(Depsgraph *graph)
|
||||
{
|
||||
/* We update evaluated scene in the following cases:
|
||||
* - It was not expanded yet.
|
||||
* - It was tagged for update of evaluated component.
|
||||
* This allows us to have proper view layer pointer. */
|
||||
Scene *scene_cow = graph->scene_cow;
|
||||
if (deg_eval_copy_is_expanded(&scene_cow->id) &&
|
||||
(scene_cow->id.recalc & ID_RECALC_SYNC_TO_EVAL) == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
const IDNode *scene_id_node = graph->find_id_node(&graph->scene->id);
|
||||
deg_update_eval_copy_datablock(graph, scene_id_node);
|
||||
}
|
||||
|
||||
TaskPool *deg_evaluate_task_pool_create(DepsgraphEvalState *state)
|
||||
{
|
||||
if (G.debug & G_DEBUG_DEPSGRAPH_NO_THREADS) {
|
||||
return BLI_task_pool_create_no_threads(state);
|
||||
}
|
||||
|
||||
return BLI_task_pool_create_suspended(state, TASK_PRIORITY_HIGH);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void deg_evaluate_on_refresh(Depsgraph *graph)
|
||||
{
|
||||
/* Nothing to update, early out. */
|
||||
if (graph->entry_tags.is_empty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* The update counts can be used to check if the Depsgraph was changed since the last time it was
|
||||
* cached by comparing its current update count with the one stored at the moment the Depsgraph
|
||||
* data were cached.
|
||||
*
|
||||
* A global atomic is used as opposed to incrementing the update count per Depsgraph to protect
|
||||
* against the case where the Depsgraph is being recreated for each update and used to feed the
|
||||
* same running engine instances. This can happen when using a brute force update pattern (see
|
||||
* #135635). */
|
||||
static std::atomic<uint64_t> global_update_count = 0;
|
||||
graph->update_count = global_update_count.fetch_add(1) + 1;
|
||||
|
||||
graph->debug.begin_graph_evaluation();
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
/* Release the GIL so that Python drivers can be evaluated. See #91046. */
|
||||
BPy_BEGIN_ALLOW_THREADS;
|
||||
#endif
|
||||
|
||||
graph->is_evaluating = true;
|
||||
depsgraph_ensure_view_layer(graph);
|
||||
|
||||
/* Set up evaluation state. */
|
||||
DepsgraphEvalState state;
|
||||
state.graph = graph;
|
||||
state.do_stats = graph->debug.do_time_debug();
|
||||
|
||||
/* Prepare all nodes for evaluation. */
|
||||
initialize_execution(&state, graph);
|
||||
|
||||
/* Evaluation happens in several incremental steps:
|
||||
*
|
||||
* - Start with the copy-on-evaluation operations which never form dependency cycles. This will
|
||||
* ensure that if a dependency graph has a cycle evaluation functions will always "see" valid
|
||||
* expanded datablock. It might not be evaluated yet, but at least the datablock will be valid.
|
||||
*
|
||||
* - If there is potentially dynamically changing visibility in the graph update the actual
|
||||
* nodes visibilities, so that actual heavy data evaluation can benefit from knowledge that
|
||||
* something heavy is not currently visible.
|
||||
*
|
||||
* - Multi-threaded evaluation of all possible nodes.
|
||||
* Certain operations (and their subtrees) could be ignored. For example, meta-balls are not
|
||||
* safe from threading point of view, so the threaded evaluation will stop at the metaball
|
||||
* operation node.
|
||||
*
|
||||
* - Single-threaded pass of all remaining operations. */
|
||||
|
||||
TaskPool *task_pool = deg_evaluate_task_pool_create(&state);
|
||||
|
||||
evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::COPY_ON_EVAL);
|
||||
|
||||
if (graph->has_animated_visibility || graph->need_update_nodes_visibility) {
|
||||
/* Update pending parents including only the ones which are affecting operations which are
|
||||
* affecting visibility. */
|
||||
state.need_update_pending_parents = true;
|
||||
|
||||
evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::DYNAMIC_VISIBILITY);
|
||||
|
||||
deg_graph_flush_visibility_flags_if_needed(graph);
|
||||
|
||||
/* Update parents to an updated visibility and evaluation stage.
|
||||
*
|
||||
* Need to do it regardless of whether visibility is actually changed or not: current state of
|
||||
* the pending parents are all zeroes because it was previously calculated for only visibility
|
||||
* related nodes and those are fully evaluated by now. */
|
||||
state.need_update_pending_parents = true;
|
||||
}
|
||||
|
||||
evaluate_graph_threaded_stage(&state, task_pool, EvaluationStage::THREADED_EVALUATION);
|
||||
|
||||
BLI_task_pool_free(task_pool);
|
||||
|
||||
evaluate_graph_single_threaded_if_needed(&state);
|
||||
|
||||
/* Finalize statistics gathering. This is because we only gather single
|
||||
* operation timing here, without aggregating anything to avoid any extra
|
||||
* synchronization. */
|
||||
if (state.do_stats) {
|
||||
deg_eval_stats_aggregate(graph);
|
||||
}
|
||||
|
||||
/* Clear any uncleared tags. */
|
||||
deg_graph_clear_tags(graph);
|
||||
graph->is_evaluating = false;
|
||||
|
||||
#ifdef WITH_PYTHON
|
||||
BPy_END_ALLOW_THREADS;
|
||||
#endif
|
||||
|
||||
graph->debug.end_graph_evaluation();
|
||||
}
|
||||
|
||||
} // namespace blender::deg
|
||||
@@ -0,0 +1,26 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Evaluation engine entry-points for Depsgraph Engine.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
struct Depsgraph;
|
||||
|
||||
/**
|
||||
* Evaluate all nodes tagged for updating,
|
||||
* \warning This is usually done as part of main loop, but may also be
|
||||
* called from frame-change update.
|
||||
*
|
||||
* \note Time sources should be all valid!
|
||||
*/
|
||||
void deg_evaluate_on_refresh(Depsgraph *graph);
|
||||
|
||||
} // namespace blender::deg
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,82 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Depsgraph;
|
||||
struct ID;
|
||||
|
||||
/* Uncomment this to have verbose log about original and evaluated pointers
|
||||
* logged, with detailed information when they are allocated, expanded
|
||||
* and remapped.
|
||||
*/
|
||||
// #define DEG_DEBUG_COW_POINTERS
|
||||
|
||||
#ifdef DEG_DEBUG_COW_POINTERS
|
||||
# define DEG_COW_PRINT(format, ...) printf(format, __VA_ARGS__);
|
||||
#else
|
||||
# define DEG_COW_PRINT(format, ...)
|
||||
#endif
|
||||
|
||||
namespace deg {
|
||||
|
||||
struct Depsgraph;
|
||||
class DepsgraphNodeBuilder;
|
||||
struct IDNode;
|
||||
|
||||
/**
|
||||
* Makes sure given evaluated data-block is brought back to state of the original
|
||||
* data-block.
|
||||
*/
|
||||
ID *deg_update_eval_copy_datablock(const Depsgraph *depsgraph, const IDNode *id_node);
|
||||
ID *deg_update_eval_copy_datablock(const Depsgraph *depsgraph, struct ID *id_orig);
|
||||
|
||||
/** Helper function which frees memory used by copy-on-written data-block. */
|
||||
void deg_free_eval_copy_datablock(struct ID *id_cow);
|
||||
|
||||
/**
|
||||
* Callback function for depsgraph operation node which ensures evaluated
|
||||
* data-block is ready for use by further evaluation routines.
|
||||
*/
|
||||
void deg_create_eval_copy(blender::Depsgraph *depsgraph, const struct IDNode *id_node);
|
||||
|
||||
/**
|
||||
* Check that given ID is properly expanded and does not have any shallow
|
||||
* copies inside.
|
||||
*/
|
||||
bool deg_validate_eval_copy_datablock(ID *id_cow);
|
||||
|
||||
/** Tag given ID block as being copy-on-eval. */
|
||||
void deg_tag_eval_copy_id(Depsgraph &depsgraph, struct ID *id_cow, const struct ID *id_orig);
|
||||
|
||||
/**
|
||||
* Check whether ID data-block is expanded.
|
||||
*
|
||||
* TODO(sergey): Make it an inline function or a macro.
|
||||
*/
|
||||
bool deg_eval_copy_is_expanded(const struct ID *id_cow);
|
||||
|
||||
/**
|
||||
* Check whether an evaluated data-block copy is needed for given ID.
|
||||
*
|
||||
* There are some exceptions on data-blocks which are covered by dependency graph
|
||||
* but which we don't want to start duplicating.
|
||||
*
|
||||
* This includes images.
|
||||
*/
|
||||
bool deg_eval_copy_is_needed(const ID *id_orig);
|
||||
bool deg_eval_copy_is_needed(const ID_Type id_type);
|
||||
|
||||
} // namespace deg
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,361 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Core routines for how the Depsgraph works.
|
||||
*/
|
||||
|
||||
#include "intern/eval/deg_eval_flush.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <deque>
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
#include "BLI_task.h"
|
||||
#include "BLI_utildefines.h"
|
||||
|
||||
#include "BKE_global.hh"
|
||||
#include "BKE_key.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_scene.hh"
|
||||
|
||||
#include "DRW_engine.hh"
|
||||
|
||||
#include "DEG_depsgraph.hh"
|
||||
#include "DEG_depsgraph_debug.hh"
|
||||
|
||||
#include "intern/debug/deg_debug.h"
|
||||
#include "intern/depsgraph.hh"
|
||||
#include "intern/depsgraph_relation.hh"
|
||||
#include "intern/depsgraph_update.hh"
|
||||
#include "intern/node/deg_node.hh"
|
||||
#include "intern/node/deg_node_component.hh"
|
||||
#include "intern/node/deg_node_factory.hh"
|
||||
#include "intern/node/deg_node_id.hh"
|
||||
#include "intern/node/deg_node_operation.hh"
|
||||
#include "intern/node/deg_node_time.hh"
|
||||
|
||||
#include "intern/eval/deg_eval_copy_on_write.h"
|
||||
|
||||
/* Invalidate data-block data when update is flushed on it.
|
||||
*
|
||||
* The idea of this is to help catching cases when area is accessing data which
|
||||
* is not yet evaluated, which could happen due to missing relations. The issue
|
||||
* is that usually that data will be kept from previous frame, and it looks to
|
||||
* be plausible.
|
||||
*
|
||||
* This ensures that data does not look plausible, making it much easier to
|
||||
* catch usage of invalid state. */
|
||||
#undef INVALIDATE_ON_FLUSH
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
enum {
|
||||
ID_STATE_NONE = 0,
|
||||
ID_STATE_MODIFIED = 1,
|
||||
};
|
||||
|
||||
enum {
|
||||
COMPONENT_STATE_NONE = 0,
|
||||
COMPONENT_STATE_SCHEDULED = 1,
|
||||
COMPONENT_STATE_DONE = 2,
|
||||
};
|
||||
|
||||
using FlushQueue = std::deque<OperationNode *>;
|
||||
|
||||
namespace {
|
||||
|
||||
void flush_init_id_node_func(void *__restrict data_v,
|
||||
const int i,
|
||||
const TaskParallelTLS *__restrict /*tls*/)
|
||||
{
|
||||
Depsgraph *graph = static_cast<Depsgraph *>(data_v);
|
||||
IDNode *id_node = graph->id_nodes[i];
|
||||
id_node->custom_flags = ID_STATE_NONE;
|
||||
for (ComponentNode *comp_node : id_node->components.values()) {
|
||||
comp_node->custom_flags = COMPONENT_STATE_NONE;
|
||||
}
|
||||
}
|
||||
|
||||
inline void flush_prepare(Depsgraph *graph)
|
||||
{
|
||||
for (OperationNode *node : graph->operations) {
|
||||
node->scheduled = false;
|
||||
}
|
||||
|
||||
{
|
||||
const int num_id_nodes = graph->id_nodes.size();
|
||||
TaskParallelSettings settings;
|
||||
BLI_parallel_range_settings_defaults(&settings);
|
||||
settings.min_iter_per_thread = 1024;
|
||||
BLI_task_parallel_range(0, num_id_nodes, graph, flush_init_id_node_func, &settings);
|
||||
}
|
||||
}
|
||||
|
||||
inline void flush_schedule_entrypoints(Depsgraph *graph, FlushQueue *queue)
|
||||
{
|
||||
for (OperationNode *op_node : graph->entry_tags) {
|
||||
queue->push_back(op_node);
|
||||
op_node->scheduled = true;
|
||||
DEG_DEBUG_PRINTF((::blender::Depsgraph *)graph,
|
||||
EVAL,
|
||||
"Operation is entry point for update: %s\n",
|
||||
op_node->identifier().c_str());
|
||||
}
|
||||
}
|
||||
|
||||
inline void flush_handle_id_node(IDNode *id_node)
|
||||
{
|
||||
id_node->custom_flags = ID_STATE_MODIFIED;
|
||||
}
|
||||
|
||||
/* TODO(sergey): We can reduce number of arguments here. */
|
||||
inline void flush_handle_component_node(IDNode *id_node,
|
||||
ComponentNode *comp_node,
|
||||
FlushQueue *queue)
|
||||
{
|
||||
/* We only handle component once. */
|
||||
if (comp_node->custom_flags == COMPONENT_STATE_DONE) {
|
||||
return;
|
||||
}
|
||||
comp_node->custom_flags = COMPONENT_STATE_DONE;
|
||||
/* Tag all required operations in component for update, unless this is a
|
||||
* special component where we don't want all operations to be tagged.
|
||||
*
|
||||
* TODO(sergey): Make this a more generic solution. */
|
||||
if (!ELEM(comp_node->type, NodeType::PARTICLE_SETTINGS, NodeType::PARTICLE_SYSTEM)) {
|
||||
const bool is_geometry_component = comp_node->type == NodeType::GEOMETRY;
|
||||
for (OperationNode *op : comp_node->operations) {
|
||||
/* Special case for the visibility operation in the geometry component.
|
||||
*
|
||||
* This operation is a part of the geometry component so that manual tag for geometry recalc
|
||||
* ensures that the visibility is re-evaluated. This operation is not to be re-evaluated when
|
||||
* an update is flushed to the geometry component via a time dependency or a driver targeting
|
||||
* a modifier. Skipping update in this case avoids CPU time unnecessarily spent looping over
|
||||
* modifiers and looking up operations by name in the visibility evaluation function. */
|
||||
if (is_geometry_component && op->opcode == OperationCode::VISIBILITY) {
|
||||
continue;
|
||||
}
|
||||
op->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
|
||||
}
|
||||
}
|
||||
/* when some target changes bone, we might need to re-run the
|
||||
* whole IK solver, otherwise result might be unpredictable. */
|
||||
if (comp_node->type == NodeType::BONE) {
|
||||
ComponentNode *pose_comp = id_node->find_component(NodeType::EVAL_POSE);
|
||||
BLI_assert(pose_comp != nullptr);
|
||||
if (pose_comp->custom_flags == COMPONENT_STATE_NONE) {
|
||||
queue->push_front(pose_comp->get_entry_operation());
|
||||
pose_comp->custom_flags = COMPONENT_STATE_SCHEDULED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Schedule children of the given operation node for traversal.
|
||||
*
|
||||
* One of the children will bypass the queue and will be returned as a function
|
||||
* return value, so it can start being handled right away, without building too
|
||||
* much of a queue.
|
||||
*/
|
||||
inline OperationNode *flush_schedule_children(OperationNode *op_node, FlushQueue *queue)
|
||||
{
|
||||
if (op_node->flag & DEPSOP_FLAG_USER_MODIFIED) {
|
||||
IDNode *id_node = op_node->owner->owner;
|
||||
id_node->is_user_modified = true;
|
||||
}
|
||||
|
||||
OperationNode *result = nullptr;
|
||||
for (Relation *rel : op_node->outlinks) {
|
||||
/* Flush is forbidden, completely. */
|
||||
if (rel->flag & RELATION_FLAG_NO_FLUSH) {
|
||||
continue;
|
||||
}
|
||||
/* Relation only allows flushes on user changes, but the node was not
|
||||
* affected by user. */
|
||||
if ((rel->flag & RELATION_FLAG_FLUSH_USER_EDIT_ONLY) &&
|
||||
(op_node->flag & DEPSOP_FLAG_USER_MODIFIED) == 0)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
OperationNode *to_node = static_cast<OperationNode *>(rel->to);
|
||||
/* Always flush flushable flags, so children always know what happened
|
||||
* to their parents. */
|
||||
to_node->flag |= (op_node->flag & DEPSOP_FLAG_FLUSH);
|
||||
/* Flush update over the relation, if it was not flushed yet. */
|
||||
if (to_node->scheduled) {
|
||||
continue;
|
||||
}
|
||||
if (result != nullptr) {
|
||||
queue->push_front(to_node);
|
||||
}
|
||||
else {
|
||||
result = to_node;
|
||||
}
|
||||
to_node->scheduled = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/* NOTE: It will also accumulate flags from changed components. */
|
||||
void flush_editors_id_update(Depsgraph *graph, const DEGEditorUpdateContext *update_ctx)
|
||||
{
|
||||
for (IDNode *id_node : graph->id_nodes) {
|
||||
if (id_node->custom_flags != ID_STATE_MODIFIED) {
|
||||
continue;
|
||||
}
|
||||
DEG_graph_id_type_tag(reinterpret_cast<::blender::Depsgraph *>(graph),
|
||||
GS(id_node->id_orig->name));
|
||||
/* TODO(sergey): Do we need to pass original or evaluated ID here? */
|
||||
ID *id_orig = id_node->id_orig;
|
||||
ID *id_cow = id_node->id_cow;
|
||||
/* Gather recalc flags from all changed components. */
|
||||
for (ComponentNode *comp_node : id_node->components.values()) {
|
||||
if (comp_node->custom_flags != COMPONENT_STATE_DONE) {
|
||||
continue;
|
||||
}
|
||||
DepsNodeFactory *factory = type_get_factory(comp_node->type);
|
||||
BLI_assert(factory != nullptr);
|
||||
id_cow->recalc |= factory->id_recalc_tag();
|
||||
}
|
||||
DEG_DEBUG_PRINTF((blender::Depsgraph *)graph,
|
||||
EVAL,
|
||||
"Accumulated recalc bits for %s: %u\n",
|
||||
id_orig->name,
|
||||
uint(id_cow->recalc));
|
||||
|
||||
/* Inform editors. Only if the data-block is being evaluated a second
|
||||
* time, to distinguish between user edits and initial evaluation when
|
||||
* the data-block becomes visible.
|
||||
*
|
||||
* TODO: image data-blocks do not use copy-on-eval, so might not be detected
|
||||
* correctly. */
|
||||
if (deg_eval_copy_is_expanded(id_cow)) {
|
||||
if (graph->is_active && id_node->is_user_modified) {
|
||||
deg_editors_id_update(update_ctx, id_orig);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef INVALIDATE_ON_FLUSH
|
||||
void invalidate_tagged_evaluated_transform(ID *id)
|
||||
{
|
||||
const ID_Type id_type = GS(id->name);
|
||||
switch (id_type) {
|
||||
case ID_OB: {
|
||||
Object *object = (Object *)id;
|
||||
std::fill_n((float *)object->object_to_world().ptr(), 16, NAN);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void invalidate_tagged_evaluated_geometry(ID *id)
|
||||
{
|
||||
const ID_Type id_type = GS(id->name);
|
||||
switch (id_type) {
|
||||
case ID_OB: {
|
||||
Object *object = (Object *)id;
|
||||
BKE_object_free_derived_caches(object);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
void invalidate_tagged_evaluated_data(Depsgraph *graph)
|
||||
{
|
||||
#ifdef INVALIDATE_ON_FLUSH
|
||||
for (IDNode *id_node : graph->id_nodes) {
|
||||
if (id_node->custom_flags != ID_STATE_MODIFIED) {
|
||||
continue;
|
||||
}
|
||||
ID *id_cow = id_node->id_cow;
|
||||
if (!deg_eval_copy_is_expanded(id_cow)) {
|
||||
continue;
|
||||
}
|
||||
for (ComponentNode *comp_node : id_node->components.values()) {
|
||||
if (comp_node->custom_flags != COMPONENT_STATE_DONE) {
|
||||
continue;
|
||||
}
|
||||
switch (comp_node->type) {
|
||||
case ID_RECALC_TRANSFORM:
|
||||
invalidate_tagged_evaluated_transform(id_cow);
|
||||
break;
|
||||
case ID_RECALC_GEOMETRY:
|
||||
invalidate_tagged_evaluated_geometry(id_cow);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
#else
|
||||
(void)graph;
|
||||
#endif
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
void deg_graph_flush_updates(Depsgraph *graph)
|
||||
{
|
||||
/* Sanity checks. */
|
||||
BLI_assert(graph != nullptr);
|
||||
Main *bmain = graph->bmain;
|
||||
|
||||
graph->time_source->flush_update_tag(graph);
|
||||
|
||||
/* Nothing to update, early out. */
|
||||
if (graph->entry_tags.is_empty()) {
|
||||
return;
|
||||
}
|
||||
/* Reset all flags, get ready for the flush. */
|
||||
flush_prepare(graph);
|
||||
/* Starting from the tagged "entry" nodes, flush outwards. */
|
||||
FlushQueue queue;
|
||||
flush_schedule_entrypoints(graph, &queue);
|
||||
/* Prepare update context for editors. */
|
||||
DEGEditorUpdateContext update_ctx;
|
||||
update_ctx.bmain = bmain;
|
||||
update_ctx.depsgraph = reinterpret_cast<::blender::Depsgraph *>(graph);
|
||||
update_ctx.scene = graph->scene;
|
||||
update_ctx.view_layer = graph->view_layer;
|
||||
/* Do actual flush. */
|
||||
while (!queue.empty()) {
|
||||
OperationNode *op_node = queue.front();
|
||||
queue.pop_front();
|
||||
while (op_node != nullptr) {
|
||||
/* Tag operation as required for update. */
|
||||
op_node->flag |= DEPSOP_FLAG_NEEDS_UPDATE;
|
||||
/* Inform corresponding ID and component nodes about the change. */
|
||||
ComponentNode *comp_node = op_node->owner;
|
||||
IDNode *id_node = comp_node->owner;
|
||||
flush_handle_id_node(id_node);
|
||||
flush_handle_component_node(id_node, comp_node, &queue);
|
||||
/* Flush to nodes along links. */
|
||||
op_node = flush_schedule_children(op_node, &queue);
|
||||
}
|
||||
}
|
||||
/* Inform editors about all changes. */
|
||||
flush_editors_id_update(graph, &update_ctx);
|
||||
/* Reset evaluation result tagged which is tagged for update to some state
|
||||
* which is obvious to catch. */
|
||||
invalidate_tagged_evaluated_data(graph);
|
||||
}
|
||||
|
||||
void deg_graph_clear_tags(Depsgraph *graph)
|
||||
{
|
||||
/* Clear any entry tags which haven't been flushed. */
|
||||
graph->entry_tags.clear();
|
||||
|
||||
graph->time_source->tagged_for_update = false;
|
||||
}
|
||||
|
||||
} // namespace blender::deg
|
||||
@@ -0,0 +1,27 @@
|
||||
/* SPDX-FileCopyrightText: 2013 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*
|
||||
* Core routines for how the Depsgraph works.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
struct Depsgraph;
|
||||
|
||||
/**
|
||||
* Flush updates from tagged nodes outwards until all affected nodes are tagged.
|
||||
*/
|
||||
void deg_graph_flush_updates(struct Depsgraph *graph);
|
||||
|
||||
/**
|
||||
* Clear tags from all operation nodes.
|
||||
*/
|
||||
void deg_graph_clear_tags(struct Depsgraph *graph);
|
||||
|
||||
} // namespace blender::deg
|
||||
@@ -0,0 +1,96 @@
|
||||
/* SPDX-FileCopyrightText: 2017 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#include "intern/eval/deg_eval_runtime_backup.h"
|
||||
|
||||
#include "intern/eval/deg_eval_copy_on_write.h"
|
||||
|
||||
#include "DRW_engine.hh"
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
RuntimeBackup::RuntimeBackup(const Depsgraph *depsgraph)
|
||||
: have_backup(false),
|
||||
id_data({nullptr}),
|
||||
animation_backup(depsgraph),
|
||||
scene_backup(depsgraph),
|
||||
sound_backup(depsgraph),
|
||||
object_backup(depsgraph),
|
||||
movieclip_backup(depsgraph),
|
||||
volume_backup(depsgraph)
|
||||
{
|
||||
}
|
||||
|
||||
void RuntimeBackup::init_from_id(ID *id)
|
||||
{
|
||||
if (!deg_eval_copy_is_expanded(id)) {
|
||||
return;
|
||||
}
|
||||
have_backup = true;
|
||||
|
||||
/* Clear, so freeing the expanded data doesn't touch this Python reference. */
|
||||
id_data.py_instance = id->py_instance;
|
||||
id->py_instance = nullptr;
|
||||
|
||||
animation_backup.init_from_id(id);
|
||||
|
||||
const ID_Type id_type = GS(id->name);
|
||||
switch (id_type) {
|
||||
case ID_OB:
|
||||
object_backup.init_from_object(reinterpret_cast<Object *>(id));
|
||||
break;
|
||||
case ID_SCE:
|
||||
scene_backup.init_from_scene(reinterpret_cast<Scene *>(id));
|
||||
break;
|
||||
case ID_SO:
|
||||
sound_backup.init_from_sound(reinterpret_cast<bSound *>(id));
|
||||
break;
|
||||
case ID_MC:
|
||||
movieclip_backup.init_from_movieclip(reinterpret_cast<MovieClip *>(id));
|
||||
break;
|
||||
case ID_VO:
|
||||
volume_backup.init_from_volume(reinterpret_cast<Volume *>(id));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void RuntimeBackup::restore_to_id(ID *id)
|
||||
{
|
||||
if (!have_backup) {
|
||||
return;
|
||||
}
|
||||
|
||||
id->py_instance = id_data.py_instance;
|
||||
|
||||
animation_backup.restore_to_id(id);
|
||||
|
||||
const ID_Type id_type = GS(id->name);
|
||||
switch (id_type) {
|
||||
case ID_OB:
|
||||
object_backup.restore_to_object(reinterpret_cast<Object *>(id));
|
||||
break;
|
||||
case ID_SCE:
|
||||
scene_backup.restore_to_scene(reinterpret_cast<Scene *>(id));
|
||||
break;
|
||||
case ID_SO:
|
||||
sound_backup.restore_to_sound(reinterpret_cast<bSound *>(id));
|
||||
break;
|
||||
case ID_MC:
|
||||
movieclip_backup.restore_to_movieclip(reinterpret_cast<MovieClip *>(id));
|
||||
break;
|
||||
case ID_VO:
|
||||
volume_backup.restore_to_volume(reinterpret_cast<Volume *>(id));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::deg
|
||||
@@ -0,0 +1,58 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_ID.h"
|
||||
|
||||
#include "intern/eval/deg_eval_runtime_backup_animation.h"
|
||||
#include "intern/eval/deg_eval_runtime_backup_movieclip.h"
|
||||
#include "intern/eval/deg_eval_runtime_backup_object.h"
|
||||
#include "intern/eval/deg_eval_runtime_backup_scene.h"
|
||||
#include "intern/eval/deg_eval_runtime_backup_sound.h"
|
||||
#include "intern/eval/deg_eval_runtime_backup_volume.h"
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
struct Depsgraph;
|
||||
|
||||
class RuntimeBackup {
|
||||
public:
|
||||
explicit RuntimeBackup(const Depsgraph *depsgraph);
|
||||
|
||||
/* NOTE: Will reset all runtime fields which has been backed up to nullptr. */
|
||||
void init_from_id(ID *id);
|
||||
|
||||
/* Restore fields to the given ID. */
|
||||
void restore_to_id(ID *id);
|
||||
|
||||
/* Denotes whether init_from_id did put anything into the backup storage.
|
||||
* This will not be the case when init_from_id() is called for an ID which has never been
|
||||
* copied-on-eval. In this case there is no need to backup or restore anything.
|
||||
*
|
||||
* It also allows to have restore() logic to be symmetrical to init() without need to worry
|
||||
* that init() might not have happened.
|
||||
*
|
||||
* In practice this is used by audio system to lock audio while scene is going through
|
||||
* copy-on-evaluation mechanism. */
|
||||
bool have_backup;
|
||||
|
||||
/* Struct members of the ID pointer. */
|
||||
struct {
|
||||
void *py_instance;
|
||||
} id_data;
|
||||
|
||||
AnimationBackup animation_backup;
|
||||
SceneBackup scene_backup;
|
||||
SoundBackup sound_backup;
|
||||
ObjectRuntimeBackup object_backup;
|
||||
MovieClipBackup movieclip_backup;
|
||||
VolumeBackup volume_backup;
|
||||
};
|
||||
|
||||
} // namespace blender::deg
|
||||
@@ -0,0 +1,101 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#include "intern/eval/deg_eval_runtime_backup_animation.h"
|
||||
|
||||
#include "DNA_anim_types.h"
|
||||
|
||||
#include "BKE_anim_data.hh"
|
||||
#include "BKE_animsys.h"
|
||||
|
||||
#include "RNA_access.hh"
|
||||
#include "RNA_types.hh"
|
||||
|
||||
#include "intern/depsgraph.hh"
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
AnimationValueBackup::AnimationValueBackup(const std::string &rna_path,
|
||||
int array_index,
|
||||
float value)
|
||||
: rna_path(rna_path), array_index(array_index), value(value)
|
||||
{
|
||||
}
|
||||
|
||||
AnimationBackup::AnimationBackup(const Depsgraph *depsgraph)
|
||||
{
|
||||
meed_value_backup = !depsgraph->is_active;
|
||||
reset();
|
||||
}
|
||||
|
||||
void AnimationBackup::reset() {}
|
||||
|
||||
void AnimationBackup::init_from_id(ID *id)
|
||||
{
|
||||
/* NOTE: This animation backup nicely preserves values which are animated and
|
||||
* are not touched by frame/depsgraph post_update handler.
|
||||
*
|
||||
* But it makes it impossible to have user edits to animated properties: for
|
||||
* example, translation of object with animated location will not work with
|
||||
* the current version of backup. */
|
||||
return;
|
||||
|
||||
PointerRNA id_pointer_rna = RNA_id_pointer_create(id);
|
||||
BKE_fcurves_id_cb(id, [&](ID *cb_id, FCurve *fcurve) {
|
||||
if (fcurve->rna_path == nullptr || fcurve->rna_path[0] == '\0') {
|
||||
return;
|
||||
}
|
||||
if (id != cb_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
/* Resolve path to the property. */
|
||||
PathResolvedRNA resolved_rna;
|
||||
if (!BKE_animsys_rna_path_resolve(
|
||||
&id_pointer_rna, fcurve->rna_path, fcurve->array_index, &resolved_rna))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Read property value. */
|
||||
float value;
|
||||
if (!BKE_animsys_read_from_rna_path(&resolved_rna, &value)) {
|
||||
return;
|
||||
}
|
||||
|
||||
this->values_backup.append({fcurve->rna_path, fcurve->array_index, value});
|
||||
});
|
||||
}
|
||||
|
||||
void AnimationBackup::restore_to_id(ID *id)
|
||||
{
|
||||
return;
|
||||
|
||||
PointerRNA id_pointer_rna = RNA_id_pointer_create(id);
|
||||
for (const AnimationValueBackup &value_backup : values_backup) {
|
||||
/* Resolve path to the property.
|
||||
*
|
||||
* NOTE: Do it again (after storing), since the sub-data pointers might be
|
||||
* changed after copy-on-evaluation. */
|
||||
PathResolvedRNA resolved_rna;
|
||||
if (!BKE_animsys_rna_path_resolve(&id_pointer_rna,
|
||||
value_backup.rna_path.c_str(),
|
||||
value_backup.array_index,
|
||||
&resolved_rna))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* Write property value. */
|
||||
if (!BKE_animsys_write_to_rna_path(&resolved_rna, value_backup.value)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::deg
|
||||
@@ -0,0 +1,54 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "BLI_vector.hh"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ID;
|
||||
|
||||
namespace deg {
|
||||
|
||||
struct Depsgraph;
|
||||
|
||||
class AnimationValueBackup {
|
||||
public:
|
||||
AnimationValueBackup() = default;
|
||||
AnimationValueBackup(const std::string &rna_path, int array_index, float value);
|
||||
|
||||
AnimationValueBackup(const AnimationValueBackup &other) = default;
|
||||
AnimationValueBackup(AnimationValueBackup &&other) noexcept = default;
|
||||
|
||||
AnimationValueBackup &operator=(const AnimationValueBackup &other) = default;
|
||||
AnimationValueBackup &operator=(AnimationValueBackup &&other) = default;
|
||||
|
||||
std::string rna_path;
|
||||
int array_index;
|
||||
float value;
|
||||
};
|
||||
|
||||
/* Backup of animated properties values. */
|
||||
class AnimationBackup {
|
||||
public:
|
||||
AnimationBackup(const Depsgraph *depsgraph);
|
||||
|
||||
void reset();
|
||||
|
||||
void init_from_id(ID *id);
|
||||
void restore_to_id(ID *id);
|
||||
|
||||
bool meed_value_backup;
|
||||
Vector<AnimationValueBackup> values_backup;
|
||||
};
|
||||
|
||||
} // namespace deg
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,20 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#include "intern/eval/deg_eval_runtime_backup_modifier.h"
|
||||
|
||||
#include "DNA_modifier_types.h"
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
ModifierDataBackup::ModifierDataBackup(ModifierData *modifier_data)
|
||||
: type(modifier_data->type), runtime(modifier_data->runtime)
|
||||
{
|
||||
}
|
||||
|
||||
} // namespace blender::deg
|
||||
@@ -0,0 +1,28 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "DNA_modifier_types.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct ModifierData;
|
||||
|
||||
namespace deg {
|
||||
|
||||
class ModifierDataBackup {
|
||||
public:
|
||||
explicit ModifierDataBackup(ModifierData *modifier_data);
|
||||
|
||||
ModifierType type;
|
||||
void *runtime;
|
||||
};
|
||||
|
||||
} // namespace deg
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,44 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#include "intern/eval/deg_eval_runtime_backup_movieclip.h"
|
||||
|
||||
#include "DNA_movieclip_types.h"
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
MovieClipBackup::MovieClipBackup(const Depsgraph * /*depsgraph*/)
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
void MovieClipBackup::reset()
|
||||
{
|
||||
anim = nullptr;
|
||||
cache = nullptr;
|
||||
}
|
||||
|
||||
void MovieClipBackup::init_from_movieclip(MovieClip *movieclip)
|
||||
{
|
||||
anim = movieclip->anim;
|
||||
cache = movieclip->cache;
|
||||
/* Clear pointers stored in the movie clip, so they are not freed when copied-on-written
|
||||
* datablock is freed for re-allocation. */
|
||||
movieclip->anim = nullptr;
|
||||
movieclip->cache = nullptr;
|
||||
}
|
||||
|
||||
void MovieClipBackup::restore_to_movieclip(MovieClip *movieclip)
|
||||
{
|
||||
movieclip->anim = anim;
|
||||
movieclip->cache = cache;
|
||||
|
||||
reset();
|
||||
}
|
||||
|
||||
} // namespace blender::deg
|
||||
@@ -0,0 +1,36 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct MovieClip;
|
||||
struct MovieClipCache;
|
||||
struct MovieReader;
|
||||
|
||||
namespace deg {
|
||||
|
||||
struct Depsgraph;
|
||||
|
||||
/* Backup of movie clip runtime data. */
|
||||
class MovieClipBackup {
|
||||
public:
|
||||
MovieClipBackup(const Depsgraph *depsgraph);
|
||||
|
||||
void reset();
|
||||
|
||||
void init_from_movieclip(MovieClip *movieclip);
|
||||
void restore_to_movieclip(MovieClip *movieclip);
|
||||
|
||||
struct MovieReader *anim;
|
||||
struct MovieClipCache *cache;
|
||||
};
|
||||
|
||||
} // namespace deg
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,192 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#include "BLI_session_uid.h"
|
||||
|
||||
#include "intern/eval/deg_eval_runtime_backup_object.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
#include "DNA_mesh_types.h"
|
||||
|
||||
#include "BLI_listbase.h"
|
||||
|
||||
#include "BKE_action.hh"
|
||||
#include "BKE_light_linking.h"
|
||||
#include "BKE_mesh_types.hh"
|
||||
#include "BKE_modifier.hh"
|
||||
#include "BKE_object.hh"
|
||||
#include "BKE_object_types.hh"
|
||||
|
||||
namespace blender::deg {
|
||||
|
||||
ObjectRuntimeBackup::ObjectRuntimeBackup(const Depsgraph * /*depsgraph*/)
|
||||
: base_flag(0), base_local_view_bits(0)
|
||||
{
|
||||
/* TODO(sergey): Use something like BKE_object_runtime_reset(). */
|
||||
runtime = {};
|
||||
}
|
||||
|
||||
void ObjectRuntimeBackup::init_from_object(Object *object)
|
||||
{
|
||||
/* Store evaluated mesh and curve_cache, and make sure we don't free it. */
|
||||
runtime = *object->runtime;
|
||||
if (object->light_linking) {
|
||||
light_linking_runtime = object->light_linking->runtime;
|
||||
}
|
||||
BKE_object_runtime_reset(object);
|
||||
/* Keep bounding-box (for now at least). */
|
||||
object->runtime->bounds_eval = runtime.bounds_eval;
|
||||
/* Object update will override actual object->data to an evaluated version.
|
||||
* Need to make sure we don't have data set to evaluated one before free
|
||||
* anything. */
|
||||
object->data = runtime.data_orig;
|
||||
/* Make a backup of base flags. */
|
||||
base_flag = object->base_flag;
|
||||
base_local_view_bits = object->base_local_view_bits;
|
||||
/* Backup runtime data of all modifiers. */
|
||||
backup_modifier_runtime_data(object);
|
||||
/* Backup runtime data of all pose channels. */
|
||||
backup_pose_channel_runtime_data(object);
|
||||
}
|
||||
|
||||
void ObjectRuntimeBackup::backup_modifier_runtime_data(Object *object)
|
||||
{
|
||||
for (ModifierData &modifier_data : object->modifiers) {
|
||||
if (modifier_data.runtime == nullptr) {
|
||||
continue;
|
||||
}
|
||||
|
||||
modifier_runtime_data.add(modifier_data.persistent_uid, ModifierDataBackup(&modifier_data));
|
||||
modifier_data.runtime = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectRuntimeBackup::backup_pose_channel_runtime_data(Object *object)
|
||||
{
|
||||
if (object->pose != nullptr) {
|
||||
for (bPoseChannel &pchan : object->pose->chanbase) {
|
||||
const SessionUID &session_uid = pchan.runtime.session_uid;
|
||||
BLI_assert(BLI_session_uid_is_generated(&session_uid));
|
||||
|
||||
pose_channel_runtime_data.add(session_uid, pchan.runtime);
|
||||
BKE_pose_channel_runtime_reset(&pchan.runtime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectRuntimeBackup::restore_to_object(Object *object)
|
||||
{
|
||||
ID *data_orig = object->runtime->data_orig;
|
||||
ID *data_eval = runtime.data_eval;
|
||||
std::optional<Bounds<float3>> bounds = object->runtime->bounds_eval;
|
||||
SculptSession *sculpt_session = object->runtime->sculpt_session;
|
||||
*object->runtime = runtime;
|
||||
object->runtime->data_orig = data_orig;
|
||||
object->runtime->bounds_eval = bounds;
|
||||
object->runtime->sculpt_session = sculpt_session;
|
||||
if (ELEM(object->type, OB_MESH, OB_LATTICE, OB_CURVES_LEGACY, OB_FONT) && data_eval != nullptr) {
|
||||
if (object->id.recalc & ID_RECALC_GEOMETRY) {
|
||||
/* If geometry is tagged for update it means, that part of
|
||||
* evaluated mesh are not valid anymore. In this case we can not
|
||||
* have any "persistent" pointers to point to an invalid data.
|
||||
*
|
||||
* We restore object's data datablock to an original copy of
|
||||
* that datablock. */
|
||||
object->data = data_orig;
|
||||
|
||||
/* After that, immediately free the invalidated caches. */
|
||||
BKE_object_free_derived_caches(object);
|
||||
}
|
||||
else {
|
||||
/* Do same thing as object update: override actual object data pointer with evaluated
|
||||
* datablock, but only if the evaluated data has the same type as the original data. */
|
||||
if (GS(((ID *)object->data)->name) == GS(data_eval->name)) {
|
||||
object->data = data_eval;
|
||||
}
|
||||
|
||||
/* Evaluated mesh simply copied edit_mesh pointer from
|
||||
* original mesh during update, need to make sure no dead
|
||||
* pointers are left behind. */
|
||||
if (object->type == OB_MESH) {
|
||||
Mesh *mesh_eval = id_cast<Mesh *>(data_eval);
|
||||
Mesh *mesh_orig = id_cast<Mesh *>(data_orig);
|
||||
mesh_eval->runtime->edit_mesh = mesh_orig->runtime->edit_mesh;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (ELEM(object->type, OB_CURVES, OB_POINTCLOUD, OB_VOLUME, OB_GREASE_PENCIL)) {
|
||||
if (object->id.recalc & ID_RECALC_GEOMETRY) {
|
||||
/* Free evaluated caches. */
|
||||
object->data = data_orig;
|
||||
BKE_object_free_derived_caches(object);
|
||||
}
|
||||
else {
|
||||
object->data = object->runtime->data_eval;
|
||||
}
|
||||
}
|
||||
|
||||
if (light_linking_runtime) {
|
||||
/* Lazily allocate light linking on the evaluated object for the cases when the object is only
|
||||
* a receiver or a blocker and does not need its own LightLinking on the original object. */
|
||||
BKE_light_linking_ensure(object);
|
||||
object->light_linking->runtime = *light_linking_runtime;
|
||||
}
|
||||
|
||||
object->base_flag = base_flag;
|
||||
object->base_local_view_bits = base_local_view_bits;
|
||||
|
||||
/* Restore modifier's runtime data.
|
||||
* NOTE: Data of unused modifiers will be freed there. */
|
||||
restore_modifier_runtime_data(object);
|
||||
restore_pose_channel_runtime_data(object);
|
||||
}
|
||||
|
||||
void ObjectRuntimeBackup::restore_modifier_runtime_data(Object *object)
|
||||
{
|
||||
for (ModifierData &modifier_data : object->modifiers) {
|
||||
std::optional<ModifierDataBackup> backup = modifier_runtime_data.pop_try(
|
||||
modifier_data.persistent_uid);
|
||||
if (backup.has_value()) {
|
||||
modifier_data.runtime = backup->runtime;
|
||||
}
|
||||
}
|
||||
|
||||
for (ModifierDataBackup &backup : modifier_runtime_data.values()) {
|
||||
const ModifierTypeInfo *modifier_type_info = BKE_modifier_get_info(backup.type);
|
||||
BLI_assert(modifier_type_info != nullptr);
|
||||
modifier_type_info->free_runtime_data(backup.runtime);
|
||||
|
||||
if (backup.type == eModifierType_Subsurf) {
|
||||
if (object->type == OB_MESH) {
|
||||
Mesh *mesh = id_cast<Mesh *>(object->data);
|
||||
if (mesh->runtime->subsurf_runtime_data == backup.runtime) {
|
||||
mesh->runtime->subsurf_runtime_data = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ObjectRuntimeBackup::restore_pose_channel_runtime_data(Object *object)
|
||||
{
|
||||
if (object->pose != nullptr) {
|
||||
for (bPoseChannel &pchan : object->pose->chanbase) {
|
||||
const SessionUID &session_uid = pchan.runtime.session_uid;
|
||||
std::optional<bPoseChannel_Runtime> runtime = pose_channel_runtime_data.pop_try(session_uid);
|
||||
if (runtime.has_value()) {
|
||||
pchan.runtime = *runtime;
|
||||
}
|
||||
}
|
||||
}
|
||||
for (bPoseChannel_Runtime &runtime : pose_channel_runtime_data.values()) {
|
||||
BKE_pose_channel_runtime_free(&runtime);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace blender::deg
|
||||
@@ -0,0 +1,56 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <optional>
|
||||
|
||||
#include "DNA_object_types.h"
|
||||
#include "DNA_session_uid_types.h"
|
||||
|
||||
#include "BKE_object_types.hh"
|
||||
|
||||
#include "BLI_map.hh"
|
||||
|
||||
#include "intern/eval/deg_eval_runtime_backup_modifier.h"
|
||||
|
||||
namespace blender {
|
||||
|
||||
struct Object;
|
||||
|
||||
namespace deg {
|
||||
|
||||
struct Depsgraph;
|
||||
|
||||
class ObjectRuntimeBackup {
|
||||
public:
|
||||
ObjectRuntimeBackup(const Depsgraph *depsgraph);
|
||||
|
||||
/* Make a backup of object's evaluation runtime data, additionally
|
||||
* make object to be safe for free without invalidating backed up
|
||||
* pointers. */
|
||||
void init_from_object(Object *object);
|
||||
void backup_modifier_runtime_data(Object *object);
|
||||
void backup_pose_channel_runtime_data(Object *object);
|
||||
|
||||
/* Restore all fields to the given object. */
|
||||
void restore_to_object(Object *object);
|
||||
/* NOTE: Will free all runtime data which has not been restored. */
|
||||
void restore_modifier_runtime_data(Object *object);
|
||||
void restore_pose_channel_runtime_data(Object *object);
|
||||
|
||||
bke::ObjectRuntime runtime;
|
||||
std::optional<LightLinkingRuntime> light_linking_runtime;
|
||||
short base_flag;
|
||||
unsigned short base_local_view_bits;
|
||||
Map<int, ModifierDataBackup> modifier_runtime_data;
|
||||
Map<SessionUID, bPoseChannel_Runtime> pose_channel_runtime_data;
|
||||
};
|
||||
|
||||
} // namespace deg
|
||||
} // namespace blender
|
||||
@@ -0,0 +1,9 @@
|
||||
/* SPDX-FileCopyrightText: 2019 Blender Authors
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0-or-later */
|
||||
|
||||
/** \file
|
||||
* \ingroup depsgraph
|
||||
*/
|
||||
|
||||
#include "intern/eval/deg_eval_runtime_backup_pose.h"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user