Blink Language Features¶
The Blink language is based on C++ with specialisations to support optimized image-processing on both CPU and GPUs.
Blink Types¶
Blink supports:
Scalar types:
int
,float
, etc.Vector types:
float4
,int3
, etc.Matrix types:
float3x3
,float4x4
.Fixed-size arrays:
float4 arr[16];
.
Kernel definition¶
Blink kernels are described with a kernel
definition.
kernel *kernel-name* : ImageComputationKernel<*granularity*> {
*image-specifications*
*optional-parameter-block*
*optional-local-block*
*optional-user-functions*
*process-function*
};
The granularity specifies whether the kernel’s process()
function will be
ran for each pixel or for each component. See Kernel Granularity for details.
The sections in the kernel block are listed in their traditional order. It is recommended to follow this order but not strictly necessary.
Image input and output definitions¶
Image specifications describe the kernels input and output images as well as
information about they will be accessed from the process()
function.
Each image specification has the following parts:
Image<*read-spec* [, *access-pattern* [, *edge-method*]]> *image-name*;
See Image Specification.
Ranged-access images should to have their ranges set in the init()
special
function. See The init() Function.
Note
Blink kernels run in Nuke’s BlinkScript node can only have exactly one output image.
Kernel parameters and locals¶
Kernel parameters can be specified in the optional-parameter-block. Parameters are set before launching the kernel and can be used to alter the behaviour of the kernel without recompilation.
param:
*type* *name*;
...
void define() {
defineParam(*name*, "User-friendly name", *default-value* [, *options*]);
...
}
The special function define()
can be used to set user-friendly names,
default values and other information for kernel parameters by calling
defineParam()
. See The define() Function.
Local variables can be specified in the optional-local-block. The init()
function should be written so that it initialises each local variable. init()
will be called once, before any calls to process()
. See The init() Function.
local:
*type* *name*;
...
void init() {
*name* = *initialiser*;
...
}
Locals should only be assigned to in the init()
function. Locals can be read
from process()
and user-functions but cannot be assigned to.
Parameters and locals can be scalar, vector or matrix types and arrays of those types. See Data Types.
Special Kernel functions¶
The define()
, init()
and process()
functions are special kernel
functions. These names are reserved and can’t be used for other things (e.g.
image names, local or param variables, function-scope variables, etc).
User-defined functions¶
User functions can be defined inside the Blink kernel specification and can
be called from the init()
and process()
functions.
User functions can access params, locals and Image properties (e.g. bounds).
Image accessors can be used in user functions, but only if they are called
from the process()
function.
kernel UserFunctionExample : ImageComputationKernel<ePixelWise>
{
Image<eWrite, eAccessPoint> dst;
param:
int borderWidth;
void define() {
defineParam(borderWidth, "Border width", 5, eParamProxyScale);
}
local:
int2 middle; // Position of the middle pixel.
int4 innerBox; // Area inside the border.
int2 calcDstMiddle() {
int midx = dst.bounds.x1 + dst.bounds.x2;
int midy = dst.bounds.y1 + dst.bounds.y2;
return int2(midx, midy) / 2;
}
int4 calcInnerBox() {
return int4(dst.bounds.x1 + borderWidth,
dst.bounds.y1 + borderWidth,
dst.bounds.x2 - borderWidth,
dst.bounds.y2 - borderWidth);
}
void init() {
middle = calcDstMiddle();
innerBox = calcInnerBox();
}
void process(int2 pos) {
if (pos.x <= innerBox.x || pos.y <= innerBox.y ||
pos.x >= innerBox.z || pos.y >= innerBox.w) {
dst() = float4(1,1,1,0);
}
else if (pos.x < middle.x && pos.y < middle.y) {
dst() = float4(1,0,0,0);
}
else if (pos.x >= middle.x && pos.y < middle.y) {
dst() = float4(0,1,0,0);
}
else if (pos.x < middle.x && pos.y >= middle.y) {
dst() = float4(0,0,1,0);
}
else if (pos.x >= middle.x && pos.y >= middle.y) {
dst() = float4(1,0,1,0);
}
}
};
Functions can take array arguments. See arrays.
There are some restrictions on calling user functions from the Blink kernel special functions:
The
define()
function cannot call any user-defined functions.The
init()
function can call user-defined functions, as long as they do not include accessing Images.The
process()
function can call user-defined functions, including those that access Images.
Note
Recursive functions are not supported. User-defined functions cannot call themselves, or have a ‘cycle’ of calls through other functions.
Flow Control¶
Conditional statements and expressions¶
Blink has if
and if else
statements, and the ternary-operator ?:
in expressions.
kernel FlowControlIfElseExample : ImageComputationKernel<eComponentWise>
{
Image<eWrite, eAccessPoint> dst;
void process(int2 pos) {
bool leftedge = pos.x == 0 ? 1 : 0;
bool topedge = pos.y == 0 ? 1 : 0;
if (leftedge) {
dst() = 1.0f;
} else if (topedge) {
dst() = 0.5f;
} else {
dst() = 0.0f;
}
}
};
Note
switch
statements are not supported.
Loop statements¶
Blink has for
, while
loops.
kernel FlowControlLoopExample : ImageComputationKernel<ePixelWise>
{
Image<eRead, eAccessPoint> src;
Image<eWrite, eAccessPoint> dst;
void process(int2 pos) {
float sum = 0.0f;
for (int i = 0; i < src.kComps; i++) {
sum += src(i);
}
while(sum > 0.0f) {
sum -= 1.0f;
}
dst(0) = sum;
}
};
Note
do-while
loops are not supported.
Jump statements¶
Loops and functions can be exited early with jump-statements, return
break
and continue
.
kernel FlowControlLoopJumpsExample : ImageComputationKernel<ePixelWise>
{
Image<eRead, eAccessPoint> src;
Image<eWrite, eAccessPoint> dst;
void process(int2 pos) {
if (pos.x == 0 && pos.y == 0) {
return;
}
float sum = 0.0f;
for (int i = 0; i < src.kComps; i++) {
if (src(i) == 0.0f) {
continue;
}
if (sum > 1.0f) {
break;
}
sum += src(i);
}
dst(0) = sum;
}
};
Note
Labels and goto
statements are not supported.
Expressions and operators¶
Mixing vector and scalar types¶
Expressions can include operators that use a mix of types. The type of the result is whichever type has the highest rank.
float
has a higher rank than int
and other scalar types, and vector-types have higher rank than scalar types.
int a = 1 + 1; // int + int -> int
float b = 1.0f + 1; // float + int -> float
int2 c = int2(0, 1) + 42; // int2 + int -> int2
float2 d = float2(0.1f, 0.2f) + 42; // float2 + int -> float2
float2 e = 0.1f + int2(0); // float + int2 -> float2
Arithmetic and bitwise operators¶
Name |
Operator |
Types |
---|---|---|
Addition |
|
scalars and vectors |
Subtraction |
|
scalars and vectors |
Multiplication |
|
scalars and vectors |
Division |
|
scalars and vectors |
Unary minus |
|
scalars and vectors |
Modulo |
|
integer scalars |
Pre-increment |
|
scalars |
Post-increment |
|
scalars |
Pre-decrement |
|
scalars |
Post-decrement |
|
scalars |
Bitwise NOT |
|
bool and integer scalars |
Bitwise AND |
|
bool and integer scalars |
Bitwise OR |
|
bool and integer scalars |
Bitwise XOR |
|
bool and integer scalars |
Bitwise left-shift |
|
bool and integer scalars |
Bitwise right-shift |
|
bool and integer scalars |
Comparison operators¶
Name |
Operator |
Types |
---|---|---|
Equals |
|
scalars |
Not equals |
|
scalars |
Greater than |
|
scalars |
Less than |
|
scalars |
Greater or equal |
|
scalars |
Less or equal |
|
scalars |
Short-circuiting logical operators¶
Blinks logical operators are short circuiting like C and C++. In a && b
expressions, a
is evaluated first and only if true is b
evaluated.
In a || b
expressions, a
is evaluated first and only if false is
b
evaluated.
Name |
Operator |
Types |
---|---|---|
Logical NOT |
|
scalars |
Logical AND |
|
scalars |
Logical OR |
|
scalars |
Assignment operators¶
Name |
Operator |
Types |
---|---|---|
Assignment |
|
scalars and vectors |
Add-assign |
|
scalars and vectors |
Sub-assign |
|
scalars and vectors |
Multiply-assign |
|
scalars and vectors |
Divide-assign |
|
scalars and vectors |
Assignments between of different scalar types and vectors of the same length are generally allowed and will do a suitable conversion.
Assignments to vectors of different lengths is not allowed.
int x = 2;
int3 i3 = int3(0, 1, 2);
float f = 3.0f;
float2 f2 = float2(0.3f, 1.7f);
x = 0.1f; // converts to int
f = 1; // converts to float
f2 = 42; // converts to float2, with 42.0f in both elements
i3 = float3(1.1f, 2.2f, 3.3f); // converts to int3
// f2 = i3; // not allowed: vectors are of different lengths
Other operators¶
Name |
Operator |
Types |
---|---|---|
Subscript |
|
vectors, matrices, arrays |
Function calls |
|
|
Ternary conditional |
|
|
Swizzling |
|
vectors |
Preprocessor directives¶
Some C preprocessor features are available in Blink to support macro-replacement and conditional compilation.
Macro replacement directives:
#define
,#undef
.Conditional compilation directives:
#ifdef
,#else
#endif
, etc.
Restricted C++ Features¶
Many C++ features are not supported by Blink. These restrictions are necessary so Blink kernels can be compiled and optimized for a wide-range of CPU and GPU targets.
Blink kernels that use any of these features can cause undefined behaviour. Compiling a kernel that uses restricted features may succeed for some targets, but may produce unexpected results or crash.
The following C++ common features are not supported:
Pointers
Pointer types and pointer variables.
Pointer arithmetic and related operators.
Enums,
switch
-statements anddo-while
loops.User-defined
class
,struct
orunion
types.The C++ standard library is not supported.
Other less common C++ features are not supported:
Templates.
Recursive functions.
Variable length arrays are not supported. All arrays must be fixed-length.
Virtual functions.
Lambda functions and function pointers.
Type qualifies (except
const
) likestatic
,volatile
andthread_local
, etc.C++ style casts
static_cast<>
,dynamic_cast<>
,reinterpret_cast<>
.Cases where a C or C++ style cast would be useful can be replaced with a conversion-constructor call for the target type.
Exception handling.
goto
and labels.new
,delete
operators and other dynamic-memory allocation functions.Function and variable attributes, e.g.
[[nodiscard]]
,noexcept
.C++ type-size and type-id operators:
sizeof
,typeid
.typedef
andusing
type-aliases.Some Preprocessor directives are not supported:
File inclusion:
#include
,#once
, etc.Implementation specific pragmas:
#pragma
File name and line reference directives are unsupported:
#line
,#file
, etc.