###### tags: `Design` # Function Socket Notes Function sockets are single-valued (no fields). This should keep overhead from type checking and dispatching to a minimum. That does **not** mean function sockets cannot take and return fields, it just means the function itself is the same for all elements of a field. ## Data flow for functions in geometry nodes ```mermaid flowchart G[Node Tree]:::data F[Default Bindings]:::data G --> E([bind]):::op F --> E E --> D[Closure]:::data D -->|passed through the node tree| B([evaluate]):::op B --> A[Result]:::data classDef data fill:#f96 classDef op fill:#6bf ``` The node tree is the main function definition. There is no up-front distinction between "free" and "bound" variables: Every node tree input has a default value, either from the socket constant or from connected nodes at the point of binding. "Function zones" might also be introduced to define functions, but i consider this a separate feature. Function zones with the ability to bypass the formal interface declaration, basically adding anonymous bindings. The advantage is that "free" and "bound" parameters could be distinguished, but whether that actually helps or confuses users remains to be seen. [<img src="https://i.imgur.com/Llq5dBz.png" width="300">](https://i.imgur.com/Llq5dBz.png) _Initial mockup by Jacques Lucke_ ## Bind Node Has a node tree property defining the function to bind. Tree inputs are shown on the node to define default and bound values. This way node tree default constants can be overridden at binding time. Output of the node is a closure that wraps the node tree function and the bound values. ## Evaluate Node Also has a node tree property. The node tree interface is a quick way to define the expected signature (inputs and outputs). It may be preferable to have an explicit list of inputs and outputs on the node, instead of the node tree interface. There may also be a more abstracted way of defining signatures from C/C++ code. For now, using a node tree is the easiest to implement. ### Comparison: Group node vs. Evaluate node The lazy function evaluation of group nodes is similar to the deferred evaluation of a node graph by the _Evaluate Function_ node. A `bNodeTree` is represented for execution by a `LazyFunction` (specifically a `GraphExecutor` lazy function). Some nodes (`bNode`) inside the tree also use lazy functions. In particular, the group nodes (not to be confused with the node _trees_ they use) are represented by a `LazyFunctionForGroupNode` inside their parent graph. So when a modifier executes nodes it creates a `GraphExecutor` LF, which contains a `LazyFunctionForGroupNode`, each of which also creates a `GraphExecutor` for its internal node graph. ```mermaid flowchart LR M([Modifier]):::modifier subgraph tree_a [Node Tree A] direction LR A(Node):::node B(Group Node):::group C(Node):::node end subgraph tree_b [Node Tree B] direction LR D(Node):::node E(Node):::node end A --o B B --o C B -.-> tree_b D --o E M -.-> tree_a classDef tree fill:#400 classDef node fill:#999 classDef group fill:#581 classDef modifier fill:#99f ``` ```mermaid flowchart LR M([compute_geometry]):::modifier subgraph tree_a [Graph] direction LR A(FunctionNode):::node B(LazyFunctionForGroupNode):::group C(FunctionNode):::node end subgraph tree_b [Graph] direction LR D(FunctionNode):::node E(FunctionNode):::node end M -.-> |GraphExecutor|tree_a A --o B B --o C B -.-> |GraphExecutor|tree_b D --o E classDef tree fill:#400 classDef node fill:#999 classDef group fill:#581 classDef modifier fill:#99f ``` The difference between group nodes and evaluation nodes is that the group tree is not known in advance by the evaluation node. That means setting up the executor has to happen at execution time rather than when inserting the `LazyFunctionForEvaluationNode` into its parent graph. Furthermore, the socket layout of the evaluation node does not match the sockets of the node graph it evaluates: - The _Function_ input of the node carries the graph and is not part of the graph inputs. - Inputs and outputs of the graph are optional: The Node can specify a subset of the graph inputs and any unspecified graph inputs will use a default or bound value. Likewise any unspecified graph outputs will simply be regarded as unused. The evaluation node must, however, make sure that any specified inputs and outputs are actually part of the graph it evaluates. Passing in a graph that does not have all required inputs or outputs constitutes a runtime error. ### Evaluate Function Executor Parameters A special implementation of the [`Params`](https://projects.blender.org/blender/blender/src/commit/8e70ab9905471c94a0b8b79a92401fdff59a4588/source/blender/functions/FN_lazy_function.hh#L131) class is needed to pass parameters between the evaluation node and the internal graph executor. We want requests for input data to be passed on to inputs of the evaluation nodes. Unlike the group node, we cannot simply use the same `Params` instance for the internal tree executor, because of the discrepancies between the evaluation node and the tree interface mentioned above.