1 like 0 dislike
There are a lot of cases where I would like to reuse a function in multiple effects in a project. For example, I have an orbit function that will be used in different pkfx files. Is there a way for Popcorn to reference a global function rather than having to copy it into each particle system that needs it?

1 Answer

1 like 0 dislike
Best answer


This isn't possible yet.

One of the reasons is that currently, making custom functions has horrible performance.

See this page here about functions, and the warning: http://wiki.popcornfx.com/index.php/Scripting_language_reference#function

And when I say horrible performance, I mean really really, really, horrible performance. you can count on execution times around 10 times slower. that's for one call of a custom function. if you call custom functions multiple times, it'll be worse. way worse. calling two functions will make it 20 times slower than the initial script, etc..
In v1.9, the editor will actually issue a warning whenever you use a custom function.

We plan to fix this issue in the future, but it won't be in v1.9 (which is about to released)
You really should avoid using custom functions as much as possible for the time being.


Gory details on why it's so awful:
(in case you're interested and want to understand why. otherwise you can just skip all this)

The script execution backend keeps its performance high by using streamed execution. This is similar to how a GPU works. when you write a pixel or vertex shader and multiply two float values together, like "a * b", each shader core runs that shader in parallel for multiple pixels or vertices at once.
NVIDIA GPUs call this a "warp", AMD GPUs call this a "wavefront". NVIDIA can run 32 simultaneous 'threads' (1 thread = 1 pixel or 1 vertex, 1 execution of your shader) that runs the same shader at the same time, and AMDs can run 64.

for this "a * b" expression, the shader core / compute-unit will run a single multiply instruction, but on 32 values at a time (and 64 if it's an AMD GPU) : it'll take the 32 values of 'a' for the 32 pixels, the 32 values of 'b', multiply them together and output the 32 results.

so all the operations you write in your shader, mutliplies, adds, lerps, texture samples, etc.. are in fact instructions that process 32 or 64 values at a time. (If you're familiar with the "SIMD" terminology, CPUs usually have 4-wide float SIMD (8-wide with AVX), Nvidia GPUs are a 32-wide float SIMD architecture, and AMD 64-wide)

Now, the PopcornFX script execution backend works on exactly the same principle, except its 'wavefronts' are larger, and have the size of what we call a particle memory page, which is 1024 particles. (yeah, you can see the PopcornFX script backend as a sort of GPU emulator, with even fatter wavefronts)

so for that same multiply, we'll take the 1024 values of 'a' for the 1024 particles, and multiply them with the 1024 values of 'b', and then move to processing the next operation in the script.

That's how we manage to keep the performance high.

However, this has a downside: whe can't easily do dynamic flow-control. which includes 'if' statements, 'for' loops, and function calls.

To call other script functions, the script backend is forced to explode the wavefront, unbatch everything, and ends up calling the function 1024 times, each call running serially for each particle. Which, obviously, totally kills performance.

The solution to this is well known, it's the exact same technique current-gen GPUs use to do dynamic flow control, so we could do it, it's just we haven't taken the time yet (lots of other work in progress :) )

The most likely way we'll make custom-function's performance acceptable is by inlining them. Also, it'll allow the script compiler to perform better optimizations.

but at the moment, if you care about performance at all, stay away from them.

by Julien (32.8k points)
FYI, calling custom functions is now disabled / not supported anymore, in the latest versions.
We've published an article on the tech blog that talks a bit more about script execution details and its similarities with GPUs :)