Synthesisers
Procedure evaluation is performed in the background across multiple threads (configurable) to decouple the process from any frame locked update loop. This removes generation cost/time from the factors that affect the design of your procedural systems and you can largely ‘just wait’ for your content to appear. This also helps scalability by providing another axis for performance to scale with platform or hardware capabilities. i.e. you would expect a slower machine to take longer to ‘load’ content.
Once triggered, a generation run is queued up until it can be assigned to one of the procedural synthesiser instances present on the dedicated background threads. Each of these is responsible for evaluating the procedure outputs, expanding the sub-procedures, building the operator graph, walking through all the required IO points, and calling all of the actual operator code.
Buffers
The procedure graph expansion and data allocation all occurs within a fixed (configurable) synthesis buffer. This is reset at the start of a run and then allocated from sequentially as needed by the operator/procedure control structures, output values, or the generated content. If the buffer gets used up before the run completes then the procedure is considered to have failed and returns an error message to this effect. Any content accumulated up until this point is usually still passed to the engine, but there are various ways this can appear broken (missing content, bad materials, etc).
Stack
The synthesis process utilises the thread’s stack when evaluating the chain of connections back from the procedure output. For an operator output to be evaluated, the internal implementation (a function) context is held on the stack while all required inputs are evaluated. Since any input can be connected to still more outputs, this can amount to a long chain of ‘in progress’ function calls which occupy more and more stack. Whilst the stack available is large (1MB) there are still situations where you can exceed the available stack space. This is caught and the procedure failed with an error. Accidentally recursing infinitely is a common way to trigger this, but even properly executing procedures with many levels, or that have a high use of recursive techniques can do this too.
Evaluation
Procedures are evaluated from their top-level outputs, in order (top to bottom). Currently only content outputs are accumulated and returned for use in engine. All paths back through the procedure hierarchy are evaluated on demand, and only once. Internal procedure and operator inputs are always evaluated in order from top to bottom. Only inputs that are involved in generating the specific output required are evaluated further. This ‘short cutting’ and ‘lazy evaluation’ make many complex procedures very fast to evaluate. For example, the If operator will always evaluate the Control input, but then only evaluate one of the two conditional inputs as dictated by the value the Control input resolved to. One effect of this strategy is that the actual operator evaluation order across all instances of all procedures is not particularly obvious. Multiple evaluation chains can evaluate across many procedures in sequence effectively meaning the same procedure will be ‘visited’ many times. This isn’t normally relevant to building and understanding the effect of a procedural system and what it generates, and has no influence on its efficiency. Another valuable property of this approach is that, due to the way Lists are implemented, all list inputs are maintained as ‘connections’ to their original source and so will only cause that source to be evaluated if the list entry is actually needed. This means construction of complex ‘structures’ of data into lists to pass around is not as wasteful as it might be if you are only ever using a sub-set of the entries elsewhere in the system.
Mutability
Most simple values (including strings and lists) are immutable, meaning that any output can be re-used in different ways (connected to multiple inputs) yielding the same value for each.
Content connections (i.e. generated geometry, placed objects, and images) are stateful, and hence cannot be treated as immutable. This has some important implications for how you use (and re-use) content within a procedural system. Operations that manipulate content are performed according to the order of evaluation through the procedure graph (see Evaluation above). If you use an earlier content output in a sequence after it has evaluated fully then subsequent operations will be acting on the end result, not the earlier result. This is useful, but can catch you out if you aren’t used to it. It can be quite a powerful technique to re-use images for multiple operations, resetting them before each new set of operations, so as to avoid the large synthesis buffer cost of creating a new one each time.
Error Situations
Apart from a few system errors mentioned above (buffer exhaustion, and stack overflow), many operators can report specific problems with their provided input data. Some examples are: * List index out of range – trying to get a value that isn’t present. * Bad floating point value – some operators will fail if passed infinite, nan, or denormal, value. * Enum/flag values –enum and flag inputs for operators are just integers and so can be set to invalid values. * Merge order – the content merging operators can’t be connected to the same source twice, or any input that has already been evaluated, breaking the implied evaluation=accumulation rules. * Negative size – there are some operators for which negative size values don’t make sense (e.g. image). Errors are currently only reported in the Apparance Editor (Advanced UI mode), and with the engine diagnostics panel open.
Collision
Primitives and complex geometry

Instancing
Configurable, per asset, group, entity, or global
