|On this page
In geometry networks, you can put a part of the network inside a compiled block (if all the nodes inside are “compilable”). In a way, this makes the block act as if it were one node.
This imposes a number of restrictions on how the network can work, but can potentially deliver big benefits in the right circumstances.
The main benefit is multithreaded for-each loops where the network runs the same block of nodes over a large number of separate pieces. Compiled blocks let Houdini spread those iterations across multiple cores.
Another benefit is more efficient use of OpenCL. Normally, even if a node processes geometry on the graphics card, the geometry must be copied back into main memory after each node, because any other node could potentially try to access it. In a compiled block, however, multiple OpenCL-based nodes can keep data on the graphics card as they process it without copying it back, increasing speed.
In a normal network, each node theoretically makes a copy of the geometry it is working on. There are a lot of optimizations that can make this efficient in practice, but it still has a cost. In a compiled block, the nodes can work in-place on the same geometry because external references are not allowed. This can give compiled blocks additional speedups.
Unfortunately, to get these benefits, we had to limit some of the flexibility that makes working in Houdini dynamic and fluid.
Only “compile-able” nodes
You can only use nodes that have been modified to allow compiling inside compiled blocks. You can enable a badge in the network editor to see the nodes that cannot be used in a compiled block.
We are continuing to convert nodes based on perceived need. It is unlikely that we will get to all the existing SOPs (numbered in the hundreds), but if there is a node you would like to use inside a compiled block that has not yet been converted, let us know.
The stamp expression function is the ultimate spooky action at a distance. It relies on any node being able to force another node anywhere else in the network to cook. This complete freedom is not possible with compiled blocks.
No local variables, no per-component expressions
When a node is part of a compiled block, its algorithm is compiled into the block’s operation, and its parameters are left behind. Expressions are not evaluated over each component in the geometry. Only expressions that work statically “ahead of time” on the entire geometry will work (for example, reading from a numbered input).
If you need to do something per-component (for example, move points based on the value of a point attribute), you must use a VEX-based node, such as Attribute Wrangle.
stamp(), this restriction may be lifted someday, depending on necessity.)
No internal geometry references by name
Normally, if you need information about another node, you can evaluate an expression and refer to that node by its path. For example,
npoints("/obj/sphere1") to get the number of points in a node’s geometry. This is not very efficient, but it works. In a compiled block, you cannot refer to the geometry in a node using the node path. To accomplish the same thing in a compiled block, you need to replace internal named references with references to spare inputs.
You can reference channel values on nodes inside the block by path (for example,
ch("../sphere1/tx")). You just cannot read geometry, as in a
You can reference geometry outside the compiled block. However, all access to the exterior node will be serialized by a lock, so it will probably hurt performance in a threaded loop. In this case, you could use a spare input instead.
Nodes outside the block can reference geometry on nodes inside the block, but doing so would cook the interior node (and any dependencies) normally, without any of the benefits of compilation.
Nodes cannot read from their direct inputs
Expressions cannot read data from/about the node’s direct inputs (for example,
point(0, …) or
npoints(0)). You have to use a spare input to reference the input node.
This is another restriction that may be partially lifted in a future version of Houdini, at least for input 0.
Stop Condition not supported
The Stop Condition on For Each nodes is not supported inside a compiled block.
At this point in the development of compiled blocks, resist the urge to overuse compiled blocks in an attempt to gain maximum efficiency, especially in a production environment. The drawbacks are such that the reward is often not worth the effort.
Target the use of compiled blocks where they will give the most benefit - in iterations over large numbers of pieces.
You may want to get your network working and finalized, and then based on profiling, try converting any slow parts of the network into compiled blocks.
Create a compiled block
Put loops inside the compiled block, rather than a compiled block inside a loop.
On the For-Each Block End node of the top-level loop, turn on Multithread when Compiled. Turning on this checkbox tells Houdini to distribute the different iterations of this loop to different cores. You may want to do this only on the outer loop to avoid an explosion of the number of distributed tasks.
When using compiled blocks and loops, be careful to properly encapsulate and nest each block with Begin/End nodes at the “borders” of each block. Visually, in the network, wires crossing into a block should only be connected to block begin nodes.
In this network, the wire to
merge3 crosses a block boundary. This would be valid in a normal cook, where Houdini would just re-cook that path each iteration. The loops in a compiled block are compiled as a separate unit, however, so they have to stand on their own.
The fix is to insert a Block Begin node before the merge to properly nest the compiled and looping blocks. Set the Method parameter on this Begin node to Fetch Input.
As noted in the restrictions section, geometry expressions in a compiled block cannot reference the node’s direct inputs, and you cannot reference nodes inside a compiled block by name. The rule is there cannot be any “surprises” (dynamic expressions) about what to cook. Any SOPs that need data from another SOP in the compiled block have to make this clear statically before they cook, not during the cook.
(The case of VEX-based nodes such as Attribute Wrangle and Attribute Expression is a bit different. In a VEX node,
point(0, …) will work in VEX, because VEX SOPs always evaluate all their inputs, as it is very inefficient to evaluate them on demand.)
The workaround for this is to add a spare input to the node, and point that input to the node you want to reference. Then you can use the spare input’s number where you would otherwise use a node path. Spare inputs are pre-cooked before the compiled block runs. This allows multiple threads to refer to the pre-cooked geometry without having to worry about locking.
For expression functions that take a node path string as an argument (for example,
npoints("/obj/sphere1")), you can use an integer instead to refer to an input (for example,
Use a spare input to reference another node
Tips and notes
Spare inputs are also useful for referring to extra geometry inputs in a Wrangle node (beyond the four direct inputs that Wrangle nodes come with).
If you turn on the display flag on a node inside a compiled block, the network will cook normally (uncompiled) to that point. The display flag must be on after the Block End node for the block to compile.
You can turn on a badge to see the nodes you cannot use inside a compiled block.
In the network editor, choose View ▸ Display Options or press D. In the Context Specific Badges tab, set Non-compilable SOP Badge to Normal or Large. Nodes that you cannot use in a compiled block will be marked with the badge.
No support for compiling this node type
Many SOP node types have yet to be converted to work in compiled blocks. See the tips above for how to turn on a badge in the network editor to show the nodes that are not compilable.
If there is a node you would like to use inside a compiled block that is not yet supported, let us know. It will help us decide which nodes to focus on converting.
Attempt to internally reference a compiled node
You cannot reference nodes inside a compiled block by name. Use a spare input and refer to the node using the spare input number instead. See spare inputs.
Violation of strict nesting of blocks. Incompatible For Block Begin encountered while processing Block End. A Block Begin in Fetch Input mode may be needed
See nesting blocks. Look at the nodes mentioned in the error message and see if they have wires crossing into a block that are connected to something other than a block/loop begin node.
To fix this problem, add a loop/compiled block begin node to the incoming wire. For loop begin nodes, if you just want to use the input, set the Method to Fetch Input.