Nodes: add separately allocated run-time data for bNodeTree

`bNodeTree` has a lot of run-time embedded in it currently. Having a separately
allocated run-time struct has some benefits:
* Run-time data is not stored in files.
* Makes it easy to use c++ types as run-time data.
* More clear distinction between what data only exists at run-time and which doesn't.

This commit doesn't move all run-time data to the new struct yet, only the data where
I know for sure how it is used. The remaining data can be moved separately.

Differential Revision: https://developer.blender.org/D15033
This commit is contained in:
Jacques Lucke 2022-05-30 12:54:07 +02:00
parent 2f77b2daac
commit bb0fc67582
Notes: blender-bot 2023-02-14 04:40:22 +01:00
Referenced by commit 6a59cf0530, Nodes: add separately allocated runtime data for nodes and sockets
8 changed files with 109 additions and 80 deletions

View File

@ -0,0 +1,40 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#pragma once
#include <memory>
#include "BLI_sys_types.h"
#include "BLI_utility_mixins.hh"
namespace blender::nodes {
struct FieldInferencingInterface;
}
namespace blender::bke {
class bNodeTreeRuntime : NonCopyable, NonMovable {
public:
/**
* Keeps track of what changed in the node tree until the next update.
* Should not be changed directly, instead use the functions in `BKE_node_tree_update.h`.
* #eNodeTreeChangedFlag.
*/
uint32_t changed_flag = 0;
/**
* A hash of the topology of the node tree leading up to the outputs. This is used to determine
* of the node tree changed in a way that requires updating geometry nodes or shaders.
*/
uint32_t output_topology_hash = 0;
/**
* Used to cache run-time information of the node tree.
* #eNodeTreeRuntimeFlag.
*/
uint8_t runtime_flag = 0;
/** Information about how inputs and outputs of the node group interact with fields. */
std::unique_ptr<nodes::FieldInferencingInterface> field_inferencing_interface;
};
} // namespace blender::bke

View File

@ -431,6 +431,7 @@ set(SRC
BKE_multires.h
BKE_nla.h
BKE_node.h
BKE_node_runtime.hh
BKE_node_tree_update.h
BKE_object.h
BKE_object_deform.h

View File

@ -60,6 +60,7 @@
#include "BKE_lib_query.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.h"
#include "RNA_access.h"
@ -94,6 +95,7 @@ using blender::Stack;
using blender::StringRef;
using blender::Vector;
using blender::VectorSet;
using blender::bke::bNodeTreeRuntime;
using blender::nodes::FieldInferencingInterface;
using blender::nodes::InputSocketFieldType;
using blender::nodes::NodeDeclaration;
@ -123,6 +125,7 @@ static void nodeMuteRerouteOutputLinks(struct bNodeTree *ntree,
static void ntree_init_data(ID *id)
{
bNodeTree *ntree = (bNodeTree *)id;
ntree->runtime = MEM_new<bNodeTreeRuntime>(__func__);
ntree_set_typeinfo(ntree, nullptr);
}
@ -134,6 +137,8 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c
/* We never handle usercount here for own data. */
const int flag_subdata = flag | LIB_ID_CREATE_NO_USER_REFCOUNT;
ntree_dst->runtime = MEM_new<bNodeTreeRuntime>(__func__);
/* in case a running nodetree is copied */
ntree_dst->execdata = nullptr;
@ -203,9 +208,9 @@ static void ntree_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, c
/* node tree will generate its own interface type */
ntree_dst->interface_type = nullptr;
if (ntree_src->field_inferencing_interface) {
ntree_dst->field_inferencing_interface = new FieldInferencingInterface(
*ntree_src->field_inferencing_interface);
if (ntree_src->runtime->field_inferencing_interface) {
ntree_dst->runtime->field_inferencing_interface = std::make_unique<FieldInferencingInterface>(
*ntree_src->runtime->field_inferencing_interface);
}
if (flag & LIB_ID_COPY_NO_PREVIEW) {
@ -258,8 +263,6 @@ static void ntree_free_data(ID *id)
MEM_freeN(sock);
}
delete ntree->field_inferencing_interface;
/* free preview hash */
if (ntree->previews) {
BKE_node_instance_hash_free(ntree->previews, (bNodeInstanceValueFP)BKE_node_preview_free);
@ -270,6 +273,7 @@ static void ntree_free_data(ID *id)
}
BKE_previewimg_free(&ntree->preview);
MEM_delete(ntree->runtime);
}
static void library_foreach_node_socket(LibraryForeachIDData *data, bNodeSocket *sock)
@ -670,9 +674,7 @@ void ntreeBlendReadData(BlendDataReader *reader, bNodeTree *ntree)
ntree->progress = nullptr;
ntree->execdata = nullptr;
ntree->runtime_flag = 0;
ntree->field_inferencing_interface = nullptr;
ntree->runtime = MEM_new<bNodeTreeRuntime>(__func__);
BKE_ntree_update_tag_missing_runtime_data(ntree);
BLO_read_data_address(reader, &ntree->adt);

View File

@ -15,6 +15,7 @@
#include "BKE_image.h"
#include "BKE_main.h"
#include "BKE_node.h"
#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.h"
#include "MOD_nodes.h"
@ -48,7 +49,7 @@ enum eNodeTreeChangedFlag {
static void add_tree_tag(bNodeTree *ntree, const eNodeTreeChangedFlag flag)
{
ntree->changed_flag |= flag;
ntree->runtime->changed_flag |= flag;
}
static void add_node_tag(bNodeTree *ntree, bNode *node, const eNodeTreeChangedFlag flag)
@ -172,11 +173,11 @@ static FieldInferencingInterface get_node_field_inferencing_interface(const Node
/* This can happen when there is a linked node group that was not found (see T92799). */
return get_dummy_field_inferencing_interface(node);
}
if (group->field_inferencing_interface == nullptr) {
if (!group->runtime->field_inferencing_interface) {
/* This shouldn't happen because referenced node groups should always be updated first. */
BLI_assert_unreachable();
}
return *group->field_inferencing_interface;
return *group->runtime->field_inferencing_interface;
}
FieldInferencingInterface inferencing_interface;
@ -551,7 +552,8 @@ static bool update_field_inferencing(const NodeTreeRef &tree)
bNodeTree &btree = *tree.btree();
/* Create new inferencing interface for this node group. */
FieldInferencingInterface *new_inferencing_interface = new FieldInferencingInterface();
std::unique_ptr<FieldInferencingInterface> new_inferencing_interface =
std::make_unique<FieldInferencingInterface>();
new_inferencing_interface->inputs.resize(BLI_listbase_count(&btree.inputs),
InputSocketFieldType::IsSupported);
new_inferencing_interface->outputs.resize(BLI_listbase_count(&btree.outputs),
@ -567,11 +569,10 @@ static bool update_field_inferencing(const NodeTreeRef &tree)
update_socket_shapes(tree, field_state_by_socket_id);
/* Update the previous group interface. */
const bool group_interface_changed = btree.field_inferencing_interface == nullptr ||
*btree.field_inferencing_interface !=
const bool group_interface_changed = !btree.runtime->field_inferencing_interface ||
*btree.runtime->field_inferencing_interface !=
*new_inferencing_interface;
delete btree.field_inferencing_interface;
btree.field_inferencing_interface = new_inferencing_interface;
btree.runtime->field_inferencing_interface = std::move(new_inferencing_interface);
return group_interface_changed;
}
@ -799,7 +800,7 @@ class NodeTreeMainUpdater {
{
Vector<bNodeTree *> changed_ntrees;
FOREACH_NODETREE_BEGIN (bmain_, ntree, id) {
if (ntree->changed_flag != NTREE_CHANGED_NOTHING) {
if (ntree->runtime->changed_flag != NTREE_CHANGED_NOTHING) {
changed_ntrees.append(ntree);
}
}
@ -817,7 +818,7 @@ class NodeTreeMainUpdater {
if (root_ntrees.size() == 1) {
bNodeTree *ntree = root_ntrees[0];
if (ntree->changed_flag == NTREE_CHANGED_NOTHING) {
if (ntree->runtime->changed_flag == NTREE_CHANGED_NOTHING) {
return;
}
const TreeUpdateResult result = this->update_tree(*ntree);
@ -830,7 +831,7 @@ class NodeTreeMainUpdater {
if (!is_single_tree_update) {
Vector<bNodeTree *> ntrees_in_order = this->get_tree_update_order(root_ntrees);
for (bNodeTree *ntree : ntrees_in_order) {
if (ntree->changed_flag == NTREE_CHANGED_NOTHING) {
if (ntree->runtime->changed_flag == NTREE_CHANGED_NOTHING) {
continue;
}
if (!update_result_by_tree_.contains(ntree)) {
@ -1002,7 +1003,8 @@ class NodeTreeMainUpdater {
ntreeTexCheckCyclics(&ntree);
}
if (ntree.changed_flag & NTREE_CHANGED_INTERFACE || ntree.changed_flag & NTREE_CHANGED_ANY) {
if (ntree.runtime->changed_flag & NTREE_CHANGED_INTERFACE ||
ntree.runtime->changed_flag & NTREE_CHANGED_ANY) {
result.interface_changed = true;
}
@ -1057,18 +1059,18 @@ class NodeTreeMainUpdater {
this->ensure_tree_ref(ntree, tree_ref);
const NodeRef &node = *tree_ref->find_node(*bnode);
if (this->should_update_individual_node(node)) {
const uint32_t old_changed_flag = ntree.changed_flag;
ntree.changed_flag = NTREE_CHANGED_NOTHING;
const uint32_t old_changed_flag = ntree.runtime->changed_flag;
ntree.runtime->changed_flag = NTREE_CHANGED_NOTHING;
/* This may set #ntree.changed_flag which is detected below. */
/* This may set #ntree.runtime->changed_flag which is detected below. */
this->update_individual_node(node);
if (ntree.changed_flag != NTREE_CHANGED_NOTHING) {
if (ntree.runtime->changed_flag != NTREE_CHANGED_NOTHING) {
/* The tree ref is outdated and needs to be rebuilt. Generally, only very few update
* functions change the node. Typically zero or one nodes change after an update. */
tree_ref.reset();
}
ntree.changed_flag |= old_changed_flag;
ntree.runtime->changed_flag |= old_changed_flag;
}
}
}
@ -1077,13 +1079,13 @@ class NodeTreeMainUpdater {
{
bNodeTree &ntree = *node.btree();
bNode &bnode = *node.bnode();
if (ntree.changed_flag & NTREE_CHANGED_ANY) {
if (ntree.runtime->changed_flag & NTREE_CHANGED_ANY) {
return true;
}
if (bnode.changed_flag & NTREE_CHANGED_NODE_PROPERTY) {
return true;
}
if (ntree.changed_flag & NTREE_CHANGED_LINK) {
if (ntree.runtime->changed_flag & NTREE_CHANGED_LINK) {
/* Node groups currently always rebuilt their sockets when they are updated.
* So avoid calling the update method when no new link was added to it. */
if (node.is_group_input_node()) {
@ -1101,7 +1103,7 @@ class NodeTreeMainUpdater {
return true;
}
}
if (ntree.changed_flag & NTREE_CHANGED_INTERFACE) {
if (ntree.runtime->changed_flag & NTREE_CHANGED_INTERFACE) {
if (node.is_group_input_node() || node.is_group_output_node()) {
return true;
}
@ -1232,16 +1234,16 @@ class NodeTreeMainUpdater {
}
/* Reset the changed_flag to allow detecting when the update callback changed the node tree. */
const uint32_t old_changed_flag = ntree.changed_flag;
ntree.changed_flag = NTREE_CHANGED_NOTHING;
const uint32_t old_changed_flag = ntree.runtime->changed_flag;
ntree.runtime->changed_flag = NTREE_CHANGED_NOTHING;
ntree.typeinfo->update(&ntree);
if (ntree.changed_flag != NTREE_CHANGED_NOTHING) {
if (ntree.runtime->changed_flag != NTREE_CHANGED_NOTHING) {
/* The tree ref is outdated and needs to be rebuilt. */
tree_ref.reset();
}
ntree.changed_flag |= old_changed_flag;
ntree.runtime->changed_flag |= old_changed_flag;
}
void remove_unused_previews_when_necessary(bNodeTree &ntree)
@ -1250,7 +1252,7 @@ class NodeTreeMainUpdater {
const uint32_t allowed_flags = NTREE_CHANGED_LINK | NTREE_CHANGED_SOCKET_PROPERTY |
NTREE_CHANGED_NODE_PROPERTY | NTREE_CHANGED_NODE_OUTPUT |
NTREE_CHANGED_INTERFACE;
if ((ntree.changed_flag & allowed_flags) == ntree.changed_flag) {
if ((ntree.runtime->changed_flag & allowed_flags) == ntree.runtime->changed_flag) {
return;
}
BKE_node_preview_remove_unused(&ntree);
@ -1259,7 +1261,7 @@ class NodeTreeMainUpdater {
void propagate_runtime_flags(const NodeTreeRef &tree_ref)
{
bNodeTree &ntree = *tree_ref.btree();
ntree.runtime_flag = 0;
ntree.runtime->runtime_flag = 0;
if (ntree.type != NTREE_SHADER) {
return;
}
@ -1268,7 +1270,7 @@ class NodeTreeMainUpdater {
for (const NodeRef *group_node : tree_ref.nodes_by_type("NodeGroup")) {
const bNodeTree *group = reinterpret_cast<bNodeTree *>(group_node->bnode()->id);
if (group != nullptr) {
ntree.runtime_flag |= group->runtime_flag;
ntree.runtime->runtime_flag |= group->runtime->runtime_flag;
}
}
/* Check if the tree itself has an animated image. */
@ -1276,7 +1278,7 @@ class NodeTreeMainUpdater {
for (const NodeRef *node : tree_ref.nodes_by_type(idname)) {
Image *image = reinterpret_cast<Image *>(node->bnode()->id);
if (image != nullptr && BKE_image_is_animated(image)) {
ntree.runtime_flag |= NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION;
ntree.runtime->runtime_flag |= NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION;
break;
}
}
@ -1288,7 +1290,7 @@ class NodeTreeMainUpdater {
"ShaderNodeOutputAOV"}) {
const Span<const NodeRef *> nodes = tree_ref.nodes_by_type(idname);
if (!nodes.is_empty()) {
ntree.runtime_flag |= NTREE_RUNTIME_FLAG_HAS_MATERIAL_OUTPUT;
ntree.runtime->runtime_flag |= NTREE_RUNTIME_FLAG_HAS_MATERIAL_OUTPUT;
break;
}
}
@ -1323,12 +1325,12 @@ class NodeTreeMainUpdater {
/* Compute a hash that represents the node topology connected to the output. This always has to
* be updated even if it is not used to detect changes right now. Otherwise
* #btree.output_topology_hash will go out of date. */
* #btree.runtime.output_topology_hash will go out of date. */
const Vector<const SocketRef *> tree_output_sockets = this->find_output_sockets(tree);
const uint32_t old_topology_hash = btree.output_topology_hash;
const uint32_t old_topology_hash = btree.runtime->output_topology_hash;
const uint32_t new_topology_hash = this->get_combined_socket_topology_hash(
tree, tree_output_sockets);
btree.output_topology_hash = new_topology_hash;
btree.runtime->output_topology_hash = new_topology_hash;
if (const AnimData *adt = BKE_animdata_from_id(&btree.id)) {
/* Drivers may copy values in the node tree around arbitrarily and may cause the output to
@ -1352,7 +1354,7 @@ class NodeTreeMainUpdater {
}
}
if (btree.changed_flag & NTREE_CHANGED_ANY) {
if (btree.runtime->changed_flag & NTREE_CHANGED_ANY) {
return true;
}
@ -1361,8 +1363,8 @@ class NodeTreeMainUpdater {
}
/* The topology hash can only be used when only topology-changing operations have been done. */
if (btree.changed_flag ==
(btree.changed_flag & (NTREE_CHANGED_LINK | NTREE_CHANGED_REMOVED_NODE))) {
if (btree.runtime->changed_flag ==
(btree.runtime->changed_flag & (NTREE_CHANGED_LINK | NTREE_CHANGED_REMOVED_NODE))) {
if (old_topology_hash == new_topology_hash) {
return false;
}
@ -1404,7 +1406,7 @@ class NodeTreeMainUpdater {
if (bnode.type == NODE_GROUP) {
const bNodeTree *node_group = reinterpret_cast<const bNodeTree *>(bnode.id);
if (node_group != nullptr &&
node_group->runtime_flag & NTREE_RUNTIME_FLAG_HAS_MATERIAL_OUTPUT) {
node_group->runtime->runtime_flag & NTREE_RUNTIME_FLAG_HAS_MATERIAL_OUTPUT) {
return true;
}
}
@ -1591,7 +1593,7 @@ class NodeTreeMainUpdater {
void reset_changed_flags(bNodeTree &ntree)
{
ntree.changed_flag = NTREE_CHANGED_NOTHING;
ntree.runtime->changed_flag = NTREE_CHANGED_NOTHING;
LISTBASE_FOREACH (bNode *, node, &ntree.nodes) {
node->changed_flag = NTREE_CHANGED_NOTHING;
node->update = 0;

View File

@ -78,6 +78,7 @@
#include "BKE_modifier.h"
#include "BKE_movieclip.h"
#include "BKE_node.h"
#include "BKE_node_runtime.hh"
#include "BKE_object.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
@ -1083,7 +1084,8 @@ void DepsgraphNodeBuilder::build_animation_images(ID *id)
bool has_image_animation = false;
if (ELEM(GS(id->name), ID_MA, ID_WO)) {
bNodeTree *ntree = *BKE_ntree_ptr_from_id(id);
if (ntree != nullptr && ntree->runtime_flag & NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION) {
if (ntree != nullptr &&
ntree->runtime->runtime_flag & NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION) {
has_image_animation = true;
}
}

View File

@ -71,6 +71,7 @@
#include "BKE_mball.h"
#include "BKE_modifier.h"
#include "BKE_node.h"
#include "BKE_node_runtime.hh"
#include "BKE_object.h"
#include "BKE_particle.h"
#include "BKE_pointcache.h"
@ -1459,7 +1460,8 @@ void DepsgraphRelationBuilder::build_animation_images(ID *id)
bool has_image_animation = false;
if (ELEM(GS(id->name), ID_MA, ID_WO)) {
bNodeTree *ntree = *BKE_ntree_ptr_from_id(id);
if (ntree != nullptr && ntree->runtime_flag & NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION) {
if (ntree != nullptr &&
ntree->runtime->runtime_flag & NTREE_RUNTIME_FLAG_HAS_IMAGE_ANIMATION) {
has_image_animation = true;
}
}

View File

@ -71,11 +71,16 @@ namespace blender::nodes {
class NodeDeclaration;
class SocketDeclaration;
} // namespace blender::nodes
namespace blender::bke {
class bNodeTreeRuntime;
}
using NodeDeclarationHandle = blender::nodes::NodeDeclaration;
using SocketDeclarationHandle = blender::nodes::SocketDeclaration;
using bNodeTreeRuntimeHandle = blender::bke::bNodeTreeRuntime;
#else
typedef struct NodeDeclarationHandle NodeDeclarationHandle;
typedef struct SocketDeclarationHandle SocketDeclarationHandle;
typedef struct bNodeTreeRuntimeHandle bNodeTreeRuntimeHandle;
#endif
typedef struct bNodeSocket {
@ -462,16 +467,6 @@ typedef struct bNodeLink {
#define NTREE_CHUNKSIZE_512 512
#define NTREE_CHUNKSIZE_1024 1024
/** Workaround to forward-declare C++ type in C header. */
#ifdef __cplusplus
namespace blender::nodes {
struct FieldInferencingInterface;
}
using FieldInferencingInterfaceHandle = blender::nodes::FieldInferencingInterface;
#else
typedef struct FieldInferencingInterfaceHandle FieldInferencingInterfaceHandle;
#endif
/* the basis for a Node tree, all links and nodes reside internal here */
/* only re-usable node trees are in the library though,
* materials and textures allocate own tree struct */
@ -494,31 +489,15 @@ typedef struct bNodeTree {
float view_center[2];
ListBase nodes, links;
/** Information about how inputs and outputs of the node group interact with fields. */
FieldInferencingInterfaceHandle *field_inferencing_interface;
int type;
/**
* Used to cache run-time information of the node tree.
* #eNodeTreeRuntimeFlag.
*/
uint8_t runtime_flag;
char _pad1[3];
/**
* Sockets in groups have unique identifiers, adding new sockets always
* will increase this counter.
*/
int cur_index;
int flag;
/**
* Keeps track of what changed in the node tree until the next update.
* Should not be changed directly, instead use the functions in `BKE_node_tree_update.h`.
* #eNodeTreeChangedFlag.
*/
uint32_t changed_flag;
/** Flag to prevent re-entrant update calls. */
short is_updating;
/** Generic temporary flag for recursion check (DFS/BFS). */
@ -552,11 +531,8 @@ typedef struct bNodeTree {
* in case multiple different editors are used and make context ambiguous.
*/
bNodeInstanceKey active_viewer_key;
/**
* A hash of the topology of the node tree leading up to the outputs. This is used to determine
* of the node tree changed in a way that requires updating geometry nodes or shaders.
*/
uint32_t output_topology_hash;
char _pad[4];
/** Execution data.
*
@ -579,6 +555,8 @@ typedef struct bNodeTree {
/** Image representing what the node group does. */
struct PreviewImage *preview;
bNodeTreeRuntimeHandle *runtime;
} bNodeTree;
/** #NodeTree.type, index */

View File

@ -45,6 +45,7 @@
#include "BKE_main.h"
#include "BKE_mesh.h"
#include "BKE_modifier.h"
#include "BKE_node_runtime.hh"
#include "BKE_node_tree_update.h"
#include "BKE_object.h"
#include "BKE_pointcloud.h"
@ -396,8 +397,9 @@ static bool socket_type_has_attribute_toggle(const bNodeSocket &socket)
*/
static bool input_has_attribute_toggle(const bNodeTree &node_tree, const int socket_index)
{
BLI_assert(node_tree.field_inferencing_interface != nullptr);
const FieldInferencingInterface &field_interface = *node_tree.field_inferencing_interface;
BLI_assert(node_tree.runtime->field_inferencing_interface);
const FieldInferencingInterface &field_interface =
*node_tree.runtime->field_inferencing_interface;
return field_interface.inputs[socket_index] != InputSocketFieldType::None;
}