HDK
|
VEX is an interpreted language which runs in on SIMD virtual machine.
Users can add function to the VEX library by writing a plug-in using the VEX_VexOp class.
In order to specify a plug-in function, you must specify
VEX can be run in with different precision (see VEX_Precision
). At the time, this enum has two values:
VEX_32
: Run in 32 bit precisionVEX_64
: Run in 64 bit precisionThere are typedefs for all the base types used throughout VEX which are templated based on the precision. For example, a callback might look something like:
Please see VEX_PodTypes.h for more detail.
There are multiple constructors for the VEX_VexOp
object. The simplest (and for backward compatibility) just takes 32 bit precision callbacks. When run in 64 bit mode, VEX will wrap these callbacks, automatically casting the data so the callback gets the precision it expects.
The preferred constructor though, takes callbacks for both 32 and 64 bit precision. If the callbacks are templated based on precision, this makes the constructor creation fairly straight-forward:
VEX Signatures are used to describe the return types and arguments to your function. The signature is used by the compiler (vcc) to determine what types of parameters are passed to your function. The signature is specified as a mangled name consisting of two parts separated by a @
character. The first part of the signature string represents the function name, the second, the types of parameters accepted by the function. For example
Where foobar
is the name of the function, and the arguments are specified by the string *FV&IV
.
Each argument is specified by a single character with an optional single character modifier which precedes the letter. The letter is case sensitive and specifies the type of the parameter
Array of POD types use the single character mnemonic for the type preceeded by the open square bracket ([) character. So, they are represented by:
If the type specifier is not prefixed, the parameter is considered read-only. Otherwise, the type specifier may be prefixed by either
Variadic callbacks are indicated by a trailing + token in the signature, indicating that additional arguments may be provided beyond the last concrete argument.
Examples
As seen with the second last example, sometimes VEX may not generate the signature you expect. You can verify your signatures by running
which lists all the functions available in the surface context.
There is an argument in the VEX_VexOp constructor to force the function to take the first write-only argument as a return code. Thus,
will result in the signature: int mread(string, matrix &)
There are three separate callbacks which can be declared for your user function.
VEX_VexOpCallback
).VEX_VexOpInit
)VEX_VexOpCleanup
)The initialization and cleanup functions are used to allocated and free user data for your function. The void pointer returned by the initialization function is passed to the evaluation and cleanup callbacks.
The initialization/cleanup functions are called for each instance of your user function. That is for every time your function is referenced in the VEX code, the initialization function is called. When the code using your custom function is no longer used, the cleanup function is called (one time for each instance). This means that if your function is used two times in a single VEX function, the initialization call will be made two times. This allows you to allocate data per-instance, or to have shared data between instances (using a static/global singleton). For example:
The evaluation callback takes three arguments:
argc
(the number of arguments being passed to your function)argv
(an array of void
*
data which contains pointers to the data for the parametersvoid *
returned by the initialization functionEach void pointer in the argv
[] array should be cast to the data type that you expect. For example:
Array types are provided to the callback function as pointers to UT_Array objects. For example:
String arguments must be modified using the provided stringAlloc()
and stringFree()
functions. For example, strcat()
might be implemented as:
If your function takes variadic arguments, you should use the VEX_VexOpTypedCallback
instead of VEX_VexOpCallback
. This alternative callback provides type information with each argument so you can implement the correct behavior for different types that may be provided to the variadic function.
The VEX evaluation callback currently takes arguments as void pointers. Users are required to cast these pointers to the expected types.
Please use the types defined in VEX_PodTypes.h when performing casts. This will help future-proof your code.
VEX has multiple contexts (i.e. the surface
or vex
contexts). It's possible to limit your VEX function to a specific set of contexts This is done through the context mask in the constructor.
The VEX runtime engine has an internal optimizer which can reduce a function to a constant value (constant folding), or simply remove it altogether (also known as elision). The choice is based on whether the input argument(s) are all constant values, and whether the function's result is used.
If a function returns a value that is not dependent on the input argument(s), or it has a side-effect on some internal state, the optimizer can be instructed to not attempt to optimize the function call out.
For example, if a function returns the system time:
In this case, VEX cannot automatically determine that this function may return different values at different call times. In that case, set the optimize level for this function to 1 (see the optimize_level
argument to the VEX_VexOp
constructor).
If the function modifies some internal state, and should not be removed at all from the optimized code, the optimize level should be set to 0.
VEX/VEX_Example.C has examples
VEX/VEX_Ops.C has examples
Creating VEX plug-ins using the HDK allows you to use most classes and functions available in the HDK. When compiling plugins for Mantra, there are some libraries which mantra doesn't link against, so if your plug-in needs to access SOPs, you may have to wrangle the link line so that the plug-in links against the appropriate libraries.
The .so file still needs to be installed in the VEXdso file (see below).
Houdini will automatically search for the newVEXOp()
function in all plugins found in the HOUDINI_VEX_DSO_PATH
. You can run hconfig -ap
to see the path definition.
The easiest way to test to see whether your plugin is being loaded is to run vcc -X cvex
which will print out all the functions available in the CVEX context.
You can also set HOUDINI_DSO_ERROR
to see any dynamic loading errors. See hconfig -h HOUDINI_DSO_ERROR
for more information.
Houdini's editor can automatically display help for functions as users type text. Houdini uses the help files to display this information.
When writing a plugin, Houdini will search for the help text file in /help/vex/functions/functionname.txt
If you're building a package, the help file should be located in <package_root>/help/vex/functions/functionname.txt
Please see https://www.sidefx.com/docs/houdini/help/format.html for the formatting markup reference.