Coding for Map Lab

Create simple OpenCL filter

In this example we are going to extend the previously created filter to either run in CPU or OpenCL mode of Map Lab.

 

Step 1

Change the compatibility mode to support CPU and OpenCL

[MapLabExtension]
public class ColorStripesFilter : TextureFilter 
{
    public ColorStripesFilter()
    {
        ...
        this.compatibility = FilterInfo.DeviceCompatibility.CPU;
    }
}

 

Step 2

Importing the needed namespaces.

using ch.sycoforge.Imaging;
using ch.sycoforge.Imaging.Unity;
using ch.sycoforge.MapLab;
using ch.sycoforge.Types;
using ch.sycoforge.Util.Attributes;
using cloo.template;
using System.Runtime.Serialization;

 

Step 3

Implementing the actual OpenCL filter.

Every filter supporting OpenCL needs to override the InitializeOpenCLSource() method in order to run on the GPU.

The OpenCL source can easily be inlined by assigning a constant string to the sourceOpenCL variable.

The value assigned to the kernelName variable must match the kernel name used in the source string.

In order to get a kernel running in Map Lab, the first two parameters always have to be ImageInput input and ImageOutput output in exact that order.

    protected override void InitializeOpenCLSource()
    {
        base.InitializeOpenCLSource();

        this.kernelName = "ColorStripesFilter";
        this.sourceOpenCL =
        @"__kernel void ColorStripesFilter(ImageInput input, ImageOutput output, float amplitude,  float frequency, float4 stripeColor)
        {
            int2 coord = GetCoords();

            // Get pixel from input texture
            float4 color = GetPixel(coord);

            // Calculate blend factor
            float f = (1.0f + sin(coord.x * frequency)) * 0.5f * amplitude;  
          
            // Blend final pixel color
            float4 result = Lerp_F4(color, stripeColor, f);

            // Write pixel to output texture    
            SetPixel(coord, result);
        }";
    }

 

Step 4

Parametrize and enqueue kernel.

Every filter supporting OpenCL needs to override the ProcessOpenCL(...) method in order to run on the GPU. The arguments to the EnqueueOpenCL(...) method must be passed in the same order as defined in the kernel function signature.

Primitive argument types (int, float, double, ...) must be wrapped as PrimitiveVariable. Struct argument types (Color, Vector2, Vector3, ...) must be wrapped as StructVariable

    public override void ProcessOpenCL(UnityBitmap input, UnityBitmap output)
    {
        base.ProcessOpenCL(input, output);

        MemoryObject amplitudeVar = new PrimitiveVariable(amplitude);
        MemoryObject frequencyVar = new PrimitiveVariable(frequency);
        MemoryObject stripeColorVar = new StructVariable<FloatColor>(stripeColor);

        EnqueueOpenCL(amplitudeVar, frequencyVar, stripeColorVar);
    }

 

Step 5

Testing the filter.

As this example is for CPU and OpenCL, make sure you have a valid OpenCL device activated in the System Settings.

When now opening the Add-Filter dialog in Map Lab, your custom filter should be listed under the Default category.

 

Full Code:

using ch.sycoforge.Imaging;
using ch.sycoforge.Imaging.Unity;
using ch.sycoforge.MapLab;
using ch.sycoforge.Types;
using ch.sycoforge.Util.Attributes;
using cloo.template;
using System.Runtime.Serialization;

[MapLabExtension]
public class ColorStripesFilter : TextureFilter 
{
    //------------------------------
    // Properties
    //------------------------------
    [Editable(Min = 0f, Max = 1f, Tooltip = "The amplitude of the sine function")]
    public float Amplitude
    {
        get { return amplitude; }
        set { amplitude = value; }
    }

    [Editable(Min = 0f, Max = 10f, Tooltip = "The frequency of the sine function")]
    public float Frequency
    {
        get { return frequency; }
        set { frequency = value; }
    }

    [Editable(Tooltip = "The color of the stripes")]
    public FloatColor StripeColor
    {
        get { return stripeColor; }
        set { stripeColor = value; }
    }

    //------------------------------
    // Fields
    //------------------------------

    [DataMember]
    private float amplitude;

    [DataMember]
    private float frequency;

    [DataMember]
    private FloatColor stripeColor;

    //------------------------------
    // Constructor
    //------------------------------
    public ColorStripesFilter()
    {
        this.filterName = "Color Stripes";
        this.amplitude = 1.0f;
        this.frequency = 1.0f;
        this.stripeColor = FloatColor.red;

        this.compatibility = FilterInfo.DeviceCompatibility.CPU;
    }

    //------------------------------
    // Methods
    //------------------------------

    protected override void ProcessPixel(ProcessPass pass, int x, int y)
    {
        base.ProcessPixel(pass, x, y);

        // Get pixel from input texture
        FloatColor color = input.GetPixel(x, y);

        // Calculate blend factor
        float f = (1.0f + FloatMath.Sin(x * frequency)) * 0.5f * amplitude;

        // Blend final pixel color
        FloatColor result = FloatColor.Lerp(color, stripeColor, f);

        // Write pixel to output texture
        output.SetPixel(x, y, result);
    }

    protected override void InitializeOpenCLSource()
    {
        base.InitializeOpenCLSource();

        kernelName = "ColorStripesFilter";
        sourceOpenCL =
        @"__kernel void ColorStripesFilter(ImageInput input, ImageOutput output, float amplitude,  float frequency, float4 stripeColor)
        {
            int2 coord = GetCoords();

            // Get pixel from input texture
            float4 color = GetPixel(coord);

            // Calculate blend factor
            float f = (1.0f + sin(coord.x * frequency)) * 0.5f * amplitude;  
          
            // Blend final pixel color
            float4 result = Lerp_F4(color, stripeColor, f);

            // Write pixel to output texture    
            SetPixel(coord, result);
        }";
    }

    public override void ProcessOpenCL(UnityBitmap input, UnityBitmap output)
    {
        base.ProcessOpenCL(input, output);

        MemoryObject amplitudeVar = new PrimitiveVariable(amplitude);
        MemoryObject frequencyVar = new PrimitiveVariable(frequency);
        MemoryObject stripeColorVar = new StructVariable<FloatColor>(stripeColor);

        EnqueueOpenCL(amplitudeVar, frequencyVar, stripeColorVar);
    }
}