Kernel Debugging

In this section is some advice on how to debug a kernel which crashes or doesn’t work as intended.

Safety Rails

Safety rails is the best way prevent and debug crashes inside Blinkscript during development. When the safety rails option is enabled the generated code is instrumented with safety checks to catch and prevent out-of-bounds memory accesses. It is recommended to enable safety rails at the start or during development and then disable once complete to benefit from the more performant generated code without the additional safety checks.

Introduced in Nuke 16.0

Enabling Safety Rails

To enable Safety Rails, tick the Safety Rails box in the BlinkScript Kernel Parameters tab.

../_images/safety-rails-toggle.png

Terminal Output

Attempts to access out-of-bounds memory will be caught, a safe value will be used in its place and the offending access is printed to the terminal.

../_images/safety-rails-output.png

Interpreting the output

The output from an out-of-bounds access may change based on the offending access.

Subscripts in process()

Subscripting an expression outside of its valid bounds will trigger a safety rails print-out.

int indexOOB = -8000;
float arr[3] = {1.0f, 2.0f, 3.0f};
arr[indexOOB] = 4.0f;

float4 f4 = 3;
f4[indexOOB] = 5;

float3x3 mtx3 = {
  1.0f, 2.0f, 3.0f,
  4.0f, 5.0f, 6.0f,
  7.0f, 8.0f, 9.0f};
mtx3[indexOOB][indexOOB] = 5;

In the process() function it will first specify the point in the iteration space the out-of-bounds access occurred. Then it will report the offending expression with the value that triggerred the invalid access. If the subscripted expression is a variable it will report the name of the variable, otherwise it will report the type of the expression which was accessed.

../_images/indexOOB-output.png

Subscripts in init()

Invalid accesses are reported in the same way in init() as in process() except there are no parameters for the iteration space.

../_images/indexOOB-output-init.png

Image accesses in process()

When reading from or writing to images out-of-bounds the point in the iteration space of the access is reported along side the x, y and component (z) values of the individual offending access. To avoid crashing or early termination of the kernel the invalid access is replaced with an access to the very first component i.e. (x=0, y=0, z=0).

../_images/safety-rails-output.png

Output limits

The total outputs are limited per device and kernel run to prevent an error prone kernel from locking up Nuke with print-outs.

../_images/safety-rails-output-limited.png

Safety Rails Example

Here is a basic example of how to use safety rails during Blinkscript development.

Mismatched Channels

Nuke crashed after setting a Blinkscript node with the following simple copy kernel and connecting up the input and output.

kernel Copy : ImageComputationKernel<eComponentWise> {
  Image<eRead, eAccessPoint> src;
  Image<eWrite, eAccessPoint> dst;

  void process(int3 pos) {
    dst() = src();
  }
};

When reloading the script safety rails was turned on before connecting up the input and output. The output indicated many invalid accesses of the src image were happening along the 4th component corresponding to the alpha channel.

../_images/safety-rails-output.png

Double checking the inputs and outputs in the kernel parameters it was discovered that the alpha channel is unselected for the src image.

../_images/mismatched-inputs.png

Ticking the alpha channel before connecting up and recompiling the kernel runs it without any crashes or print-outs. This indicates that the kernel has no more out-of-bounds accesses and is safe to disable the safety rails option for more performant scripts.

../_images/matched-inputs.png

The println function

The println(int x, int y, args...) call is a print function in Blink used to aid in debugging Blinkscript kernels. The function is only available in the process() kernel method. The output of the println call will be visible in the Nuke terminal.

The first two x and y arguments are the position in the iteration space to print. Optionally, any number of args... may be passed in afterwards to be printed at the position. The args... are concatenated together after the iteration position and printed with a line separator. See the Println Specification Kernel for some examples of how to use it.

Introduced in Nuke 16.0

Printing at points in the iteration space

Expressions passed into the first two arguments of println are the x and y position of the iteration space to print from. If a specific pixel or section of the output image is incorrect then this is a useful way to check the intermediary variables are correct for a given section of the iteration space.

/// Arbitrary input to x and y position.
// (5, 5):
println(5, 5);
// Will print for the range of ([0..4], [0..4]):
println(int(min(pos.x, 4)), int(min(pos.y, 4)));

Warning

An unconditional usage of println(pos.x, pos.y) will print out for every point in the iteration space. It’s recommended to avoid doing this as it will print once for every pixel of the iteration space. A 4k image will have roughly 8 million print-outs and will tank Nuke’s performance until it is finished.

Printing variables

An arbitrary mix of strings and variables may be printed out to aid debugging.

/// Mix printing of strings with variables to check their values.
// (0, 0): int i = 7
int i = 7;
println(0, 0, "int i = ", i);
// (0, 0): float f = 1.500000
float f = 1.5f;
println(0, 0, "float f = ", f);
// (0, 0): int3 i3 = int3(2, 3, 4)
int3 i3 = int3(2, 3, 4);
println(0, 0, "int3 i3 = ", i3);
// (0, 0): float4 f4 = float4(3.500000, 4.000000, 5.500000, 6.000000)
float4 f4 = float4(3.5f, 4, 5.5f, 6);
println(0, 0, "float4 f4 = ", f4);

Concatenating multiple outputs

Any number of arguments or expressions may be passed in to be concatenated into a single string.

/// Can easily chain any amount of prints together
/// concatenating arguments separated by a comma.
// (0, 0): src() = float4(2.041630, 2.570306, 2.788087, 2.944974), int i = 7
println(0, 0, "src() = ", src() + 2, ", int i = ", i);