Nodes: Add bNodeInstanceKey to DerivedNodeTree #108900

Merged
Omar Emara merged 1 commits from OmarEmaraDev/blender:derived-node-tree-instance-key into main 2023-06-21 05:04:03 +02:00
3 changed files with 84 additions and 61 deletions

View File

@ -21,58 +21,6 @@ namespace blender::realtime_compositor {
using namespace nodes::derived_node_tree_types;
/* Find the active context from the given context and its descendants contexts. The active context
* is the one whose node instance key matches the active_viewer_key stored in the root node tree.
* The instance key of each context is computed by calling BKE_node_instance_key given the key of
* the parent as well as the group node making the context. */
static const DTreeContext *find_active_context_recursive(const DTreeContext *context,
bNodeInstanceKey key)
{
/* The instance key of the given context matches the active viewer instance key, so this is the
* active context, return it. */
if (key.value == context->derived_tree().root_context().btree().active_viewer_key.value) {
return context;
}
/* For each of the group nodes, compute their instance key and contexts and call this function
* recursively. */
for (const bNode *group_node : context->btree().group_nodes()) {
const bNodeInstanceKey child_key = BKE_node_instance_key(key, &context->btree(), group_node);
const DTreeContext *child_context = context->child_context(*group_node);
const DTreeContext *found_context = find_active_context_recursive(child_context, child_key);
/* If the found context is null, that means neither the child context nor one of its descendant
* contexts is active. */
if (!found_context) {
continue;
}
/* Otherwise, we have found our active context, return it. */
return found_context;
}
/* Neither the given context nor one of its descendant contexts is active, so return null. */
return nullptr;
}
/* Find the active context for the given node tree. The active context represents the node tree
* currently being edited. In most cases, that would be the top level node tree itself, but in the
* case where the user is editing the node tree of a node group, the active context would be a
* representation of the node tree of that node group. Note that the context also stores the group
* node that the user selected to edit the node tree, so the context fully represents a particular
* instance of the node group. */
static const DTreeContext *find_active_context(const DerivedNodeTree &tree)
{
/* If the active viewer key is NODE_INSTANCE_KEY_NONE, that means it is not yet initialized and
* we return the root context in that case. See the find_active_context_recursive function. */
if (tree.root_context().btree().active_viewer_key.value == NODE_INSTANCE_KEY_NONE.value) {
return &tree.root_context();
}
/* The root context has an instance key of NODE_INSTANCE_KEY_BASE by definition. */
return find_active_context_recursive(&tree.root_context(), NODE_INSTANCE_KEY_BASE);
}
/* Add the viewer node which is marked as NODE_DO_OUTPUT in the given context to the given stack.
* If multiple types of viewer nodes are marked, then the preference will be CMP_NODE_VIEWER >
* CMP_NODE_SPLITVIEWER. If no viewer nodes were found, composite nodes can be added as a fallback
@ -114,7 +62,8 @@ static bool add_viewer_nodes_in_context(const DTreeContext *context, Stack<DNode
* Output, Composite, and Viewer nodes. Viewer nodes are a special case, as only the nodes that
* satisfies the requirements in the add_viewer_nodes_in_context function are added. First, the
* active context is searched for viewer nodes, if non were found, the root context is searched.
* For more information on what contexts mean here, see the find_active_context function. */
* For more information on what contexts mean here, see the DerivedNodeTree::active_context()
* function. */
static void add_output_nodes(const Context &context,
const DerivedNodeTree &tree,
Stack<DNode> &node_stack)
@ -139,8 +88,8 @@ static void add_output_nodes(const Context &context,
}
}
const DTreeContext *active_context = find_active_context(tree);
const bool viewer_was_added = add_viewer_nodes_in_context(active_context, node_stack);
const DTreeContext &active_context = tree.active_context();
const bool viewer_was_added = add_viewer_nodes_in_context(&active_context, node_stack);
/* An active viewer was added, no need to search further. */
if (viewer_was_added) {
@ -149,7 +98,7 @@ static void add_output_nodes(const Context &context,
/* If the active context is the root one and no viewer nodes were found, we consider this node
* tree to have no viewer nodes, even if one of the non-active descendants have viewer nodes. */
if (active_context->is_root()) {
if (active_context.is_root()) {
return;
}
@ -394,7 +343,8 @@ Schedule compute_schedule(const Context &context, const DerivedNodeTree &tree)
int insertion_position = 0;
for (int i = 0; i < sorted_dependency_nodes.size(); i++) {
if (needed_buffers.lookup(doutput.node()) >
needed_buffers.lookup(sorted_dependency_nodes[i])) {
needed_buffers.lookup(sorted_dependency_nodes[i]))
{
insertion_position++;
}
else {

View File

@ -46,6 +46,8 @@ class DTreeContext {
const bNode *parent_node_;
/* The current node tree. */
const bNodeTree *btree_;
/* The instance key of the parent node. NODE_INSTANCE_KEY_BASE for root contexts. */
bNodeInstanceKey instance_key_;
/* All the children contexts of this context. */
Map<const bNode *, DTreeContext *> children_;
DerivedNodeTree *derived_tree_;
@ -56,6 +58,7 @@ class DTreeContext {
const bNodeTree &btree() const;
const DTreeContext *parent_context() const;
const bNode *parent_node() const;
const bNodeInstanceKey instance_key() const;
const DTreeContext *child_context(const bNode &node) const;
const DerivedNodeTree &derived_tree() const;
bool is_root() const;
@ -76,6 +79,7 @@ class DNode {
const DTreeContext *context() const;
const bNode *bnode() const;
const bNodeInstanceKey instance_key() const;
const bNode *operator->() const;
const bNode &operator*() const;
@ -191,6 +195,14 @@ class DerivedNodeTree {
const DTreeContext &root_context() const;
Span<const bNodeTree *> used_btrees() const;
/** Returns the active context for the node tree. The active context represents the node tree
* currently being edited. In most cases, that would be the top level node tree itself, but in
* the case where the user is editing the node tree of a node group, the active context would be
* a representation of the node tree of that node group. Note that the context also stores the
* group node that the user selected to edit the node tree, so the context fully represents a
* particular instance of the node group. */
const DTreeContext &active_context() const;
Review

active_context sounds a bit like a wrong abstraction to me, mainly because you can have multiple node editors open at the same time.

`active_context` sounds a bit like a wrong abstraction to me, mainly because you can have multiple node editors open at the same time.
Review

@JacquesLucke But there is still a concept of an active context even in that case, right?
Otherwise, what do you propose?

@JacquesLucke But there is still a concept of an active context even in that case, right? Otherwise, what do you propose?
Review

It might just be a matter of naming or improving the comment. The comment makes it sounds like the active context is the one that the user is currently editing, but that definition doesn't really work when there are multiple node editors. Instead, it seems like you derive the active context from the active viewer, which is probably only indirectly related to the context that the user is editing right now.

It might just be a matter of naming or improving the comment. The comment makes it sounds like the active context is the one that the user is currently editing, but that definition doesn't really work when there are multiple node editors. Instead, it seems like you derive the active context from the active viewer, which is probably only indirectly related to the context that the user is editing right now.
Review

@JacquesLucke Your former definition which the comment implies is actually the more correct one as far as I can see. It is not actually derived from the active viewer, the name is probably misleading in that case. If you have multiple editors open with different context, the active one will be the one that the user is editing or is interacting with. You can try that by selecting a node in of the editors, which will make that context active.

@JacquesLucke Your former definition which the comment implies is actually the more correct one as far as I can see. It is not actually derived from the active viewer, the name is probably misleading in that case. If you have multiple editors open with different context, the active one will be the one that the user is editing or is interacting with. You can try that by selecting a node in of the editors, which will make that context active.
/**
* \return True when there is a link cycle. Unavailable sockets are ignored.
*/
@ -205,7 +217,8 @@ class DerivedNodeTree {
private:
DTreeContext &construct_context_recursively(DTreeContext *parent_context,
const bNode *parent_node,
const bNodeTree &btree);
const bNodeTree &btree,
const bNodeInstanceKey instance_key);
void destruct_context_recursively(DTreeContext *context);
void foreach_node_in_context_recursive(const DTreeContext &context,
@ -240,6 +253,11 @@ inline const bNode *DTreeContext::parent_node() const
return parent_node_;
}
inline const bNodeInstanceKey DTreeContext::instance_key() const
{
return instance_key_;
}
inline const DTreeContext *DTreeContext::child_context(const bNode &node) const
{
return children_.lookup_default(&node, nullptr);

View File

@ -15,12 +15,14 @@ DerivedNodeTree::DerivedNodeTree(const bNodeTree &btree)
/* Construct all possible contexts immediately. This is significantly cheaper than inlining all
* node groups. If it still becomes a performance issue in the future, contexts could be
* constructed lazily when they are needed. */
root_context_ = &this->construct_context_recursively(nullptr, nullptr, btree);
root_context_ = &this->construct_context_recursively(
nullptr, nullptr, btree, NODE_INSTANCE_KEY_BASE);
}
DTreeContext &DerivedNodeTree::construct_context_recursively(DTreeContext *parent_context,
const bNode *parent_node,
const bNodeTree &btree)
const bNodeTree &btree,
const bNodeInstanceKey instance_key)
{
btree.ensure_topology_cache();
DTreeContext &context = *allocator_.construct<DTreeContext>().release();
@ -28,13 +30,16 @@ DTreeContext &DerivedNodeTree::construct_context_recursively(DTreeContext *paren
context.parent_node_ = parent_node;
context.derived_tree_ = this;
context.btree_ = &btree;
context.instance_key_ = instance_key;
used_btrees_.add(context.btree_);
for (const bNode *bnode : context.btree_->all_nodes()) {
if (bnode->is_group()) {
bNodeTree *child_btree = reinterpret_cast<bNodeTree *>(bnode->id);
if (child_btree != nullptr) {
DTreeContext &child = this->construct_context_recursively(&context, bnode, *child_btree);
const bNodeInstanceKey child_key = BKE_node_instance_key(instance_key, &btree, bnode);
DTreeContext &child = this->construct_context_recursively(
&context, bnode, *child_btree, child_key);
context.children_.add_new(bnode, &child);
}
}
@ -93,6 +98,11 @@ void DerivedNodeTree::foreach_node_in_context_recursive(const DTreeContext &cont
}
}
const bNodeInstanceKey DNode::instance_key() const
{
return BKE_node_instance_key(context()->instance_key(), &context()->btree(), bnode());
}
DOutputSocket DInputSocket::get_corresponding_group_node_output() const
{
BLI_assert(*this);
@ -299,6 +309,51 @@ void DOutputSocket::foreach_target_socket(ForeachTargetSocketFn target_fn,
}
}
/* Find the active context from the given context and its descendants contexts. The active context
* is the one whose node instance key matches the active_viewer_key stored in the root node tree.
* The instance key of each context is computed by calling BKE_node_instance_key given the key of
* the parent as well as the group node making the context. */
static const DTreeContext *find_active_context_recursive(const DTreeContext *context)
{
const bNodeInstanceKey key = context->instance_key();
/* The instance key of the given context matches the active viewer instance key, so this is the
* active context, return it. */
if (key.value == context->derived_tree().root_context().btree().active_viewer_key.value) {
return context;
}
/* For each of the group nodes, compute their instance key and contexts and call this function
* recursively. */
for (const bNode *group_node : context->btree().group_nodes()) {
const DTreeContext *child_context = context->child_context(*group_node);
const DTreeContext *found_context = find_active_context_recursive(child_context);
/* If the found context is null, that means neither the child context nor one of its descendant
* contexts is active. */
if (!found_context) {
continue;
}
/* Otherwise, we have found our active context, return it. */
return found_context;
}
/* Neither the given context nor one of its descendant contexts is active, so return null. */
return nullptr;
}
const DTreeContext &DerivedNodeTree::active_context() const
{
/* If the active viewer key is NODE_INSTANCE_KEY_NONE, that means it is not yet initialized and
* we return the root context in that case. See the find_active_context_recursive function. */
if (root_context().btree().active_viewer_key.value == NODE_INSTANCE_KEY_NONE.value) {
return root_context();
}
return *find_active_context_recursive(&root_context());
}
/* Each nested node group gets its own cluster. Just as node groups, clusters can be nested. */
static dot::Cluster *get_dot_cluster_for_context(
dot::DirectedGraph &digraph,