Geometry Nodes: decouple multi-function lifetimes from modifier

Previously, some multi-functions were allocated in a resource scope.
This was fine as long as the multi-functions were only needed during
the current evaluation of the node tree. However, now cases arise
that require the multi-functions to be alive after the modifier is finished.
For example, we want to evaluate fields created with geometry nodes
outside of geometry nodes.

To make this work, `std::shared_ptr` has to be used in a few more places.
Realistically, this shouldn't have a noticable impact on performance.
If this does become a bottleneck in the future, we can think about ways
to make this work without using `shared_ptr` for multi-functions that
are only used once.
This commit is contained in:
Jacques Lucke
2021-10-18 11:40:00 +02:00
parent 746ee29d36
commit eb0d216dc1
6 changed files with 42 additions and 35 deletions

View File

@@ -204,14 +204,14 @@ class FieldOperation : public FieldNode {
* The multi-function used by this node. It is optionally owned. * The multi-function used by this node. It is optionally owned.
* Multi-functions with mutable or vector parameters are not supported currently. * Multi-functions with mutable or vector parameters are not supported currently.
*/ */
std::unique_ptr<const MultiFunction> owned_function_; std::shared_ptr<const MultiFunction> owned_function_;
const MultiFunction *function_; const MultiFunction *function_;
/** Inputs to the operation. */ /** Inputs to the operation. */
blender::Vector<GField> inputs_; blender::Vector<GField> inputs_;
public: public:
FieldOperation(std::unique_ptr<const MultiFunction> function, Vector<GField> inputs = {}); FieldOperation(std::shared_ptr<const MultiFunction> function, Vector<GField> inputs = {});
FieldOperation(const MultiFunction &function, Vector<GField> inputs = {}); FieldOperation(const MultiFunction &function, Vector<GField> inputs = {});
Span<GField> inputs() const; Span<GField> inputs() const;

View File

@@ -534,7 +534,7 @@ const GVArray *IndexFieldInput::get_varray_for_context(const fn::FieldContext &U
* FieldOperation. * FieldOperation.
*/ */
FieldOperation::FieldOperation(std::unique_ptr<const MultiFunction> function, FieldOperation::FieldOperation(std::shared_ptr<const MultiFunction> function,
Vector<GField> inputs) Vector<GField> inputs)
: FieldOperation(*function, std::move(inputs)) : FieldOperation(*function, std::move(inputs))
{ {

View File

@@ -924,7 +924,7 @@ static GeometrySet compute_geometry(const DerivedNodeTree &tree,
{ {
blender::ResourceScope scope; blender::ResourceScope scope;
blender::LinearAllocator<> &allocator = scope.linear_allocator(); blender::LinearAllocator<> &allocator = scope.linear_allocator();
blender::nodes::NodeMultiFunctions mf_by_node{tree, scope}; blender::nodes::NodeMultiFunctions mf_by_node{tree};
Map<DOutputSocket, GMutablePointer> group_inputs; Map<DOutputSocket, GMutablePointer> group_inputs;

View File

@@ -882,9 +882,9 @@ class GeometryNodesEvaluator {
} }
/* Use the multi-function implementation if it exists. */ /* Use the multi-function implementation if it exists. */
const MultiFunction *multi_function = params_.mf_by_node->try_get(node); const nodes::NodeMultiFunctions::Item &fn_item = params_.mf_by_node->try_get(node);
if (multi_function != nullptr) { if (fn_item.fn != nullptr) {
this->execute_multi_function_node(node, *multi_function, node_state); this->execute_multi_function_node(node, fn_item, node_state);
return; return;
} }
@@ -905,7 +905,7 @@ class GeometryNodesEvaluator {
} }
void execute_multi_function_node(const DNode node, void execute_multi_function_node(const DNode node,
const MultiFunction &fn, const nodes::NodeMultiFunctions::Item &fn_item,
NodeState &node_state) NodeState &node_state)
{ {
if (node->idname().find("Legacy") != StringRef::not_found) { if (node->idname().find("Legacy") != StringRef::not_found) {
@@ -933,7 +933,13 @@ class GeometryNodesEvaluator {
input_fields.append(std::move(*(GField *)single_value.value)); input_fields.append(std::move(*(GField *)single_value.value));
} }
auto operation = std::make_shared<fn::FieldOperation>(fn, std::move(input_fields)); std::shared_ptr<fn::FieldOperation> operation;
if (fn_item.owned_fn) {
operation = std::make_shared<fn::FieldOperation>(fn_item.owned_fn, std::move(input_fields));
}
else {
operation = std::make_shared<fn::FieldOperation>(*fn_item.fn, std::move(input_fields));
}
/* Forward outputs. */ /* Forward outputs. */
int output_index = 0; int output_index = 0;

View File

@@ -33,15 +33,15 @@ class NodeMultiFunctions;
*/ */
class NodeMultiFunctionBuilder : NonCopyable, NonMovable { class NodeMultiFunctionBuilder : NonCopyable, NonMovable {
private: private:
ResourceScope &resource_scope_;
bNode &node_; bNode &node_;
bNodeTree &tree_; bNodeTree &tree_;
std::shared_ptr<MultiFunction> owned_built_fn_;
const MultiFunction *built_fn_ = nullptr; const MultiFunction *built_fn_ = nullptr;
friend NodeMultiFunctions; friend NodeMultiFunctions;
public: public:
NodeMultiFunctionBuilder(ResourceScope &resource_scope, bNode &node, bNodeTree &tree); NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree);
/** /**
* Assign a multi-function for the current node. The input and output parameters of the function * Assign a multi-function for the current node. The input and output parameters of the function
@@ -58,31 +58,33 @@ class NodeMultiFunctionBuilder : NonCopyable, NonMovable {
bNode &node(); bNode &node();
bNodeTree &tree(); bNodeTree &tree();
ResourceScope &resource_scope();
}; };
/** /**
* Gives access to multi-functions for all nodes in a node tree that support them. * Gives access to multi-functions for all nodes in a node tree that support them.
*/ */
class NodeMultiFunctions { class NodeMultiFunctions {
public:
struct Item {
const MultiFunction *fn = nullptr;
std::shared_ptr<MultiFunction> owned_fn;
};
private: private:
Map<const bNode *, const MultiFunction *> map_; Map<const bNode *, Item> map_;
public: public:
NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope); NodeMultiFunctions(const DerivedNodeTree &tree);
const MultiFunction *try_get(const DNode &node) const; const Item &try_get(const DNode &node) const;
}; };
/* -------------------------------------------------------------------- */ /* -------------------------------------------------------------------- */
/** \name #NodeMultiFunctionBuilder Inline Methods /** \name #NodeMultiFunctionBuilder Inline Methods
* \{ */ * \{ */
inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(ResourceScope &resource_scope, inline NodeMultiFunctionBuilder::NodeMultiFunctionBuilder(bNode &node, bNodeTree &tree)
bNode &node, : node_(node), tree_(tree)
bNodeTree &tree)
: resource_scope_(resource_scope), node_(node), tree_(tree)
{ {
} }
@@ -96,11 +98,6 @@ inline bNodeTree &NodeMultiFunctionBuilder::tree()
return tree_; return tree_;
} }
inline ResourceScope &NodeMultiFunctionBuilder::resource_scope()
{
return resource_scope_;
}
inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction *fn) inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction *fn)
{ {
built_fn_ = fn; built_fn_ = fn;
@@ -108,14 +105,14 @@ inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction *fn)
inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction &fn) inline void NodeMultiFunctionBuilder::set_matching_fn(const MultiFunction &fn)
{ {
this->set_matching_fn(&fn); built_fn_ = &fn;
} }
template<typename T, typename... Args> template<typename T, typename... Args>
inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...args) inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...args)
{ {
const T &fn = resource_scope_.construct<T>(std::forward<Args>(args)...); owned_built_fn_ = std::make_shared<T>(std::forward<Args>(args)...);
this->set_matching_fn(&fn); built_fn_ = &*owned_built_fn_;
} }
/** \} */ /** \} */
@@ -124,9 +121,14 @@ inline void NodeMultiFunctionBuilder::construct_and_set_matching_fn(Args &&...ar
/** \name #NodeMultiFunctions Inline Methods /** \name #NodeMultiFunctions Inline Methods
* \{ */ * \{ */
inline const MultiFunction *NodeMultiFunctions::try_get(const DNode &node) const inline const NodeMultiFunctions::Item &NodeMultiFunctions::try_get(const DNode &node) const
{ {
return map_.lookup_default(node->bnode(), nullptr); static Item empty_item;
const Item *item = map_.lookup_ptr(node->bnode());
if (item == nullptr) {
return empty_item;
}
return *item;
} }
/** \} */ /** \} */

View File

@@ -18,7 +18,7 @@
namespace blender::nodes { namespace blender::nodes {
NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScope &resource_scope) NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree)
{ {
for (const NodeTreeRef *tree_ref : tree.used_node_tree_refs()) { for (const NodeTreeRef *tree_ref : tree.used_node_tree_refs()) {
bNodeTree *btree = tree_ref->btree(); bNodeTree *btree = tree_ref->btree();
@@ -27,11 +27,10 @@ NodeMultiFunctions::NodeMultiFunctions(const DerivedNodeTree &tree, ResourceScop
if (bnode->typeinfo->build_multi_function == nullptr) { if (bnode->typeinfo->build_multi_function == nullptr) {
continue; continue;
} }
NodeMultiFunctionBuilder builder{resource_scope, *bnode, *btree}; NodeMultiFunctionBuilder builder{*bnode, *btree};
bnode->typeinfo->build_multi_function(builder); bnode->typeinfo->build_multi_function(builder);
const MultiFunction *fn = builder.built_fn_; if (builder.built_fn_ != nullptr) {
if (fn != nullptr) { map_.add_new(bnode, {builder.built_fn_, std::move(builder.owned_built_fn_)});
map_.add_new(bnode, fn);
} }
} }
} }