JavaCL is an Object-Oriented API that wraps the OpenCL API.
It makes it easy to do cross-platform GPGPU with Java.

Core classes :

- JavaCL: entry point of JavaCL, useful to list platforms, create contexts.
- CLContext: all the JavaCL data and programs must be created from a context instance.
- CLQueue: OpenCL operations can be executed immediately or queued. A queue instance is however needed even for immediate operations.
- CLMem: base class for buffers and 2D and 3D images.

JavaCL relies on OpenCL4Java, low-level bindings that closely match the OpenCL C API and use JNA+JNAerator for the native access interop.

Here's a very simple vector addition JavaCL program :

    package com.nativelibs4java.opencl.demos.vectoradd;
    import com.nativelibs4java.opencl.*;
    import java.nio.*;

    public class VectorAdd {

        public static void main(String[] args) {
            try {
                FloatBuffer a = FloatBuffer.wrap(new float[] {  1,  2,  3,  4 });
                FloatBuffer b = FloatBuffer.wrap(new float[] { 10, 20, 30, 40 });

                FloatBuffer sum = add(a, b);
                for (int i = 0, n = sum.capacity(); i < n; i++)
                    System.out.println(sum.get(i));
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }

        public static FloatBuffer add(FloatBuffer a, FloatBuffer b) throws CLBuildException {
            int n = a.capacity();

            CLContext context = JavaCL.createBestContext();
            CLQueue queue = context.createDefaultQueue();

            String source =
                "__kernel void addFloats(__global const float* a, __global const float* b, __global float* output)     " +
                "{                                                                                                     " +
                "   int i = get_global_id(0);                                                                          " +
                "   output[i] = a[i] + b[i];                                                                           " +
                "}                                                                                                     ";

            CLKernel kernel = context.createProgram(source).createKernel("addFloats");
            CLFloatBuffer aBuf = context.createFloatBuffer(CLMem.Usage.Input, a, true);
            CLFloatBuffer bBuf = context.createFloatBuffer(CLMem.Usage.Input, b, true);
            CLFloatBuffer outBuf = context.createFloatBuffer(CLMem.Usage.Output, n);
            kernel.setArgs(aBuf, bBuf, outBuf);

            kernel.enqueueNDRange(queue, new int[]{n}, new int[] { 1 });
            queue.finish();

            return outBuf.read(queue);
        }
    }