Simulation: add depsgraph relations for ids referenced by node tree

I'll really have to refactor `ntreeUpdateTree` soon to avoid scanning
all node trees multiple times.
This commit is contained in:
Jacques Lucke 2020-07-23 12:09:28 +02:00
parent 6596891121
commit 634585aa68
13 changed files with 287 additions and 99 deletions

View File

@ -32,6 +32,7 @@ void *BKE_simulation_add(struct Main *bmain, const char *name);
void BKE_simulation_data_update(struct Depsgraph *depsgraph,
struct Scene *scene,
struct Simulation *simulation);
void BKE_simulation_update_dependencies(struct Simulation *simulation, struct Main *bmain);
SimulationState *BKE_simulation_state_add(Simulation *simulation,
const char *type,

View File

@ -61,6 +61,7 @@
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_simulation.h"
#include "BLI_ghash.h"
#include "BLI_threads.h"
@ -3638,6 +3639,16 @@ void ntreeUpdateAllUsers(Main *main, ID *ngroup)
FOREACH_NODETREE_END;
}
static void ntreeUpdateSimulationDependencies(Main *main, bNodeTree *simulation_ntree)
{
FOREACH_NODETREE_BEGIN (main, ntree, owner_id) {
if (GS(owner_id->name) == ID_SIM && ntree == simulation_ntree) {
BKE_simulation_update_dependencies((Simulation *)owner_id, main);
}
}
FOREACH_NODETREE_END;
}
void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
{
bNode *node;
@ -3695,6 +3706,11 @@ void ntreeUpdateTree(Main *bmain, bNodeTree *ntree)
ntree_validate_links(ntree);
}
if (bmain != NULL && ntree->typeinfo == ntreeType_Simulation &&
(ntree->id.flag & LIB_EMBEDDED_DATA)) {
ntreeUpdateSimulationDependencies(bmain, ntree);
}
/* clear update flags */
for (node = ntree->nodes.first; node; node = node->next) {
node->update = 0;

View File

@ -285,6 +285,14 @@ void BKE_simulation_data_update(Depsgraph *depsgraph, Scene *scene, Simulation *
blender::sim::update_simulation_in_depsgraph(depsgraph, scene, simulation);
}
void BKE_simulation_update_dependencies(Simulation *simulation, Main *bmain)
{
bool dependencies_changed = blender::sim::update_simulation_dependencies(simulation);
if (dependencies_changed) {
DEG_relations_tag_update(bmain);
}
}
using StateTypeMap = blender::Map<std::string, std::unique_ptr<SimulationStateType>>;
template<typename T>

View File

@ -2634,6 +2634,25 @@ void DepsgraphRelationBuilder::build_simulation(Simulation *simulation)
OperationKey nodetree_key(
&simulation->nodetree->id, NodeType::PARAMETERS, OperationCode::PARAMETERS_EXIT);
add_relation(nodetree_key, simulation_eval_key, "NodeTree -> Simulation", 0);
LISTBASE_FOREACH (
PersistentDataHandleItem *, handle_item, &simulation->persistent_data_handles) {
if (handle_item->id == nullptr) {
continue;
}
build_id(handle_item->id);
if (GS(handle_item->id->name) == ID_OB) {
Object *object = (Object *)handle_item->id;
if (handle_item->flag & SIM_HANDLE_DEPENDS_ON_TRANSFORM) {
ComponentKey object_transform_key(&object->id, NodeType::TRANSFORM);
add_relation(object_transform_key, simulation_eval_key, "Object Transform -> Simulation");
}
if (handle_item->flag & SIM_HANDLE_DEPENDS_ON_GEOMETRY) {
ComponentKey object_geometry_key(&object->id, NodeType::GEOMETRY);
add_relation(object_geometry_key, simulation_eval_key, "Object Geometry -> Simulation");
}
}
}
}
void DepsgraphRelationBuilder::build_scene_sequencer(Scene *scene)

View File

@ -72,7 +72,7 @@ typedef struct PersistentDataHandleItem {
struct PersistentDataHandleItem *prev;
struct ID *id;
int handle;
char _pad[4];
int flag;
} PersistentDataHandleItem;
/* Simulation.flag */
@ -80,6 +80,12 @@ enum {
SIM_DS_EXPAND = (1 << 0),
};
/* PersistentDataHandleItem.flag */
enum {
SIM_HANDLE_DEPENDS_ON_TRANSFORM = (1 << 0),
SIM_HANDLE_DEPENDS_ON_GEOMETRY = (1 << 1),
};
#define SIM_TYPE_NAME_PARTICLE_SIMULATION "Particle Simulation"
#define SIM_TYPE_NAME_PARTICLE_MESH_EMITTER "Particle Mesh Emitter"

View File

@ -38,6 +38,7 @@
#include "BKE_animsys.h"
#include "BKE_image.h"
#include "BKE_node.h"
#include "BKE_simulation.h"
#include "BKE_texture.h"
#include "RNA_access.h"
@ -2848,6 +2849,14 @@ static void rna_NodeSocketStandard_value_update(struct bContext *C, PointerRNA *
}
}
static void rna_NodeSocketStandard_value_and_relation_update(struct bContext *C, PointerRNA *ptr)
{
rna_NodeSocketStandard_value_update(C, ptr);
bNodeTree *ntree = (bNodeTree *)ptr->owner_id;
Main *bmain = CTX_data_main(C);
ntreeUpdateTree(bmain, ntree);
}
/* ******** Node Types ******** */
static void rna_NodeInternalSocketTemplate_name_get(PointerRNA *ptr, char *value)
@ -8862,7 +8871,8 @@ static void rna_def_node_socket_object(BlenderRNA *brna,
RNA_def_property_pointer_sdna(prop, NULL, "value");
RNA_def_property_struct_type(prop, "Object");
RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update");
RNA_def_property_update(
prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
/* socket interface */
@ -8896,7 +8906,8 @@ static void rna_def_node_socket_image(BlenderRNA *brna,
RNA_def_property_pointer_sdna(prop, NULL, "value");
RNA_def_property_struct_type(prop, "Image");
RNA_def_property_ui_text(prop, "Default Value", "Input value used for unconnected socket");
RNA_def_property_update(prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_update");
RNA_def_property_update(
prop, NC_NODE | NA_EDITED, "rna_NodeSocketStandard_value_and_relation_update");
RNA_def_property_flag(prop, PROP_EDITABLE | PROP_ID_REFCOUNT | PROP_CONTEXT_UPDATE);
/* socket interface */

View File

@ -278,6 +278,7 @@ set(SRC
intern/node_common.c
intern/node_exec.c
intern/node_socket.cc
intern/node_tree_dependencies.cc
intern/node_tree_multi_function.cc
intern/node_tree_ref.cc
intern/node_util.c
@ -292,6 +293,7 @@ set(SRC
NOD_composite.h
NOD_derived_node_tree.hh
NOD_function.h
NOD_node_tree_dependencies.hh
NOD_node_tree_multi_function.hh
NOD_node_tree_ref.hh
NOD_shader.h

View File

@ -0,0 +1,79 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#ifndef __NOD_NODE_TREE_DEPENDENCIES_H__
#define __NOD_NODE_TREE_DEPENDENCIES_H__
#include "BLI_vector_set.hh"
#include "DNA_ID.h"
#include "DNA_object_types.h"
struct bNodeTree;
namespace blender::nodes {
class NodeTreeDependencies {
private:
VectorSet<Object *> transform_deps_;
VectorSet<Object *> geometry_deps_;
VectorSet<ID *> id_deps_;
public:
void add_transform_dependency(Object *object)
{
if (object == nullptr) {
return;
}
transform_deps_.add(object);
id_deps_.add(&object->id);
}
void add_geometry_dependency(Object *object)
{
if (object == nullptr) {
return;
}
geometry_deps_.add(object);
id_deps_.add(&object->id);
}
bool depends_on(ID *id) const
{
return id_deps_.contains(id);
}
Span<Object *> transform_dependencies()
{
return transform_deps_;
}
Span<Object *> geometry_dependencies()
{
return geometry_deps_;
}
Span<ID *> id_dependencies()
{
return id_deps_;
}
};
NodeTreeDependencies find_node_tree_dependencies(bNodeTree &ntree);
} // namespace blender::nodes
#endif /* __NOD_NODE_TREE_DEPENDENCIES_H__ */

View File

@ -0,0 +1,57 @@
/*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include "NOD_node_tree_dependencies.hh"
#include "DNA_node_types.h"
#include "BKE_node.h"
namespace blender::nodes {
static void add_dependencies_of_node_tree(bNodeTree &ntree, NodeTreeDependencies &r_dependencies)
{
/* TODO: Do a bit more sophisticated parsing to see which dependencies are really required. */
LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
LISTBASE_FOREACH (bNodeSocket *, socket, &node->inputs) {
if (socket->type == SOCK_OBJECT) {
Object *object = ((bNodeSocketValueObject *)socket->default_value)->value;
if (object != nullptr) {
r_dependencies.add_transform_dependency(object);
if (object->type == OB_MESH) {
r_dependencies.add_geometry_dependency(object);
}
}
}
}
if (node->type == NODE_GROUP) {
bNodeTree *group = (bNodeTree *)node->id;
if (group != nullptr) {
add_dependencies_of_node_tree(*group, r_dependencies);
}
}
}
}
NodeTreeDependencies find_node_tree_dependencies(bNodeTree &ntree)
{
NodeTreeDependencies dependencies;
add_dependencies_of_node_tree(ntree, dependencies);
return dependencies;
}
} // namespace blender::nodes

View File

@ -27,6 +27,8 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph,
Scene *scene_cow,
Simulation *simulation_cow);
}
bool update_simulation_dependencies(Simulation *simulation);
} // namespace blender::sim
#endif /* __SIM_SIMULATION_UPDATE_HH__ */

View File

@ -436,28 +436,10 @@ static void prepare_particle_attribute_builders(nodes::MFNetworkTreeMap &network
}
}
static void find_used_persistent_data(const nodes::DerivedNodeTree &tree,
UsedPersistentData &r_used_persistent_data)
{
const bNodeSocketType *socktype = nodeSocketTypeFind("NodeSocketObject");
BLI_assert(socktype != nullptr);
for (const nodes::DInputSocket *dsocket : tree.input_sockets()) {
const bNodeSocket *bsocket = dsocket->bsocket();
if (bsocket->typeinfo == socktype) {
Object *object = ((const bNodeSocketValueObject *)bsocket->default_value)->value;
if (object != nullptr) {
r_used_persistent_data.add(DEG_get_original_id(&object->id));
}
}
}
}
void collect_simulation_influences(Simulation &simulation,
ResourceCollector &resources,
SimulationInfluences &r_influences,
RequiredStates &r_required_states,
UsedPersistentData &r_used_persistent_data)
RequiredStates &r_required_states)
{
nodes::NodeTreeRefMap tree_refs;
const nodes::DerivedNodeTree tree{simulation.nodetree, tree_refs};
@ -481,8 +463,6 @@ void collect_simulation_influences(Simulation &simulation,
for (const nodes::DNode *dnode : get_particle_simulation_nodes(tree)) {
r_required_states.add(dnode_to_path(*dnode), SIM_TYPE_NAME_PARTICLE_SIMULATION);
}
find_used_persistent_data(tree, r_used_persistent_data);
}
} // namespace blender::sim

View File

@ -58,29 +58,10 @@ class RequiredStates {
}
};
class UsedPersistentData {
private:
VectorSet<ID *> used_ids_;
public:
void add(ID *id)
{
BLI_assert(id != nullptr);
BLI_assert((id->tag & LIB_TAG_NO_MAIN) == 0);
used_ids_.add(id);
}
const VectorSet<ID *> &used_ids() const
{
return used_ids_;
}
};
void collect_simulation_influences(Simulation &simulation,
ResourceCollector &resources,
SimulationInfluences &r_influences,
RequiredStates &r_required_states,
UsedPersistentData &r_used_persistent_data);
RequiredStates &r_required_states);
} // namespace blender::sim

View File

@ -33,6 +33,8 @@
#include "BLI_set.hh"
#include "BLI_vector.hh"
#include "NOD_node_tree_dependencies.hh"
#include "particle_function.hh"
#include "simulation_collect_influences.hh"
#include "simulation_solver.hh"
@ -90,56 +92,6 @@ static void update_simulation_state_list(Simulation *simulation,
add_missing_states(simulation, required_states);
}
/* TODO: It might be better to do this as part of ntreeUpdateTree, so that the information
* about referenced data blocks is available when building the depsgraph. */
static void update_persistent_data_handles(Simulation &simulation_orig,
const UsedPersistentData &used_persistent_data)
{
Set<ID *> contained_ids;
Set<int> used_handles;
/* Remove handles that have been invalidated. */
LISTBASE_FOREACH_MUTABLE (
PersistentDataHandleItem *, handle_item, &simulation_orig.persistent_data_handles) {
if (handle_item->id == nullptr) {
BLI_remlink(&simulation_orig.persistent_data_handles, handle_item);
MEM_freeN(handle_item);
continue;
}
if (!used_persistent_data.used_ids().contains(handle_item->id)) {
id_us_min(handle_item->id);
BLI_remlink(&simulation_orig.persistent_data_handles, handle_item);
MEM_freeN(handle_item);
continue;
}
contained_ids.add_new(handle_item->id);
used_handles.add_new(handle_item->handle);
}
/* Add new handles that are not in the list yet. */
int next_handle = 0;
for (ID *id : used_persistent_data.used_ids()) {
if (contained_ids.contains(id)) {
continue;
}
/* Find the next available handle. */
while (used_handles.contains(next_handle)) {
next_handle++;
}
used_handles.add_new(next_handle);
PersistentDataHandleItem *handle_item = (PersistentDataHandleItem *)MEM_callocN(
sizeof(*handle_item), AT);
/* Cannot store const pointers in DNA. */
id_us_plus(id);
handle_item->id = id;
handle_item->handle = next_handle;
BLI_addtail(&simulation_orig.persistent_data_handles, handle_item);
}
}
void update_simulation_in_depsgraph(Depsgraph *depsgraph,
Scene *scene_cow,
Simulation *simulation_cow)
@ -159,11 +111,8 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph,
ResourceCollector resources;
SimulationInfluences influences;
RequiredStates required_states;
UsedPersistentData used_persistent_data;
collect_simulation_influences(
*simulation_cow, resources, influences, required_states, used_persistent_data);
update_persistent_data_handles(*simulation_orig, used_persistent_data);
collect_simulation_influences(*simulation_cow, resources, influences, required_states);
bke::PersistentDataHandleMap handle_map;
LISTBASE_FOREACH (
@ -191,4 +140,81 @@ void update_simulation_in_depsgraph(Depsgraph *depsgraph,
}
}
/* Returns true when dependencies have changed. */
bool update_simulation_dependencies(Simulation *simulation)
{
nodes::NodeTreeDependencies dependencies = nodes::find_node_tree_dependencies(
*simulation->nodetree);
ListBase *handle_list = &simulation->persistent_data_handles;
bool dependencies_changed = false;
Map<ID *, PersistentDataHandleItem *> handle_item_by_id;
Map<PersistentDataHandleItem *, int> old_flag_by_handle_item;
Set<int> used_handles;
/* Remove unused handle items and clear flags that are reinitialized later. */
LISTBASE_FOREACH_MUTABLE (PersistentDataHandleItem *, handle_item, handle_list) {
if (dependencies.depends_on(handle_item->id)) {
handle_item_by_id.add_new(handle_item->id, handle_item);
used_handles.add_new(handle_item->handle);
old_flag_by_handle_item.add_new(handle_item, handle_item->flag);
handle_item->flag &= ~(SIM_HANDLE_DEPENDS_ON_TRANSFORM | SIM_HANDLE_DEPENDS_ON_GEOMETRY);
}
else {
if (handle_item->id != nullptr) {
id_us_min(handle_item->id);
}
BLI_remlink(handle_list, handle_item);
MEM_freeN(handle_item);
dependencies_changed = true;
}
}
/* Add handle items for new id dependencies. */
int next_handle = 0;
for (ID *id : dependencies.id_dependencies()) {
handle_item_by_id.lookup_or_add_cb(id, [&]() {
while (used_handles.contains(next_handle)) {
next_handle++;
}
used_handles.add_new(next_handle);
PersistentDataHandleItem *handle_item = (PersistentDataHandleItem *)MEM_callocN(
sizeof(*handle_item), AT);
id_us_plus(id);
handle_item->id = id;
handle_item->handle = next_handle;
BLI_addtail(handle_list, handle_item);
return handle_item;
});
}
/* Set appropriate dependency flags. */
for (Object *object : dependencies.transform_dependencies()) {
PersistentDataHandleItem *handle_item = handle_item_by_id.lookup(&object->id);
handle_item->flag |= SIM_HANDLE_DEPENDS_ON_TRANSFORM;
}
for (Object *object : dependencies.geometry_dependencies()) {
PersistentDataHandleItem *handle_item = handle_item_by_id.lookup(&object->id);
handle_item->flag |= SIM_HANDLE_DEPENDS_ON_GEOMETRY;
}
if (!dependencies_changed) {
/* Check if any flags have changed. */
LISTBASE_FOREACH (PersistentDataHandleItem *, handle_item, handle_list) {
int old_flag = old_flag_by_handle_item.lookup_default(handle_item, 0);
int new_flag = handle_item->flag;
if (old_flag != new_flag) {
dependencies_changed = true;
break;
}
}
}
return dependencies_changed;
}
} // namespace blender::sim