Core Image Kernel Output Formats

When writing custom kernels in the Core Image Kernel Language there are easily-accessible techniques to greatly reduce the memory footprint of your composition, improving performance and allowing Core Image to render complex filter chains without errors on a larger number of systems.

When One Channel Is All You Need

Custom kernels are often used to compute intermediate results, fed to subsequent filters whose job is to produce the final output.

If your algorithm only needs one or two channels to store its results, you can instruct Core Image to use a more approriate backing surface for the output.

Take the following Color Kernel as an example. It simply copies the red channel to all color outputs, and passes the alpha value as-is:

kernel vec4 myFilter(__sample s) {
    return s.rrra;
}

While your own kernel will undoubtedly perform a more interesting computation, there is an obvious conclusion to be made: using a 4-channel half- or full-float surface to store a single value is a huge waste of space. You can add a simple attribute to your source code to have its output stored in a single-channel, full-float surface instead:

kernel vec4 myFilter(__sample s) __attribute__((outputFormat(kCIFormatRf))) {
    return s.rrra;
}

If your algorithm is happy with using a half-float surface, you can further tweak the attribute, return type and argument to the kernel to use half-float precision:

kernel hvec4 myFilter(sample_h s) __attribute__((outputFormat(kCIFormatRh))) {
    return s.rrra;
}

Better still, when a saturated 8-bit integer is enough, you can use the thriftiest of them all, kCIFormatR8, as the output format:

kernel hvec4 myFilter(sample_h s) __attribute__((outputFormat(kCIFormatR8))) {
    return s.rrra;
}

When testing the above kernel in FxCore, you should readily notice that the output only renders its red channel:


While the list of possible arguments to outputFormat(…) is long, the values that actually seem to work are only a handful: kCIFormatR8, kCIFormatRh, kCIFormatRf, kCIFormatRG8, kCIFormatRGh, kCIFormatRGf.

Or Maybe Two?

Your eyes do not betray you: the short list of output formats supported by Core Image includes kCIFormatRG8, kCIFormatRGh, kCIFormatRGf, which means you can take advantage of this performance optimization even when you need two channels worth of information.

It is entirely possible that Core Image supports more interesting combinations of output formats and types, but this is the best of our knowledge based on our exposure to the framework. Let us know what else you find out!

Conclusions

Limiting your kernel to a single- or double-channel output is extremely helpful in cutting the memory requirements of your filter chains. A single kCIFormatR8 surface uses only 8 bits per pixel, a far cry from the 128 bits per pixels required by a full float surface with four components. This could be the difference between letting your composition render on low-end systems and maintain acceptable performance.