What are Compiled Blocks?
Compiled Blocks is a feature that is probably not used by a lot of Houdini users, but it’s quite useful for couple reasons. The reasons are speed and modularity!
First let’s talk about the speed. Compiled blocks allows Houdini to compile node network into single piece of logic (imagine a single node) that works faster and uses less memory (no data duplication for each node). Also, if there are for-loops inside the compiled block, Houdini can run them in parallel (multithreaded) which gives additional speed increase.
While it might not be as important for one-off things, but it’s very important for simulations or building interatctive digital assets (For example, interactive Digital Assets used in game engines). Whenever another artist will use your HDA and spend time tweaking parameters to get what he or she wants, it’s very important for your asset to be as fast as possible.
In my case, while building “yet another cable generator” (Example that I’m showing here is like a 100x simplified version of my full cable generation system), I got more than 4x increase in speed when using compiled blocks. I guess my setup was fast already without compiled blocks ( I tend to use a lot of VEX and do things in an optimal way ). Without compiled blocks, my HDA took around 0,4 seconds to recook after I change an input. While not terrible, it’s quite annoying moving interactive handles and having a 0,4 sec stutter (With even more lag if using through Houdini Engine from Unity or Unreal – due to additional data transfer delay). With compiled blocks, it took around 0,08 sec to update which is almost realtime, interactive speed – much more pleasant to work with.
Compiled blocks are self-isolated pieces (almost like a single node) that work on input data and produce some output. You can connect them to the rest of the netork or better call them indirectly using an “Invoke Compiled Block” node. It means you can call them several times from different places and if you give different input parameters, you will get different outputs. Imagine functions in Python or other programming languages. I like this aspect very much – it allows to build simpler ar clearer node networks with isolated modules which are much easier to change that one giant web of nodes.
Now let’s talk about technical side of setting things up.
Compiled Blocks are a bit complicated at first to understand and imposes certain limitations to the artist – requiring specific workflows in some cases, but once you get it, it’s not that terrible.
There is one big problem with Compiled Blocks – not all Houdini nodes can be used inside them. In every new version since Houdini 16, when the Compiled Blocks first appeared, Sidefx is updating more and more nodes to be supported inside CBs, but still there are nodes – especially older and less frequently used ones, that are not supported. Once that happens – you either loose the benefit of “compilation” of your block, or you are stubborn enough and replace the node with other – compatible nodes. (Fortunately there usually is multiple ways to achieve the same thing in Houdini using different nodes).
Main rule of Compiled Blocks is that nodes inside the block can’t access nodes outside the block – that is because Houdini must always know what data will be needed for CB to execute. What you can do is use CB input nodes – there can be several input nodes that you can wire up or send data via the named parameter when using Invoke Compiled Block node.
Another limitation is that you can’t reference other nodes directly via parameter expressions. What you must do is use Spare Inputs (If you don’t know what spare inputs are, you should read about them in documentation – in short it is a nice way to reference nodes in expressions without typing paths). This allows Houdini to know which nodes will be accessed in expressions before compiling the block.
While you can reference other node parameters outside Compiled Block, I would not recommend doing so, because it breaks the “self sufficiency” rule of the CB. What I recommend is saving all required parameters as detail attributes on one of the CB inputs.
At the end, these limitations actually teach you to create better designed and better organized setups which are easier to maintain compared to large “spagetti” networks with million interconnected parameters and expressions.
Multi threaded Loops
Another interesting case is when using for-loops inside Compiled Blocks. There is a checkbox “Multithread when compiled” that greatly speeds up loop execution when inside CB. But doing so imposes even more limitations as you can’t access any nodes outside the loop – that is because loop must be compiled as a single self sufficient block. For eaxampe, you can’t read detail attributes from any nodes outside the for-loop ( incuding CB input nodes ). What you must do is to copy all needed detail attributes to a for-loop input node before entering the loop and just read from there. I don’t ike this approach much, but I have not figured out another way yet. So let me know if there is a better way.
All these limitations mean that a little more planning is required to create a valid setup, which might not be good while doing quick prototyping. But if you are creating a tool where performance is important, using Compiled Blocks is a must!